Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Stopping JavaScript Evaluation

666 views
Skip to first unread message

Patrick Lightbody

unread,
Feb 19, 2009, 1:28:30 AM2/19/09
to dev-tech-js-...@lists.mozilla.org
Is there any way to simply stop the execution of a running script?
I've read the docs and archives and understand that the preferred
methods are for the script to either throw an exception OR for me to
use the observeInstructionCount capability of Rhino.

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

David Parks

unread,
Feb 19, 2009, 1:49:54 AM2/19/09
to dev-tech-js-...@lists.mozilla.org
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

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


Attila Szegedi

unread,
Feb 19, 2009, 4:57:12 AM2/19/09
to David Parks, dev-tech-js-...@lists.mozilla.org
On 2009.02.19., at 7:49, David Parks wrote:

> 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

Patrick Lightbody

unread,
Feb 19, 2009, 9:25:57 AM2/19/09
to Attila Szegedi, David Parks, dev-tech-js-...@lists.mozilla.org
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
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

Attila Szegedi

unread,
Feb 19, 2009, 9:55:59 AM2/19/09
to Patrick Lightbody, David Parks, dev-tech-js-...@lists.mozilla.org
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.

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.

David Parks

unread,
Feb 19, 2009, 9:58:46 AM2/19/09
to dev-tech-js-...@lists.mozilla.org
Let me paraphrase to make sure I understand you.
You want to call observeInstructionCount() externally as the method which
will interrupt the thread?

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

Patrick Lightbody

unread,
Feb 19, 2009, 10:02:46 AM2/19/09
to David Parks, dev-tech-js-...@lists.mozilla.org
David,
I think we got a little mixed up - what I ended up doing is:

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

Patrick Lightbody

unread,
Feb 19, 2009, 10:04:42 AM2/19/09
to Attila Szegedi, David Parks, dev-tech-js-...@lists.mozilla.org
Attila,
Thanks for confirming that with me. My code appears to be running fine
despite having a null factory field. Does Context.exit() and
Context.enter() get called at some unpredictable point? I'm not
calling them at all myself and those look like the only methods I'm
slightly concerned about, since I don't know what they do.

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.
>

Attila Szegedi

unread,
Feb 19, 2009, 10:24:55 AM2/19/09
to Patrick Lightbody, David Parks, dev-tech-js-...@lists.mozilla.org
Well, for 1.7R1, I rewrote the Context code to ensure factory field
can't be null, as it complicated program logic in earlier versions.

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.

Patrick Lightbody

unread,
Feb 19, 2009, 10:27:15 AM2/19/09
to Attila Szegedi, David Parks, dev-tech-js-...@lists.mozilla.org
Perfect, thanks!

Rhino user

unread,
Mar 9, 2009, 5:58:43 PM3/9/09
to
On Feb 19, 8:02 am, Patrick Lightbody <patr...@lightbody.net> wrote:
> David,
> I think we got a little mixed up - what I ended up doing is:
>
> 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
>
> > On Thu, Feb 19, 2009 at 1:57 AM, Attila Szegedi <szege...@gmail.com> wrote:
> >> On 2009.02.19., at 7:49, David Parks wrote:
>
> >>> 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/ContextFa...

> >>> 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
>
> >> _______________________________________________
> >> dev-tech-js-engine-rhino mailing list
> >> dev-tech-js-engine-rh...@lists.mozilla.org

> >>https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino
>
> > _______________________________________________
> > dev-tech-js-engine-rhino mailing list
> > dev-tech-js-engine-rh...@lists.mozilla.org
> >https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino

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?

Rhino user

unread,
Mar 9, 2009, 6:15:00 PM3/9/09
to

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)?

Patrick Lightbody

unread,
Mar 10, 2009, 10:11:55 PM3/10/09
to Rhino user, dev-tech-js-...@lists.mozilla.org
I don't call observeInstructionCount() myself - Rhino does that. I
just call stop().

0 new messages