Well, at least one person asked me to post or blog something if I found a
way to limit how much processing any given script is allowed. 

 

So I'm posing this to provide one such solution (it's a hack, but it works
well for my particular need). Perhaps the info will be useful to others as
well.

 

 

The solution/workaround for those interested:

----------------------------------------------------------

Based on the upcoming Rhino version 1.7R2 (release candidate 1 is currently
available, a link is in this forums archives), there are some new features
around the continuation capabilities that allow you to capture and resume a
continuation outside the JS environment. The limitation is that the
continuation must be initiated from within a JS function (it can be a Native
Java Method as in the provided test example ContinuationsApiTest class). 

 

So the trick I've come up with (mind you, I'm quite sure that there are
cleaner ways to implement this, I just don't know them nor want to make the
numerous changes to the base code that would be required) is to inject a
call to a JS function that will initiate the continuation. 

 

I've done this with some code in the interpreter that watches for line
breaks (those appear to be safe places to inject a function call) and also
watches for a Boolean condition to be set (in my case I'm counting
instructions and breaking when the # of instructions executed exceeds a
threshold). At that point, instead of continuing execution of the compiled
bytecode, I inject 5 operations, and allow them to process in sequence (the
same 5 operations that would be used to make a JS function call), then
restore the registers I used to their original state at the end.

 

I've included the source that I changed Interpreter.class. It's set up to
call the same object/function used in the ContinuationsApiTest class. There
are only 2 areas in the code that I updated, you can identify them by
searching for the word "hack" added in comments anywhere I made changes. The
first is the code in the interpretLoop to inject the function when
appropriate, and the other is in the compiler to force it to include line
changes even if the script doesn't have any newline characters (this is just
to avoid a script stripping all newlines in an attempt to bypass this
mechanism, and will have the side effect of calling the onLineChange method
in the debugger multiple times for the same source line in some cases).

 

As part of my particular case I am executing many different scripts, each in
their own scope, and each time I resume a particular scope/script I have a
debugger object and contextData object associated with that scope. I set the
debugger and contextData object that I've associated with that scope each
time I resume processing a new script (Context.SetDebugger(myDebugger,
myContextDataObj)). The Context Data object that is passed in contains the
variables used by the workaround code in the interpertLoop, so it's
important to make sure that the contextData object stays with the particular
scope.

 

As far as performance, I've been able to call a bit over 2000 scripts, have
each of them performing a resume and suspend operation after executing their
allotment of ~1000 bytecode operations, then perform a few other minor
operations, all in 1 second on average using a laptops 1.9 ghz single cpu
core processor. I am pretty happy with the scalability of JS continuations.

 

Anyway, if someone tries this out I'd be happy to explain in more detail. If
you want to play with this you should start with the included
ContinuationsApiTest.class and tweak that example with this little hack in
place to see it working.

 

Dave

 

p.s. A big thanks to everyone contributing to Rhino!! And for adding the new
continuation functionality, that was a homerun for me!

 

 

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

Reply via email to