From 0fe0aba4b10459870cf7b068b199df8482b16868 Mon Sep 17 00:00:00 2001 From: "igor%mir2.org" Date: Fri, 20 Aug 2004 22:50:36 +0000 Subject: [PATCH] Resolving bug 256339: interpreter does not recurse across calls to interpreted functions and scripts and instead uses explicit State object to store JS frame information. It removes the limit on maximum recursion depth in scripts. --- .../src/org/mozilla/javascript/Context.java | 14 +- .../javascript/InterpretedFunction.java | 6 +- .../org/mozilla/javascript/Interpreter.java | 1297 +++++++++-------- 3 files changed, 702 insertions(+), 615 deletions(-) diff --git a/js/rhino/src/org/mozilla/javascript/Context.java b/js/rhino/src/org/mozilla/javascript/Context.java index 0078e2d86cd..4dee48da5ac 100644 --- a/js/rhino/src/org/mozilla/javascript/Context.java +++ b/js/rhino/src/org/mozilla/javascript/Context.java @@ -2163,15 +2163,6 @@ public class Context { } - final void addInstructionCount(int n) - { - instructionCount += n; - if (instructionCount > instructionThreshold) { - observeInstructionCount(instructionCount); - instructionCount = 0; - } - } - public GeneratedClassLoader createClassLoader(ClassLoader parent) { return new DefiningClassLoader(parent); @@ -2428,7 +2419,7 @@ public class Context Context cx = getCurrentContext(); if (cx == null) return null; - if (cx.interpreterData != null) { + if (cx.interpreterLineCounting != null) { return Interpreter.getSourcePositionFromStack(cx, linep); } /** @@ -2636,8 +2627,7 @@ public class Context Hashtable activationNames; // For the interpreter to indicate line/source for error reports. - int interpreterLineIndex; - InterpreterData interpreterData; + Object interpreterLineCounting; // For instruction counting (interpreter only) int instructionCount; diff --git a/js/rhino/src/org/mozilla/javascript/InterpretedFunction.java b/js/rhino/src/org/mozilla/javascript/InterpretedFunction.java index 1528201c7ce..9dda013abac 100644 --- a/js/rhino/src/org/mozilla/javascript/InterpretedFunction.java +++ b/js/rhino/src/org/mozilla/javascript/InterpretedFunction.java @@ -137,11 +137,7 @@ final class InterpretedFunction extends NativeFunction implements Script public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); - } - return Interpreter.interpret(cx, scope, thisObj, args, null, - 0, args.length, this); + return Interpreter.interpret(this, cx, scope, thisObj, args); } public Object exec(Context cx, Scriptable scope) diff --git a/js/rhino/src/org/mozilla/javascript/Interpreter.java b/js/rhino/src/org/mozilla/javascript/Interpreter.java index 51fa554bb1e..f2faacff37f 100644 --- a/js/rhino/src/org/mozilla/javascript/Interpreter.java +++ b/js/rhino/src/org/mozilla/javascript/Interpreter.java @@ -98,59 +98,58 @@ public class Interpreter Icode_RETUNDEF = -21, // Exception handling implementation - Icode_CATCH = -22, - Icode_GOSUB = -23, - Icode_RETSUB = -24, + Icode_GOSUB = -22, + Icode_RETSUB = -23, // To indicating a line number change in icodes. - Icode_LINE = -25, + Icode_LINE = -24, // To store shorts and ints inline - Icode_SHORTNUMBER = -26, - Icode_INTNUMBER = -27, + Icode_SHORTNUMBER = -25, + Icode_INTNUMBER = -26, // To create and populate array to hold values for [] and {} literals - Icode_LITERAL_NEW = -28, - Icode_LITERAL_SET = -29, + Icode_LITERAL_NEW = -27, + Icode_LITERAL_SET = -28, // Array literal with skipped index like [1,,2] - Icode_SPARE_ARRAYLIT = -30, + Icode_SPARE_ARRAYLIT = -29, // Load index register to prepare for the following index operation - Icode_REG_IND_C0 = -31, - Icode_REG_IND_C1 = -32, - Icode_REG_IND_C2 = -33, - Icode_REG_IND_C3 = -34, - Icode_REG_IND_C4 = -35, - Icode_REG_IND_C5 = -36, - Icode_REG_IND1 = -37, - Icode_REG_IND2 = -38, - Icode_REG_IND4 = -39, + Icode_REG_IND_C0 = -30, + Icode_REG_IND_C1 = -31, + Icode_REG_IND_C2 = -32, + Icode_REG_IND_C3 = -33, + Icode_REG_IND_C4 = -34, + Icode_REG_IND_C5 = -35, + Icode_REG_IND1 = -36, + Icode_REG_IND2 = -37, + Icode_REG_IND4 = -38, // Load string register to prepare for the following string operation - Icode_REG_STR_C0 = -40, - Icode_REG_STR_C1 = -41, - Icode_REG_STR_C2 = -42, - Icode_REG_STR_C3 = -43, - Icode_REG_STR1 = -44, - Icode_REG_STR2 = -45, - Icode_REG_STR4 = -46, + Icode_REG_STR_C0 = -39, + Icode_REG_STR_C1 = -40, + Icode_REG_STR_C2 = -41, + Icode_REG_STR_C3 = -42, + Icode_REG_STR1 = -43, + Icode_REG_STR2 = -44, + Icode_REG_STR4 = -45, // Version of getvar/setvar that read var index directly from bytecode - Icode_GETVAR1 = -47, - Icode_SETVAR1 = -48, + Icode_GETVAR1 = -46, + Icode_SETVAR1 = -47, // Load unefined - Icode_UNDEF = -49, - Icode_ZERO = -50, - Icode_ONE = -51, + Icode_UNDEF = -48, + Icode_ZERO = -49, + Icode_ONE = -50, // entrance and exit from .() - Icode_ENTERDQ = -52, - Icode_LEAVEDQ = -53, + Icode_ENTERDQ = -51, + Icode_LEAVEDQ = -52, // Last icode - MIN_ICODE = -53; + MIN_ICODE = -52; static { // Checks for byte code consistencies, good compiler can eliminate them @@ -203,7 +202,6 @@ public class Interpreter case Icode_CLOSURE_STMT: return "CLOSURE_STMT"; case Icode_CALLSPECIAL: return "CALLSPECIAL"; case Icode_RETUNDEF: return "RETUNDEF"; - case Icode_CATCH: return "CATCH"; case Icode_GOSUB: return "GOSUB"; case Icode_RETSUB: return "RETSUB"; case Icode_LINE: return "LINE"; @@ -335,9 +333,6 @@ public class Interpreter if (itsData.itsFunctionType == 0) { addToken(Token.RETURN_RESULT); } - // Add special CATCH to simplify Interpreter.interpret logic - // and workaround lack of goto in Java - addIcode(Icode_CATCH); if (itsData.itsICode.length != itsICodeTop) { // Make itsData.itsICode length exactly itsICodeTop to save memory @@ -1047,8 +1042,6 @@ public class Interpreter } else { generateCallFunAndThis(child); } - // To get better debugging output for undefined or null calls. - int debugNameIndex = itsLastStringIndex; int argCount = 0; while ((child = child.getNext()) != null) { visitExpression(child); @@ -1064,16 +1057,6 @@ public class Interpreter addUint16(itsLineNumber); } else { addIndexOp(type, argCount); - if (type == Token.NEW) { - // Code for generateCallFunAndThis takes care about debugging - // for Token.CALL abd Token.REF_CALL; - if (debugNameIndex < 0xFFFF) { - // Use only 2 bytes to store debug index - addUint16(debugNameIndex); - } else { - addUint16(0xFFFF); - } - } } // adjust stack if (type == Token.NEW) { @@ -1504,7 +1487,6 @@ public class Interpreter index = itsStrings.size(); itsStrings.put(str, index); } - itsLastStringIndex = index; if (index < 4) { addIcode(Icode_REG_STR_C0 - index); } else if (index <= 0xFF) { @@ -1705,11 +1687,8 @@ public class Interpreter break; case Token.CALL : case Token.REF_CALL : - out.println(tname+' '+indexReg); - break; case Token.NEW : out.println(tname+' '+indexReg); - pc += 2; break; case Token.THROW : { int line = getShort(iCode, pc); @@ -1833,10 +1812,6 @@ public class Interpreter // line number return 1 + 1 + 1 + 2; - case Token.NEW : - // index of potential function name for debugging - return 1 + 2; - case Icode_VAR_INC_DEC: case Icode_NAME_INC_DEC: case Icode_PROP_INC_DEC: @@ -1912,9 +1887,10 @@ public class Interpreter static String getSourcePositionFromStack(Context cx, int[] linep) { - InterpreterData idata = cx.interpreterData; - if (cx.interpreterLineIndex >= 0) { - linep[0] = getShort(idata.itsICode, cx.interpreterLineIndex); + State state = (State)cx.interpreterLineCounting; + InterpreterData idata = state.idata; + if (state.pcSourceLineStart >= 0) { + linep[0] = getShort(idata.itsICode, state.pcSourceLineStart); } else { linep[0] = 0; } @@ -1939,263 +1915,127 @@ public class Interpreter parent.evalScriptFlag); } - static Object interpret(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args, double[] argsDbl, - int argShift, int argCount, - InterpretedFunction fnOrScript) + static class State { - if (cx.interpreterSecurityDomain != fnOrScript.securityDomain) { - if (argsDbl != null) { - args = getArgsArray(args, argsDbl, argShift, argCount); - } - SecurityController sc = fnOrScript.securityController; + State callerState; + + InterpretedFunction fnOrScript; + InterpreterData idata; + +// Stack structure +// stack[0 <= i < localShift]: variables +// stack[localShift <= i <= emptyStackTop]: used for local temporaries +// stack[emptyStackTop < i < stack.length]: stack data +// sDbl[i]: if stack[i] is DBL_MRK, sDbl[i] holds the number value + + Object[] stack; + double[] sDbl; + int localShift; + int emptyStackTop; + + DebugFrame debuggerFrame; + boolean useActivation; + Scriptable thisObj; + + Scriptable[] scriptRegExps; + + State savedInterpreterLineCounting; + +// The values that change during interpretation + + Object result; + int pc; + int pcPrevBranch; + int pcSourceLineStart; + int withDepth; + Scriptable scope; + + int savedStackTop; + int savedCallOp; + } + + static Object interpret(InterpretedFunction ifun, + Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!ScriptRuntime.hasTopCall(cx)) { + return ScriptRuntime.doTopCall(ifun, cx, scope, thisObj, args); + } + if (cx.interpreterSecurityDomain != ifun.securityDomain) { Object savedDomain = cx.interpreterSecurityDomain; - cx.interpreterSecurityDomain = fnOrScript.securityDomain; + cx.interpreterSecurityDomain = ifun.securityDomain; try { - return fnOrScript.securityController.callWithDomain( - fnOrScript.securityDomain, cx, fnOrScript, scope, - thisObj, args); + return ifun.securityController.callWithDomain( + ifun.securityDomain, cx, ifun, scope, thisObj, args); } finally { cx.interpreterSecurityDomain = savedDomain; } } - InterpreterData idata = fnOrScript.idata; + State state = new State(); + initState(cx, scope, thisObj, args, null, 0, args.length, + ifun, state); + interpret(cx, state); + return state.result; + } + private static void interpret(Context cx, State state) + { final Object DBL_MRK = Interpreter.DBL_MRK; final Scriptable undefined = Undefined.instance; - - final int LOCAL_SHFT = idata.itsMaxVars; - final int STACK_SHFT = LOCAL_SHFT + idata.itsMaxLocals; - -// stack[0 <= i < LOCAL_SHFT]: variables -// stack[LOCAL_SHFT <= i < TRY_STACK_SHFT]: used for local temporaries -// stack[STACK_SHFT <= i < STACK_SHFT + idata.itsMaxStack]: stack data - -// sDbl[i]: if stack[i] is DBL_MRK, sDbl[i] holds the number value - - int maxFrameArray = idata.itsMaxFrameArray; - if (maxFrameArray != STACK_SHFT + idata.itsMaxStack) - Kit.codeBug(); - - Object[] stack = new Object[maxFrameArray]; - double[] sDbl = new double[maxFrameArray]; - - int stackTop = STACK_SHFT - 1; - - int withDepth = 0; - - int definedArgs = fnOrScript.argCount; - if (definedArgs > argCount) { definedArgs = argCount; } - for (int i = 0; i != definedArgs; ++i) { - Object arg = args[argShift + i]; - stack[i] = arg; - if (arg == DBL_MRK) { - sDbl[i] = argsDbl[argShift + i]; - } - } - for (int i = definedArgs; i != idata.itsMaxVars; ++i) { - stack[i] = undefined; - } - - DebugFrame debuggerFrame = null; - boolean useActivationVars = false; - if (cx.debugger != null) { - debuggerFrame = cx.debugger.getFrame(cx, idata); - useActivationVars = (debuggerFrame != null); - } - - if (idata.itsNeedsActivation || useActivationVars) { - if (argsDbl != null) { - args = getArgsArray(args, argsDbl, argShift, argCount); - argShift = 0; - argsDbl = null; - } - } - - if (idata.itsFunctionType != 0) { - if (!idata.useDynamicScope) { - scope = fnOrScript.getParentScope(); - } - - if (idata.itsCheckThis) { - thisObj = ScriptRuntime.getThis(thisObj); - } - - if (idata.itsNeedsActivation || useActivationVars) { - scope = ScriptRuntime.enterActivationFunction(cx, scope, - fnOrScript, - thisObj, args); - } - } else { - ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope, - fnOrScript.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); - } - } - } - - Scriptable[] scriptRegExps = null; - if (idata.itsRegExpLiterals != null) { - // Wrapped regexps for functions are stored in InterpretedFunction - // but for script which should not contain references to scope - // the regexps re-wrapped during each script execution - if (idata.itsFunctionType != 0) { - scriptRegExps = fnOrScript.functionRegExps; - } else { - scriptRegExps = fnOrScript.createRegExpWraps(cx, scope); - } - } - - if (debuggerFrame != null) { - debuggerFrame.onEnter(cx, scope, thisObj, args); - } - - InterpreterData savedData = cx.interpreterData; - int savedLineIndex = cx.interpreterLineIndex; - cx.interpreterData = idata; - cx.interpreterLineIndex = idata.firstLinePC; - - Object result = undefined; - // If javaException != null on exit, it will be throw instead of - // normal return - Throwable javaException = null; - int exceptionPC = -1; - - byte[] iCode = idata.itsICode; - int pc = 0; - int pcPrevBranch = 0; - final boolean instructionCounting = (cx.instructionThreshold != 0); // arbitrary number to add to instructionCount when calling // other functions final int INVOCATION_COST = 100; + // arbitrary exception cost + final int EXCEPTION_COST = 100; - String[] strings = idata.itsStringTable; String stringReg = null; int indexReg = 0; + final boolean instructionCounting = (cx.instructionThreshold != 0); + + StateLoop: for (;;) { + // Use local variables for constant values in state + // for faster access + Object[] stack = state.stack; + double[] sDbl = state.sDbl; + byte[] iCode = state.idata.itsICode; + String[] strings = state.idata.itsStringTable; + boolean useActivation = state.useActivation; + + // 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 = state.savedStackTop; + + Loop: for (;;) { + + // Exception object to rethrow or catch + Throwable throwable; + + withoutExceptions: try { + int op = iCode[state.pc++]; + jumplessRun: { - Loop: for (;;) { - try { - int op = iCode[pc++]; - jumplessRun: - { // Back indent to ease imlementation reading switch (op) { - - case Icode_CATCH: { - // The following code should be executed inside try/catch inside main - // loop, not in the loop catch block itself to deal with exceptions - // from observeInstructionCount. A special bytecode is used only to - // simplify logic. - if (javaException == null) Kit.codeBug(); - - pc = -1; - boolean doCatch = false; - int handlerOffset = getExceptionHandler(idata.itsExceptionTable, - exceptionPC); - - if (handlerOffset >= 0) { - - final int SCRIPT_CAN_CATCH = 0, ONLY_FINALLY = 1, OTHER = 2; - int exType; - if (javaException instanceof JavaScriptException) { - exType = SCRIPT_CAN_CATCH; - } else if (javaException instanceof EcmaError) { - // an offical ECMA error object, - exType = SCRIPT_CAN_CATCH; - } else if (javaException instanceof EvaluatorException) { - exType = SCRIPT_CAN_CATCH; - } else if (javaException instanceof RuntimeException) { - exType = ONLY_FINALLY; - } else { - // Error instance - exType = OTHER; - } - - if (exType != OTHER) { - // Do not allow for JS to interfere with Error instances - // (exType == OTHER), as they can be used to terminate - // long running script - if (exType == SCRIPT_CAN_CATCH) { - // Allow JS to catch only JavaScriptException and - // EcmaError - pc = idata.itsExceptionTable[handlerOffset - + EXCEPTION_CATCH_SLOT]; - if (pc >= 0) { - // Has catch block - doCatch = true; - } - } - if (pc < 0) { - pc = idata.itsExceptionTable[handlerOffset - + EXCEPTION_FINALLY_SLOT]; - } - } - } - - if (debuggerFrame != null && !(javaException instanceof Error)) { - debuggerFrame.onExceptionThrown(cx, javaException); - } - - if (pc < 0) { - break Loop; - } - - // We caught an exception - - // restore scope at try point - int tryWithDepth = idata.itsExceptionTable[ - handlerOffset + EXCEPTION_WITH_DEPTH_SLOT]; - while (tryWithDepth != withDepth) { - if (scope == null) Kit.codeBug(); - scope = ScriptRuntime.leaveWith(scope); - --withDepth; - } - - if (doCatch) { - stackTop = STACK_SHFT - 1; - int exLocal = idata.itsExceptionTable[ - handlerOffset + EXCEPTION_LOCAL_SLOT]; - stack[LOCAL_SHFT + exLocal] = ScriptRuntime.getCatchObject( - cx, scope, javaException); - } else { - stackTop = STACK_SHFT; - // Call finally handler with javaException on stack top to - // distinguish from normal invocation through GOSUB - // which would contain DBL_MRK on the stack - stack[stackTop] = javaException; - } - // clear exception - javaException = null; - - if (instructionCounting) { - // 500: catch cost - cx.addInstructionCount(500); - pcPrevBranch = pc; - } - continue Loop; - } case Token.THROW: { Object value = stack[stackTop]; if (value == DBL_MRK) value = doubleWrap(sDbl[stackTop]); --stackTop; - int sourceLine = getShort(iCode, pc); - throw new JavaScriptException(value, idata.itsSourceFile, sourceLine); + int sourceLine = getShort(iCode, state.pc); + throwable = new JavaScriptException(value, + state.idata.itsSourceFile, + sourceLine); + break withoutExceptions; } case Token.GE : case Token.LE : case Token.GT : case Token.LT : - stackTop = do_cmp(stack, sDbl, stackTop, op); + --stackTop; + do_cmp(state, stackTop, op); continue Loop; case Token.IN : { Object rhs = stack[stackTop]; @@ -2203,7 +2043,7 @@ switch (op) { --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - boolean valBln = ScriptRuntime.in(lhs, rhs, cx, scope); + boolean valBln = ScriptRuntime.in(lhs, rhs, cx, state.scope); stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE; continue Loop; } @@ -2213,33 +2053,35 @@ switch (op) { --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - boolean valBln = ScriptRuntime.instanceOf(lhs, rhs, cx, scope); + boolean valBln = ScriptRuntime.instanceOf(lhs, rhs, cx, state.scope); stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE; continue Loop; } case Token.EQ : case Token.NE : - stackTop = do_eq(stack, sDbl, stackTop, op); + --stackTop; + do_eq(state, stackTop, op); continue Loop; case Token.SHEQ : case Token.SHNE : - stackTop = do_sheq(stack, sDbl, stackTop, op); + --stackTop; + do_sheq(state, stackTop, op); continue Loop; case Token.IFNE : - if (stack_boolean(stack, sDbl, stackTop--)) { - pc += 2; + if (stack_boolean(state, stackTop--)) { + state.pc += 2; continue Loop; } break jumplessRun; case Token.IFEQ : - if (!stack_boolean(stack, sDbl, stackTop--)) { - pc += 2; + if (!stack_boolean(state, stackTop--)) { + state.pc += 2; continue Loop; } break jumplessRun; case Icode_IFEQ_POP : - if (!stack_boolean(stack, sDbl, stackTop--)) { - pc += 2; + if (!stack_boolean(state, stackTop--)) { + state.pc += 2; continue Loop; } stack[stackTop--] = null; @@ -2249,23 +2091,24 @@ switch (op) { case Icode_GOSUB : ++stackTop; stack[stackTop] = DBL_MRK; - sDbl[stackTop] = pc + 2; + sDbl[stackTop] = state.pc + 2; break jumplessRun; case Icode_RETSUB : { // indexReg: local to store return address if (instructionCounting) { - cx.addInstructionCount(pc - pcPrevBranch); + addInstructionCount(cx, state, 0); } - Object value = stack[LOCAL_SHFT + indexReg]; + indexReg += state.localShift; + Object value = stack[indexReg]; if (value != DBL_MRK) { // Invocation from exception handler, restore object to rethrow - javaException = (Throwable)value; - exceptionPC = pc - 1; - pc = getJavaCatchPC(iCode); - } else { - // Normal return from GOSUB - pc = (int)sDbl[LOCAL_SHFT + indexReg]; - pcPrevBranch = pc; + throwable = (Throwable)value; + break withoutExceptions; + } + // Normal return from GOSUB + state.pc = (int)sDbl[indexReg]; + if (instructionCounting) { + state.pcPrevBranch = state.pc; } continue Loop; } @@ -2274,8 +2117,8 @@ switch (op) { stackTop--; continue Loop; case Icode_POP_RESULT : - result = stack[stackTop]; - if (result == DBL_MRK) result = doubleWrap(sDbl[stackTop]); + state.result = stack[stackTop]; + if (state.result == DBL_MRK) state.result = doubleWrap(sDbl[stackTop]); stack[stackTop] = null; --stackTop; continue Loop; @@ -2301,14 +2144,14 @@ switch (op) { continue Loop; } case Token.RETURN : - result = stack[stackTop]; - if (result == DBL_MRK) result = doubleWrap(sDbl[stackTop]); + state.result = stack[stackTop]; + if (state.result == DBL_MRK) state.result = doubleWrap(sDbl[stackTop]); --stackTop; break Loop; case Token.RETURN_RESULT : break Loop; case Icode_RETUNDEF : - result = undefined; + state.result = undefined; break Loop; case Token.BITNOT : { int rIntValue = stack_int32(stack, sDbl, stackTop); @@ -2359,19 +2202,19 @@ switch (op) { case Token.URSH : { int rIntValue = stack_int32(stack, sDbl, stackTop) & 0x1F; --stackTop; - double lDbl = stack_double(stack, sDbl, stackTop); + double lDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = ScriptRuntime.toUint32(lDbl) >>> rIntValue; continue Loop; } case Token.NEG : { - double rDbl = stack_double(stack, sDbl, stackTop); + double rDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = -rDbl; continue Loop; } case Token.POS : { - double rDbl = stack_double(stack, sDbl, stackTop); + double rDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = rDbl; continue Loop; @@ -2380,51 +2223,52 @@ switch (op) { stackTop = do_add(stack, sDbl, stackTop, cx); continue Loop; case Token.SUB : { - double rDbl = stack_double(stack, sDbl, stackTop); + double rDbl = stack_double(state, stackTop); --stackTop; - double lDbl = stack_double(stack, sDbl, stackTop); + double lDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = lDbl - rDbl; continue Loop; } case Token.MUL : { - double rDbl = stack_double(stack, sDbl, stackTop); + double rDbl = stack_double(state, stackTop); --stackTop; - double lDbl = stack_double(stack, sDbl, stackTop); + double lDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = lDbl * rDbl; continue Loop; } case Token.DIV : { - double rDbl = stack_double(stack, sDbl, stackTop); + double rDbl = stack_double(state, stackTop); --stackTop; - double lDbl = stack_double(stack, sDbl, stackTop); + double lDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; // Detect the divide by zero or let Java do it ? sDbl[stackTop] = lDbl / rDbl; continue Loop; } case Token.MOD : { - double rDbl = stack_double(stack, sDbl, stackTop); + double rDbl = stack_double(state, stackTop); --stackTop; - double lDbl = stack_double(stack, sDbl, stackTop); + double lDbl = stack_double(state, stackTop); stack[stackTop] = DBL_MRK; sDbl[stackTop] = lDbl % rDbl; continue Loop; } case Token.NOT : - stack[stackTop] = stack_boolean(stack, sDbl, stackTop) + stack[stackTop] = stack_boolean(state, stackTop) ? Boolean.FALSE : Boolean.TRUE; continue Loop; case Token.BINDNAME : - stack[++stackTop] = ScriptRuntime.bind(cx, scope, stringReg); + stack[++stackTop] = ScriptRuntime.bind(cx, state.scope, stringReg); continue Loop; case Token.SETNAME : { Object rhs = stack[stackTop]; if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]); --stackTop; Scriptable lhs = (Scriptable)stack[stackTop]; - stack[stackTop] = ScriptRuntime.setName(lhs, rhs, cx, scope, stringReg); + stack[stackTop] = ScriptRuntime.setName(lhs, rhs, cx, + state.scope, stringReg); continue Loop; } case Token.DELPROP : { @@ -2433,14 +2277,14 @@ switch (op) { --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.delete(cx, scope, lhs, rhs); + stack[stackTop] = ScriptRuntime.delete(cx, state.scope, lhs, rhs); continue Loop; } case Token.GETPROP : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.getObjectProp(lhs, stringReg, - cx, scope); + cx, state.scope); continue Loop; } case Token.SETPROP : { @@ -2450,22 +2294,25 @@ switch (op) { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.setObjectProp(lhs, stringReg, rhs, - cx, scope); + cx, state.scope); continue Loop; } case Icode_PROP_INC_DEC : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.propIncrDecr(lhs, stringReg, scope, - iCode[pc]); - ++pc; + stack[stackTop] = ScriptRuntime.propIncrDecr(lhs, stringReg, + state.scope, + iCode[state.pc]); + ++state.pc; continue Loop; } case Token.GETELEM : - stackTop = do_getElem(stack, sDbl, stackTop, cx, scope); + --stackTop; + do_getElem(state, stackTop, cx); continue Loop; case Token.SETELEM : - stackTop = do_setElem(stack, sDbl, stackTop, cx, scope); + stackTop -= 2; + do_setElem(state, stackTop, cx); continue Loop; case Icode_ELEM_INC_DEC: { Object rhs = stack[stackTop]; @@ -2473,9 +2320,9 @@ switch (op) { --stackTop; Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, scope, - iCode[pc]); - ++pc; + stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, state.scope, + iCode[state.pc]); + ++state.pc; continue Loop; } case Token.GET_REF : { @@ -2502,25 +2349,27 @@ switch (op) { case Icode_REF_INC_DEC : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.referenceIncrDecr(lhs, iCode[pc]); - ++pc; + stack[stackTop] = ScriptRuntime.referenceIncrDecr(lhs, iCode[state.pc]); + ++state.pc; continue Loop; } case Token.LOCAL_SAVE : - stack[LOCAL_SHFT + indexReg] = stack[stackTop]; - sDbl[LOCAL_SHFT + indexReg] = sDbl[stackTop]; + indexReg += state.localShift; + stack[indexReg] = stack[stackTop]; + sDbl[indexReg] = sDbl[stackTop]; --stackTop; continue Loop; case Token.LOCAL_LOAD : ++stackTop; - stack[stackTop] = stack[LOCAL_SHFT + indexReg]; - sDbl[stackTop] = sDbl[LOCAL_SHFT + indexReg]; + indexReg += state.localShift; + stack[stackTop] = stack[indexReg]; + sDbl[stackTop] = sDbl[indexReg]; continue Loop; case Icode_NAME_AND_THIS : // stringReg: name ++stackTop; stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(stringReg, - cx, scope); + cx, state.scope); ++stackTop; stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; @@ -2529,7 +2378,7 @@ switch (op) { if (obj == DBL_MRK) obj = doubleWrap(sDbl[stackTop]); // stringReg: property stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(obj, stringReg, - cx, scope); + cx, state.scope); ++stackTop; stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; @@ -2540,7 +2389,8 @@ switch (op) { Object id = stack[stackTop]; if (id == DBL_MRK) id = doubleWrap(sDbl[stackTop]); stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(obj, id, - cx, scope); + cx, + state.scope); stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; } @@ -2556,82 +2406,95 @@ switch (op) { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } + int callType = iCode[state.pc] & 0xFF; + boolean isNew = (iCode[state.pc + 1] != 0); + int sourceLine = getShort(iCode, state.pc + 2); + // indexReg: number of arguments - int callType = iCode[pc] & 0xFF; - boolean isNew = (iCode[pc + 1] != 0); - int sourceLine = getShort(iCode, pc + 2); - stackTop -= indexReg; - Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg); if (isNew) { + // stack change: function arg0 .. argN -> newResult + stackTop -= indexReg; + Object function = stack[stackTop]; if (function == DBL_MRK) function = doubleWrap(sDbl[stackTop]); + Object[] outArgs = getArgsArray( + stack, sDbl, stackTop + 1, indexReg); stack[stackTop] = ScriptRuntime.newSpecial( - cx, function, outArgs, scope, callType); + cx, function, outArgs, state.scope, callType); } else { + // stack change: function thisObj arg0 .. argN -> result + stackTop -= 1 + indexReg; + // Call code generation ensure that stack here // is ... Function Scriptable - Scriptable functionThis = (Scriptable)stack[stackTop]; - --stackTop; + Scriptable functionThis = (Scriptable)stack[stackTop + 1]; Function function = (Function)stack[stackTop]; + Object[] outArgs = getArgsArray( + stack, sDbl, stackTop + 2, indexReg); stack[stackTop] = ScriptRuntime.callSpecial( cx, function, functionThis, outArgs, - scope, thisObj, callType, - idata.itsSourceFile, sourceLine); + state.scope, state.thisObj, callType, + state.idata.itsSourceFile, sourceLine); } - pc += 4; + state.pc += 4; continue Loop; } case Token.CALL : { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } + // stack change: function thisObj arg0 .. argN -> result // indexReg: number of arguments - stackTop -= indexReg; - int calleeArgShft = stackTop + 1; + stackTop -= 1 + indexReg; // CALL generation ensures that funThisObj and fun // are already Scriptable and Function objects respectively - Scriptable funThisObj = (Scriptable)stack[stackTop]; - --stackTop; + Scriptable funThisObj = (Scriptable)stack[stackTop + 1]; Function fun = (Function)stack[stackTop]; - Scriptable funScope = scope; - if (idata.itsNeedsActivation) { - funScope = ScriptableObject.getTopLevelScope(scope); + Scriptable funScope = state.scope; + if (useActivation) { + funScope = ScriptableObject.getTopLevelScope(state.scope); } if (fun instanceof InterpretedFunction) { // Inlining of InterpretedFunction.call not to create // argument array InterpretedFunction ifun = (InterpretedFunction)fun; - stack[stackTop] = interpret(cx, funScope, funThisObj, stack, sDbl, - calleeArgShft, indexReg, ifun); - } else { - Object[] outArgs = getArgsArray(stack, sDbl, calleeArgShft, - indexReg); - stack[stackTop] = fun.call(cx, funScope, funThisObj, outArgs); + if (state.fnOrScript.securityDomain == ifun.securityDomain) { + State calleeState = new State(); + initState(cx, funScope, funThisObj, stack, sDbl, + stackTop + 2, indexReg, ifun, calleeState); + + state.savedStackTop = stackTop; + state.savedCallOp = op; + calleeState.callerState = state; + state = calleeState; + continue StateLoop; + } } + Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg); + stack[stackTop] = fun.call(cx, funScope, funThisObj, outArgs); continue Loop; } case Token.REF_CALL : { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } + // stack change: function thisObj arg0 .. argN -> reference // indexReg: number of arguments - stackTop -= indexReg; - int calleeArgShft = stackTop + 1; + stackTop -= indexReg + 1; // REF_CALL generation ensures that funThisObj and fun // are already Scriptable and Function objects respectively - Scriptable funThisObj = (Scriptable)stack[stackTop]; - --stackTop; + Scriptable funThisObj = (Scriptable)stack[stackTop + 1]; Function fun = (Function)stack[stackTop]; - Scriptable funScope = scope; - if (idata.itsNeedsActivation) { - funScope = ScriptableObject.getTopLevelScope(scope); + Scriptable funScope = state.scope; + if (useActivation) { + funScope = ScriptableObject.getTopLevelScope(state.scope); } - Object[] outArgs = getArgsArray(stack, sDbl, calleeArgShft, indexReg); + Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg); stack[stackTop] = ScriptRuntime.referenceCall(fun, funThisObj, outArgs, cx, funScope); continue Loop; @@ -2640,33 +2503,36 @@ switch (op) { if (instructionCounting) { cx.instructionCount += INVOCATION_COST; } + // stack change: function arg0 .. argN -> newResult // indexReg: number of arguments stackTop -= indexReg; - int calleeArgShft = stackTop + 1; Object lhs = stack[stackTop]; if (lhs instanceof InterpretedFunction) { // Inlining of InterpretedFunction.construct not to create // argument array InterpretedFunction f = (InterpretedFunction)lhs; - Scriptable newInstance = f.createObject(cx, scope); - Object callResult = interpret(cx, scope, newInstance, stack, sDbl, - calleeArgShft, indexReg, f); - if (callResult instanceof Scriptable && callResult != undefined) { - stack[stackTop] = callResult; - } else { + if (state.fnOrScript.securityDomain == f.securityDomain) { + Scriptable newInstance = f.createObject(cx, state.scope); + State calleeState = new State(); + initState(cx, state.scope, newInstance, stack, sDbl, + stackTop + 1, indexReg, f, calleeState); + stack[stackTop] = newInstance; + state.savedStackTop = stackTop; + state.savedCallOp = op; + calleeState.callerState = state; + state = calleeState; + continue StateLoop; } - } else if (lhs instanceof Function) { - Function f = (Function)lhs; - Object[] outArgs = getArgsArray(stack, sDbl, calleeArgShft, - indexReg); - stack[stackTop] = f.construct(cx, scope, outArgs); - } else { - if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - throw notAFunction(lhs, idata, pc); } - pc += 2; // skip debug name index + if (!(lhs instanceof Function)) { + if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); + throw ScriptRuntime.notFunctionError(lhs); + } + Function f = (Function)lhs; + Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg); + stack[stackTop] = f.construct(cx, state.scope, outArgs); continue Loop; } case Token.TYPEOF : { @@ -2676,7 +2542,7 @@ switch (op) { continue Loop; } case Icode_TYPEOFNAME : - stack[++stackTop] = ScriptRuntime.typeofName(scope, stringReg); + stack[++stackTop] = ScriptRuntime.typeofName(state.scope, stringReg); continue Loop; case Token.STRING : stack[++stackTop] = stringReg; @@ -2684,58 +2550,58 @@ switch (op) { case Icode_SHORTNUMBER : ++stackTop; stack[stackTop] = DBL_MRK; - sDbl[stackTop] = getShort(iCode, pc); - pc += 2; + sDbl[stackTop] = getShort(iCode, state.pc); + state.pc += 2; continue Loop; case Icode_INTNUMBER : ++stackTop; stack[stackTop] = DBL_MRK; - sDbl[stackTop] = getInt(iCode, pc); - pc += 4; + sDbl[stackTop] = getInt(iCode, state.pc); + state.pc += 4; continue Loop; case Token.NUMBER : ++stackTop; stack[stackTop] = DBL_MRK; - sDbl[stackTop] = idata.itsDoubleTable[indexReg]; + sDbl[stackTop] = state.idata.itsDoubleTable[indexReg]; continue Loop; case Token.NAME : - stack[++stackTop] = ScriptRuntime.name(cx, scope, stringReg); + stack[++stackTop] = ScriptRuntime.name(cx, state.scope, stringReg); continue Loop; case Icode_NAME_INC_DEC : - stack[++stackTop] = ScriptRuntime.nameIncrDecr(scope, stringReg, - iCode[pc]); - ++pc; + stack[++stackTop] = ScriptRuntime.nameIncrDecr(state.scope, stringReg, + iCode[state.pc]); + ++state.pc; continue Loop; case Icode_SETVAR1: - indexReg = iCode[pc++]; + indexReg = iCode[state.pc++]; // fallthrough case Token.SETVAR : - if (!useActivationVars) { + if (!useActivation) { stack[indexReg] = stack[stackTop]; sDbl[indexReg] = sDbl[stackTop]; } else { Object val = stack[stackTop]; if (val == DBL_MRK) val = doubleWrap(sDbl[stackTop]); - activationPut(fnOrScript, scope, indexReg, val); + activationPut(state, indexReg, val); } continue Loop; case Icode_GETVAR1: - indexReg = iCode[pc++]; + indexReg = iCode[state.pc++]; // fallthrough case Token.GETVAR : ++stackTop; - if (!useActivationVars) { + if (!useActivation) { stack[stackTop] = stack[indexReg]; sDbl[stackTop] = sDbl[indexReg]; } else { - stack[stackTop] = activationGet(fnOrScript, scope, indexReg); + stack[stackTop] = activationGet(state, indexReg); } continue Loop; case Icode_VAR_INC_DEC : { // indexReg : varindex ++stackTop; - int incrDecrMask = iCode[pc]; - if (!useActivationVars) { + int incrDecrMask = iCode[state.pc]; + if (!useActivation) { stack[stackTop] = DBL_MRK; Object varValue = stack[indexReg]; double d; @@ -2750,11 +2616,11 @@ switch (op) { sDbl[indexReg] = d2; sDbl[stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2 : d; } else { - String varName = fnOrScript.argNames[indexReg]; - stack[stackTop] = ScriptRuntime.nameIncrDecr(scope, varName, + String varName = state.fnOrScript.argNames[indexReg]; + stack[stackTop] = ScriptRuntime.nameIncrDecr(state.scope, varName, incrDecrMask); } - ++pc; + ++state.pc; continue Loop; } case Icode_ZERO : @@ -2771,10 +2637,10 @@ switch (op) { stack[++stackTop] = null; continue Loop; case Token.THIS : - stack[++stackTop] = thisObj; + stack[++stackTop] = state.thisObj; continue Loop; case Token.THISFN : - stack[++stackTop] = fnOrScript; + stack[++stackTop] = state.fnOrScript; continue Loop; case Token.FALSE : stack[++stackTop] = Boolean.FALSE; @@ -2789,13 +2655,13 @@ switch (op) { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); --stackTop; - scope = ScriptRuntime.enterWith(lhs, scope); - ++withDepth; + state.scope = ScriptRuntime.enterWith(lhs, state.scope); + ++state.withDepth; continue Loop; } case Token.LEAVEWITH : - scope = ScriptRuntime.leaveWith(scope); - --withDepth; + state.scope = ScriptRuntime.leaveWith(state.scope); + --state.withDepth; continue Loop; case Token.CATCH_SCOPE : stack[stackTop] = ScriptRuntime.newCatchScope(stringReg, @@ -2805,19 +2671,22 @@ switch (op) { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); --stackTop; - stack[LOCAL_SHFT + indexReg] = ScriptRuntime.enumInit(lhs, scope); + indexReg += state.localShift; + stack[indexReg] = ScriptRuntime.enumInit(lhs, state.scope); continue Loop; } case Token.ENUM_INIT_VALUES : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); --stackTop; - stack[LOCAL_SHFT + indexReg] = ScriptRuntime.enumValuesInit(lhs, scope); + indexReg += state.localShift; + stack[indexReg] = ScriptRuntime.enumValuesInit(lhs, state.scope); continue Loop; } case Token.ENUM_NEXT : case Token.ENUM_ID : { - Object val = stack[LOCAL_SHFT + indexReg]; + indexReg += state.localShift; + Object val = stack[indexReg]; ++stackTop; stack[stackTop] = (op == Token.ENUM_NEXT) ? (Object)ScriptRuntime.enumNext(val) @@ -2829,28 +2698,28 @@ switch (op) { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); stack[stackTop] = ScriptRuntime.specialReference(lhs, stringReg, - cx, scope); + cx, state.scope); continue Loop; } case Token.XML_REF : { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.xmlReference(lhs, cx, scope); + stack[stackTop] = ScriptRuntime.xmlReference(lhs, cx, state.scope); continue Loop; } case Icode_SCOPE : - stack[++stackTop] = scope; + stack[++stackTop] = state.scope; continue Loop; case Icode_CLOSURE_EXPR : - stack[++stackTop] = InterpretedFunction.createFunction(cx, scope, - fnOrScript, + stack[++stackTop] = InterpretedFunction.createFunction(cx, state.scope, + state.fnOrScript, indexReg); continue Loop; case Icode_CLOSURE_STMT : - initFunction(cx, scope, fnOrScript, indexReg); + initFunction(cx, state.scope, state.fnOrScript, indexReg); continue Loop; case Token.REGEXP : - stack[++stackTop] = scriptRegExps[indexReg]; + stack[++stackTop] = state.scriptRegExps[indexReg]; continue Loop; case Icode_LITERAL_NEW : // indexReg: number of values in the literal @@ -2873,14 +2742,15 @@ switch (op) { Object[] data = (Object[])stack[stackTop]; Object val; if (op == Token.OBJECTLIT) { - Object[] ids = (Object[])idata.literalIds[indexReg]; - val = ScriptRuntime.newObjectLiteral(ids, data, cx, scope); + Object[] ids = (Object[])state.idata.literalIds[indexReg]; + val = ScriptRuntime.newObjectLiteral(ids, data, cx, state.scope); } else { int[] skipIndexces = null; if (op == Icode_SPARE_ARRAYLIT) { - skipIndexces = (int[])idata.literalIds[indexReg]; + skipIndexces = (int[])state.idata.literalIds[indexReg]; } - val = ScriptRuntime.newArrayLiteral(data, skipIndexces, cx, scope); + val = ScriptRuntime.newArrayLiteral(data, skipIndexces, cx, + state.scope); } stack[stackTop] = val; continue Loop; @@ -2889,18 +2759,18 @@ switch (op) { Object lhs = stack[stackTop]; if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]); --stackTop; - scope = ScriptRuntime.enterDotQuery(lhs, scope); - ++withDepth; + state.scope = ScriptRuntime.enterDotQuery(lhs, state.scope); + ++state.withDepth; continue Loop; } case Icode_LEAVEDQ : { - boolean valBln = stack_boolean(stack, sDbl, stackTop); - Object x = ScriptRuntime.updateDotQuery(valBln, scope); + boolean valBln = stack_boolean(state, stackTop); + Object x = ScriptRuntime.updateDotQuery(valBln, state.scope); if (x != null) { stack[stackTop] = x; - scope = ScriptRuntime.leaveDotQuery(scope); - --withDepth; - pc += 2; + state.scope = ScriptRuntime.leaveDotQuery(state.scope); + --state.withDepth; + state.pc += 2; continue Loop; } // reset stack and PC to code after ENTERDQ @@ -2944,16 +2814,16 @@ switch (op) { if (value == DBL_MRK) value = doubleWrap(sDbl[stackTop]); // stringReg contains namespace stack[stackTop] = ScriptRuntime.toQualifiedName(stringReg, value, - cx, scope); + cx, state.scope); continue Loop; } case Icode_LINE : - cx.interpreterLineIndex = pc; - if (debuggerFrame != null) { - int line = getShort(iCode, pc); - debuggerFrame.onLineChange(cx, line); + state.pcSourceLineStart = state.pc; + if (state.debuggerFrame != null) { + int line = getShort(iCode, state.pc); + state.debuggerFrame.onLineChange(cx, line); } - pc += 2; + state.pc += 2; continue Loop; case Icode_REG_IND_C0: indexReg = 0; @@ -2974,16 +2844,16 @@ switch (op) { indexReg = 5; continue Loop; case Icode_REG_IND1: - indexReg = 0xFF & iCode[pc]; - ++pc; + indexReg = 0xFF & iCode[state.pc]; + ++state.pc; continue Loop; case Icode_REG_IND2: - indexReg = getIndex(iCode, pc); - pc += 2; + indexReg = getIndex(iCode, state.pc); + state.pc += 2; continue Loop; case Icode_REG_IND4: - indexReg = getInt(iCode, pc); - pc += 4; + indexReg = getInt(iCode, state.pc); + state.pc += 4; continue Loop; case Icode_REG_STR_C0: stringReg = strings[0]; @@ -2998,79 +2868,346 @@ switch (op) { stringReg = strings[3]; continue Loop; case Icode_REG_STR1: - stringReg = strings[0xFF & iCode[pc]]; - ++pc; + stringReg = strings[0xFF & iCode[state.pc]]; + ++state.pc; continue Loop; case Icode_REG_STR2: - stringReg = strings[getIndex(iCode, pc)]; - pc += 2; + stringReg = strings[getIndex(iCode, state.pc)]; + state.pc += 2; continue Loop; case Icode_REG_STR4: - stringReg = strings[getInt(iCode, pc)]; - pc += 4; + stringReg = strings[getInt(iCode, state.pc)]; + state.pc += 4; continue Loop; default : - dumpICode(idata); - throw new RuntimeException("Unknown icode : "+op+" @ pc : "+(pc-1)); + dumpICode(state.idata); + throw new RuntimeException( + "Unknown icode : "+op+" @ pc : "+(state.pc-1)); } // end of interpreter switch - } // end of jumplessRun label block + } // end of jumplessRun label block - // This should be reachable only for jump implementation - // when pc points to encoded target offset - if (instructionCounting) { - cx.addInstructionCount(pc + 2 - pcPrevBranch); + // This should be reachable only for jump implementation + // when pc points to encoded target offset + if (instructionCounting) { + addInstructionCount(cx, state, 2); + } + int offset = getShort(iCode, state.pc); + if (offset != 0) { + // -1 accounts for pc pointing to jump opcode + 1 + state.pc += offset - 1; + } else { + state.pc = state.idata.longJumps. + getExistingInt(state.pc); + } + if (instructionCounting) { + state.pcPrevBranch = state.pc; + } + continue Loop; + + } // end of interpreter withoutExceptions: try + catch (Throwable ex) { + throwable = ex; } - int offset = getShort(iCode, pc); - if (offset != 0) { - // -1 accounts for pc pointing to jump opcode + 1 - pc += offset - 1; + + // 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(); + + final int EX_CATCH_STATE = 2; // Can execute JS catch + final int EX_FINALLY_STATE = 1; // Can execute JS finally + final int EX_NO_JS_STATE = 0; // Terminate JS execution + + int exState; + + if (throwable instanceof JavaScriptException) { + exState = EX_CATCH_STATE; + } else if (throwable instanceof EcmaError) { + // an offical ECMA error object, + exState = EX_CATCH_STATE; + } else if (throwable instanceof EvaluatorException) { + exState = EX_CATCH_STATE; + } else if (throwable instanceof RuntimeException) { + exState = EX_FINALLY_STATE; } else { - pc = idata.longJumps.getExistingInt(pc); + // Error instance + exState = EX_NO_JS_STATE; } - pcPrevBranch = pc; - continue Loop; - } // end of interpreter try - catch (Throwable ex) { if (instructionCounting) { - // Can not call addInstructionCount as it may trigger - // exception - cx.instructionCount += pc - pcPrevBranch; + try { + addInstructionCount(cx, state, EXCEPTION_COST); + } catch (RuntimeException ex) { + throwable = ex; + exState = EX_FINALLY_STATE; + } catch (Error ex) { + // Error from instruction counting + // => unconditionally terminate JS + throwable = ex; + exState = EX_NO_JS_STATE; + } } - javaException = ex; - exceptionPC = pc - 1; - pc = getJavaCatchPC(iCode); - continue Loop; + if (state.debuggerFrame != null + && !(throwable instanceof Error)) + { + try { + state.debuggerFrame.onExceptionThrown( + cx, throwable); + } catch (Throwable ex) { + // Any exception from debugger + // => unconditionally terminate JS + throwable = ex; + exState = EX_NO_JS_STATE; + } + } + + int[] table; + int handler; + for (;;) { + if (exState != EX_NO_JS_STATE) { + table = state.idata.itsExceptionTable; + handler = getExceptionHandler(table, state.pc); + if (handler >= 0) { + if (table[handler + EXCEPTION_CATCH_SLOT] >= 0) { + // Found catch handler, check if it is allowed + // to run + if (exState == EX_CATCH_STATE) { + break; + } + } + if (table[handler + EXCEPTION_FINALLY_SLOT] >= 0) { + // Found finally handler, make state + // to match always handle type + exState = EX_FINALLY_STATE; + break; + } + } + } + // No allowed execption handlers in this frame, unwind + // to parent and try to look there + + while (state.withDepth != 0) { + state.scope = ScriptRuntime.leaveWith(state.scope); + --state.withDepth; + } + releaseState(cx, state, throwable); + + if (state.callerState == null) { + // No more parent state frames, rethrow the exception + if (throwable instanceof RuntimeException) { + throw (RuntimeException)throwable; + } else { + // Must be instance of Error or code bug + throw (Error)throwable; + } + } + + state = state.callerState; + } + + // We caught an exception, since the loop above + // can unwind accross frame boundary, always use + // stack.something instead of something that were cached + // at the beginning of StateLoop and refer to values in the + // original frame + + int tryWithDepth = table[handler + EXCEPTION_WITH_DEPTH_SLOT]; + while (state.withDepth != tryWithDepth) { + if (state.scope == null) Kit.codeBug(); + state.scope = ScriptRuntime.leaveWith(state.scope); + --state.withDepth; + } + + state.savedStackTop = state.emptyStackTop; + if (exState == EX_CATCH_STATE) { + int exLocal = table[handler + EXCEPTION_LOCAL_SLOT]; + state.stack[state.localShift + exLocal] + = ScriptRuntime.getCatchObject(cx, state.scope, + throwable); + state.pc = table[handler + EXCEPTION_CATCH_SLOT]; + } else { + ++state.savedStackTop; + // Call finally handler with throwable on stack top to + // distinguish from normal invocation through GOSUB + // which would contain DBL_MRK on the stack + state.stack[state.savedStackTop] = throwable; + state.pc = table[handler + EXCEPTION_FINALLY_SLOT]; + } + + if (instructionCounting) { + state.pcPrevBranch = state.pc; + } + continue StateLoop; + } // end of Loop: for + + releaseState(cx, state, null); + if (state.callerState != null) { + Object calleeResult = state.result; + state = state.callerState; + + if (state.savedCallOp == Token.CALL) { + state.stack[state.savedStackTop] = calleeResult; + } else if (state.savedCallOp == Token.NEW) { + // If construct returns scriptable, + // then it replaces on stack top saved original instance + // of the object. + if (calleeResult instanceof Scriptable + && calleeResult != undefined) + { + state.stack[state.savedStackTop] = calleeResult; + } + } else { + Kit.codeBug(); + } + continue StateLoop; } - } // end of interpreter loop - cx.interpreterData = savedData; - cx.interpreterLineIndex = savedLineIndex; + return; - if (debuggerFrame != null) { - if (javaException != null) { - debuggerFrame.onExit(cx, true, javaException); - } else { - debuggerFrame.onExit(cx, false, result); + } // end of StateLoop: for(;;) + } + + private static void initState(Context cx, Scriptable callerScope, + Scriptable origThisObj, + Object[] args, double[] argsDbl, + int argShift, int argCount, + InterpretedFunction fnOrScript, + State state) + { + InterpreterData idata = fnOrScript.idata; + + state.fnOrScript = fnOrScript; + state.idata = idata; + + state.localShift = idata.itsMaxVars; + state.emptyStackTop = state.localShift + idata.itsMaxLocals - 1; + int maxFrameArray = idata.itsMaxFrameArray; + if (maxFrameArray != state.emptyStackTop + idata.itsMaxStack + 1) + Kit.codeBug(); + state.stack = new Object[maxFrameArray]; + state.sDbl = new double[maxFrameArray]; + + int definedArgs = fnOrScript.argCount; + if (definedArgs > argCount) { definedArgs = argCount; } + for (int i = 0; i != definedArgs; ++i) { + Object arg = args[argShift + i]; + state.stack[i] = arg; + if (arg == DBL_MRK) { + state.sDbl[i] = argsDbl[argShift + i]; + } + } + for (int i = definedArgs; i != idata.itsMaxVars; ++i) { + state.stack[i] = Undefined.instance; + } + + state.useActivation = idata.itsNeedsActivation; + if (cx.debugger != null) { + state.debuggerFrame = cx.debugger.getFrame(cx, idata); + if (state.debuggerFrame != null) { + state.useActivation = true; } } + if (state.useActivation) { + if (argsDbl != null) { + args = getArgsArray(args, argsDbl, argShift, argCount); + } + } + + Scriptable scope; + Scriptable thisObj = origThisObj; if (idata.itsFunctionType != 0) { - if (idata.itsNeedsActivation || useActivationVars) { + if (!idata.useDynamicScope) { + scope = fnOrScript.getParentScope(); + } else { + scope = callerScope; + } + + if (idata.itsCheckThis) { + thisObj = ScriptRuntime.getThis(thisObj); + } + + if (state.useActivation) { + scope = ScriptRuntime.enterActivationFunction(cx, scope, + fnOrScript, + thisObj, args); + } + } else { + scope = callerScope; + ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope, + fnOrScript.evalScriptFlag); + } + state.thisObj = thisObj; + + 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); + } + } + } + + state.scriptRegExps = null; + if (idata.itsRegExpLiterals != null) { + // Wrapped regexps for functions are stored in InterpretedFunction + // but for script which should not contain references to scope + // the regexps re-wrapped during each script execution + if (idata.itsFunctionType != 0) { + state.scriptRegExps = fnOrScript.functionRegExps; + } else { + state.scriptRegExps = fnOrScript.createRegExpWraps(cx, scope); + } + } + + if (state.debuggerFrame != null) { + state.debuggerFrame.onEnter(cx, scope, thisObj, args); + } + + state.savedInterpreterLineCounting = (State)cx.interpreterLineCounting; + cx.interpreterLineCounting = state; + + // Initialize initial values of variables that change during + // interpretation. + state.result = Undefined.instance; + state.pc = 0; + state.pcPrevBranch = 0; + state.pcSourceLineStart = idata.firstLinePC; + state.withDepth = 0; + state.scope = scope; + + state.savedStackTop = state.emptyStackTop; + state.savedCallOp = 0; + } + + private static void releaseState(Context cx, State state, + Throwable throwable) + { + cx.interpreterLineCounting = state.savedInterpreterLineCounting; + + if (state.debuggerFrame != null) { + try { + if (throwable != null) { + state.debuggerFrame.onExit(cx, true, throwable); + } else { + state.debuggerFrame.onExit(cx, false, state.result); + } + } catch (Throwable ex) { + System.err.println( +"RHINO USAGE WARNING: onExit terminated with exception"); + ex.printStackTrace(System.err); + } + } + + if (state.idata.itsFunctionType != 0) { + if (state.useActivation) { ScriptRuntime.exitActivationFunction(cx); } } - - if (javaException != null) { - if (javaException instanceof RuntimeException) { - throw (RuntimeException)javaException; - } else { - // Must be instance of Error or code bug - throw (Error)javaException; - } - } - return result; } private static Object doubleWrap(double x) @@ -3090,23 +3227,21 @@ switch (op) { return ScriptRuntime.toInt32(value); } - private static double stack_double(Object[] stack, double[] stackDbl, - int i) + private static double stack_double(State state, int i) { - Object x = stack[i]; - return (x != DBL_MRK) ? ScriptRuntime.toNumber(x) : stackDbl[i]; + Object x = state.stack[i]; + return (x != DBL_MRK) ? ScriptRuntime.toNumber(x) : state.sDbl[i]; } - private static boolean stack_boolean(Object[] stack, double[] stackDbl, - int i) + private static boolean stack_boolean(State state, int i) { - Object x = stack[i]; + Object x = state.stack[i]; if (x == Boolean.TRUE) { return true; } else if (x == Boolean.FALSE) { return false; } else if (x == DBL_MRK) { - double d = stackDbl[i]; + double d = state.sDbl[i]; return d == d && d != 0.0; } else if (x == null || x == Undefined.instance) { return false; @@ -3192,12 +3327,10 @@ switch (op) { } } - private static int do_cmp(Object[] stack, double[] sDbl, int stackTop, - int op) + private static void do_cmp(State state, int i, int op) { - --stackTop; - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; + Object rhs = state.stack[i + 1]; + Object lhs = state.stack[i]; boolean result; object_compare: { @@ -3205,11 +3338,11 @@ switch (op) { { double rDbl, lDbl; if (rhs == DBL_MRK) { - rDbl = sDbl[stackTop + 1]; - lDbl = stack_double(stack, sDbl, stackTop); + rDbl = state.sDbl[i + 1]; + lDbl = stack_double(state, i); } else if (lhs == DBL_MRK) { rDbl = ScriptRuntime.toNumber(rhs); - lDbl = sDbl[stackTop]; + lDbl = state.sDbl[i]; } else { break number_compare; } @@ -3247,48 +3380,42 @@ switch (op) { throw Kit.codeBug(); } } - stack[stackTop] = result ? Boolean.TRUE : Boolean.FALSE; - return stackTop; + state.stack[i] = result ? Boolean.TRUE : Boolean.FALSE; } - private static int do_eq(Object[] stack, double[] sDbl, int stackTop, - int op) + private static void do_eq(State state, int i, int op) { - --stackTop; boolean result; - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; + Object rhs = state.stack[i + 1]; + Object lhs = state.stack[i]; if (rhs == DBL_MRK) { if (lhs == DBL_MRK) { - result = (sDbl[stackTop] == sDbl[stackTop + 1]); + result = (state.sDbl[i] == state.sDbl[i + 1]); } else { - result = ScriptRuntime.eqNumber(sDbl[stackTop + 1], lhs); + result = ScriptRuntime.eqNumber(state.sDbl[i + 1], lhs); } } else { if (lhs == DBL_MRK) { - result = ScriptRuntime.eqNumber(sDbl[stackTop], rhs); + result = ScriptRuntime.eqNumber(state.sDbl[i], rhs); } else { result = ScriptRuntime.eq(lhs, rhs); } } result ^= (op == Token.NE); - stack[stackTop] = (result) ? Boolean.TRUE : Boolean.FALSE; - return stackTop; + state.stack[i] = (result) ? Boolean.TRUE : Boolean.FALSE; } - private static int do_sheq(Object[] stack, double[] sDbl, int stackTop, - int op) + private static void do_sheq(State state, int i, int op) { - --stackTop; - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; + Object rhs = state.stack[i + 1]; + Object lhs = state.stack[i]; boolean result; double_compare: { double rdbl, ldbl; if (rhs == DBL_MRK) { - rdbl = sDbl[stackTop + 1]; + rdbl = state.sDbl[i + 1]; if (lhs == DBL_MRK) { - ldbl = sDbl[stackTop]; + ldbl = state.sDbl[i]; } else if (lhs instanceof Number) { ldbl = ((Number)lhs).doubleValue(); } else { @@ -3296,9 +3423,9 @@ switch (op) { break double_compare; } } else if (lhs == DBL_MRK) { - ldbl = sDbl[stackTop]; + ldbl = state.sDbl[i]; if (rhs == DBL_MRK) { - rdbl = sDbl[stackTop + 1]; + rdbl = state.sDbl[i + 1]; } else if (rhs instanceof Number) { rdbl = ((Number)rhs).doubleValue(); } else { @@ -3312,29 +3439,27 @@ switch (op) { result = ldbl == rdbl; } result ^= (op == Token.SHNE); - stack[stackTop] = (result) ? Boolean.TRUE : Boolean.FALSE; - return stackTop; + state.stack[i] = (result) ? Boolean.TRUE : Boolean.FALSE; } - private static int do_getElem(Object[] stack, double[] sDbl, int stackTop, - Context cx, Scriptable scope) + private static void do_getElem(State state, int i, Context cx) { - Object lhs = stack[stackTop - 1]; - if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop - 1]); + Object lhs = state.stack[i]; + if (lhs == DBL_MRK) lhs = doubleWrap(state.sDbl[i]); Object result; - Object id = stack[stackTop]; + Object id = state.stack[i + 1]; if (id != DBL_MRK) { - result = ScriptRuntime.getObjectElem(lhs, id, cx, scope); + result = ScriptRuntime.getObjectElem(lhs, id, cx, state.scope); } else { - double val = sDbl[stackTop]; + double val = state.sDbl[i + 1]; if (lhs == null || lhs == Undefined.instance) { throw ScriptRuntime.undefReadError( lhs, ScriptRuntime.toString(val)); } Scriptable obj = (lhs instanceof Scriptable) ? (Scriptable)lhs - : ScriptRuntime.toObject(cx, scope, lhs); + : ScriptRuntime.toObject(cx, state.scope, lhs); int index = (int)val; if (index == val) { result = ScriptRuntime.getObjectIndex(obj, index, cx); @@ -3343,32 +3468,29 @@ switch (op) { result = ScriptRuntime.getObjectProp(obj, s, cx); } } - --stackTop; - stack[stackTop] = result; - return stackTop; + state.stack[i] = result; } - private static int do_setElem(Object[] stack, double[] sDbl, int stackTop, - Context cx, Scriptable scope) + private static void do_setElem(State state, int i, Context cx) { - Object rhs = stack[stackTop]; - if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]); - Object lhs = stack[stackTop - 2]; - if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop - 2]); + Object rhs = state.stack[i + 2]; + if (rhs == DBL_MRK) rhs = doubleWrap(state.sDbl[i + 2]); + Object lhs = state.stack[i]; + if (lhs == DBL_MRK) lhs = doubleWrap(state.sDbl[i]); Object result; - Object id = stack[stackTop - 1]; + Object id = state.stack[i + 1]; if (id != DBL_MRK) { - result = ScriptRuntime.setObjectElem(lhs, id, rhs, cx, scope); + result = ScriptRuntime.setObjectElem(lhs, id, rhs, cx, state.scope); } else { - double val = sDbl[stackTop - 1]; + double val = state.sDbl[i + 1]; if (lhs == null || lhs == Undefined.instance) { throw ScriptRuntime.undefWriteError( lhs, ScriptRuntime.toString(val), rhs); } Scriptable obj = (lhs instanceof Scriptable) ? (Scriptable)lhs - : ScriptRuntime.toObject(cx, scope, lhs); + : ScriptRuntime.toObject(cx, state.scope, lhs); int index = (int)val; if (index == val) { result = ScriptRuntime.setObjectIndex(obj, index, rhs, cx); @@ -3377,28 +3499,7 @@ switch (op) { result = ScriptRuntime.setObjectProp(obj, s, rhs, cx); } } - stackTop -= 2; - stack[stackTop] = result; - return stackTop; - } - - static RuntimeException notAFunction(Object notAFunction, - InterpreterData idata, int namePC) - { - String debugName; - if (notAFunction == Undefined.instance || notAFunction == null) { - // special code for better error message for call - // to undefined or null - int index = getIndex(idata.itsICode, namePC); - if (index == 0xFFFF) { - debugName = ""; - } else { - debugName = idata.itsStringTable[index]; - } - } else { - debugName = ScriptRuntime.toString(notAFunction); - } - throw ScriptRuntime.notFunctionError(notAFunction, debugName); + state.stack[i] = result; } private static Object[] getArgsArray(Object[] stack, double[] sDbl, @@ -3416,29 +3517,30 @@ switch (op) { return args; } - private static Object activationGet(NativeFunction f, - Scriptable activation, int slot) + private static Object activationGet(State state, int slot) { - String name = f.argNames[slot]; - Object val = activation.get(name, activation); + String name = state.fnOrScript.argNames[slot]; + Scriptable scope = state.scope; + Object val = scope.get(name, scope); // Activation parameter or var is permanent if (val == Scriptable.NOT_FOUND) Kit.codeBug(); return val; } - private static void activationPut(NativeFunction f, - Scriptable activation, int slot, - Object value) + private static void activationPut(State state, int slot, Object value) { - String name = f.argNames[slot]; - activation.put(name, activation, value); + String name = state.fnOrScript.argNames[slot]; + Scriptable scope = state.scope; + scope.put(name, scope, value); } - private static int getJavaCatchPC(byte[] iCode) + private static void addInstructionCount(Context cx, State state, int extra) { - int pc = iCode.length - 1; - if (iCode[pc] != Icode_CATCH) Kit.codeBug(); - return pc; + cx.instructionCount += state.pc - state.pcPrevBranch + extra; + if (cx.instructionCount > cx.instructionThreshold) { + cx.observeInstructionCount(cx.instructionCount); + cx.instructionCount = 0; + } } private CompilerEnvirons compilerEnv; @@ -3453,7 +3555,6 @@ switch (op) { private int itsLineNumber; private int itsDoubleTableTop; private ObjToIntMap itsStrings = new ObjToIntMap(20); - private int itsLastStringIndex; private int itsLocalTop; private static final int MIN_LABEL_TABLE_SIZE = 32;