eobermuhlner / jshell-scriptengine

JShell script engine for Java (JSR-223 compatible)
MIT License
32 stars 8 forks source link

return value not always returned #7

Open sdyura opened 11 months ago

sdyura commented 11 months ago

here is some code:


private static void runJShellBindingExample2() {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("jshell");

            String script = "" +
                    "if (inputA != null && inputB != null) {" +
                    "   System.out.println(\"Input A: \" + inputA);" +
                    "   System.out.println(\"Input B: \" + inputB);" +
                    "   var output = inputA + \" \" + inputB;" +
                    "   System.out.println(\"GOING TO RETURN: \" + output);" +
                    "   return output;" +
                    "}" +
                    "return \"\"";

            engine.put("inputA", "hello");
            engine.put("inputB", "world");

            Object result = engine.eval(script);
            System.out.println("runJShellBindingExample2 Result: " + result);

            Object output = engine.get("output");
            System.out.println("Output Variable: " + output);

        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

when i run it i get this back:

Input A: hello
Input B: world
GOING TO RETURN: hello world
runJShellBindingExample2 Result: 
Output Variable: null

even though i would expect to get:

Input A: hello
Input B: world
GOING TO RETURN: hello world
runJShellBindingExample2 Result: **hello world**
Output Variable: null

(tested on version 1.1.0)

eobermuhlner commented 11 months ago

There are two issues in your code.

1) The local variable output is only valid in the scope of the if and will not be exported to the engine caller. 2) The return statement does not work the same way in jshellas it does in java. It will not end the execution of the script.

You can solve both issues by writing your snippet like this:

    private static void runJShellBindingExample2() {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("jshell");

            String script = "" +
                    "var output = \"\";" +
                    "if (inputA != null && inputB != null) {" +
                    "   System.out.println(\"Input A: \" + inputA);" +
                    "   System.out.println(\"Input B: \" + inputB);" +
                    "   output = inputA + \" \" + inputB;" +
                    "   System.out.println(\"OUTPUT HAS BEEN SET: \" + output);" +
                    "}" +
                    "output";

            engine.put("inputA", "hello");
            engine.put("inputB", "world");

            Object result = engine.eval(script);
            System.out.println("runJShellBindingExample2 Result: " + result);

            Object output = engine.get("output");
            System.out.println("Output Variable: " + output);

        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

This will give the following output in the console:

Input A: hello
Input B: world
OUTPUT HAS BEEN SET: hello world
runJShellBindingExample2 Result: hello world
Output Variable: hello world
sdyura commented 10 months ago

sorry about my including the printout of the "output" variable, thats was just copied over from another example and you can ignore it

the issue is the return statement, if i call return with one value, i do not expect the 2nd return to also get called in java good practice i have always been told to return early when i have the result i need and returns not working makes this impossible.

I am trying to add scripting in a familiar language to my app, but your second point:

The return statement does not work the same way in jshellas it does in java. It will not end the execution of the script.

is very confusing, everything else works like java, but return does not end execution? this seems to just be asking for things to go wrong and mistakes to slip in.

why can this not be fixed so that return DOES end the script. what would be the harm in that?

it seems the first return statement literally does nothing, thats not expected.

even if for whatever reason you want to run the script AFTER the first return, it would make much more sense for: Object result = engine.eval(script); to give me the value of the FIRST return statement, not the last.

so if the code does execute over several return statements, only the first one is kept, and any subsequent returns can be ignored. so that the eval method returns the result of the First return that was called.

eobermuhlner commented 10 months ago

Maybe I could not explain it clearly.

In JShell the return statement is only defined to work inside of functions, not outside of functions.

For example the following script:


int a = 2;
int b = 2;

System.out.println("Before if statement");
if (a > b) {
    System.out.println("a is larger than b");
    return a;
} else {
    System.out.println("a is smaller or equal than b");
    return b;
}
System.out.println("After if statement");

will show warnings on the return statements (at least in the IntelliJ IDEA) and when executeed will produce the following output:

Defined field int a = 2

Defined field int b = 2

System.out.println("Before if statement")
Before if statement

if (a > b) {
    System.out.println("a is larger than b");
    return a;
} else {
    System.out.println("a is smaller or equal than b");
    return b;
}
a is smaller or equal than b

System.out.println("After if statement")
After if statement

Please note that the println("After if statement") is being executed although there where return statements in the if.

This is a feature of the JShell and has nothing to do with the JSR233 implementation of jshell-scriptengine.