Search code examples
javanashornscriptengine

Nashorn put compiledsript into engine scope


I have two js files,

  • one is js library
  • second one is a simple script usually somewhat around 50 lines, that needs to access functions from the library.

In my project im trying to precompile all javascripts during my application startup, and then at runtime only invoke CompiledScripts with desired parameters.

I ended up with the following code



    static String LIBRARY = "function hello(arg) {return 'Hello ' + arg;};";

    static String SCRIPT = "hello(arg)";

    public static void main(String... args) throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("Nashorn");
        Compilable compilable = ((Compilable) engine);
        CompiledScript compiledLib = compilable.compile(LIBRARY);
        compiledLib.eval();

        CompiledScript actualScript = compilable.compile(SCRIPT);

        Bindings helloParams = new SimpleBindings();
        helloParams.put("arg","world");
        ScriptObjectMirror result = (ScriptObjectMirror) actualScript.eval(helloParams);
        System.out.println(result);
}

But this code throws an error

> compiledScript.eval(helloParams);
<eval>:1 ReferenceError: "hello" is not defined

How can i access context of "compiledLib" (ie methods and variables) from within the "actualScript"?


Solution

  • Compiling doesn't register the hello() function, it just parses the JavaScript code.

    You need to execute the code for the function to be registered.

    Remember, in JavaScript, there is very little difference between these two statements, except that function declarations are hoisted and can therefore be used before the declaration statement:

    function hello(arg) {return 'Hello ' + arg;};
    
    var hello = function(arg) {return 'Hello ' + arg;};
    

    There is therefore little reason to separately compile the LIBRARY code, you just run it and save off all the created global variables, which are the library methods. E.g. after executing your LIBRARY code, you'll have a global variable named hello.


    ScriptEngine engine = new ScriptEngineManager().getEngineByName("Nashorn");
    Compilable compilable = ((Compilable) engine);
    
    // Process LIBRARY code
    Bindings bindings = new SimpleBindings();
    engine.eval(LIBRARY, bindings);
    
    // Compile SCRIPT code
    CompiledScript actualScript = compilable.compile(SCRIPT);
    
    // Run SCRIPT code
    bindings.put("foo", "world");
    Object result = actualScript.eval(bindings);
    System.out.println(result);
    

    Output

    Hello world