Search code examples
javascriptmultithreadingjava-8nashorn

Can I invoke a function in a new engine scope with Nashorn?


Nashorn allows me to evaluate a JavaScript in a new scope, like this:

public Object evalInNewScope(String code, Bindings bindings) throws Exception  {
    ScriptContext context = new SimpleScriptContext();
    context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
    return engine.eval(code, context);
}

This is great for thread-safety: The engine instance may be re-used across threads, and each thread can make its own call with thread-specific bindings.

However, I have not found a similar capability for Invocable#invokeFunction(String, Object...).

Does anyone know how I could do what I want? Is there a good reason for this asymmetry?


Solution

  • invokeFunction calls functions only from current context, you cannot provide a context directly.

    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("nashorn");
    
    ScriptContext ctx = new SimpleScriptContext();
    ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
    engine.eval("function hello() { return 'Hello!'; }", ctx);
    engine.setContext(ctx);
    
    ((Invocable) engine).invokeFunction("hello");
    

    Removing the engine.setContext(ctx) line would result in a java.lang.NoSuchMethodException: No such function hello exception.

    Depending on your situation, you may have to re-set the original context!