Reusing compiled JS code keeping speed and thread-safety

yikes aroni yikesaroni at gmail.com
Thu Oct 30 15:23:18 UTC 2014


Hi Benjamin -- thanks for your response. Interesting approach tho not sure
if it'll work for my situation. A couple of questions:

> *Any one of these scripts gets evaluated by the engine once and the
returned ScriptObjectMirror*
Do these ScriptObjectMirror objects have your library in scope somehow?
That is, if you load library into the engine, then eval() a secondary
script to get a ScriptObjectMirror, how can you call a function on it via
callMember() if that function leverages functions/obj/props of the library?

> *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.*
Not clear to me. Any chance you could share a code snippet?

thanks for your time and thoughts!


On Thu, Oct 30, 2014 at 10:03 AM, Benjamin Sieffert <
benjamin.sieffert at metrigo.de> wrote:

> 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