static object InterpretLoop (Context cx, 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.DoubleMark; object undefined = Undefined.Value; bool instructionCounting = (cx.instructionThreshold != 0); // arbitrary number to add to instructionCount when calling // other functions const int INVOCATION_COST = 100; // arbitrary exception cost for instruction counting const int EXCEPTION_COST = 100; string stringReg = null; int indexReg = -1; if (cx.lastInterpreterFrame != null) { // save the top frame from the previous interpreterLoop // 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 throable == null and indexReg == -1 allows to // catch bugs with using indeReg to access array eleemnts before // initializing indexReg. if (throwable != null) { // Assert assumptions if (!(throwable is ContinuationJump)) { // It should be continuation Context.CodeBug (); } } object interpreterResult = null; double interpreterResultDbl = 0.0; for (; ; ) { try { if (throwable != null) { // Recovering from exception, indexReg contains // the index of handler if (indexReg >= 0) { // Normal excepton handler, transfer // control appropriately if (frame.frozen) { // TODO: 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 = (IScriptable)frame.stack [scopeLocal]; frame.stack [exLocal] = throwable; throwable = null; } else { // Continuation restoration ContinuationJump cjump = (ContinuationJump)throwable; // Clear throwable to indicate that execptions are OK throwable = null; if (cjump.branchFrame != frame) Context.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) Context.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; CallFrame [] enterFrames = null; CallFrame x = cjump.capturedFrame; for (int i = 0; i != rewindCount; ++i) { if (!x.frozen) Context.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 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); } // 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 } // Should be already cleared if (throwable != null) Context.CodeBug (); } else { if (frame.frozen) Context.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; sbyte [] 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 accross // 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 Token.THROW: { object value = stack [stackTop]; if (value == DBL_MRK) value = sDbl [stackTop]; stackTop--; int sourceLine = GetIndex (iCode, frame.pc); throwable = new EcmaScriptThrow ( value, frame.idata.itsSourceFile, sourceLine); goto withoutExceptions_brk; } case Token.RETHROW: { indexReg += frame.localShift; throwable = stack [indexReg]; break; } case Token.GE: case Token.LE: case Token.GT: case Token.LT: { --stackTop; object rhs = stack [stackTop + 1]; object lhs = stack [stackTop]; bool valBln; { { double rDbl, lDbl; if (rhs == DBL_MRK) { rDbl = sDbl [stackTop + 1]; lDbl = stack_double (frame, stackTop); } else if (lhs == DBL_MRK) { rDbl = ScriptConvert.ToNumber (rhs); lDbl = sDbl [stackTop]; } else { goto number_compare_brk; } switch (op) { case Token.GE: valBln = (lDbl >= rDbl); goto object_compare_brk; case Token.LE: valBln = (lDbl <= rDbl); goto object_compare_brk; case Token.GT: valBln = (lDbl > rDbl); goto object_compare_brk; case Token.LT: valBln = (lDbl < rDbl); goto object_compare_brk; default: throw Context.CodeBug (); } } number_compare_brk: ; 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 Context.CodeBug (); } } object_compare_brk: ; stack [stackTop] = valBln; goto Loop; } goto case Token.IN; case Token.IN: case Token.INSTANCEOF: { object rhs = stack [stackTop]; if (rhs == DBL_MRK) rhs = sDbl [stackTop]; --stackTop; object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; bool valBln; if (op == Token.IN) { valBln = ScriptRuntime.In (lhs, rhs, cx); } else { valBln = ScriptRuntime.InstanceOf (lhs, rhs, cx); } stack [stackTop] = valBln; goto Loop; } goto case Token.EQ; case Token.EQ: case Token.NE: { --stackTop; bool valBln; object rhs = stack [stackTop + 1]; object lhs = stack [stackTop]; if (rhs == DBL_MRK) { if (lhs == DBL_MRK) { valBln = (sDbl [stackTop] == sDbl [stackTop + 1]); } else { valBln = ScriptRuntime.eqNumber (sDbl [stackTop + 1], lhs); } } else { if (lhs == DBL_MRK) { valBln = ScriptRuntime.eqNumber (sDbl [stackTop], rhs); } else { valBln = ScriptRuntime.eq (lhs, rhs); } } valBln ^= (op == Token.NE); stack [stackTop] = valBln; goto Loop; } goto case Token.SHEQ; case Token.SHEQ: case Token.SHNE: { --stackTop; object rhs = stack [stackTop + 1]; object lhs = stack [stackTop]; bool valBln; { double rdbl, ldbl; if (rhs == DBL_MRK) { rdbl = sDbl [stackTop + 1]; if (lhs == DBL_MRK) { ldbl = sDbl [stackTop]; } else if (CliHelper.IsNumber (lhs)) { ldbl = Convert.ToDouble (lhs); } else { valBln = false; goto shallow_compare_brk; } } else if (lhs == DBL_MRK) { ldbl = sDbl [stackTop]; if (rhs == DBL_MRK) { rdbl = sDbl [stackTop + 1]; } else if (CliHelper.IsNumber (rhs)) { rdbl = Convert.ToDouble (rhs); } else { valBln = false; goto shallow_compare_brk; } } else { valBln = ScriptRuntime.shallowEq (lhs, rhs); goto shallow_compare_brk; } valBln = (ldbl == rdbl); } shallow_compare_brk: ; valBln ^= (op == Token.SHNE); stack [stackTop] = valBln; goto Loop; } goto case Token.IFNE; case Token.IFNE: if (stack_boolean (frame, stackTop--)) { frame.pc += 2; goto Loop; } goto jumplessRun_brk; case Token.IFEQ: if (!stack_boolean (frame, stackTop--)) { frame.pc += 2; goto Loop; } goto jumplessRun_brk; case Icode_IFEQ_POP: if (!stack_boolean (frame, stackTop--)) { frame.pc += 2; goto Loop; } stack [stackTop--] = null; goto jumplessRun_brk; case Token.GOTO: goto jumplessRun_brk; case Icode_GOSUB: ++stackTop; stack [stackTop] = DBL_MRK; sDbl [stackTop] = frame.pc + 2; goto jumplessRun_brk; 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) Context.CodeBug (); } goto Loop; goto case Icode_RETSUB; 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_brk; } // Normal return from GOSUB frame.pc = (int)sDbl [indexReg]; if (instructionCounting) { frame.pcPrevBranch = frame.pc; } goto Loop; } goto case Icode_POP; case Icode_POP: stack [stackTop] = null; stackTop--; goto Loop; goto case Icode_POP_RESULT; case Icode_POP_RESULT: frame.result = stack [stackTop]; frame.resultDbl = sDbl [stackTop]; stack [stackTop] = null; --stackTop; goto Loop; goto case Icode_DUP; case Icode_DUP: stack [stackTop + 1] = stack [stackTop]; sDbl [stackTop + 1] = sDbl [stackTop]; stackTop++; goto Loop; goto case Icode_DUP2; 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; goto case Icode_SWAP; 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; } goto case Token.RETURN; case Token.RETURN: frame.result = stack [stackTop]; frame.resultDbl = sDbl [stackTop]; --stackTop; goto Loop_brk; case Token.RETURN_RESULT: goto Loop_brk; case Icode_RETUNDEF: frame.result = undefined; goto Loop_brk; case Token.BITNOT: { int rIntValue = stack_int32 (frame, stackTop); stack [stackTop] = DBL_MRK; sDbl [stackTop] = ~rIntValue; goto Loop; } goto case Token.BITAND; case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.LSH: case Token.RSH: { int rIntValue = stack_int32 (frame, stackTop); --stackTop; int lIntValue = stack_int32 (frame, stackTop); stack [stackTop] = DBL_MRK; 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; goto Loop; } goto case Token.URSH; case Token.URSH: { int rIntValue = stack_int32 (frame, stackTop) & 0x1F; --stackTop; double lDbl = stack_double (frame, stackTop); stack [stackTop] = DBL_MRK; uint i = (uint)ScriptConvert.ToUint32 (lDbl); sDbl [stackTop] = i >> rIntValue; goto Loop; } goto case Token.NEG; 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; } goto case Token.ADD; case Token.ADD: --stackTop; DoAdd (stack, sDbl, stackTop, cx); goto Loop; goto case Token.SUB; case Token.SUB: case Token.MUL: case Token.DIV: case Token.MOD: { double rDbl = stack_double (frame, stackTop); --stackTop; double lDbl = stack_double (frame, stackTop); stack [stackTop] = DBL_MRK; 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; goto Loop; } goto case Token.NOT; case Token.NOT: stack [stackTop] = !stack_boolean (frame, stackTop); goto Loop; goto case Token.BINDNAME; case Token.BINDNAME: stack [++stackTop] = ScriptRuntime.bind (cx, frame.scope, stringReg); goto Loop; goto case Token.SETNAME; case Token.SETNAME: { object rhs = stack [stackTop]; if (rhs == DBL_MRK) rhs = sDbl [stackTop]; --stackTop; IScriptable lhs = (IScriptable)stack [stackTop]; stack [stackTop] = ScriptRuntime.setName (lhs, rhs, cx, frame.scope, stringReg); goto Loop; } goto case Token.DELPROP; case Token.DELPROP: { object rhs = stack [stackTop]; if (rhs == DBL_MRK) rhs = sDbl [stackTop]; --stackTop; object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.delete (lhs, rhs, cx); goto Loop; } goto case Token.GETPROP; case Token.GETPROP: { object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.getObjectProp (lhs, stringReg, cx); goto Loop; } goto case Token.SETPROP; case Token.SETPROP_GETTER: case Token.SETPROP_SETTER: case Token.SETPROP: { object rhs = stack [stackTop]; if (rhs == DBL_MRK) rhs = sDbl [stackTop]; --stackTop; object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; switch (op) { case Token.SETPROP_GETTER: ((ScriptableObject)lhs).DefineGetter(stringReg, ((ICallable)rhs)); stack[stackTop] = rhs; break; case Token.SETPROP_SETTER: ((ScriptableObject)lhs).DefineSetter(stringReg, ((ICallable)rhs)); stack[stackTop] = rhs; break; default: stack [stackTop] = ScriptRuntime.setObjectProp (lhs, stringReg, rhs, cx); break; } goto Loop; } goto case Icode_PROP_INC_DEC; case Icode_PROP_INC_DEC: { object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.propIncrDecr (lhs, stringReg, cx, iCode [frame.pc]); ++frame.pc; goto Loop; } goto case Token.GETELEM; case Token.GETELEM: { --stackTop; object lhs = stack [stackTop]; if (lhs == DBL_MRK) { lhs = sDbl [stackTop]; } object value; object id = stack [stackTop + 1]; if (id != DBL_MRK) { value = ScriptRuntime.getObjectElem (lhs, id, cx); } else { double d = sDbl [stackTop + 1]; value = ScriptRuntime.getObjectIndex (lhs, d, cx); } stack [stackTop] = value; goto Loop; } goto case Token.SETELEM; case Token.SETELEM: { stackTop -= 2; object rhs = stack [stackTop + 2]; if (rhs == DBL_MRK) { rhs = sDbl [stackTop + 2]; } object lhs = stack [stackTop]; if (lhs == DBL_MRK) { lhs = sDbl [stackTop]; } object value; object id = stack [stackTop + 1]; if (id != DBL_MRK) { value = ScriptRuntime.setObjectElem (lhs, id, rhs, cx); } else { double d = sDbl [stackTop + 1]; value = ScriptRuntime.setObjectIndex (lhs, d, rhs, cx); } stack [stackTop] = value; goto Loop; } goto case Icode_ELEM_INC_DEC; case Icode_ELEM_INC_DEC: { object rhs = stack [stackTop]; if (rhs == DBL_MRK) rhs = sDbl [stackTop]; --stackTop; object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.elemIncrDecr (lhs, rhs, cx, iCode [frame.pc]); ++frame.pc; goto Loop; } goto case Token.GET_REF; case Token.GET_REF: { IRef rf = (IRef)stack [stackTop]; stack [stackTop] = ScriptRuntime.refGet (rf, cx); goto Loop; } goto case Token.SET_REF; case Token.SET_REF: { object value = stack [stackTop]; if (value == DBL_MRK) value = sDbl [stackTop]; --stackTop; IRef rf = (IRef)stack [stackTop]; stack [stackTop] = ScriptRuntime.refSet (rf, value, cx); goto Loop; } goto case Token.DEL_REF; case Token.DEL_REF: { IRef rf = (IRef)stack [stackTop]; stack [stackTop] = ScriptRuntime.refDel (rf, cx); goto Loop; } goto case Icode_REF_INC_DEC; case Icode_REF_INC_DEC: { IRef rf = (IRef)stack [stackTop]; stack [stackTop] = ScriptRuntime.refIncrDecr (rf, cx, iCode [frame.pc]); ++frame.pc; goto Loop; } goto case Token.LOCAL_LOAD; case Token.LOCAL_LOAD: ++stackTop; indexReg += frame.localShift; stack [stackTop] = stack [indexReg]; sDbl [stackTop] = sDbl [indexReg]; goto Loop; goto case Icode_LOCAL_CLEAR; case Icode_LOCAL_CLEAR: indexReg += frame.localShift; stack [indexReg] = null; goto Loop; goto case Icode_NAME_AND_THIS; case Icode_NAME_AND_THIS: // stringReg: name ++stackTop; stack [stackTop] = ScriptRuntime.getNameFunctionAndThis (stringReg, cx, frame.scope); ++stackTop; stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); goto Loop; goto case Icode_PROP_AND_THIS; case Icode_PROP_AND_THIS: { object obj = stack [stackTop]; if (obj == DBL_MRK) obj = sDbl [stackTop]; // stringReg: property stack [stackTop] = ScriptRuntime.getPropFunctionAndThis (obj, stringReg, cx); ++stackTop; stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); goto Loop; } goto case Icode_ELEM_AND_THIS; case Icode_ELEM_AND_THIS: { object obj = stack [stackTop - 1]; if (obj == DBL_MRK) obj = sDbl [stackTop - 1]; object id = stack [stackTop]; if (id == DBL_MRK) id = sDbl [stackTop]; stack [stackTop - 1] = ScriptRuntime.GetElemFunctionAndThis (obj, id, cx); stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); goto Loop; } goto case Icode_VALUE_AND_THIS; case Icode_VALUE_AND_THIS: { object value = stack [stackTop]; if (value == DBL_MRK) value = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.getValueFunctionAndThis (value, cx); ++stackTop; stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); goto Loop; } goto case Icode_CALLSPECIAL; case Icode_CALLSPECIAL: { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } int callType = iCode [frame.pc] & 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 == DBL_MRK) function = 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 IScriptable functionThis = (IScriptable)stack [stackTop + 1]; ICallable function = (ICallable)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; goto Loop; } goto case Token.CALL; 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 ICallable fun = (ICallable)stack [stackTop]; IScriptable funThisObj = (IScriptable)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; } IScriptable 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) { CallFrame callParentFrame = frame; CallFrame calleeFrame = new 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 iteraction with GC. callParentFrame = frame.parentFrame; } initFrame (cx, calleeScope, funThisObj, stack, sDbl, stackTop + 2, indexReg, ifun, callParentFrame, calleeFrame); if (op == Icode_TAIL_CALL) { // Release the parent ExitFrame (cx, frame, (object)null); } else { frame.savedStackTop = stackTop; frame.savedCallOp = op; } frame = calleeFrame; goto StateLoop; } } if (fun is Continuation) { // Jump to the captured continuation ContinuationJump cjump; cjump = new ContinuationJump ((Continuation)fun, frame); // continuation result is the first argument if any // of contination 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; break; } if (fun is IdFunctionObject) { IdFunctionObject ifun = (IdFunctionObject)fun; if (Continuation.IsContinuationConstructor (ifun)) { captureContinuation (cx, frame, stackTop); goto Loop; } } object [] outArgs2 = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); stack [stackTop] = fun.Call (cx, calleeScope, funThisObj, outArgs2); goto Loop; } goto case Token.NEW; 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) { IScriptable newInstance = f.CreateObject (cx, frame.scope); CallFrame calleeFrame = new 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; } } if (!(lhs is IFunction)) { if (lhs == DBL_MRK) lhs = sDbl [stackTop]; throw ScriptRuntime.NotFunctionError (lhs); } IFunction fun = (IFunction)lhs; if (fun is IdFunctionObject) { IdFunctionObject ifun = (IdFunctionObject)fun; if (Continuation.IsContinuationConstructor (ifun)) { captureContinuation (cx, frame, stackTop); goto Loop; } } object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 1, indexReg); stack [stackTop] = fun.Construct (cx, frame.scope, outArgs); goto Loop; } goto case Token.TYPEOF; case Token.TYPEOF: { object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.Typeof (lhs); goto Loop; } goto case Icode_TYPEOFNAME; case Icode_TYPEOFNAME: stack [++stackTop] = ScriptRuntime.TypeofName (frame.scope, stringReg); goto Loop; goto case Token.STRING; case Token.STRING: stack [++stackTop] = stringReg; goto Loop; goto case Icode_SHORTNUMBER; case Icode_SHORTNUMBER: ++stackTop; stack [stackTop] = DBL_MRK; sDbl [stackTop] = GetShort (iCode, frame.pc); frame.pc += 2; goto Loop; goto case Icode_INTNUMBER; case Icode_INTNUMBER: ++stackTop; stack [stackTop] = DBL_MRK; sDbl [stackTop] = GetInt (iCode, frame.pc); frame.pc += 4; goto Loop; goto case Token.NUMBER; case Token.NUMBER: ++stackTop; stack [stackTop] = DBL_MRK; sDbl [stackTop] = frame.idata.itsDoubleTable [indexReg]; goto Loop; goto case Token.NAME; case Token.NAME: stack [++stackTop] = ScriptRuntime.name (cx, frame.scope, stringReg); goto Loop; goto case Icode_NAME_INC_DEC; case Icode_NAME_INC_DEC: stack [++stackTop] = ScriptRuntime.nameIncrDecr (frame.scope, stringReg, iCode [frame.pc]); ++frame.pc; goto Loop; goto case Icode_SETVAR1; case Icode_SETVAR1: indexReg = iCode [frame.pc++]; // fallthrough goto case Token.SETVAR; case Token.SETVAR: if (!frame.useActivation) { vars [indexReg] = stack [stackTop]; varDbls [indexReg] = sDbl [stackTop]; } else { object val = stack [stackTop]; if (val == DBL_MRK) val = sDbl [stackTop]; stringReg = frame.idata.argNames [indexReg]; frame.scope.Put (stringReg, frame.scope, val); } goto Loop; goto case Icode_GETVAR1; case Icode_GETVAR1: indexReg = iCode [frame.pc++]; // fallthrough goto case Token.GETVAR; case Token.GETVAR: ++stackTop; if (!frame.useActivation) { stack [stackTop] = vars [indexReg]; sDbl [stackTop] = varDbls [indexReg]; } else { stringReg = frame.idata.argNames [indexReg]; stack [stackTop] = frame.scope.Get (stringReg, frame.scope); } goto Loop; goto case Icode_VAR_INC_DEC; case Icode_VAR_INC_DEC: { // indexReg : varindex ++stackTop; int incrDecrMask = iCode [frame.pc]; if (!frame.useActivation) { stack [stackTop] = DBL_MRK; object varValue = vars [indexReg]; double d; if (varValue == DBL_MRK) { d = varDbls [indexReg]; } else { d = ScriptConvert.ToNumber (varValue); vars [indexReg] = DBL_MRK; } 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, incrDecrMask); } ++frame.pc; goto Loop; } goto case Icode_ZERO; case Icode_ZERO: ++stackTop; stack [stackTop] = DBL_MRK; sDbl [stackTop] = 0; goto Loop; goto case Icode_ONE; case Icode_ONE: ++stackTop; stack [stackTop] = DBL_MRK; sDbl [stackTop] = 1; goto Loop; goto case Token.NULL; case Token.NULL: stack [++stackTop] = null; goto Loop; goto case Token.THIS; case Token.THIS: stack [++stackTop] = frame.thisObj; goto Loop; goto case Token.THISFN; case Token.THISFN: stack [++stackTop] = frame.fnOrScript; goto Loop; goto case Token.FALSE; case Token.FALSE: stack [++stackTop] = false; goto Loop; goto case Token.TRUE; case Token.TRUE: stack [++stackTop] = true; goto Loop; goto case Icode_UNDEF; case Icode_UNDEF: stack [++stackTop] = undefined; goto Loop; goto case Token.ENTERWITH; case Token.ENTERWITH: { object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; --stackTop; frame.scope = ScriptRuntime.enterWith (lhs, cx, frame.scope); goto Loop; } goto case Token.LEAVEWITH; case Token.LEAVEWITH: frame.scope = ScriptRuntime.leaveWith (frame.scope); goto Loop; goto case Token.CATCH_SCOPE; 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]; IScriptable lastCatchScope; if (!afterFirstScope) { lastCatchScope = null; } else { lastCatchScope = (IScriptable)stack [indexReg]; } stack [indexReg] = ScriptRuntime.NewCatchScope (caughtException, lastCatchScope, stringReg, cx, frame.scope); ++frame.pc; goto Loop; } goto case Token.ENUM_INIT_KEYS; case Token.ENUM_INIT_KEYS: case Token.ENUM_INIT_VALUES: { object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; --stackTop; indexReg += frame.localShift; if (lhs is IIdEnumerable) { stack [indexReg] = ((IIdEnumerable)lhs).GetEnumeration (cx, (op == Token.ENUM_INIT_VALUES)); } else { stack [indexReg] = new IdEnumeration (lhs, cx, (op == Token.ENUM_INIT_VALUES)); } goto Loop; } goto case Token.ENUM_NEXT; case Token.ENUM_NEXT: case Token.ENUM_ID: { indexReg += frame.localShift; IdEnumeration val = (IdEnumeration)stack [indexReg]; ++stackTop; stack [stackTop] = (op == Token.ENUM_NEXT) ? val.MoveNext () : val.Current (cx); goto Loop; } goto case Token.REF_SPECIAL; case Token.REF_SPECIAL: { //stringReg: name of special property object obj = stack [stackTop]; if (obj == DBL_MRK) obj = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.specialRef (obj, stringReg, cx); goto Loop; } goto case Token.REF_MEMBER; case Token.REF_MEMBER: { //indexReg: flags object elem = stack [stackTop]; if (elem == DBL_MRK) elem = sDbl [stackTop]; --stackTop; object obj = stack [stackTop]; if (obj == DBL_MRK) obj = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.memberRef (obj, elem, cx, indexReg); goto Loop; } goto case Token.REF_NS_MEMBER; case Token.REF_NS_MEMBER: { //indexReg: flags object elem = stack [stackTop]; if (elem == DBL_MRK) elem = sDbl [stackTop]; --stackTop; object ns = stack [stackTop]; if (ns == DBL_MRK) ns = sDbl [stackTop]; --stackTop; object obj = stack [stackTop]; if (obj == DBL_MRK) obj = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.memberRef (obj, ns, elem, cx, indexReg); goto Loop; } goto case Token.REF_NAME; case Token.REF_NAME: { //indexReg: flags object name = stack [stackTop]; if (name == DBL_MRK) name = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.nameRef (name, cx, frame.scope, indexReg); goto Loop; } goto case Token.REF_NS_NAME; case Token.REF_NS_NAME: { //indexReg: flags object name = stack [stackTop]; if (name == DBL_MRK) name = sDbl [stackTop]; --stackTop; object ns = stack [stackTop]; if (ns == DBL_MRK) ns = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.nameRef (ns, name, cx, frame.scope, indexReg); goto Loop; } goto case Icode_SCOPE_LOAD; case Icode_SCOPE_LOAD: indexReg += frame.localShift; frame.scope = (IScriptable)stack [indexReg]; goto Loop; goto case Icode_SCOPE_SAVE; case Icode_SCOPE_SAVE: indexReg += frame.localShift; stack [indexReg] = frame.scope; goto Loop; goto case Icode_CLOSURE_EXPR; case Icode_CLOSURE_EXPR: { InterpretedFunction fun = InterpretedFunction.createFunction (cx, frame.scope, frame.fnOrScript, indexReg); stack [++stackTop] = fun; } goto Loop; goto case Icode_CLOSURE_STMT; case Icode_CLOSURE_STMT: initFunction (cx, frame.scope, frame.fnOrScript, indexReg); goto Loop; goto case Token.REGEXP; case Token.REGEXP: stack [++stackTop] = frame.scriptRegExps [indexReg]; goto Loop; goto case Icode_LITERAL_NEW; case Icode_LITERAL_NEW: // indexReg: number of values in the literal ++stackTop; stack [stackTop] = new object [indexReg]; sDbl [stackTop] = 0; goto Loop; goto case Icode_LITERAL_SET; case Icode_LITERAL_SET: { object value = stack [stackTop]; if (value == DBL_MRK) value = sDbl [stackTop]; --stackTop; int i = (int)sDbl [stackTop]; ((object [])stack [stackTop]) [i] = value; sDbl [stackTop] = i + 1; goto Loop; } goto case Token.ARRAYLIT; case Token.ARRAYLIT: case Icode_SPARE_ARRAYLIT: case Token.OBJECTLIT: { object [] data = (object [])stack [stackTop]; object val; if (op == Token.OBJECTLIT) { object [] ids = (object [])frame.idata.literalIds [indexReg]; val = ScriptRuntime.newObjectLiteral (ids, data, 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; } goto case Icode_ENTERDQ; case Icode_ENTERDQ: { object lhs = stack [stackTop]; if (lhs == DBL_MRK) lhs = sDbl [stackTop]; --stackTop; frame.scope = ScriptRuntime.enterDotQuery (lhs, frame.scope); goto Loop; } goto case Icode_LEAVEDQ; 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; } // reset stack and PC to code after ENTERDQ --stackTop; goto jumplessRun_brk; } case Token.DEFAULTNAMESPACE: { object value = stack [stackTop]; if (value == DBL_MRK) value = sDbl [stackTop]; stack [stackTop] = ScriptRuntime.setDefaultNamespace (value, cx); goto Loop; } goto case Token.ESCXMLATTR; case Token.ESCXMLATTR: { object value = stack [stackTop]; if (value != DBL_MRK) { stack [stackTop] = ScriptRuntime.escapeAttributeValue (value, cx); } goto Loop; } goto case Token.ESCXMLTEXT; case Token.ESCXMLTEXT: { object value = stack [stackTop]; if (value != DBL_MRK) { stack [stackTop] = ScriptRuntime.escapeTextValue (value, cx); } goto Loop; } goto case Icode_LINE; case Icode_DEBUGGER: { if (frame.debuggerFrame != null) { frame.debuggerFrame.OnDebuggerStatement(cx); } break; } 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; goto case Icode_REG_IND_C0; case Icode_REG_IND_C0: indexReg = 0; goto Loop; goto case Icode_REG_IND_C1; case Icode_REG_IND_C1: indexReg = 1; goto Loop; goto case Icode_REG_IND_C2; case Icode_REG_IND_C2: indexReg = 2; goto Loop; goto case Icode_REG_IND_C3; case Icode_REG_IND_C3: indexReg = 3; goto Loop; goto case Icode_REG_IND_C4; case Icode_REG_IND_C4: indexReg = 4; goto Loop; goto case Icode_REG_IND_C5; case Icode_REG_IND_C5: indexReg = 5; goto Loop; goto case Icode_REG_IND1; case Icode_REG_IND1: indexReg = 0xFF & iCode [frame.pc]; ++frame.pc; goto Loop; goto case Icode_REG_IND2; case Icode_REG_IND2: indexReg = GetIndex (iCode, frame.pc); frame.pc += 2; goto Loop; goto case Icode_REG_IND4; case Icode_REG_IND4: indexReg = GetInt (iCode, frame.pc); frame.pc += 4; goto Loop; goto case Icode_REG_STR_C0; case Icode_REG_STR_C0: stringReg = strings [0]; goto Loop; goto case Icode_REG_STR_C1; case Icode_REG_STR_C1: stringReg = strings [1]; goto Loop; goto case Icode_REG_STR_C2; case Icode_REG_STR_C2: stringReg = strings [2]; goto Loop; goto case Icode_REG_STR_C3; case Icode_REG_STR_C3: stringReg = strings [3]; goto Loop; goto case Icode_REG_STR1; case Icode_REG_STR1: stringReg = strings [0xFF & iCode [frame.pc]]; ++frame.pc; goto Loop; goto case Icode_REG_STR2; case Icode_REG_STR2: stringReg = strings [GetIndex (iCode, frame.pc)]; frame.pc += 2; goto Loop; goto case Icode_REG_STR4; case Icode_REG_STR4: stringReg = strings [GetInt (iCode, frame.pc)]; frame.pc += 4; goto Loop; goto default; default: dumpICode (frame.idata); throw new ApplicationException ("Unknown icode : " + op + " @ pc : " + (frame.pc - 1)); } // end of interpreter switch } jumplessRun_brk: ; // 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; Loop: ; } Loop_brk: ; // end of Loop: for ExitFrame (cx, frame, (object)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; } goto StateLoop_brk; } // end of interpreter withoutExceptions: try catch (Exception ex) { if (throwable != null) { // This is serious bug and it is better to track it ASAP throw new ApplicationException (); } throwable = ex; } withoutExceptions_brk: // This should be reachable only after above catch or from // finally when it needs to propagate exception or from // explicit throw if (throwable == null) Context.CodeBug (); // Exception type const int EX_CATCH_STATE = 2; // Can execute JS catch const int EX_FINALLY_STATE = 1; // Can execute JS finally const int EX_NO_JS_STATE = 0; // Terminate JS execution int exState; ContinuationJump cjump2 = null; if (throwable is EcmaScriptThrow) { exState = EX_CATCH_STATE; } else if (throwable is EcmaScriptError) { // an offical ECMA error object, exState = EX_CATCH_STATE; } else if (throwable is EcmaScriptRuntimeException) { exState = EX_CATCH_STATE; } else if (throwable is EcmaScriptException) { exState = EX_FINALLY_STATE; } else if (throwable is Exception) { exState = EX_NO_JS_STATE; } else { // It must be ContinuationJump exState = EX_FINALLY_STATE; cjump2 = (ContinuationJump)throwable; } if (instructionCounting) { try { addInstructionCount (cx, frame, EXCEPTION_COST); } catch (ApplicationException ex) { // Error from instruction counting // => unconditionally terminate JS throwable = ex; cjump2 = null; exState = EX_NO_JS_STATE; } } if (frame.debuggerFrame != null && throwable is ApplicationException) { // Call debugger only for RuntimeException ApplicationException rex = (ApplicationException)throwable; try { frame.debuggerFrame.OnExceptionThrown (cx, rex); } catch (Exception ex) { // Any exception from debugger // => unconditionally terminate JS throwable = ex; cjump2 = 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; } } // No allowed execption handlers in this frame, unwind // to parent and try to look there ExitFrame (cx, frame, throwable); frame = frame.parentFrame; if (frame == null) { break; } if (cjump2 != null && cjump2.branchFrame == frame) { // Continuation branch point was hit, // restart the state loop to reenter continuation indexReg = -1; goto StateLoop; } } // No more frames, rethrow the exception or deal with continuation if (cjump2 != null) { if (cjump2.branchFrame != null) { // The above loop should locate the top frame Context.CodeBug (); } if (cjump2.capturedFrame != null) { // Restarting detached continuation indexReg = -1; goto StateLoop; } // Return continuation result to the caller interpreterResult = cjump2.result; interpreterResultDbl = cjump2.resultDbl; throwable = null; } goto StateLoop_brk; StateLoop: ; } StateLoop_brk: ; // 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 Helpers.StackOverflowVerifierException) { throw Context.ReportRuntimeError ( ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion")); } throw (Exception)throwable; } return (interpreterResult != DBL_MRK) ? interpreterResult : interpreterResultDbl; }
public static object restartContinuation (Continuation c, Context cx, IScriptable scope, object [] args) { if (!ScriptRuntime.hasTopCall (cx)) { return ScriptRuntime.DoTopCall (c, cx, scope, null, args); } object arg; if (args.Length == 0) { arg = Undefined.Value; } else { arg = args [0]; } CallFrame capturedFrame = (CallFrame)c.Implementation; if (capturedFrame == null) { // No frames to restart return arg; } ContinuationJump cjump = new ContinuationJump (c, null); cjump.result = arg; return InterpretLoop (cx, null, cjump); }