My issue is that neither of those are ideal for my case. I provide a
hosted service and I want to allow untrusted end-users to execute
arbitrary code written in JavaScript. I've been able to lock down what
objects they can talk to, thanks to ClassShutter. I can also use the
instruction count trick as one method to stop runaway scripts.
But I also have a need to simply stop a script on command at any given
moment in time. Calling Thread.interrupt() works well for when the
script is engaged in an interruptible operation (ie: Thread.sleep(),
file I/O, network calls, etc). But what if the script is in while loop
and isn't calling any interruptible code?
Maybe I'm trivializing how Rhino works, but because it's evaluated on
the fly could there not be some simple "check" that happens after each
instruction and looks to see if a stop flag has been set to true? This
is of course the simple-but-recommended way to stop things in Java,
even since they deprecated Thread.stop():
http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html
I found one email archive basically saying they bit the bullet and
called Thread.stop(), but I have a LOT of synchronization going on and
the risk that I'd break the multi-threaded code is too high for me to
consider it right now. It also doesn't help that the thread is
actually managed by an Executor, so I don't actually get a handle to
the executing Thread from other threads and instead only get access to
either the calling object (w/ references to the Rhino Context) and the
Future.cancel(true), which causes an interrupt.
I would really appreciate any thoughts on how I can achieve this. Thanks!
Patrick
http://www.mozilla.org/rhino/apidocs/org/mozilla/javascript/ContextFactory.h
tml
David
http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.ht
ml
Patrick
_______________________________________________
dev-tech-js-engine-rhino mailing list
dev-tech-js-...@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino
> Can you not add the stop flag check to the observeInstructionCount
> callback
> method? That would be the most straight forward way to stop the
> script and
> really take very few lines of code to implement. Can you explain why
> this
> wouldn't work in your case? If you're concerned with finding a
> reference to
> the stop check flag you can always extend Context and add your own
> reference
> to whatever object contains the check you need to perform as nearly
> demonstrated in this example:
>
> http://www.mozilla.org/rhino/apidocs/org/mozilla/javascript/ContextFactory.h
> tml
Hm... I'm thinking I might update that example a bit - throw a
java.lang.ThreadDeath specifically in place of generic java.lang. Error.
Attila.
> David
I'd rather do it on the Context directly (less indirection), but am
curious about one thing: It doesn't appear a subclass (MyContext) can
set the internal factory field. This will cause an error in many of
the default methods of Context. For example:
protected void observeInstructionCount(int instructionCount)
{
ContextFactory f = getFactory();
f.observeInstructionCount(this, instructionCount);
}
Because the "factory" field is private and is only set in a
package-protected constructor, and because getFactory() is
public+final, there is no way for me to extend Context while still
passing in a reference to the factory. In the example JavaDocs, this
looks like it's skipped entirely.
It may work for me, since I'll be overriding observeInstructionCount
and not calling super, but it worries me a bit. There are other
methods that also depend on the factory being not-null:
public boolean hasFeature(int featureIndex)
{
ContextFactory f = getFactory();
return f.hasFeature(this, featureIndex);
}
public static void exit()
{
Object helper = VMBridge.instance.getThreadContextHelper();
Context cx = VMBridge.instance.getContext(helper);
if (cx == null) {
throw new IllegalStateException(
"Calling Context.exit without previous Context.enter");
}
if (cx.enterCount < 1) Kit.codeBug();
if (--cx.enterCount == 0) {
VMBridge.instance.setContext(helper, null);
cx.factory.onContextReleased(cx);
}
}
Any ideas?
Patrick
> Because the "factory" field is private and is only set in a
> package-protected constructor, and because getFactory() is
> public+final, there is no way for me to extend Context while still
> passing in a reference to the factory. In the example JavaDocs, this
> looks like it's skipped entirely.
Yep, others hit that problem too. I promoted the visibility of that
constructor to protected for benefit of subclasses in 1.7R2. 1.7R2 is
still unreleased, but you can grab a RC at <ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_7R2-RC1.zip
>
Hope that helps,
Attila.
If I understood that right then all you want to do is create another method
in the "MyContext" factory which will set an interrupt flag that
observeInstructionCount will monitor.
To extend the example given:
// Custom Context to store execution time.
private static class MyContext extends Context
{
long startTime;
boolean interruptFlag = false;
}
public synchronized interruptScript(){
interruptFlag = true;
}
// Override observeInstructionCount(Context, int)
protected void observeInstructionCount(Context cx, int
instructionCount)
{
//All the other stuff from the example...
if(interruptFlag == true) throw new Error();
}
observeInstructionConnt() is only being called by the javascript engine
which in turn monitors for the exception to be thrown and propagates it
through the script (the Error's can't be caught by the script, hence it
propagates right through and out of the script context).
Let me know if I'm off base with my understanding.
David
-----Original Message-----
From: plig...@gmail.com [mailto:plig...@gmail.com] On Behalf Of Patrick
Lightbody
Sent: Thursday, February 19, 2009 9:26 AM
To: Attila Szegedi
Cc: David Parks; dev-tech-js-...@lists.mozilla.org
Subject: Re: Stopping JavaScript Evaluation
Duh - thanks! I was starting to do that last night, though instead of
a subclass I was looking at getThreadLocal() and putThreadLocal() as a
way to get a "Stoppable" object that had the flag in it.
I'd rather do it on the Context directly (less indirection), but am
curious about one thing: It doesn't appear a subclass (MyContext) can
set the internal factory field. This will cause an error in many of
the default methods of Context. For example:
protected void observeInstructionCount(int instructionCount)
{
ContextFactory f = getFactory();
f.observeInstructionCount(this, instructionCount);
}
Because the "factory" field is private and is only set in a
package-protected constructor, and because getFactory() is
public+final, there is no way for me to extend Context while still
passing in a reference to the factory. In the example JavaDocs, this
looks like it's skipped entirely.
It may work for me, since I'll be overriding observeInstructionCount
public class StoppableContext extends Context {
private boolean stop;
@Override
protected void observeInstructionCount(int instructionCount) {
if (stop) {
throw new StoppedExecutionException();
}
}
public void stop() {
stop = true;
}
}
Which is basically what you just wrote :) My only complaint was the
visibility of the constructor, but it sounds like that's already a
known issue and will be solved soon.
Patrick
In other words: am I safe to extend Context in the latest released
version of Rhino, or do you strongly recommend I go with 1.7R2 so that
I can pass in the factory?
Patrick
On Thu, Feb 19, 2009 at 6:55 AM, Attila Szegedi <szeg...@gmail.com> wrote:
> On 2009.02.19., at 15:25, Patrick Lightbody wrote:
>
>> Because the "factory" field is private and is only set in a
>> package-protected constructor, and because getFactory() is
>> public+final, there is no way for me to extend Context while still
>> passing in a reference to the factory. In the example JavaDocs, this
>> looks like it's skipped entirely.
>
The said complicated logic in older (1.6R7 and older) Rhino versions
was actually falling back to using ContextFactory.getGlobal() when the
factory field was null - it allowed null factory fields and was
retrieving ContextFactory.getGlobal() whenever it needed a factory
instance. From 1.7R1 the field is final and can't be assigned null in
constructor, which simplifies the code quite a lot.
The answer is then: you can safely extend Context even with currently
released versions, with the caveat that it will then always be bound
to ContextFactory.getGlobal() - in 1.7R1 it'll set its factory field
to refer to whatever is the global factory at constructor time, in
earlier versions it'll retrieve the global factory whenever it needs
to use it (1.6R7 and earlier).
Attila.
When does the observeInstructionCount() of Context get called? I
thught that it gets called only if the setOptimizationLevel is set to
-1. Is that correct?
Also , If the ContextFactory is not global will the
observeInstructionCount() on the context will get called
automatically?
If not , is it ok to do something like ((MyContext)Cx).getFactory
().observeInstructionCount(Cx, instructionCount)?