0

In Java, is there a way to store, edit, convert, print, access, evaluate and compare blocks of code (possibly user entered), while also being able to execute them?

An example for where this would be useful is if someone codes an software that is designed to teach people how to code, where the user would input code to the system, and the program would check if the user developed code is

I'm looking for something like this:

CodeBlock line20And21 = `String x = "hello"; System.out.println(x);`; // stores a block of code
line20And21.replace("ln",""); //edits the block of code
System.out.println(line20And21.toString()); // converts/prints the block of code
CodeBlock usersCode = Scanner.nextCodeBlock(); // accesses block of code
if(! line20And21.wouldThrowError()); // evaluates block of code
    if(line20And21.wouldDoTheSameThingAs(line18And19)) // compares blocks of code
        line20And21.execute(); // executes the block of code

The code I would be using is of course much more complicated than just defining a String and printing it, but I'm sure the idea would be the same. I really appriciate any help with this. Thanks!

Ankit
  • 107
  • 7
  • 1
    It is possible, yes. But not as easy as you probably think it is. If you want to execute Java Code dynamically you have to compile it using javac, load the resulting class using a new ClassLoader and finally execute the method using the loaded class. There may be libraries that handle that for you. See https://stackoverflow.com/questions/3447359/how-to-provide-an-interface-to-javacompiler-when-compiling-a-source-file-dynamic – Felix Jul 17 '20 at 21:39

3 Answers3

2

Since Java 9, Java includes a shell to evaluate snippets, called JShell. JShell is programatically available via jdk.shell.

First you have to create an instance of the JShell via JShell js = JShell.create().
Evaluating a String as Java code (a so called code Snippet) is done via js.eval("System.out.println(/"Hello World/")");, which returns a list of SnippetEvents you can inspect to find out what effect the execution of the code snippet had.

Since the code is stored as a String, you can edit it as you would edit any String.

Here is an example of JShell taking user inputted code and storing/evaluating it, taken from the official java docs, where code is read as string from stdin and executed:

 import java.io.ByteArrayInputStream;
 import java.io.Console;
 import java.util.List;
 import jdk.jshell.*;
 import jdk.jshell.Snippet.Status;

 class ExampleJShell {
     public static void main(String[] args) {
         Console console = System.console();
         try (JShell js = JShell.create()) {
             do {
                 System.out.print("Enter some Java code: ");
                 String input = console.readLine();
                 if (input == null) {
                     break;
                 }
                 List<SnippetEvent> events = js.eval(input);
                 for (SnippetEvent e : events) {
                     StringBuilder sb = new StringBuilder();
                     if (e.causeSnippet == null) {
                         //  We have a snippet creation event
                         switch (e.status) {
                             case VALID:
                                 sb.append("Successful ");
                                 break;
                             case RECOVERABLE_DEFINED:
                                 sb.append("With unresolved references ");
                                 break;
                             case RECOVERABLE_NOT_DEFINED:
                                 sb.append("Possibly reparable, failed  ");
                                 break;
                             case REJECTED:
                                 sb.append("Failed ");
                                 break;
                         }
                         if (e.previousStatus == Status.NONEXISTENT) {
                             sb.append("addition");
                         } else {
                             sb.append("modification");
                         }
                         sb.append(" of ");
                         sb.append(e.snippet.source());
                         System.out.println(sb);
                         if (e.value != null) {
                             System.out.printf("Value is: %s\n", e.value);
                         }
                         System.out.flush();
                     }
                 }
             } while (true);
         }
         System.out.println("\nGoodbye");
     }
 }
Polygnome
  • 7,639
  • 2
  • 37
  • 57
  • Hi thanks for the answer, but this doesn't really answer my question. I specifically asked how to do a list of things and while I understand you are saying I can use the JShell package to do so, I still don't understand how to do so. I would really apriciat if you can structure your answer like @thatotherguy 's answer. – Ankit Jul 19 '20 at 20:41
  • @Ankit This answer shows how to store code as a string and how to evaluate it. The question of whether to strings of code are actually equal is a surprisingly difficult problem and a question of current, active open research and undecideable in general (Rice's theorem, Halting Problem), unless you opt to do a literal string compare, and its feasibility depends *a lot* of your notion of equality. Printing this code boils down to printing the string. Editing the code is just string manipulation. Likewise, everything else you asked boils down to the very basic of programming. – Polygnome Jul 19 '20 at 22:55
  • Hi. I read through your answer multiple times and wasn't able to understand it. I think I may have figured out the evaluating portion and String storing portion. I'm adding an edit to make your answer more clear. – Ankit Jul 20 '20 at 04:35
1

You can do things like this with BeanShell, a Java interpreter written in Java:

import bsh.Interpreter;

class Test {
  public static void main(String[] args) throws Exception {
    String code = "String x = \"hello\"; System.out.println(x);";
    String newCode = code.replace("ln", "");

    System.out.println("Here's the result of running: " +newCode);

    Interpreter p = new Interpreter();
    p.eval(newCode);
  }
}

If compiled and built with the right dependencies, you can evaluate the snippet:

$ javac -cp bsh-2.0b4.jar:. Test.java && java -cp bsh-2.0b4.jar:. Test
Here's the result of running: String x = "hello"; System.out.print(x);
hello$

You can run the code and get its output or return values, or whether it throws an exception. Sandboxing and comparing the output of two snippets is up to you.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 2
    Java has JShell since Java 9 to do this. No dependency needed. – Polygnome Jul 17 '20 at 22:48
  • Hi, thanks for the answer. This is really straightforard and directly answers my question. I tried importing bsh.Interpreter and it wouldn't work. Do I have to download the bsh package or something, or is this only accessible on outdated versions of java? – Ankit Jul 19 '20 at 20:37
  • It depends on the third party Beanshell package (bsh-2.0b4.jar here). JShell sounds like a more modern version of the same, and it ships with JDK9 – that other guy Jul 19 '20 at 22:00
-2

Mostly: No.

Java code goes through a compilation step, code needs to be in methods (which need to be in types), and the reasons for this take quite a while to explain. The compiler does not need to be there at runtime (and often isn't).

So, if you really want to do this:

  1. Code has to be 'complete', including a package statement, a class declaration, a method, etc.
  2. You need to ship the compiler with the app. For javac, that's tricky (GPL); you could ship ecj (eclipse's compiler) which is MIT licensed. It's quite a dep.
  3. You can then 'store' code as strings.
  4. ecj can then turn this into bytecode for you, but you'll have quite a time managing the classpath properly to make this code compile correctly.
  5. You can then dynamically load this bytecode by using a classloader which has a learning curve of a few days all by itself.

In addition, looking at code without executing it to determine if it would throw an exception or not is literally impossible - this is called the halting problem. There exists proof that it is unsolvable. You can't go faster than light. You can't determine from arbitrary code written in a 'turing complete' language (and java is turing complete) if it halts or not (or in this case, if it throws or not).

Similar rules apply to 'would this do the same thing as'.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Thanks for this answer, but "would do the same thing as" or "would throw exception" could avoid the Halting Problem by testing the code, similar to how "Try, Catch, Finally" works – Ankit Jul 17 '20 at 22:08
  • You know JShell exists, right? And that its programatically accessible via `jdk.jshell`? – Polygnome Jul 17 '20 at 22:47