-
Notifications
You must be signed in to change notification settings - Fork 127
Description
Hi,
some time ago I opened #42 to write a wiki page that shows how to apply a timeout to script execution.
Just now I was looking at the emit for the TryCatchFinallyStatement and realized that it emits JS catch
and finally
blocks as .NET catch
and finally
blocks. This means the timeout (which is done by Thread.Abort()) can easily be bypassed:
try {} finally {
while (true);
}
Drat!
Also, when the script calls an API in the catch or finally block which uses ScriptTimeoutHelper.EnterCriticalSection
, it will lead to a deadlock as that method waits for a ThreadAbortException
, which will not happen since the code is currently in a protected region.
Further, this means that JS code in a finally
block is executed even when non-JavaScriptException
s, e.g. a ThreadAbortException or a StackOverflowException from the recursion depth check, are thrown (whereas in my case I would like to ensure no more script code is executed when throwing a non-JavaScriptException).
Unfortunately, I'm not sure if it is somehow possible to mark generated catch and finally blocks as non-protected region, so that the CLR does not delay thread aborts for these regions. If this is not possible, the only way I can think of which would still be practically, is to generate functions for the contents of a try
and catch
statement, and catching an exception before running the code for a catch
and finally
clause.
For example, if the JS is this:
function myFunc() {
for (var i = 0; i < 20; i++) {
try {
console.log("Try!");
if (i == 10)
break;
else if (i == 11)
continue;
else if (i == 12)
return "i: " + i;
}
catch (e) {
console.log("Catch!");
}
finally {
console.log("Finally!");
}
}
}
the generated code would look in C# like this:
private static object JS_myFunc()
{
for (var i = 0; i < 20; i++)
{
// Try
ExceptionDispatchInfo caughtException1 = null;
int state1 = 0;
object returnValue1 = null;
try
{
state1 = TryFunction1(ref returnValue1, ref i);
}
catch (Exception ex) when (ex is JavaScriptException)
{
caughtException1 = ExceptionDispatchInfo.Capture(ex);
}
// Finally
Console.WriteLine("Finally!");
if (caughtException1 != null)
caughtException1.Throw();
// Handle return state
if (state1 == 0)
break;
else if (state1 == 1)
continue;
else if (state1 == 2)
return returnValue1;
}
return null;
}
private static int TryFunction1(ref object returnValue, ref int i)
{
Exception caughtException1 = null;
try
{
Console.WriteLine("Try!");
if (i == 10)
return 0;
else if (i == 11)
return 1;
else if (i == 12)
{
returnValue = "i: " + i;
return 2;
}
}
catch (Exception ex) when (ex is JavaScriptException)
{
caughtException1 = ex;
}
if (caughtException1 != null)
{
Console.WriteLine("Catch!");
}
return 0;
}
(although that seems already quite a bit complicated).
Unfortunately, I also don't know much of the internals of CIL, so I'm not sure if I can do this.
Do you have any other idea how this could be solved?
Thanks!