Okay... this is way harder than I thought it would be.
From what I can see looking at the code, the answer to my question is
"no, once a Skulpt program starts, it controls the universe."
I made a pretty simple change, by modifying the four
Compiler.prototype._jump routines to also add the line:
::: out("if(Sk.userSkInterrupt != null){/*user interrupt*/
console.log('Skulpt Python Program interrupted by user
request.');throw new Error('Program interrupted by user request.')}");
I added a way to set Sk.userSkInterrupt to a value by clicking an
"Interrupt" button on my page.
It didn't work. I'm running Mac OS X, and tried Firefox and Safari.
It actually messed up my OS, which is dang hard to do on a Mac. If I
put an infinite loop in, it seems that javascript wasn't surrendering
any CPU time at all, so the browser wouldn't even respond to the
button click. Safari would let me close the window, but it hung all
of Firefox, requiring a "Force Quit."
I tried a simpler alternative... I added the following to the web
page, just before the eval call to run the compiled Python-Javascript:
::: setTimeout('Sk.userSkInterrupt=1;alert("interrupt issued")',
1000);
In theory, this should have set the interrupt flag after a second and
halted the infinite loop using the _jump code above. It didn't.
So it seems that Javascript (or maybe the eval function specifically?)
is so badly behaved that it won't even yield to a Javascript timeout.
The only real answer here is not doable... to rewrite the compiler to
generate the code in chunks that periodically terminate and then
resume after a brief pause using a javascript setTimeout. This means
a total rewrite of how everything is done, so that's not happening.
The poor man's alternative would be to allow the user to configure an
"infinite loop check" time into Skulpt (Sk.timeout), and change the
Compiler.prototype._jump statements (or maybe better yet the
individual potential looping constructs, like "while" and "for") to
check if the system time has exceeded the loop-check time, and if so
to perform some action (at a minimum throw an exception to terminate
the program, or maybe issue an alert asking the user if they want to
terminate, and if so throw the interrupt exception). If I get clever,
I could make the action configurable, perhaps through Sk.configure.
If you have any ideas I'm all ears. Feel free to e-mail me if you
care to.
I do have one question: For irrelevant reasons I tried to change the
script that runs the code in page to break it up, so that:
eval(Sk.importMainWithBody("<stdin>", false,
mainEditor.getCode()));
became
var mycode = (Sk.importMainWithBody("<stdin>", false,
mainEditor.getCode()));
eval(mycode);
Surprisingly, this executed the code twice, as if the second eval
statement were superfluous. What's going on there?
-- Bob