Reusing compiled JS code keeping speed and thread-safety

Benjamin Sieffert benjamin.sieffert at metrigo.de
Thu Oct 30 14:03:43 UTC 2014


Hello there,

we had a similar aim / similar issues with nashorn. Since I don't know your
code exactly, I'll just describe our case / what we ended up doing and
maybe you can get some ideas off that.

We use one ScriptEngine. Among all our threads, just one engine. On
start-up we let it compile the library once. The library has no mutable
state and is not dependant on global variables. It's just a collection of
functions / classes.
Then we have a few hundred small javascripts that basically instantiate a
class defined by the library and do some configurations on that instance.
Any one of these scripts gets evaluated by the engine once and the returned
ScriptObjectMirror (the configured javascript object) is then kept on the
java side. As this ScriptObjectMirror also does not have any mutable state,
we can now safely use it among multiple threads via callMember().
To get nice stack traces, we also wrap each small javascript into a
load()-call in the engine and name it. The library is also wrapped in a
load()-call.

All approaches including some sort of mutable javascript-side state sadly
do not seem to be workable. The nashorn devs have (understandably)
explicitly stated that since javascript does not define multithreaded
semantics, they will not be implementing any.
Everything evaluated in a NashornScriptEngine keeps its original
context/global, even when seemingly "put" into another engine. You cannot
prevent pollution. I tried for some time. : )

Regards

On 30 October 2014 14:09, yikes aroni <yikesaroni at gmail.com> wrote:

> 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
>



-- 
Benjamin Sieffert
metrigo GmbH
Sternstr. 106
20357 Hamburg

Geschäftsführer: Christian Müller, Tobias Schlottke, Philipp Westermeyer,
Martin Rieß
Die Gesellschaft ist eingetragen beim Registergericht Hamburg
Nr. HRB 120447.


More information about the nashorn-dev mailing list