Search code examples
javajshell

In JShell, How to evaluate whole java code?


I'm using the JShell API to run Java code. However, I got an error when I run a whole code.

For example:

import jdk.jshell.JShell;

var code= """
          void hello(){
              System.out.println("Hello world");
          }
                                        
          hello();
          """;

JShell shell = JShell.create();
List<SnippetEvent> snippets = shell.eval(code);

After evaluation, I see the error in output snippet.

cannot find symbol
  symbol:   method hello()
  location: class 

What is the problem here ?


Solution

  • eval is not designed to handle code like that. The code you showed is actually 2 "complete snippets" that needs to be broken up before you pass into eval. From the docs:

    The input should be exactly one complete snippet of source code, that is, one expression, statement, variable declaration, method declaration, class declaration, or import. To break arbitrary input into individual complete snippets, use SourceCodeAnalysis.analyzeCompletion(String).

    The "real" JShell command line program probably uses the SourceCodeAnalysis.analyzeCompletion(String) method to break up your input into two complete snippets, and passes each of them into eval.

    Here is how to use SourceCodeAnalysis.analyzeCompletion(String):

    var code = "...";
    JShell jshell = JShell.create();
    SourceCodeAnalysis.CompletionInfo completionInfo = jshell.sourceCodeAnalysis().analyzeCompletion(code);
    
    // this is the shortest complete code
    System.out.println(completionInfo.source());
    
    // this is what remains after removing the shortest complete code
    // you'd probably want to analyze the completion of this recursively
    System.out.println(completionInfo.remaining());
    

    hello(); fails to compile for the same reason that

    class Foo {
        void hello() {
    
        }
    
        hello();
    }
    

    doesn't compile.

    If you look at the diagnostics:

    JShell shell = JShell.create();
    List<SnippetEvent> events = shell.eval(code);
    shell.diagnostics(events.get(0).snippet()).forEach(x -> System.out.println(x.getMessage(Locale.ENGLISH)));
    

    You get:

    Invalid method declaration; return type required

    That is the exact error message you get if you put a method call at class level.

    Anyway, to make your code work, simply eval the method declaration first, then eval the hello(); call.