16

I'm trying to evaluate javascript in Java by using the ScriptEngine class. Here is a short example of what I am trying to do:

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;

public class Test {
    public static void main(String[] args) {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); //Creates a ScriptEngine
        Object obj = engine.eval("var obj = { value: 1 }; return obj; "); // Evals the creation of a simple object
        System.out.println(obj.value); // I get an invalid token error when trying to print a property of the object
    }
}

I'm pretty sure that this should work... but I'm stumped, and I'll take any help I can get.

5
  • Does it work if you drop return obj;? Return statements should be in functions, but then I don't know how that class evaluates the JS code. Commented Jun 26, 2016 at 1:44
  • 1
    I'm not really sure how it works either; I just saw a couple of examples and the documentation. I get the same error when ommiting. Commented Jun 26, 2016 at 1:46
  • Probably because of the var statement, which won't return the object itself. Just the object wrapped in parentheses may work. The parens will be necessary for a valid program. Commented Jun 26, 2016 at 2:00
  • @squint OP has a compile error, (the "invalid token error" mentioned in the comment to the println() statement), not a runtime error. See my answer for why. Commented Jun 26, 2016 at 2:03
  • @Andreas: I know, see my first comment. I'm just saying that neither var obj = { value: 1 }; nor { value: 1 }; would likely work because neither statement alone produces an object, while ({value: 1}) would. Commented Jun 26, 2016 at 2:14

1 Answer 1

15

Note: The following is for Java 8, using the Nashorn engine.

First, to make the code compile, remove the .value from the println() statement. obj is declared to be type Object, and Object doesn't have a value field.

Once you do that, you get the following exception when running the code:

Exception in thread "main" javax.script.ScriptException: <eval>:1:25 Invalid return statement
var obj = { value: 1 };  return obj; 
                         ^ in <eval> at line number 1 at column number 25

That is because you don't have a function, so you cannot call return. The return value of the script is the value of the last expression, so just say obj.

Now it will run and print [object Object]. To see what type of object you got back, change to println(obj.getClass().getName()). That will print jdk.nashorn.api.scripting.ScriptObjectMirror. I've linked to the javadoc for your convenience.

ScriptObjectMirror implements Bindings which in turn implements Map<String, Object>, so you can call get("value").

Working code is:

import javax.script.*;

public class Test {
    public static void main(String[] args) throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        Bindings obj = (Bindings)engine.eval("var obj = { value: 1 };  obj; ");
        Integer value = (Integer)obj.get("value");
        System.out.println(value); // prints: 1
    }
}

UPDATE

The whole point was to create an object with functions, is that possible with this engine? There isn't a Function object.

Example for how to do that:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import jdk.nashorn.api.scripting.ScriptObjectMirror;

public class Test {
    public static void main(String[] args) throws Exception {
        String script = "var f = {\n" +
                        "  value: 0,\n" +
                        "  add: function(n) {\n" +
                        "    this.value += n;\n" +
                        "    return this.value;\n" +
                        "  }\n" +
                        "};\n" +
                        "f; // return object to Java\n";
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        ScriptObjectMirror obj = (ScriptObjectMirror)engine.eval(script);
        System.out.println("obj.value = " + obj.getMember("value"));
        System.out.println("obj.add(5): " + obj.callMember("add", 5));
        System.out.println("obj.add(-3): " + obj.callMember("add", -3));
        System.out.println("obj.value = " + obj.getMember("value"));
    }
}

OUTPUT

obj.value = 0
obj.add(5): 5.0
obj.add(-3): 2.0
obj.value = 2.0
Sign up to request clarification or add additional context in comments.

8 Comments

/tmp/java_4eJZg9/Test.java:16: error: unreported exception ScriptException; must be caught or declared to be thrown Bindings obj = (Bindings)engine.eval("var obj = { value: 1 }; obj; "); ^ 1 error at the parentheses after eval.
So add throws ScriptException to main().
Man, I am so dumb. Thanks for the help.
I know this question is answered, but I can't seem to use the same code for Strings. Also, is it possible to set value to a function in javascript and then execute that function in Java? That was the entire purpose for this, it was to create dynamic functions.
Yes, just do what I described doing. Print the class name of the returned object, so you can see what you get. Then check the javadoc of how to use it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.