It is not safe, as you yourself pointed out correctly that you'll have  
leftover state between executions, or even worse, you will have  
multiple requests working concurrently in the same scope, overwriting  
each other's data.

You can however do this on each request:

ScriptableObject requestScope = Context.newObject(topScope);
requestScope.setPrototype(topScope);

this way, you will have an empty scope for every request, and all  
functions will be looked up in its prototype (since they usually won't  
be found in the request scope). The prototype is created once, and  
shared across all request instances (hence the approach is usually  
named "shared prototype").

There are caveats with this approach though too. Specifically, the  
lexical scope for your functions will remain topScope (except with  
dyamic scopes, more on that later), so they will not see variables in  
the request scope, except when they're qualified with "this". I.e. if  
you have a variable "a" in requestScope, your functions must be  
written with "this.a" instead of "a" reference. Similarly, if function  
f calls function g, instead of "g()" you'll have to write "this.g()",  
otherwise  "a" will not be visible (not even as "this.a") inside g.

You can get around this by using Rhino's dynamic scopes functionality.  
It will replace lexical scoping (based on nesting of code in the  
source code) with dynamic scoping (based on nesting of function  
invocations in the actual execution). Then you won't have to use  
"this." in your functions everywhere.

For different reasons though, I personally discourage use of dynamic  
scopes. They make program execution harder to comprehend, and they're  
also completely nonstandard - JS normally uses lexical scoping. Many  
JS libraries out there will rely on scoping to achieve things like  
simulating private functions etc. Using dynamic scopes can break them.  
This mightn't matter much if all your code is homegrown, but if you  
want to reuse some existing libraries, or hope that your homegrown  
code might need to run in some other JS runtime in the future, it is a  
concern.

And now for something completely different.

You can often get good performance even without resorting to any of  
the above. Compile your script that defines functions and initializes  
objects ("init script"). Then just create a new top level scope, run  
initStandardObjects(), and run your init script in it, then proceed  
with the request specific script. The init script is already compiled;  
by executing it again you'll just have it create Function object  
instances for top-level functions, bind them to their precompiled code  
(it's available to the Script object they're in), and have it assigned  
to variable names. There's nothing too costly involved in this step --  
compilation already happened when you invoked Context.compileScript.  
If you have nested functions, they don't even contribute to the cost;  
a similar process will happen for them when the function containing  
them is invoked.

Granted, it might be more intensive than the above two liner for  
creating a requestScope and setting its prototype. But it is also  
simpler; all your executing requests have a vanilla setup with a  
single scope, they share nothing (well, except the immutable code of  
the functions).

I've implemented Rhino-based JS runtimes using all of these  
approaches: shared prototype with dynamic scopes, shared prototype  
with static scopes + "this." everywhere in functions, and the vanilla  
solution. The vanilla solution is surprisingly well performant and  
more than sufficient in most cases.

Later on, if you find your system slow, and you can prove by profiling  
that the bottleneck is in repeated initStandardObjects() + your init  
script execution, you can step up to the shared prototype approach.  
But I encourage you to try the vanilla approach first, as it results  
in simplest, most easily maintained architecture.

Hope that helps.
Attila.


On 2008.05.18., at 5:00, James Burke wrote:

> Using Rhino with servlets: I want to do as much initialization and
> script compiling as possible when using Rhino in a servlet
> environment, to get the best performance. I am using Rhino 1.7R1. I
> have some library JS files that define objects/functions I always want
> available, but there will be specific JS files executed per servlet
> request.
>
> Right now I am using a *static block* in a servlet to do
> Context.initStandardObjects() and I save the returned Scriptable in a
> static servlet variable, called topScope, which is used when
> processing every servlet request. I want to use
> topScope.evaluateString/Reader to execute the library JS files in the
> static block, since I always want those library objects/functions
> available.
>
> Questions:
> 1) Is it safe to create topScope and call
> topScope.evaluateString/Reader in the static servlet block, then reuse
> topScope for all Servlet requests? I am concerned that when the
> context exits in the static block, some state information that was on
> that thread will be lost.
>
> 2) Any general concerns about using a static initialization block in
> the servlet? Should I just stick with doing the work mentioned above
> in the servlet init method, or even take the hit and do the work for
> each servlet request?
>
> Thank you,
> James Burke




_______________________________________________
dev-tech-js-engine-rhino mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino

Reply via email to