Reusing compiled JS code keeping speed and thread-safety
Hannes Wallnoefer
hannes.wallnoefer at oracle.com
Thu Oct 30 13:53:23 UTC 2014
Hi,
The main requirement for reusing compiled scripts is to use a single
script engine. You can use multiple JS environments/globals within a
script engine by using multiple bindings/script contexts. The nashorn
script engine implements javax.script.Compilable, so you can use the
compile methods to get a CompiledScript that can be evaluated with
multiple bindings or script contexts.
http://docs.oracle.com/javase/7/docs/api/javax/script/Compilable.html
Also, we have been improving reuse of compiled scripts since the initial
JDK8 release, so later versions of Nashorn may work better. You probably
should use JDK 8u20 or even a 8u40 preview release (avalable at
<https://jdk8.java.net/>). In those releases, we have added caching of
compiled scripts within the script engine, so even plain eval() should
be faster as long as you use multiple globals/bindings within the same
engine.
I hope this helps. Let us know how it goes!
Hannes
Am 2014-10-30 um 14:09 schrieb yikes aroni:
> 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