Reusing compiled JS code keeping speed and thread-safety

yikes aroni yikesaroni at gmail.com
Thu Oct 30 13:09:16 UTC 2014


Hi ... I’m using nashorn by embedding it in Java (not via command-line). I
can make it work, but I have a two primary challenges: 1) speed of
execution; and 2) thread safety.



I have a “base” JS library that I compile more or less like this:



ScriptEngineManager *factory* = *new* ScriptEngineManager();

       ScriptEngine *baseEngine* = *factory*.getEngineByName("nashorn");

Compilable *baseCompilable* = (Compilable) *baseEngine*;

baseCompilable.compile(*new* java.io.FileReader(*pathDsBase*)).eval();



where *pathDsBase* is a file system path to my javascript library. This
library isn’t huge, but it takes about 500+ ms to compile and eval().
That’s a long time for my purposes, so I want to do it only once.



Other parts of my code then launch multiple Java threads that do various
work and occasionally invoke small, “temporary” Javascript scripts that
need to reference objects and functions in this “base” library. Since I
want these small scripts to run fast and thread-safe, I **could** simply
create a brand new ScriptEngine every time I want to run one of these
small, “temporary” scripts and recompile the base library into it. Problem
with that is that it takes too much time.



I’ve tried a lot of approaches -- none have worked.



My main approach (which has failed miserably at every turn) is to create a
“base” ScriptEngine, compile the “base” library into it, then create a
“temporary” ScriptEngine and attempt to set  the base bindings / context to
it so that the temp ScriptEngine only eval()s the “temp” script, but has
the base library in scope. For example something like this  test code:



       ScriptEngine tempEngine =  engineFactory.getScriptEngine(); // a
temporary engine. This is the one that will evaluate the “small” scripts
that reference the “base” script library.

ScriptContext ctxBase = *new* SimpleScriptContext();

       ctxBase.setBindings(baseEngine.createBindings(), ScriptContext.
*GLOBAL_SCOPE*);

       // evaluate a script at add it to base’s context. This is where the
“base” script libraries would be eval()-ed into the baseEngine context.

baseEngine.eval("var y = 'hello';", ctxBase);



                // Now attempt to set the baseEngine’s bindings (which I
assume have y = “hello” in them (since I’m not clear where they end up,
I’ve tried every possible combination or ENGINE and GLOBAL scope.)

       ScriptContext ctxTemp = *new* SimpleScriptContext();

       ctxTemp.setBindings(baseEngine.createBindings(), ScriptContext.
*ENGINE_SCOPE*);



       // Now for the temporary, “small” script. The print references a
variable (y) that I had hoped would be set to the temp engine’s scope from
the “base” engine’s context.

       String script = "var x = 'world'; print(y + x);";

       tempEngine.eval(script, ctxTemp);



Exception is Exception in thread "main" *javax.script.ScriptException*:
ReferenceError: "y" is not defined in <eval> at line number 1



I’ve also tried using loadWithNewGlobal, which solves the threading issue,
but doesn’t appear to solve the speed issue: the script has to be
recompiled every time.



I am sure I am barking up the wrong tree. Can somebody help point me in the
right direction for how to do this? Again, my two primary questions are:



1) (Speed) Generally, how do I make it so that I don’t have to re-eval() a
large library every time I want to run a script that references it? Or,
more specifically, how can I reuse code compiled (eval()ed) by one engine
(“base”) in another engine (“temp”)?

2) (Thread-safety) How do I reuse such a library in a way that running the
subsequent “temp” scripts doesn’t pollute the “base” bindings that I’m
reusing?



I will be overjoyed if there is a clean solution to this. If not, I will
still be happy to clear up this mystery with a “can’t do that...” if that
is, in fact, the answer....



thanks


More information about the nashorn-dev mailing list