зеркало из https://github.com/mozilla/gecko-dev.git
Changes to make Interpreter to recover from exceptions during execution of ScriptRuntime.getCatchObject() and during initState and do not leave runtime in inconsitent o even corrupt state.
This commit is contained in:
Родитель
bc4e34d054
Коммит
1e75534222
|
@ -660,31 +660,26 @@ public class Interpreter
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
boolean generated = false;
|
boolean generated = false;
|
||||||
|
|
||||||
|
if (child != catchTarget && child != finallyTarget) {
|
||||||
|
visitStatement(child);
|
||||||
|
} else {
|
||||||
if (child == catchTarget) {
|
if (child == catchTarget) {
|
||||||
if (tryEnd >= 0) Kit.codeBug();
|
if (tryEnd >= 0) Kit.codeBug();
|
||||||
tryEnd = itsICodeTop;
|
tryEnd = itsICodeTop;
|
||||||
catchStart = itsICodeTop;
|
catchStart = itsICodeTop;
|
||||||
|
} else {
|
||||||
markTargetLabel((Node.Target)child);
|
|
||||||
generated = true;
|
|
||||||
|
|
||||||
} else if (child == finallyTarget) {
|
|
||||||
if (tryEnd < 0) {
|
if (tryEnd < 0) {
|
||||||
tryEnd = itsICodeTop;
|
tryEnd = itsICodeTop;
|
||||||
}
|
}
|
||||||
finallyStart = itsICodeTop;
|
finallyStart = itsICodeTop;
|
||||||
|
}
|
||||||
markTargetLabel((Node.Target)child);
|
markTargetLabel((Node.Target)child);
|
||||||
generated = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!generated) {
|
|
||||||
visitStatement(child);
|
|
||||||
}
|
|
||||||
child = child.getNext();
|
child = child.getNext();
|
||||||
}
|
}
|
||||||
// [tryStart, tryEnd) contains GOSUB to call finally when it
|
// [tryStart, tryEnd) contains GOSUB to call finally when it
|
||||||
// presents at the end of try code and before return, break
|
// presents at the end of try code and before return, break and
|
||||||
// continue that transfer control outside try.
|
// continue that transfer control outside try.
|
||||||
// After finally is executed the control will be
|
// After finally is executed the control will be
|
||||||
// transfered back into [tryStart, tryEnd) and exception
|
// transfered back into [tryStart, tryEnd) and exception
|
||||||
|
@ -2074,6 +2069,9 @@ public class Interpreter
|
||||||
Throwable throwable;
|
Throwable throwable;
|
||||||
|
|
||||||
withoutExceptions: try {
|
withoutExceptions: try {
|
||||||
|
// Exception handler assumes that PC is already incremented
|
||||||
|
// pass the instruction start when it searches the
|
||||||
|
// exception handler
|
||||||
int op = iCode[state.pc++];
|
int op = iCode[state.pc++];
|
||||||
jumplessRun: {
|
jumplessRun: {
|
||||||
|
|
||||||
|
@ -3053,6 +3051,10 @@ switch (op) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean activationFunctionIsEntered = false;
|
||||||
|
// If any exception happens then ScriptRuntime.exitActivationFunction
|
||||||
|
// and debugger.onExit should be called to restore state properly
|
||||||
|
try {
|
||||||
if (useActivation) {
|
if (useActivation) {
|
||||||
// Copy args to new array to pass to enterActivationFunction
|
// Copy args to new array to pass to enterActivationFunction
|
||||||
// or debuggerFrame.onEnter
|
// or debuggerFrame.onEnter
|
||||||
|
@ -3074,7 +3076,9 @@ switch (op) {
|
||||||
if (useActivation) {
|
if (useActivation) {
|
||||||
scope = ScriptRuntime.enterActivationFunction(cx, scope,
|
scope = ScriptRuntime.enterActivationFunction(cx, scope,
|
||||||
fnOrScript,
|
fnOrScript,
|
||||||
thisObj, args);
|
thisObj,
|
||||||
|
args);
|
||||||
|
activationFunctionIsEntered = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scope = callerScope;
|
scope = callerScope;
|
||||||
|
@ -3087,7 +3091,9 @@ switch (op) {
|
||||||
Kit.codeBug();
|
Kit.codeBug();
|
||||||
for (int i = 0; i < idata.itsNestedFunctions.length; i++) {
|
for (int i = 0; i < idata.itsNestedFunctions.length; i++) {
|
||||||
InterpreterData fdata = idata.itsNestedFunctions[i];
|
InterpreterData fdata = idata.itsNestedFunctions[i];
|
||||||
if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) {
|
if (fdata.itsFunctionType
|
||||||
|
== FunctionNode.FUNCTION_STATEMENT)
|
||||||
|
{
|
||||||
initFunction(cx, scope, fnOrScript, i);
|
initFunction(cx, scope, fnOrScript, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3095,7 +3101,8 @@ switch (op) {
|
||||||
|
|
||||||
Scriptable[] scriptRegExps = null;
|
Scriptable[] scriptRegExps = null;
|
||||||
if (idata.itsRegExpLiterals != null) {
|
if (idata.itsRegExpLiterals != null) {
|
||||||
// Wrapped regexps for functions are stored in InterpretedFunction
|
// Wrapped regexps for functions are stored in
|
||||||
|
// InterpretedFunction
|
||||||
// but for script which should not contain references to scope
|
// but for script which should not contain references to scope
|
||||||
// the regexps re-wrapped during each script execution
|
// the regexps re-wrapped during each script execution
|
||||||
if (idata.itsFunctionType != 0) {
|
if (idata.itsFunctionType != 0) {
|
||||||
|
@ -3176,11 +3183,33 @@ switch (op) {
|
||||||
stack[i] = null;
|
stack[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
// Restore activation records and debugger
|
||||||
|
if (activationFunctionIsEntered) {
|
||||||
|
ScriptRuntime.exitActivationFunction(cx);
|
||||||
|
state.useActivation = false;
|
||||||
|
}
|
||||||
|
if (debuggerFrame != null) {
|
||||||
|
debuggerFrame.onExit(cx, true, ex);
|
||||||
|
}
|
||||||
|
if (ex instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException)ex;
|
||||||
|
} else {
|
||||||
|
// Must be instance of Error or code bug
|
||||||
|
throw (Error)ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void releaseState(Context cx, State state,
|
private static void releaseState(Context cx, State state,
|
||||||
Throwable throwable)
|
Throwable throwable)
|
||||||
{
|
{
|
||||||
|
if (state.idata.itsFunctionType != 0) {
|
||||||
|
if (state.useActivation) {
|
||||||
|
ScriptRuntime.exitActivationFunction(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.debuggerFrame != null) {
|
if (state.debuggerFrame != null) {
|
||||||
try {
|
try {
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
|
@ -3194,12 +3223,6 @@ switch (op) {
|
||||||
ex.printStackTrace(System.err);
|
ex.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.idata.itsFunctionType != 0) {
|
|
||||||
if (state.useActivation) {
|
|
||||||
ScriptRuntime.exitActivationFunction(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static State handleException(Context cx, State state,
|
private static State handleException(Context cx, State state,
|
||||||
|
@ -3259,22 +3282,26 @@ switch (op) {
|
||||||
|
|
||||||
int[] table;
|
int[] table;
|
||||||
int handler;
|
int handler;
|
||||||
|
int handlerPC;
|
||||||
|
boolean catchHandler = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (exState != EX_NO_JS_STATE) {
|
if (exState != EX_NO_JS_STATE) {
|
||||||
table = state.idata.itsExceptionTable;
|
table = state.idata.itsExceptionTable;
|
||||||
handler = getExceptionHandler(table, state.pc);
|
// Icode switch in the interpreter increments PC immediately
|
||||||
|
// and it is necessary to subtract 1 from the saved PC
|
||||||
|
// to point it before the start of the next instruction.
|
||||||
|
handler = getExceptionHandler(table, state.pc - 1);
|
||||||
if (handler >= 0) {
|
if (handler >= 0) {
|
||||||
if (table[handler + EXCEPTION_CATCH_SLOT] >= 0) {
|
|
||||||
// Found catch handler, check if it is allowed
|
|
||||||
// to run
|
|
||||||
if (exState == EX_CATCH_STATE) {
|
if (exState == EX_CATCH_STATE) {
|
||||||
|
// Check for catch but only if it is allowed
|
||||||
|
handlerPC = table[handler + EXCEPTION_CATCH_SLOT];
|
||||||
|
if (handlerPC >= 0) {
|
||||||
|
catchHandler = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (table[handler + EXCEPTION_FINALLY_SLOT] >= 0) {
|
handlerPC = table[handler + EXCEPTION_FINALLY_SLOT];
|
||||||
// Found finally handler, make state
|
if (handlerPC >= 0) {
|
||||||
// to match always handle type
|
|
||||||
exState = EX_FINALLY_STATE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3282,10 +3309,6 @@ switch (op) {
|
||||||
// No allowed execption handlers in this frame, unwind
|
// No allowed execption handlers in this frame, unwind
|
||||||
// to parent and try to look there
|
// to parent and try to look there
|
||||||
|
|
||||||
while (state.withDepth != 0) {
|
|
||||||
state.scope = ScriptRuntime.leaveWith(state.scope);
|
|
||||||
--state.withDepth;
|
|
||||||
}
|
|
||||||
releaseState(cx, state, throwable);
|
releaseState(cx, state, throwable);
|
||||||
|
|
||||||
if (state.callerState == null) {
|
if (state.callerState == null) {
|
||||||
|
@ -3301,11 +3324,7 @@ switch (op) {
|
||||||
state = state.callerState;
|
state = state.callerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We caught an exception, since the loop above
|
// We caught an exception
|
||||||
// can unwind accross frame boundary, always use
|
|
||||||
// stack.something instead of something that were cached
|
|
||||||
// at the beginning of StateLoop and refer to values in the
|
|
||||||
// original frame
|
|
||||||
|
|
||||||
int tryWithDepth = table[handler + EXCEPTION_WITH_DEPTH_SLOT];
|
int tryWithDepth = table[handler + EXCEPTION_WITH_DEPTH_SLOT];
|
||||||
while (state.withDepth != tryWithDepth) {
|
while (state.withDepth != tryWithDepth) {
|
||||||
|
@ -3314,24 +3333,36 @@ switch (op) {
|
||||||
--state.withDepth;
|
--state.withDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.pc = handlerPC;
|
||||||
|
if (instructionCounting) {
|
||||||
|
state.pcPrevBranch = handlerPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The state is almost restored and only stack initialization is left.
|
||||||
|
|
||||||
state.savedStackTop = state.emptyStackTop;
|
state.savedStackTop = state.emptyStackTop;
|
||||||
if (exState == EX_CATCH_STATE) {
|
if (catchHandler) {
|
||||||
int exLocal = table[handler + EXCEPTION_LOCAL_SLOT];
|
int exLocal = table[handler + EXCEPTION_LOCAL_SLOT];
|
||||||
|
try {
|
||||||
state.stack[state.localShift + exLocal]
|
state.stack[state.localShift + exLocal]
|
||||||
= ScriptRuntime.getCatchObject(cx, state.scope,
|
= ScriptRuntime.getCatchObject(cx, state.scope,
|
||||||
throwable);
|
throwable);
|
||||||
state.pc = table[handler + EXCEPTION_CATCH_SLOT];
|
} catch (Throwable ex) {
|
||||||
|
// getCatchObject can even theoretically execute script code so
|
||||||
|
// it is allowed to throw exceptions: call handleException
|
||||||
|
// recusrsively but before that make the execption like
|
||||||
|
// it was executed during first icode of the catch block
|
||||||
|
// which is simulated by increasing PC by one
|
||||||
|
++state.pc;
|
||||||
|
return handleException(cx, state, ex);
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
++state.savedStackTop;
|
++state.savedStackTop;
|
||||||
// Call finally handler with throwable on stack top to
|
// Call finally handler with throwable on stack top to
|
||||||
// distinguish from normal invocation through GOSUB
|
// distinguish from normal invocation through GOSUB
|
||||||
// which would contain DBL_MRK on the stack
|
// which would contain DBL_MRK on the stack
|
||||||
state.stack[state.savedStackTop] = throwable;
|
state.stack[state.savedStackTop] = throwable;
|
||||||
state.pc = table[handler + EXCEPTION_FINALLY_SLOT];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instructionCounting) {
|
|
||||||
state.pcPrevBranch = state.pc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче