internal ContinuationJump(NativeContinuation c, Interpreter.CallFrame current) { this.capturedFrame = (Interpreter.CallFrame)c.GetImplementation(); if (this.capturedFrame == null || current == null) { // Continuation and current execution does not share // any frames if there is nothing to capture or // if there is no currently executed frames this.branchFrame = null; } else { // Search for branch frame where parent frame chains starting // from captured and current meet. Interpreter.CallFrame chain1 = this.capturedFrame; Interpreter.CallFrame chain2 = current; // First work parents of chain1 or chain2 until the same // frame depth. int diff = chain1.frameIndex - chain2.frameIndex; if (diff != 0) { if (diff < 0) { // swap to make sure that // chain1.frameIndex > chain2.frameIndex and diff > 0 chain1 = current; chain2 = this.capturedFrame; diff = -diff; } do { chain1 = chain1.parentFrame; } while (--diff != 0); if (chain1.frameIndex != chain2.frameIndex) { Kit.CodeBug(); } } // Now walk parents in parallel until a shared frame is found // or until the root is reached. while (chain1 != chain2 && chain1 != null) { chain1 = chain1.parentFrame; chain2 = chain2.parentFrame; } this.branchFrame = chain1; if (this.branchFrame != null && !this.branchFrame.frozen) { Kit.CodeBug(); } } }
private static double Stack_double(Interpreter.CallFrame frame, int i) { object x = frame.stack[i]; if (x != UniqueTag.DOUBLE_MARK) { return ScriptRuntime.ToNumber(x); } else { return frame.sDbl[i]; } }
private static int DoArithmetic(Interpreter.CallFrame frame, int op, object[] stack, double[] sDbl, int stackTop) { double rDbl = Stack_double(frame, stackTop); --stackTop; double lDbl = Stack_double(frame, stackTop); stack[stackTop] = UniqueTag.DOUBLE_MARK; switch (op) { case Token.SUB: { lDbl -= rDbl; break; } case Token.MUL: { lDbl *= rDbl; break; } case Token.DIV: { lDbl /= rDbl; break; } case Token.MOD: { lDbl %= rDbl; break; } } sDbl[stackTop] = lDbl; return stackTop; }
private static void ExitFrame(Context cx, Interpreter.CallFrame frame, object throwable) { if (frame.idata.itsNeedsActivation) { ScriptRuntime.ExitActivationFunction(cx); } if (frame.debuggerFrame != null) { try { if (throwable is Exception) { frame.debuggerFrame.OnExit(cx, true, throwable); } else { object result; Interpreter.ContinuationJump cjump = (Interpreter.ContinuationJump)throwable; if (cjump == null) { result = frame.result; } else { result = cjump.result; } if (result == UniqueTag.DOUBLE_MARK) { double resultDbl; if (cjump == null) { resultDbl = frame.resultDbl; } else { resultDbl = cjump.resultDbl; } result = ScriptRuntime.WrapNumber(resultDbl); } frame.debuggerFrame.OnExit(cx, false, result); } } catch (Exception ex) { System.Console.Error.WriteLine("RHINO USAGE WARNING: onExit terminated with exception"); Sharpen.Runtime.PrintStackTrace(ex, System.Console.Error); } } }
private static NativeContinuation CaptureContinuation(Context cx, Interpreter.CallFrame frame, bool requireContinuationsTopFrame) { NativeContinuation c = new NativeContinuation(); ScriptRuntime.SetObjectProtoAndParent(c, ScriptRuntime.GetTopCallScope(cx)); // Make sure that all frames are frozen Interpreter.CallFrame x = frame; Interpreter.CallFrame outermost = frame; while (x != null && !x.frozen) { x.frozen = true; // Allow to GC unused stack space for (int i = x.savedStackTop + 1; i != x.stack.Length; ++i) { // Allow to GC unused stack space x.stack[i] = null; x.stackAttributes[i] = ScriptableObject.EMPTY; } if (x.savedCallOp == Token.CALL) { // the call will always overwrite the stack top with the result x.stack[x.savedStackTop] = null; } else { if (x.savedCallOp != Token.NEW) { Kit.CodeBug(); } } // the new operator uses stack top to store the constructed // object so it shall not be cleared: see comments in // setCallResult outermost = x; x = x.parentFrame; } if (requireContinuationsTopFrame) { while (outermost.parentFrame != null) { outermost = outermost.parentFrame; } if (!outermost.isContinuationsTopFrame) { throw new InvalidOperationException("Cannot capture continuation " + "from JavaScript code not called directly by " + "executeScriptWithContinuations or " + "callFunctionWithContinuations"); } } c.InitImplementation(frame); return c; }
private static Interpreter.CallFrame InitFrameForApplyOrCall(Context cx, Interpreter.CallFrame frame, int indexReg, object[] stack, double[] sDbl, int stackTop, int op, Scriptable calleeScope, IdFunctionObject ifun, InterpretedFunction iApplyCallable) { Scriptable applyThis; if (indexReg != 0) { object obj = stack[stackTop + 2]; if (obj == UniqueTag.DOUBLE_MARK) { obj = ScriptRuntime.WrapNumber(sDbl[stackTop + 2]); } applyThis = ScriptRuntime.ToObjectOrNull(cx, obj); } else { applyThis = null; } if (applyThis == null) { // This covers the case of args[0] == (null|undefined) as well. applyThis = ScriptRuntime.GetTopCallScope(cx); } if (op == Icode_TAIL_CALL) { ExitFrame(cx, frame, null); frame = frame.parentFrame; } else { frame.savedStackTop = stackTop; frame.savedCallOp = op; } Interpreter.CallFrame calleeFrame = new Interpreter.CallFrame(); if (BaseFunction.IsApply(ifun)) { object[] callArgs = indexReg < 2 ? ScriptRuntime.emptyArgs : ScriptRuntime.GetApplyArguments(cx, stack[stackTop + 3]); InitFrame(cx, calleeScope, applyThis, callArgs, null, 0, callArgs.Length, iApplyCallable, frame, calleeFrame); } else { // Shift args left for (int i = 1; i < indexReg; ++i) { stack[stackTop + 1 + i] = stack[stackTop + 2 + i]; sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i]; } int argCount = indexReg < 2 ? 0 : indexReg - 1; InitFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2, argCount, iApplyCallable, frame, calleeFrame); } frame = calleeFrame; return frame; }
private static bool IsFrameEnterExitRequired(Interpreter.CallFrame frame) { return frame.debuggerFrame != null || frame.idata.itsNeedsActivation; }
private static int DoElemIncDec(Context cx, Interpreter.CallFrame frame, byte[] iCode, object[] stack, double[] sDbl, int stackTop) { object rhs = stack[stackTop]; if (rhs == UniqueTag.DOUBLE_MARK) { rhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; object lhs = stack[stackTop]; if (lhs == UniqueTag.DOUBLE_MARK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.ElemIncrDecr(lhs, rhs, cx, iCode[frame.pc]); ++frame.pc; return stackTop; }
private static int DoCallSpecial(Context cx, Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop, byte[] iCode, int indexReg) { int callType = iCode[frame.pc] & unchecked((int)(0xFF)); bool isNew = (iCode[frame.pc + 1] != 0); int sourceLine = GetIndex(iCode, frame.pc + 2); // indexReg: number of arguments if (isNew) { // stack change: function arg0 .. argN -> newResult stackTop -= indexReg; object function = stack[stackTop]; if (function == UniqueTag.DOUBLE_MARK) { function = ScriptRuntime.WrapNumber(sDbl[stackTop]); } object[] outArgs = GetArgsArray(stack, sDbl, stackTop + 1, indexReg); stack[stackTop] = ScriptRuntime.NewSpecial(cx, function, outArgs, frame.scope, callType); } else { // stack change: function thisObj arg0 .. argN -> result stackTop -= 1 + indexReg; // Call code generation ensure that stack here // is ... Callable Scriptable Scriptable functionThis = (Scriptable)stack[stackTop + 1]; Callable function = (Callable)stack[stackTop]; object[] outArgs = GetArgsArray(stack, sDbl, stackTop + 2, indexReg); stack[stackTop] = ScriptRuntime.CallSpecial(cx, function, functionThis, outArgs, frame.scope, frame.thisObj, callType, frame.idata.itsSourceFile, sourceLine); } frame.pc += 4; return stackTop; }
private static int DoBitOp(Interpreter.CallFrame frame, int op, object[] stack, double[] sDbl, int stackTop) { int lIntValue = Stack_int32(frame, stackTop - 1); int rIntValue = Stack_int32(frame, stackTop); stack[--stackTop] = UniqueTag.DOUBLE_MARK; switch (op) { case Token.BITAND: { lIntValue &= rIntValue; break; } case Token.BITOR: { lIntValue |= rIntValue; break; } case Token.BITXOR: { lIntValue ^= rIntValue; break; } case Token.LSH: { lIntValue <<= rIntValue; break; } case Token.RSH: { lIntValue >>= rIntValue; break; } } sDbl[stackTop] = lIntValue; return stackTop; }
private static int DoGetElem(Context cx, Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop) { --stackTop; object lhs = stack[stackTop]; if (lhs == UniqueTag.DOUBLE_MARK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } object value; object id = stack[stackTop + 1]; if (id != UniqueTag.DOUBLE_MARK) { value = ScriptRuntime.GetObjectElem(lhs, id, cx, frame.scope); } else { double d = sDbl[stackTop + 1]; value = ScriptRuntime.GetObjectIndex(lhs, d, cx); } stack[stackTop] = value; return stackTop; }
private static int DoCompare(Interpreter.CallFrame frame, int op, object[] stack, double[] sDbl, int stackTop) { --stackTop; object rhs = stack[stackTop + 1]; object lhs = stack[stackTop]; bool valBln; double rDbl; double lDbl; if (rhs == UniqueTag.DOUBLE_MARK) { rDbl = sDbl[stackTop + 1]; lDbl = Stack_double(frame, stackTop); } else { if (lhs == UniqueTag.DOUBLE_MARK) { rDbl = ScriptRuntime.ToNumber(rhs); lDbl = sDbl[stackTop]; } else { goto number_compare_break; } } switch (op) { case Token.GE: { valBln = (lDbl >= rDbl); goto object_compare_break; } case Token.LE: { valBln = (lDbl <= rDbl); goto object_compare_break; } case Token.GT: { valBln = (lDbl > rDbl); goto object_compare_break; } case Token.LT: { valBln = (lDbl < rDbl); goto object_compare_break; } default: { throw Kit.CodeBug(); } } number_compare_break: ; switch (op) { case Token.GE: { valBln = ScriptRuntime.Cmp_LE(rhs, lhs); break; } case Token.LE: { valBln = ScriptRuntime.Cmp_LE(lhs, rhs); break; } case Token.GT: { valBln = ScriptRuntime.Cmp_LT(rhs, lhs); break; } case Token.LT: { valBln = ScriptRuntime.Cmp_LT(lhs, rhs); break; } default: { throw Kit.CodeBug(); } } object_compare_break: ; stack[stackTop] = ScriptRuntime.WrapBoolean(valBln); return stackTop; }
private static int GetExceptionHandler(Interpreter.CallFrame frame, bool onlyFinally) { int[] exceptionTable = frame.idata.itsExceptionTable; if (exceptionTable == null) { // No exception handlers return -1; } // 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. int pc = frame.pc - 1; // OPT: use binary search int best = -1; int bestStart = 0; int bestEnd = 0; for (int i = 0; i != exceptionTable.Length; i += EXCEPTION_SLOT_SIZE) { int start = exceptionTable[i + EXCEPTION_TRY_START_SLOT]; int end = exceptionTable[i + EXCEPTION_TRY_END_SLOT]; if (!(start <= pc && pc < end)) { continue; } if (onlyFinally && exceptionTable[i + EXCEPTION_TYPE_SLOT] != 1) { continue; } if (best >= 0) { // Since handlers always nest and they never have shared end // although they can share start it is sufficient to compare // handlers ends if (bestEnd < end) { continue; } // Check the above assumption if (bestStart > start) { Kit.CodeBug(); } // should be nested if (bestEnd == end) { Kit.CodeBug(); } } // no ens sharing best = i; bestStart = start; bestEnd = end; } return best; }
private static Interpreter.CallFrame CaptureFrameForGenerator(Interpreter.CallFrame frame) { frame.frozen = true; Interpreter.CallFrame result = frame.CloneFrozen(); frame.frozen = false; // now isolate this frame from its previous context result.parentFrame = null; result.frameIndex = 0; return result; }
private static object FreezeGenerator(Context cx, Interpreter.CallFrame frame, int stackTop, Interpreter.GeneratorState generatorState) { if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) { // Error: no yields when generator is closing throw ScriptRuntime.TypeError0("msg.yield.closing"); } // return to our caller (which should be a method of NativeGenerator) frame.frozen = true; frame.result = frame.stack[stackTop]; frame.resultDbl = frame.sDbl[stackTop]; frame.savedStackTop = stackTop; frame.pc--; // we want to come back here when we resume ScriptRuntime.ExitActivationFunction(cx); return (frame.result != UniqueTag.DOUBLE_MARK) ? frame.result : ScriptRuntime.WrapNumber(frame.resultDbl); }
private static int DoSetConstVar(Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop, object[] vars, double[] varDbls, int[] varAttributes, int indexReg) { if (!frame.useActivation) { if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { throw Context.ReportRuntimeError1("msg.var.redecl", frame.idata.argNames[indexReg]); } if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST) != 0) { vars[indexReg] = stack[stackTop]; varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST; varDbls[indexReg] = sDbl[stackTop]; } } else { object val = stack[stackTop]; if (val == UniqueTag.DOUBLE_MARK) { val = ScriptRuntime.WrapNumber(sDbl[stackTop]); } string stringReg = frame.idata.argNames[indexReg]; if (frame.scope is ConstProperties) { ConstProperties cp = (ConstProperties)frame.scope; cp.PutConst(stringReg, frame.scope, val); } else { throw Kit.CodeBug(); } } return stackTop; }
private static object ThawGenerator(Interpreter.CallFrame frame, int stackTop, Interpreter.GeneratorState generatorState, int op) { // we are resuming execution frame.frozen = false; int sourceLine = GetIndex(frame.idata.itsICode, frame.pc); frame.pc += 2; // skip line number data if (generatorState.operation == NativeGenerator.GENERATOR_THROW) { // processing a call to <generator>.throw(exception): must // act as if exception was thrown from resumption point return new JavaScriptException(generatorState.value, frame.idata.itsSourceFile, sourceLine); } if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) { return generatorState.value; } if (generatorState.operation != NativeGenerator.GENERATOR_SEND) { throw Kit.CodeBug(); } if (op == Token.YIELD) { frame.stack[stackTop] = generatorState.value; } return ScriptableConstants.NOT_FOUND; }
private static int DoSetVar(Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop, object[] vars, double[] varDbls, int[] varAttributes, int indexReg) { if (!frame.useActivation) { if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { vars[indexReg] = stack[stackTop]; varDbls[indexReg] = sDbl[stackTop]; } } else { object val = stack[stackTop]; if (val == UniqueTag.DOUBLE_MARK) { val = ScriptRuntime.WrapNumber(sDbl[stackTop]); } string stringReg = frame.idata.argNames[indexReg]; frame.scope.Put(stringReg, frame.scope, val); } return stackTop; }
private static void InitFrame(Context cx, Scriptable callerScope, Scriptable thisObj, object[] args, double[] argsDbl, int argShift, int argCount, InterpretedFunction fnOrScript, Interpreter.CallFrame parentFrame, Interpreter.CallFrame frame) { InterpreterData idata = fnOrScript.idata; bool useActivation = idata.itsNeedsActivation; DebugFrame debuggerFrame = null; if (cx.debugger != null) { debuggerFrame = cx.debugger.GetFrame(cx, idata); if (debuggerFrame != null) { useActivation = true; } } if (useActivation) { // Copy args to new array to pass to enterActivationFunction // or debuggerFrame.onEnter if (argsDbl != null) { args = GetArgsArray(args, argsDbl, argShift, argCount); } argShift = 0; argsDbl = null; } Scriptable scope; if (idata.itsFunctionType != 0) { scope = fnOrScript.GetParentScope(); if (useActivation) { scope = ScriptRuntime.CreateFunctionActivation(fnOrScript, scope, args); } } else { scope = callerScope; ScriptRuntime.InitScript(fnOrScript, thisObj, cx, scope, fnOrScript.idata.evalScriptFlag); } if (idata.itsNestedFunctions != null) { if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) { Kit.CodeBug(); } for (int i = 0; i < idata.itsNestedFunctions.Length; i++) { InterpreterData fdata = idata.itsNestedFunctions[i]; if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) { InitFunction(cx, scope, fnOrScript, i); } } } // Initialize args, vars, locals and stack int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1; int maxFrameArray = idata.itsMaxFrameArray; if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1) { Kit.CodeBug(); } object[] stack; int[] stackAttributes; double[] sDbl; bool stackReuse; if (frame.stack != null && maxFrameArray <= frame.stack.Length) { // Reuse stacks from old frame stackReuse = true; stack = frame.stack; stackAttributes = frame.stackAttributes; sDbl = frame.sDbl; } else { stackReuse = false; stack = new object[maxFrameArray]; stackAttributes = new int[maxFrameArray]; sDbl = new double[maxFrameArray]; } int varCount = idata.GetParamAndVarCount(); for (int i_1 = 0; i_1 < varCount; i_1++) { if (idata.GetParamOrVarConst(i_1)) { stackAttributes[i_1] = ScriptableObject.CONST; } } int definedArgs = idata.argCount; if (definedArgs > argCount) { definedArgs = argCount; } // Fill the frame structure frame.parentFrame = parentFrame; frame.frameIndex = (parentFrame == null) ? 0 : parentFrame.frameIndex + 1; if (frame.frameIndex > cx.GetMaximumInterpreterStackDepth()) { throw Context.ReportRuntimeError("Exceeded maximum stack depth"); } frame.frozen = false; frame.fnOrScript = fnOrScript; frame.idata = idata; frame.stack = stack; frame.stackAttributes = stackAttributes; frame.sDbl = sDbl; frame.varSource = frame; frame.localShift = idata.itsMaxVars; frame.emptyStackTop = emptyStackTop; frame.debuggerFrame = debuggerFrame; frame.useActivation = useActivation; frame.thisObj = thisObj; // Initialize initial values of variables that change during // interpretation. frame.result = Undefined.instance; frame.pc = 0; frame.pcPrevBranch = 0; frame.pcSourceLineStart = idata.firstLinePC; frame.scope = scope; frame.savedStackTop = emptyStackTop; frame.savedCallOp = 0; System.Array.Copy(args, argShift, stack, 0, definedArgs); if (argsDbl != null) { System.Array.Copy(argsDbl, argShift, sDbl, 0, definedArgs); } for (int i_2 = definedArgs; i_2 != idata.itsMaxVars; ++i_2) { stack[i_2] = Undefined.instance; } if (stackReuse) { // Clean the stack part and space beyond stack if any // of the old array to allow to GC objects there for (int i = emptyStackTop + 1; i_2 != stack.Length; ++i_2) { stack[i_2] = null; } } EnterFrame(cx, frame, args, false); }
private static int DoGetVar(Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop, object[] vars, double[] varDbls, int indexReg) { ++stackTop; if (!frame.useActivation) { stack[stackTop] = vars[indexReg]; sDbl[stackTop] = varDbls[indexReg]; } else { string stringReg = frame.idata.argNames[indexReg]; stack[stackTop] = frame.scope.Get(stringReg, frame.scope); } return stackTop; }
private static void EnterFrame(Context cx, Interpreter.CallFrame frame, object[] args, bool continuationRestart) { bool usesActivation = frame.idata.itsNeedsActivation; bool isDebugged = frame.debuggerFrame != null; if (usesActivation || isDebugged) { Scriptable scope = frame.scope; if (scope == null) { Kit.CodeBug(); } else { if (continuationRestart) { // Walk the parent chain of frame.scope until a NativeCall is // found. Normally, frame.scope is a NativeCall when called // from initFrame() for a debugged or activatable function. // However, when called from interpretLoop() as part of // restarting a continuation, it can also be a NativeWith if // the continuation was captured within a "with" or "catch" // block ("catch" implicitly uses NativeWith to create a scope // to expose the exception variable). for (; ; ) { if (scope is NativeWith) { scope = scope.GetParentScope(); if (scope == null || (frame.parentFrame != null && frame.parentFrame.scope == scope)) { // If we get here, we didn't find a NativeCall in // the call chain before reaching parent frame's // scope. This should not be possible. Kit.CodeBug(); break; } } else { // Never reached, but keeps the static analyzer // happy about "scope" not being null 5 lines above. break; } } } } if (isDebugged) { frame.debuggerFrame.OnEnter(cx, scope, frame.thisObj, args); } // Enter activation only when itsNeedsActivation true, // since debugger should not interfere with activation // chaining if (usesActivation) { ScriptRuntime.EnterActivationFunction(cx, scope); } } }
private static int DoVarIncDec(Context cx, Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop, object[] vars, double[] varDbls, int indexReg) { // indexReg : varindex ++stackTop; int incrDecrMask = frame.idata.itsICode[frame.pc]; if (!frame.useActivation) { stack[stackTop] = UniqueTag.DOUBLE_MARK; object varValue = vars[indexReg]; double d; if (varValue == UniqueTag.DOUBLE_MARK) { d = varDbls[indexReg]; } else { d = ScriptRuntime.ToNumber(varValue); vars[indexReg] = UniqueTag.DOUBLE_MARK; } double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0) ? d + 1.0 : d - 1.0; varDbls[indexReg] = d2; sDbl[stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2 : d; } else { string varName = frame.idata.argNames[indexReg]; stack[stackTop] = ScriptRuntime.NameIncrDecr(frame.scope, varName, cx, incrDecrMask); } ++frame.pc; return stackTop; }
private static void SetCallResult(Interpreter.CallFrame frame, object callResult, double callResultDbl) { if (frame.savedCallOp == Token.CALL) { frame.stack[frame.savedStackTop] = callResult; frame.sDbl[frame.savedStackTop] = callResultDbl; } else { if (frame.savedCallOp == Token.NEW) { // If construct returns scriptable, // then it replaces on stack top saved original instance // of the object. if (callResult is Scriptable) { frame.stack[frame.savedStackTop] = callResult; } } else { Kit.CodeBug(); } } frame.savedCallOp = 0; }
private static int DoRefNsName(Context cx, Interpreter.CallFrame frame, object[] stack, double[] sDbl, int stackTop, int flags) { object name = stack[stackTop]; if (name == UniqueTag.DOUBLE_MARK) { name = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; object ns = stack[stackTop]; if (ns == UniqueTag.DOUBLE_MARK) { ns = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.NameRef(ns, name, cx, frame.scope, flags); return stackTop; }
private static int Stack_int32(Interpreter.CallFrame frame, int i) { object x = frame.stack[i]; if (x == UniqueTag.DOUBLE_MARK) { return ScriptRuntime.ToInt32(frame.sDbl[i]); } else { return ScriptRuntime.ToInt32(x); } }
/// <summary>Call __noSuchMethod__.</summary> /// <remarks>Call __noSuchMethod__.</remarks> private static Interpreter.CallFrame InitFrameForNoSuchMethod(Context cx, Interpreter.CallFrame frame, int indexReg, object[] stack, double[] sDbl, int stackTop, int op, Scriptable funThisObj, Scriptable calleeScope, ScriptRuntime.NoSuchMethodShim noSuchMethodShim, InterpretedFunction ifun) { // create an args array from the stack object[] argsArray = null; // exactly like getArgsArray except that the first argument // is the method name from the shim int shift = stackTop + 2; object[] elements = new object[indexReg]; for (int i = 0; i < indexReg; ++i, ++shift) { object val = stack[shift]; if (val == UniqueTag.DOUBLE_MARK) { val = ScriptRuntime.WrapNumber(sDbl[shift]); } elements[i] = val; } argsArray = new object[2]; argsArray[0] = noSuchMethodShim.methodName; argsArray[1] = cx.NewArray(calleeScope, elements); // exactly the same as if it's a regular InterpretedFunction Interpreter.CallFrame callParentFrame = frame; Interpreter.CallFrame calleeFrame = new Interpreter.CallFrame(); if (op == Icode_TAIL_CALL) { callParentFrame = frame.parentFrame; ExitFrame(cx, frame, null); } // init the frame with the underlying method with the // adjusted args array and shim's function InitFrame(cx, calleeScope, funThisObj, argsArray, null, 0, 2, ifun, callParentFrame, calleeFrame); if (op != Icode_TAIL_CALL) { frame.savedStackTop = stackTop; frame.savedCallOp = op; } return calleeFrame; }
private static bool Stack_boolean(Interpreter.CallFrame frame, int i) { object x = frame.stack[i]; if (x == true) { return true; } else { if (x == false) { return false; } else { if (x == UniqueTag.DOUBLE_MARK) { double d = frame.sDbl[i]; return d == d && d != 0.0; } else { if (x == null || x == Undefined.instance) { return false; } else { if (x is Number) { double d = System.Convert.ToDouble(((Number)x)); return (d == d && d != 0.0); } else { if (x is bool) { return ((bool)x); } else { return ScriptRuntime.ToBoolean(x); } } } } } } }
private static Interpreter.CallFrame ProcessThrowable(Context cx, object throwable, Interpreter.CallFrame frame, int indexReg, bool instructionCounting) { // Recovering from exception, indexReg contains // the index of handler if (indexReg >= 0) { // Normal exception handler, transfer // control appropriately if (frame.frozen) { // XXX Deal with exceptios!!! frame = frame.CloneFrozen(); } int[] table = frame.idata.itsExceptionTable; frame.pc = table[indexReg + EXCEPTION_HANDLER_SLOT]; if (instructionCounting) { frame.pcPrevBranch = frame.pc; } frame.savedStackTop = frame.emptyStackTop; int scopeLocal = frame.localShift + table[indexReg + EXCEPTION_SCOPE_SLOT]; int exLocal = frame.localShift + table[indexReg + EXCEPTION_LOCAL_SLOT]; frame.scope = (Scriptable)frame.stack[scopeLocal]; frame.stack[exLocal] = throwable; throwable = null; } else { // Continuation restoration Interpreter.ContinuationJump cjump = (Interpreter.ContinuationJump)throwable; // Clear throwable to indicate that exceptions are OK throwable = null; if (cjump.branchFrame != frame) { Kit.CodeBug(); } // Check that we have at least one frozen frame // in the case of detached continuation restoration: // unwind code ensure that if (cjump.capturedFrame == null) { Kit.CodeBug(); } // Need to rewind branchFrame, capturedFrame // and all frames in between int rewindCount = cjump.capturedFrame.frameIndex + 1; if (cjump.branchFrame != null) { rewindCount -= cjump.branchFrame.frameIndex; } int enterCount = 0; Interpreter.CallFrame[] enterFrames = null; Interpreter.CallFrame x = cjump.capturedFrame; for (int i = 0; i != rewindCount; ++i) { if (!x.frozen) { Kit.CodeBug(); } if (IsFrameEnterExitRequired(x)) { if (enterFrames == null) { // Allocate enough space to store the rest // of rewind frames in case all of them // would require to enter enterFrames = new Interpreter.CallFrame[rewindCount - i]; } enterFrames[enterCount] = x; ++enterCount; } x = x.parentFrame; } while (enterCount != 0) { // execute enter: walk enterFrames in the reverse // order since they were stored starting from // the capturedFrame, not branchFrame --enterCount; x = enterFrames[enterCount]; EnterFrame(cx, x, ScriptRuntime.emptyArgs, true); } // Continuation jump is almost done: capturedFrame // points to the call to the function that captured // continuation, so clone capturedFrame and // emulate return that function with the suplied result frame = cjump.capturedFrame.CloneFrozen(); SetCallResult(frame, cjump.result, cjump.resultDbl); } // restart the execution frame.throwable = throwable; return frame; }
private static void AddInstructionCount(Context cx, Interpreter.CallFrame frame, int extra) { cx.instructionCount += frame.pc - frame.pcPrevBranch + extra; if (cx.instructionCount > cx.instructionThreshold) { cx.ObserveInstructionCount(cx.instructionCount); cx.instructionCount = 0; } }
private static object InterpretLoop(Context cx, Interpreter.CallFrame frame, object throwable) { // throwable holds exception object to rethrow or catch // It is also used for continuation restart in which case // it holds ContinuationJump object DBL_MRK = UniqueTag.DOUBLE_MARK; object undefined = Undefined.instance; bool instructionCounting = (cx.instructionThreshold != 0); // arbitrary number to add to instructionCount when calling // other functions int INVOCATION_COST = 100; // arbitrary exception cost for instruction counting int EXCEPTION_COST = 100; string stringReg = null; int indexReg = -1; if (cx.lastInterpreterFrame != null) { // save the top frame from the previous interpretLoop // invocation on the stack if (cx.previousInterpreterInvocations == null) { cx.previousInterpreterInvocations = new ObjArray(); } cx.previousInterpreterInvocations.Push(cx.lastInterpreterFrame); } // When restarting continuation throwable is not null and to jump // to the code that rewind continuation state indexReg should be set // to -1. // With the normal call throwable == null and indexReg == -1 allows to // catch bugs with using indeReg to access array elements before // initializing indexReg. Interpreter.GeneratorState generatorState = null; if (throwable != null) { if (throwable is Interpreter.GeneratorState) { generatorState = (Interpreter.GeneratorState)throwable; // reestablish this call frame EnterFrame(cx, frame, ScriptRuntime.emptyArgs, true); throwable = null; } else { if (!(throwable is Interpreter.ContinuationJump)) { // It should be continuation Kit.CodeBug(); } } } object interpreterResult = null; double interpreterResultDbl = 0.0; for (; ; ) { try { if (throwable != null) { // Need to return both 'frame' and 'throwable' from // 'processThrowable', so just added a 'throwable' // member in 'frame'. frame = ProcessThrowable(cx, throwable, frame, indexReg, instructionCounting); throwable = frame.throwable; frame.throwable = null; } else { if (generatorState == null && frame.frozen) { Kit.CodeBug(); } } // Use local variables for constant values in frame // for faster access object[] stack = frame.stack; double[] sDbl = frame.sDbl; object[] vars = frame.varSource.stack; double[] varDbls = frame.varSource.sDbl; int[] varAttributes = frame.varSource.stackAttributes; byte[] iCode = frame.idata.itsICode; string[] strings = frame.idata.itsStringTable; // Use local for stackTop as well. Since execption handlers // can only exist at statement level where stack is empty, // it is necessary to save/restore stackTop only across // function calls and normal returns. int stackTop = frame.savedStackTop; // Store new frame in cx which is used for error reporting etc. cx.lastInterpreterFrame = frame; for (; ; ) { // Exception handler assumes that PC is already incremented // pass the instruction start when it searches the // exception handler int op = iCode[frame.pc++]; switch (op) { case Icode_GENERATOR: { // Back indent to ease implementation reading if (!frame.frozen) { // First time encountering this opcode: create new generator // object and return frame.pc--; // we want to come back here when we resume Interpreter.CallFrame generatorFrame = CaptureFrameForGenerator(frame); generatorFrame.frozen = true; NativeGenerator generator = new NativeGenerator(frame.scope, generatorFrame.fnOrScript, generatorFrame); frame.result = generator; goto Loop_break; } goto case Token.YIELD; } case Token.YIELD: { // We are now resuming execution. Fall through to YIELD case. // fall through... if (!frame.frozen) { return FreezeGenerator(cx, frame, stackTop, generatorState); } else { object obj = ThawGenerator(frame, stackTop, generatorState, op); if (obj != ScriptableConstants.NOT_FOUND) { throwable = obj; goto withoutExceptions_break; } goto Loop_continue; } goto case Icode_GENERATOR_END; } case Icode_GENERATOR_END: { // throw StopIteration frame.frozen = true; int sourceLine = GetIndex(iCode, frame.pc); generatorState.returnedException = new JavaScriptException(NativeIterator.GetStopIterationObject(frame.scope), frame.idata.itsSourceFile, sourceLine); goto Loop_break; } case Token.THROW: { object value = stack[stackTop]; if (value == DBL_MRK) { value = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; int sourceLine = GetIndex(iCode, frame.pc); throwable = new JavaScriptException(value, frame.idata.itsSourceFile, sourceLine); goto withoutExceptions_break; } case Token.RETHROW: { indexReg += frame.localShift; throwable = stack[indexReg]; goto withoutExceptions_break; } case Token.GE: case Token.LE: case Token.GT: case Token.LT: { stackTop = DoCompare(frame, op, stack, sDbl, stackTop); goto Loop_continue; } case Token.IN: case Token.INSTANCEOF: { stackTop = DoInOrInstanceof(cx, op, stack, sDbl, stackTop); goto Loop_continue; } case Token.EQ: case Token.NE: { --stackTop; bool valBln = DoEquals(stack, sDbl, stackTop); valBln ^= (op == Token.NE); stack[stackTop] = ScriptRuntime.WrapBoolean(valBln); goto Loop_continue; } case Token.SHEQ: case Token.SHNE: { --stackTop; bool valBln = DoShallowEquals(stack, sDbl, stackTop); valBln ^= (op == Token.SHNE); stack[stackTop] = ScriptRuntime.WrapBoolean(valBln); goto Loop_continue; } case Token.IFNE: { if (Stack_boolean(frame, stackTop--)) { frame.pc += 2; goto Loop_continue; } goto jumplessRun_break; } case Token.IFEQ: { if (!Stack_boolean(frame, stackTop--)) { frame.pc += 2; goto Loop_continue; } goto jumplessRun_break; } case Icode_IFEQ_POP: { if (!Stack_boolean(frame, stackTop--)) { frame.pc += 2; goto Loop_continue; } stack[stackTop--] = null; goto jumplessRun_break; } case Token.GOTO: { goto jumplessRun_break; } case Icode_GOSUB: { ++stackTop; stack[stackTop] = DBL_MRK; sDbl[stackTop] = frame.pc + 2; goto jumplessRun_break; } case Icode_STARTSUB: { if (stackTop == frame.emptyStackTop + 1) { // Call from Icode_GOSUB: store return PC address in the local indexReg += frame.localShift; stack[indexReg] = stack[stackTop]; sDbl[indexReg] = sDbl[stackTop]; --stackTop; } else { // Call from exception handler: exception object is already stored // in the local if (stackTop != frame.emptyStackTop) { Kit.CodeBug(); } } goto Loop_continue; } case Icode_RETSUB: { // indexReg: local to store return address if (instructionCounting) { AddInstructionCount(cx, frame, 0); } indexReg += frame.localShift; object value = stack[indexReg]; if (value != DBL_MRK) { // Invocation from exception handler, restore object to rethrow throwable = value; goto withoutExceptions_break; } // Normal return from GOSUB frame.pc = (int)sDbl[indexReg]; if (instructionCounting) { frame.pcPrevBranch = frame.pc; } goto Loop_continue; } case Icode_POP: { stack[stackTop] = null; stackTop--; goto Loop_continue; } case Icode_POP_RESULT: { frame.result = stack[stackTop]; frame.resultDbl = sDbl[stackTop]; stack[stackTop] = null; --stackTop; goto Loop_continue; } case Icode_DUP: { stack[stackTop + 1] = stack[stackTop]; sDbl[stackTop + 1] = sDbl[stackTop]; stackTop++; goto Loop_continue; } case Icode_DUP2: { stack[stackTop + 1] = stack[stackTop - 1]; sDbl[stackTop + 1] = sDbl[stackTop - 1]; stack[stackTop + 2] = stack[stackTop]; sDbl[stackTop + 2] = sDbl[stackTop]; stackTop += 2; goto Loop_continue; } case Icode_SWAP: { object o = stack[stackTop]; stack[stackTop] = stack[stackTop - 1]; stack[stackTop - 1] = o; double d = sDbl[stackTop]; sDbl[stackTop] = sDbl[stackTop - 1]; sDbl[stackTop - 1] = d; goto Loop_continue; } case Token.RETURN: { frame.result = stack[stackTop]; frame.resultDbl = sDbl[stackTop]; --stackTop; goto Loop_break; } case Token.RETURN_RESULT: { goto Loop_break; } case Icode_RETUNDEF: { frame.result = undefined; goto Loop_break; } case Token.BITNOT: { int rIntValue = Stack_int32(frame, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = ~rIntValue; goto Loop_continue; } case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.LSH: case Token.RSH: { stackTop = DoBitOp(frame, op, stack, sDbl, stackTop); goto Loop_continue; } case Token.URSH: { double lDbl = Stack_double(frame, stackTop - 1); int rIntValue = Stack_int32(frame, stackTop) & unchecked((int)(0x1F)); stack[--stackTop] = DBL_MRK; sDbl[stackTop] = (long)(((ulong)ScriptRuntime.ToUint32(lDbl)) >> rIntValue); goto Loop_continue; } case Token.NEG: case Token.POS: { double rDbl = Stack_double(frame, stackTop); stack[stackTop] = DBL_MRK; if (op == Token.NEG) { rDbl = -rDbl; } sDbl[stackTop] = rDbl; goto Loop_continue; } case Token.ADD: { --stackTop; DoAdd(stack, sDbl, stackTop, cx); goto Loop_continue; } case Token.SUB: case Token.MUL: case Token.DIV: case Token.MOD: { stackTop = DoArithmetic(frame, op, stack, sDbl, stackTop); goto Loop_continue; } case Token.NOT: { stack[stackTop] = ScriptRuntime.WrapBoolean(!Stack_boolean(frame, stackTop)); goto Loop_continue; } case Token.BINDNAME: { stack[++stackTop] = ScriptRuntime.Bind(cx, frame.scope, stringReg); goto Loop_continue; } case Token.STRICT_SETNAME: case Token.SETNAME: { object rhs = stack[stackTop]; if (rhs == DBL_MRK) { rhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; Scriptable lhs = (Scriptable)stack[stackTop]; stack[stackTop] = op == Token.SETNAME ? ScriptRuntime.SetName(lhs, rhs, cx, frame.scope, stringReg) : ScriptRuntime.StrictSetName(lhs, rhs, cx, frame.scope, stringReg); goto Loop_continue; } case Icode_SETCONST: { object rhs = stack[stackTop]; if (rhs == DBL_MRK) { rhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; Scriptable lhs = (Scriptable)stack[stackTop]; stack[stackTop] = ScriptRuntime.SetConst(lhs, rhs, cx, stringReg); goto Loop_continue; } case Token.DELPROP: case Icode_DELNAME: { stackTop = DoDelName(cx, op, stack, sDbl, stackTop); goto Loop_continue; } case Token.GETPROPNOWARN: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.GetObjectPropNoWarn(lhs, stringReg, cx); goto Loop_continue; } case Token.GETPROP: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.GetObjectProp(lhs, stringReg, cx, frame.scope); goto Loop_continue; } case Token.SETPROP: { object rhs = stack[stackTop]; if (rhs == DBL_MRK) { rhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.SetObjectProp(lhs, stringReg, rhs, cx); goto Loop_continue; } case Icode_PROP_INC_DEC: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.PropIncrDecr(lhs, stringReg, cx, iCode[frame.pc]); ++frame.pc; goto Loop_continue; } case Token.GETELEM: { stackTop = DoGetElem(cx, frame, stack, sDbl, stackTop); goto Loop_continue; } case Token.SETELEM: { stackTop = DoSetElem(cx, stack, sDbl, stackTop); goto Loop_continue; } case Icode_ELEM_INC_DEC: { stackTop = DoElemIncDec(cx, frame, iCode, stack, sDbl, stackTop); goto Loop_continue; } case Token.GET_REF: { Ref @ref = (Ref)stack[stackTop]; stack[stackTop] = ScriptRuntime.RefGet(@ref, cx); goto Loop_continue; } case Token.SET_REF: { object value = stack[stackTop]; if (value == DBL_MRK) { value = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; Ref @ref = (Ref)stack[stackTop]; stack[stackTop] = ScriptRuntime.RefSet(@ref, value, cx); goto Loop_continue; } case Token.DEL_REF: { Ref @ref = (Ref)stack[stackTop]; stack[stackTop] = ScriptRuntime.RefDel(@ref, cx); goto Loop_continue; } case Icode_REF_INC_DEC: { Ref @ref = (Ref)stack[stackTop]; stack[stackTop] = ScriptRuntime.RefIncrDecr(@ref, cx, iCode[frame.pc]); ++frame.pc; goto Loop_continue; } case Token.LOCAL_LOAD: { ++stackTop; indexReg += frame.localShift; stack[stackTop] = stack[indexReg]; sDbl[stackTop] = sDbl[indexReg]; goto Loop_continue; } case Icode_LOCAL_CLEAR: { indexReg += frame.localShift; stack[indexReg] = null; goto Loop_continue; } case Icode_NAME_AND_THIS: { // stringReg: name ++stackTop; stack[stackTop] = ScriptRuntime.GetNameFunctionAndThis(stringReg, cx, frame.scope); ++stackTop; stack[stackTop] = ScriptRuntime.LastStoredScriptable(cx); goto Loop_continue; } case Icode_PROP_AND_THIS: { object obj = stack[stackTop]; if (obj == DBL_MRK) { obj = ScriptRuntime.WrapNumber(sDbl[stackTop]); } // stringReg: property stack[stackTop] = ScriptRuntime.GetPropFunctionAndThis(obj, stringReg, cx, frame.scope); ++stackTop; stack[stackTop] = ScriptRuntime.LastStoredScriptable(cx); goto Loop_continue; } case Icode_ELEM_AND_THIS: { object obj = stack[stackTop - 1]; if (obj == DBL_MRK) { obj = ScriptRuntime.WrapNumber(sDbl[stackTop - 1]); } object id = stack[stackTop]; if (id == DBL_MRK) { id = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop - 1] = ScriptRuntime.GetElemFunctionAndThis(obj, id, cx); stack[stackTop] = ScriptRuntime.LastStoredScriptable(cx); goto Loop_continue; } case Icode_VALUE_AND_THIS: { object value = stack[stackTop]; if (value == DBL_MRK) { value = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.GetValueFunctionAndThis(value, cx); ++stackTop; stack[stackTop] = ScriptRuntime.LastStoredScriptable(cx); goto Loop_continue; } case Icode_CALLSPECIAL: { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } stackTop = DoCallSpecial(cx, frame, stack, sDbl, stackTop, iCode, indexReg); goto Loop_continue; } case Token.CALL: case Icode_TAIL_CALL: case Token.REF_CALL: { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } // stack change: function thisObj arg0 .. argN -> result // indexReg: number of arguments stackTop -= 1 + indexReg; // CALL generation ensures that fun and funThisObj // are already Scriptable and Callable objects respectively Callable fun = (Callable)stack[stackTop]; Scriptable funThisObj = (Scriptable)stack[stackTop + 1]; if (op == Token.REF_CALL) { object[] outArgs = GetArgsArray(stack, sDbl, stackTop + 2, indexReg); stack[stackTop] = ScriptRuntime.CallRef(fun, funThisObj, outArgs, cx); goto Loop_continue; } Scriptable calleeScope = frame.scope; if (frame.useActivation) { calleeScope = ScriptableObject.GetTopLevelScope(frame.scope); } if (fun is InterpretedFunction) { InterpretedFunction ifun = (InterpretedFunction)fun; if (frame.fnOrScript.securityDomain == ifun.securityDomain) { Interpreter.CallFrame callParentFrame = frame; Interpreter.CallFrame calleeFrame = new Interpreter.CallFrame(); if (op == Icode_TAIL_CALL) { // In principle tail call can re-use the current // frame and its stack arrays but it is hard to // do properly. Any exceptions that can legally // happen during frame re-initialization including // StackOverflowException during innocent looking // System.arraycopy may leave the current frame // data corrupted leading to undefined behaviour // in the catch code bellow that unwinds JS stack // on exceptions. Then there is issue about frame release // end exceptions there. // To avoid frame allocation a released frame // can be cached for re-use which would also benefit // non-tail calls but it is not clear that this caching // would gain in performance due to potentially // bad interaction with GC. callParentFrame = frame.parentFrame; // Release the current frame. See Bug #344501 to see why // it is being done here. ExitFrame(cx, frame, null); } InitFrame(cx, calleeScope, funThisObj, stack, sDbl, stackTop + 2, indexReg, ifun, callParentFrame, calleeFrame); if (op != Icode_TAIL_CALL) { frame.savedStackTop = stackTop; frame.savedCallOp = op; } frame = calleeFrame; goto StateLoop_continue; } } if (fun is NativeContinuation) { // Jump to the captured continuation Interpreter.ContinuationJump cjump; cjump = new Interpreter.ContinuationJump((NativeContinuation)fun, frame); // continuation result is the first argument if any // of continuation call if (indexReg == 0) { cjump.result = undefined; } else { cjump.result = stack[stackTop + 2]; cjump.resultDbl = sDbl[stackTop + 2]; } // Start the real unwind job throwable = cjump; goto withoutExceptions_break; } if (fun is IdFunctionObject) { IdFunctionObject ifun = (IdFunctionObject)fun; if (NativeContinuation.IsContinuationConstructor(ifun)) { frame.stack[stackTop] = CaptureContinuation(cx, frame.parentFrame, false); goto Loop_continue; } // Bug 405654 -- make best effort to keep Function.apply and // Function.call within this interpreter loop invocation if (BaseFunction.IsApplyOrCall(ifun)) { Callable applyCallable = ScriptRuntime.GetCallable(funThisObj); if (applyCallable is InterpretedFunction) { InterpretedFunction iApplyCallable = (InterpretedFunction)applyCallable; if (frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) { frame = InitFrameForApplyOrCall(cx, frame, indexReg, stack, sDbl, stackTop, op, calleeScope, ifun, iApplyCallable); goto StateLoop_continue; } } } } // Bug 447697 -- make best effort to keep __noSuchMethod__ within this // interpreter loop invocation if (fun is ScriptRuntime.NoSuchMethodShim) { // get the shim and the actual method ScriptRuntime.NoSuchMethodShim noSuchMethodShim = (ScriptRuntime.NoSuchMethodShim)fun; Callable noSuchMethodMethod = noSuchMethodShim.noSuchMethodMethod; // if the method is in fact an InterpretedFunction if (noSuchMethodMethod is InterpretedFunction) { InterpretedFunction ifun = (InterpretedFunction)noSuchMethodMethod; if (frame.fnOrScript.securityDomain == ifun.securityDomain) { frame = InitFrameForNoSuchMethod(cx, frame, indexReg, stack, sDbl, stackTop, op, funThisObj, calleeScope, noSuchMethodShim, ifun); goto StateLoop_continue; } } } cx.lastInterpreterFrame = frame; frame.savedCallOp = op; frame.savedStackTop = stackTop; stack[stackTop] = fun.Call(cx, calleeScope, funThisObj, GetArgsArray(stack, sDbl, stackTop + 2, indexReg)); goto Loop_continue; } case Token.NEW: { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } // stack change: function arg0 .. argN -> newResult // indexReg: number of arguments stackTop -= indexReg; object lhs = stack[stackTop]; if (lhs is InterpretedFunction) { InterpretedFunction f = (InterpretedFunction)lhs; if (frame.fnOrScript.securityDomain == f.securityDomain) { Scriptable newInstance = f.CreateObject(cx, frame.scope); Interpreter.CallFrame calleeFrame = new Interpreter.CallFrame(); InitFrame(cx, frame.scope, newInstance, stack, sDbl, stackTop + 1, indexReg, f, frame, calleeFrame); stack[stackTop] = newInstance; frame.savedStackTop = stackTop; frame.savedCallOp = op; frame = calleeFrame; goto StateLoop_continue; } } if (!(lhs is Function)) { if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } throw ScriptRuntime.NotFunctionError(lhs); } Function fun = (Function)lhs; if (fun is IdFunctionObject) { IdFunctionObject ifun = (IdFunctionObject)fun; if (NativeContinuation.IsContinuationConstructor(ifun)) { frame.stack[stackTop] = CaptureContinuation(cx, frame.parentFrame, false); goto Loop_continue; } } object[] outArgs = GetArgsArray(stack, sDbl, stackTop + 1, indexReg); stack[stackTop] = fun.Construct(cx, frame.scope, outArgs); goto Loop_continue; } case Token.TYPEOF: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.Typeof(lhs); goto Loop_continue; } case Icode_TYPEOFNAME: { stack[++stackTop] = ScriptRuntime.TypeofName(frame.scope, stringReg); goto Loop_continue; } case Token.STRING: { stack[++stackTop] = stringReg; goto Loop_continue; } case Icode_SHORTNUMBER: { ++stackTop; stack[stackTop] = DBL_MRK; sDbl[stackTop] = GetShort(iCode, frame.pc); frame.pc += 2; goto Loop_continue; } case Icode_INTNUMBER: { ++stackTop; stack[stackTop] = DBL_MRK; sDbl[stackTop] = GetInt(iCode, frame.pc); frame.pc += 4; goto Loop_continue; } case Token.NUMBER: { ++stackTop; stack[stackTop] = DBL_MRK; sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg]; goto Loop_continue; } case Token.NAME: { stack[++stackTop] = ScriptRuntime.Name(cx, frame.scope, stringReg); goto Loop_continue; } case Icode_NAME_INC_DEC: { stack[++stackTop] = ScriptRuntime.NameIncrDecr(frame.scope, stringReg, cx, iCode[frame.pc]); ++frame.pc; goto Loop_continue; } case Icode_SETCONSTVAR1: { indexReg = iCode[frame.pc++]; goto case Token.SETCONSTVAR; } case Token.SETCONSTVAR: { // fallthrough stackTop = DoSetConstVar(frame, stack, sDbl, stackTop, vars, varDbls, varAttributes, indexReg); goto Loop_continue; } case Icode_SETVAR1: { indexReg = iCode[frame.pc++]; goto case Token.SETVAR; } case Token.SETVAR: { // fallthrough stackTop = DoSetVar(frame, stack, sDbl, stackTop, vars, varDbls, varAttributes, indexReg); goto Loop_continue; } case Icode_GETVAR1: { indexReg = iCode[frame.pc++]; goto case Token.GETVAR; } case Token.GETVAR: { // fallthrough stackTop = DoGetVar(frame, stack, sDbl, stackTop, vars, varDbls, indexReg); goto Loop_continue; } case Icode_VAR_INC_DEC: { stackTop = DoVarIncDec(cx, frame, stack, sDbl, stackTop, vars, varDbls, indexReg); goto Loop_continue; } case Icode_ZERO: { ++stackTop; stack[stackTop] = DBL_MRK; sDbl[stackTop] = 0; goto Loop_continue; } case Icode_ONE: { ++stackTop; stack[stackTop] = DBL_MRK; sDbl[stackTop] = 1; goto Loop_continue; } case Token.NULL: { stack[++stackTop] = null; goto Loop_continue; } case Token.THIS: { stack[++stackTop] = frame.thisObj; goto Loop_continue; } case Token.THISFN: { stack[++stackTop] = frame.fnOrScript; goto Loop_continue; } case Token.FALSE: { stack[++stackTop] = false; goto Loop_continue; } case Token.TRUE: { stack[++stackTop] = true; goto Loop_continue; } case Icode_UNDEF: { stack[++stackTop] = undefined; goto Loop_continue; } case Token.ENTERWITH: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; frame.scope = ScriptRuntime.EnterWith(lhs, cx, frame.scope); goto Loop_continue; } case Token.LEAVEWITH: { frame.scope = ScriptRuntime.LeaveWith(frame.scope); goto Loop_continue; } case Token.CATCH_SCOPE: { // stack top: exception object // stringReg: name of exception variable // indexReg: local for exception scope --stackTop; indexReg += frame.localShift; bool afterFirstScope = (frame.idata.itsICode[frame.pc] != 0); Exception caughtException = (Exception)stack[stackTop + 1]; Scriptable lastCatchScope; if (!afterFirstScope) { lastCatchScope = null; } else { lastCatchScope = (Scriptable)stack[indexReg]; } stack[indexReg] = ScriptRuntime.NewCatchScope(caughtException, lastCatchScope, stringReg, cx, frame.scope); ++frame.pc; goto Loop_continue; } case Token.ENUM_INIT_KEYS: case Token.ENUM_INIT_VALUES: case Token.ENUM_INIT_ARRAY: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; indexReg += frame.localShift; int enumType = op == Token.ENUM_INIT_KEYS ? ScriptRuntime.ENUMERATE_KEYS : op == Token.ENUM_INIT_VALUES ? ScriptRuntime.ENUMERATE_VALUES : ScriptRuntime.ENUMERATE_ARRAY; stack[indexReg] = ScriptRuntime.EnumInit(lhs, cx, enumType); goto Loop_continue; } case Token.ENUM_NEXT: case Token.ENUM_ID: { indexReg += frame.localShift; object val = stack[indexReg]; ++stackTop; stack[stackTop] = (op == Token.ENUM_NEXT) ? (object)ScriptRuntime.EnumNext(val) : (object)ScriptRuntime.EnumId(val, cx); goto Loop_continue; } case Token.REF_SPECIAL: { //stringReg: name of special property object obj = stack[stackTop]; if (obj == DBL_MRK) { obj = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.SpecialRef(obj, stringReg, cx); goto Loop_continue; } case Token.REF_MEMBER: { //indexReg: flags stackTop = DoRefMember(cx, stack, sDbl, stackTop, indexReg); goto Loop_continue; } case Token.REF_NS_MEMBER: { //indexReg: flags stackTop = DoRefNsMember(cx, stack, sDbl, stackTop, indexReg); goto Loop_continue; } case Token.REF_NAME: { //indexReg: flags object name = stack[stackTop]; if (name == DBL_MRK) { name = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.NameRef(name, cx, frame.scope, indexReg); goto Loop_continue; } case Token.REF_NS_NAME: { //indexReg: flags stackTop = DoRefNsName(cx, frame, stack, sDbl, stackTop, indexReg); goto Loop_continue; } case Icode_SCOPE_LOAD: { indexReg += frame.localShift; frame.scope = (Scriptable)stack[indexReg]; goto Loop_continue; } case Icode_SCOPE_SAVE: { indexReg += frame.localShift; stack[indexReg] = frame.scope; goto Loop_continue; } case Icode_CLOSURE_EXPR: { stack[++stackTop] = InterpretedFunction.CreateFunction(cx, frame.scope, frame.fnOrScript, indexReg); goto Loop_continue; } case Icode_CLOSURE_STMT: { InitFunction(cx, frame.scope, frame.fnOrScript, indexReg); goto Loop_continue; } case Token.REGEXP: { object re = frame.idata.itsRegExpLiterals[indexReg]; stack[++stackTop] = ScriptRuntime.WrapRegExp(cx, frame.scope, re); goto Loop_continue; } case Icode_LITERAL_NEW: { // indexReg: number of values in the literal ++stackTop; stack[stackTop] = new int[indexReg]; ++stackTop; stack[stackTop] = new object[indexReg]; sDbl[stackTop] = 0; goto Loop_continue; } case Icode_LITERAL_SET: { object value = stack[stackTop]; if (value == DBL_MRK) { value = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; int i = (int)sDbl[stackTop]; ((object[])stack[stackTop])[i] = value; sDbl[stackTop] = i + 1; goto Loop_continue; } case Icode_LITERAL_GETTER: { object value = stack[stackTop]; --stackTop; int i = (int)sDbl[stackTop]; ((object[])stack[stackTop])[i] = value; ((int[])stack[stackTop - 1])[i] = -1; sDbl[stackTop] = i + 1; goto Loop_continue; } case Icode_LITERAL_SETTER: { object value = stack[stackTop]; --stackTop; int i = (int)sDbl[stackTop]; ((object[])stack[stackTop])[i] = value; ((int[])stack[stackTop - 1])[i] = +1; sDbl[stackTop] = i + 1; goto Loop_continue; } case Token.ARRAYLIT: case Icode_SPARE_ARRAYLIT: case Token.OBJECTLIT: { object[] data = (object[])stack[stackTop]; --stackTop; int[] getterSetters = (int[])stack[stackTop]; object val; if (op == Token.OBJECTLIT) { object[] ids = (object[])frame.idata.literalIds[indexReg]; val = ScriptRuntime.NewObjectLiteral(ids, data, getterSetters, cx, frame.scope); } else { int[] skipIndexces = null; if (op == Icode_SPARE_ARRAYLIT) { skipIndexces = (int[])frame.idata.literalIds[indexReg]; } val = ScriptRuntime.NewArrayLiteral(data, skipIndexces, cx, frame.scope); } stack[stackTop] = val; goto Loop_continue; } case Icode_ENTERDQ: { object lhs = stack[stackTop]; if (lhs == DBL_MRK) { lhs = ScriptRuntime.WrapNumber(sDbl[stackTop]); } --stackTop; frame.scope = ScriptRuntime.EnterDotQuery(lhs, frame.scope); goto Loop_continue; } case Icode_LEAVEDQ: { bool valBln = Stack_boolean(frame, stackTop); object x = ScriptRuntime.UpdateDotQuery(valBln, frame.scope); if (x != null) { stack[stackTop] = x; frame.scope = ScriptRuntime.LeaveDotQuery(frame.scope); frame.pc += 2; goto Loop_continue; } // reset stack and PC to code after ENTERDQ --stackTop; goto jumplessRun_break; } case Token.DEFAULTNAMESPACE: { object value = stack[stackTop]; if (value == DBL_MRK) { value = ScriptRuntime.WrapNumber(sDbl[stackTop]); } stack[stackTop] = ScriptRuntime.SetDefaultNamespace(value, cx); goto Loop_continue; } case Token.ESCXMLATTR: { object value = stack[stackTop]; if (value != DBL_MRK) { stack[stackTop] = ScriptRuntime.EscapeAttributeValue(value, cx); } goto Loop_continue; } case Token.ESCXMLTEXT: { object value = stack[stackTop]; if (value != DBL_MRK) { stack[stackTop] = ScriptRuntime.EscapeTextValue(value, cx); } goto Loop_continue; } case Icode_DEBUGGER: { if (frame.debuggerFrame != null) { frame.debuggerFrame.OnDebuggerStatement(cx); } goto Loop_continue; } case Icode_LINE: { frame.pcSourceLineStart = frame.pc; if (frame.debuggerFrame != null) { int line = GetIndex(iCode, frame.pc); frame.debuggerFrame.OnLineChange(cx, line); } frame.pc += 2; goto Loop_continue; } case Icode_REG_IND_C0: { indexReg = 0; goto Loop_continue; } case Icode_REG_IND_C1: { indexReg = 1; goto Loop_continue; } case Icode_REG_IND_C2: { indexReg = 2; goto Loop_continue; } case Icode_REG_IND_C3: { indexReg = 3; goto Loop_continue; } case Icode_REG_IND_C4: { indexReg = 4; goto Loop_continue; } case Icode_REG_IND_C5: { indexReg = 5; goto Loop_continue; } case Icode_REG_IND1: { indexReg = unchecked((int)(0xFF)) & iCode[frame.pc]; ++frame.pc; goto Loop_continue; } case Icode_REG_IND2: { indexReg = GetIndex(iCode, frame.pc); frame.pc += 2; goto Loop_continue; } case Icode_REG_IND4: { indexReg = GetInt(iCode, frame.pc); frame.pc += 4; goto Loop_continue; } case Icode_REG_STR_C0: { stringReg = strings[0]; goto Loop_continue; } case Icode_REG_STR_C1: { stringReg = strings[1]; goto Loop_continue; } case Icode_REG_STR_C2: { stringReg = strings[2]; goto Loop_continue; } case Icode_REG_STR_C3: { stringReg = strings[3]; goto Loop_continue; } case Icode_REG_STR1: { stringReg = strings[unchecked((int)(0xFF)) & iCode[frame.pc]]; ++frame.pc; goto Loop_continue; } case Icode_REG_STR2: { stringReg = strings[GetIndex(iCode, frame.pc)]; frame.pc += 2; goto Loop_continue; } case Icode_REG_STR4: { stringReg = strings[GetInt(iCode, frame.pc)]; frame.pc += 4; goto Loop_continue; } default: { DumpICode(frame.idata); throw new Exception("Unknown icode : " + op + " @ pc : " + (frame.pc - 1)); } } jumplessRun_break: ; // end of interpreter switch // end of jumplessRun label block // This should be reachable only for jump implementation // when pc points to encoded target offset if (instructionCounting) { AddInstructionCount(cx, frame, 2); } int offset = GetShort(iCode, frame.pc); if (offset != 0) { // -1 accounts for pc pointing to jump opcode + 1 frame.pc += offset - 1; } else { frame.pc = frame.idata.longJumps.GetExistingInt(frame.pc); } if (instructionCounting) { frame.pcPrevBranch = frame.pc; } goto Loop_continue; Loop_continue: ; } Loop_break: ; // end of Loop: for ExitFrame(cx, frame, null); interpreterResult = frame.result; interpreterResultDbl = frame.resultDbl; if (frame.parentFrame != null) { frame = frame.parentFrame; if (frame.frozen) { frame = frame.CloneFrozen(); } SetCallResult(frame, interpreterResult, interpreterResultDbl); interpreterResult = null; // Help GC goto StateLoop_continue; } goto StateLoop_break; } catch (Exception ex) { // end of interpreter withoutExceptions: try if (throwable != null) { // This is serious bug and it is better to track it ASAP Sharpen.Runtime.PrintStackTrace(ex, System.Console.Error); throw new InvalidOperationException(); } throwable = ex; } withoutExceptions_break: ; // This should be reachable only after above catch or from // finally when it needs to propagate exception or from // explicit throw if (throwable == null) { Kit.CodeBug(); } // Exception type int EX_CATCH_STATE = 2; // Can execute JS catch int EX_FINALLY_STATE = 1; // Can execute JS finally int EX_NO_JS_STATE = 0; // Terminate JS execution int exState; Interpreter.ContinuationJump cjump_1 = null; if (generatorState != null && generatorState.operation == NativeGenerator.GENERATOR_CLOSE && throwable == generatorState.value) { exState = EX_FINALLY_STATE; } else { if (throwable is JavaScriptException) { exState = EX_CATCH_STATE; } else { if (throwable is EcmaError) { // an offical ECMA error object, exState = EX_CATCH_STATE; } else { if (throwable is EvaluatorException) { exState = EX_CATCH_STATE; } else { if (throwable is ContinuationPending) { exState = EX_NO_JS_STATE; } else { if (throwable is Exception) { exState = cx.HasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) ? EX_CATCH_STATE : EX_FINALLY_STATE; } else { if (throwable is Exception) { exState = cx.HasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) ? EX_CATCH_STATE : EX_NO_JS_STATE; } else { if (throwable is Interpreter.ContinuationJump) { // It must be ContinuationJump exState = EX_FINALLY_STATE; cjump_1 = (Interpreter.ContinuationJump)throwable; } else { exState = cx.HasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) ? EX_CATCH_STATE : EX_FINALLY_STATE; } } } } } } } } if (instructionCounting) { try { AddInstructionCount(cx, frame, EXCEPTION_COST); } catch (Exception ex) { throwable = ex; exState = EX_FINALLY_STATE; } catch (Exception ex) { // Error from instruction counting // => unconditionally terminate JS throwable = ex; cjump_1 = null; exState = EX_NO_JS_STATE; } } if (frame.debuggerFrame != null && throwable is Exception) { // Call debugger only for RuntimeException Exception rex = (Exception)throwable; try { frame.debuggerFrame.OnExceptionThrown(cx, rex); } catch (Exception ex) { // Any exception from debugger // => unconditionally terminate JS throwable = ex; cjump_1 = null; exState = EX_NO_JS_STATE; } } for (; ; ) { if (exState != EX_NO_JS_STATE) { bool onlyFinally = (exState != EX_CATCH_STATE); indexReg = GetExceptionHandler(frame, onlyFinally); if (indexReg >= 0) { // We caught an exception, restart the loop // with exception pending the processing at the loop // start goto StateLoop_continue; } } // No allowed exception handlers in this frame, unwind // to parent and try to look there ExitFrame(cx, frame, throwable); frame = frame.parentFrame; if (frame == null) { break; } if (cjump_1 != null && cjump_1.branchFrame == frame) { // Continuation branch point was hit, // restart the state loop to reenter continuation indexReg = -1; goto StateLoop_continue; } } // No more frames, rethrow the exception or deal with continuation if (cjump_1 != null) { if (cjump_1.branchFrame != null) { // The above loop should locate the top frame Kit.CodeBug(); } if (cjump_1.capturedFrame != null) { // Restarting detached continuation indexReg = -1; goto StateLoop_continue; } // Return continuation result to the caller interpreterResult = cjump_1.result; interpreterResultDbl = cjump_1.resultDbl; throwable = null; } goto StateLoop_break; StateLoop_continue: ; } StateLoop_break: ; // end of StateLoop: for(;;) // Do cleanups/restorations before the final return or throw if (cx.previousInterpreterInvocations != null && cx.previousInterpreterInvocations.Size() != 0) { cx.lastInterpreterFrame = cx.previousInterpreterInvocations.Pop(); } else { // It was the last interpreter frame on the stack cx.lastInterpreterFrame = null; // Force GC of the value cx.previousInterpreterInvocations cx.previousInterpreterInvocations = null; } if (throwable != null) { if (throwable is Exception) { throw (Exception)throwable; } else { // Must be instance of Error or code bug throw (Exception)throwable; } } return (interpreterResult != DBL_MRK) ? interpreterResult : ScriptRuntime.WrapNumber(interpreterResultDbl); }