Access variable of ScriptContext using Nashorn JavaScript Engine (Java 8)

谁说胖子不能爱 提交于 2019-12-13 13:49:51

问题


I used the following code with the Rhino JavaScript engine in Java:

@Test
public void testRhino() throws ScriptException {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("rhino");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();
    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
}

The output of the script (using Rhino) is:

I am the raw value injected
I am a result
I am a returned value

Within the Nashorn JavaScript engine, I get no value for the result:

@Test
public void testNashorn() throws ScriptException {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("nashorn");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();
    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
}

returns

I am the raw value injected
null
I am a returned value

How can I access the value of the result variable of the ScriptContext using the nashorn engine?


回答1:


If you use ScriptEngine.createEngine API to create ENGINE_SCOPE Bindings, it'll work as expected:

import javax.script.*;

public class Main {
  public static void main(String[] args) throws Exception {

    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("nashorn");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();

    // **This is the inserted line**
    ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);

    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
 }
}



回答2:


Nashorn treats the Bindings stored in ScriptContext as "read-only". Any attempt to set a variable stored in a Bindings object (or to create a new variable) will result in a new variable being created in nashorn.global which shadows the Bindings parameter by that name.

You can use the engine to "evaluate" the variable, using this code:

System.out.println( engine.eval("result", ctx) );

This is, however, quite ugly. "result" is first compiled into a script, and then that script is evaluated, to return the variable's value. Fine for testing, but perhaps a little too inefficient for a general solution.

A better, but perhaps more fragile method, is to extract the "nashorn.global" variable, and query it for the desired value.

Bindings nashorn_global = (Bindings) ctx.getAttribute("nashorn.global");
System.out.println( nashorn_global.get("result") );

See also my hack/answer in Capturing Nashorn's Global Variables for automated way of moving nashorn.global values back to a Map<String,Object> after evaluating a script.



来源:https://stackoverflow.com/questions/42338239/access-variable-of-scriptcontext-using-nashorn-javascript-engine-java-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!