diff options
Diffstat (limited to 'infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java')
-rw-r--r-- | infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java | 4643 |
1 files changed, 4643 insertions, 0 deletions
diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java new file mode 100644 index 0000000..a68c025 --- /dev/null +++ b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java @@ -0,0 +1,4643 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Beard + * Norris Boyd + * Igor Bukanov + * Ethan Hugg + * Bob Jervis + * Terry Lucas + * Roger Lawrence + * Milen Nankov + * Hannes Wallnoefer + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.io.PrintStream; +import java.io.Serializable; +import java.util.List; +import java.util.ArrayList; + +import org.mozilla.javascript.continuations.Continuation; +import org.mozilla.javascript.debug.DebugFrame; + +public class Interpreter implements Evaluator +{ + +// Additional interpreter-specific codes + + private static final int + + // Stack: ... value1 -> ... value1 value1 + Icode_DUP = -1, + + // Stack: ... value2 value1 -> ... value2 value1 value2 value1 + Icode_DUP2 = -2, + + // Stack: ... value2 value1 -> ... value1 value2 + Icode_SWAP = -3, + + // Stack: ... value1 -> ... + Icode_POP = -4, + + // Store stack top into return register and then pop it + Icode_POP_RESULT = -5, + + // To jump conditionally and pop additional stack value + Icode_IFEQ_POP = -6, + + // various types of ++/-- + Icode_VAR_INC_DEC = -7, + Icode_NAME_INC_DEC = -8, + Icode_PROP_INC_DEC = -9, + Icode_ELEM_INC_DEC = -10, + Icode_REF_INC_DEC = -11, + + // load/save scope from/to local + Icode_SCOPE_LOAD = -12, + Icode_SCOPE_SAVE = -13, + + Icode_TYPEOFNAME = -14, + + // helper for function calls + Icode_NAME_AND_THIS = -15, + Icode_PROP_AND_THIS = -16, + Icode_ELEM_AND_THIS = -17, + Icode_VALUE_AND_THIS = -18, + + // Create closure object for nested functions + Icode_CLOSURE_EXPR = -19, + Icode_CLOSURE_STMT = -20, + + // Special calls + Icode_CALLSPECIAL = -21, + + // To return undefined value + Icode_RETUNDEF = -22, + + // Exception handling implementation + Icode_GOSUB = -23, + Icode_STARTSUB = -24, + Icode_RETSUB = -25, + + // To indicating a line number change in icodes. + Icode_LINE = -26, + + // To store shorts and ints inline + Icode_SHORTNUMBER = -27, + Icode_INTNUMBER = -28, + + // To create and populate array to hold values for [] and {} literals + Icode_LITERAL_NEW = -29, + Icode_LITERAL_SET = -30, + + // Array literal with skipped index like [1,,2] + Icode_SPARE_ARRAYLIT = -31, + + // Load index register to prepare for the following index operation + Icode_REG_IND_C0 = -32, + Icode_REG_IND_C1 = -33, + Icode_REG_IND_C2 = -34, + Icode_REG_IND_C3 = -35, + Icode_REG_IND_C4 = -36, + Icode_REG_IND_C5 = -37, + Icode_REG_IND1 = -38, + Icode_REG_IND2 = -39, + Icode_REG_IND4 = -40, + + // Load string register to prepare for the following string operation + Icode_REG_STR_C0 = -41, + Icode_REG_STR_C1 = -42, + Icode_REG_STR_C2 = -43, + Icode_REG_STR_C3 = -44, + Icode_REG_STR1 = -45, + Icode_REG_STR2 = -46, + Icode_REG_STR4 = -47, + + // Version of getvar/setvar that read var index directly from bytecode + Icode_GETVAR1 = -48, + Icode_SETVAR1 = -49, + + // Load unefined + Icode_UNDEF = -50, + Icode_ZERO = -51, + Icode_ONE = -52, + + // entrance and exit from .() + Icode_ENTERDQ = -53, + Icode_LEAVEDQ = -54, + + Icode_TAIL_CALL = -55, + + // Clear local to allow GC its context + Icode_LOCAL_CLEAR = -56, + + // Literal get/set + Icode_LITERAL_GETTER = -57, + Icode_LITERAL_SETTER = -58, + + // const + Icode_SETCONST = -59, + Icode_SETCONSTVAR = -60, + Icode_SETCONSTVAR1 = -61, + + // Generator opcodes (along with Token.YIELD) + Icode_GENERATOR = -62, + Icode_GENERATOR_END = -63, + + Icode_DEBUGGER = -64, + + // Last icode + MIN_ICODE = -64; + + // data for parsing + + private CompilerEnvirons compilerEnv; + + private boolean itsInFunctionFlag; + private boolean itsInTryFlag; + + private InterpreterData itsData; + private ScriptOrFnNode scriptOrFn; + private int itsICodeTop; + private int itsStackDepth; + private int itsLineNumber; + private int itsDoubleTableTop; + private ObjToIntMap itsStrings = new ObjToIntMap(20); + private int itsLocalTop; + + private static final int MIN_LABEL_TABLE_SIZE = 32; + private static final int MIN_FIXUP_TABLE_SIZE = 40; + private int[] itsLabelTable; + private int itsLabelTableTop; +// itsFixupTable[i] = (label_index << 32) | fixup_site + private long[] itsFixupTable; + private int itsFixupTableTop; + private ObjArray itsLiteralIds = new ObjArray(); + + private int itsExceptionTableTop; + private static final int EXCEPTION_TRY_START_SLOT = 0; + private static final int EXCEPTION_TRY_END_SLOT = 1; + private static final int EXCEPTION_HANDLER_SLOT = 2; + private static final int EXCEPTION_TYPE_SLOT = 3; + private static final int EXCEPTION_LOCAL_SLOT = 4; + private static final int EXCEPTION_SCOPE_SLOT = 5; + // SLOT_SIZE: space for try start/end, handler, start, handler type, + // exception local and scope local + private static final int EXCEPTION_SLOT_SIZE = 6; + +// ECF_ or Expression Context Flags constants: for now only TAIL is available + private static final int ECF_TAIL = 1 << 0; + + /** + * Class to hold data corresponding to one interpreted call stack frame. + */ + private static class CallFrame implements Cloneable, Serializable + { + static final long serialVersionUID = -2843792508994958978L; + + CallFrame parentFrame; + // amount of stack frames before this one on the interpretation stack + int frameIndex; + // If true indicates read-only frame that is a part of continuation + boolean frozen; + + InterpretedFunction fnOrScript; + InterpreterData idata; + +// Stack structure +// stack[0 <= i < localShift]: arguments and local variables +// stack[localShift <= i <= emptyStackTop]: used for local temporaries +// stack[emptyStackTop < i < stack.length]: stack data +// sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value + + Object[] stack; + int[] stackAttributes; + double[] sDbl; + CallFrame varSource; // defaults to this unless continuation frame + int localShift; + int emptyStackTop; + + DebugFrame debuggerFrame; + boolean useActivation; + + Scriptable thisObj; + Scriptable[] scriptRegExps; + +// The values that change during interpretation + + Object result; + double resultDbl; + int pc; + int pcPrevBranch; + int pcSourceLineStart; + Scriptable scope; + + int savedStackTop; + int savedCallOp; + Object throwable; + + CallFrame cloneFrozen() + { + if (!frozen) Kit.codeBug(); + + CallFrame copy; + try { + copy = (CallFrame)clone(); + } catch (CloneNotSupportedException ex) { + throw new IllegalStateException(); + } + + // clone stack but keep varSource to point to values + // from this frame to share variables. + + copy.stack = stack.clone(); + copy.stackAttributes = stackAttributes.clone(); + copy.sDbl = sDbl.clone(); + + copy.frozen = false; + return copy; + } + } + + private static final class ContinuationJump implements Serializable + { + static final long serialVersionUID = 7687739156004308247L; + + CallFrame capturedFrame; + CallFrame branchFrame; + Object result; + double resultDbl; + + ContinuationJump(Continuation c, CallFrame current) + { + this.capturedFrame = (CallFrame)c.getImplementation(); + if (this.capturedFrame == null || current == null) { + // Continuation and current execution does not share + // any frames if there is nothing to capture or + // if there is no currently executed frames + this.branchFrame = null; + } else { + // Search for branch frame where parent frame chains starting + // from captured and current meet. + CallFrame chain1 = this.capturedFrame; + CallFrame chain2 = current; + + // First work parents of chain1 or chain2 until the same + // frame depth. + int diff = chain1.frameIndex - chain2.frameIndex; + if (diff != 0) { + if (diff < 0) { + // swap to make sure that + // chain1.frameIndex > chain2.frameIndex and diff > 0 + chain1 = current; + chain2 = this.capturedFrame; + diff = -diff; + } + do { + chain1 = chain1.parentFrame; + } while (--diff != 0); + if (chain1.frameIndex != chain2.frameIndex) Kit.codeBug(); + } + + // Now walk parents in parallel until a shared frame is found + // or until the root is reached. + while (chain1 != chain2 && chain1 != null) { + chain1 = chain1.parentFrame; + chain2 = chain2.parentFrame; + } + + this.branchFrame = chain1; + if (this.branchFrame != null && !this.branchFrame.frozen) + Kit.codeBug(); + } + } + } + + private static CallFrame captureFrameForGenerator(CallFrame frame) { + frame.frozen = true; + CallFrame result = frame.cloneFrozen(); + frame.frozen = false; + + // now isolate this frame from its previous context + result.parentFrame = null; + result.frameIndex = 0; + + return result; + } + + static { + // Checks for byte code consistencies, good compiler can eliminate them + + if (Token.LAST_BYTECODE_TOKEN > 127) { + String str = "Violation of Token.LAST_BYTECODE_TOKEN <= 127"; + System.err.println(str); + throw new IllegalStateException(str); + } + if (MIN_ICODE < -128) { + String str = "Violation of Interpreter.MIN_ICODE >= -128"; + System.err.println(str); + throw new IllegalStateException(str); + } + } + + private static String bytecodeName(int bytecode) + { + if (!validBytecode(bytecode)) { + throw new IllegalArgumentException(String.valueOf(bytecode)); + } + + if (!Token.printICode) { + return String.valueOf(bytecode); + } + + if (validTokenCode(bytecode)) { + return Token.name(bytecode); + } + + switch (bytecode) { + case Icode_DUP: return "DUP"; + case Icode_DUP2: return "DUP2"; + case Icode_SWAP: return "SWAP"; + case Icode_POP: return "POP"; + case Icode_POP_RESULT: return "POP_RESULT"; + case Icode_IFEQ_POP: return "IFEQ_POP"; + case Icode_VAR_INC_DEC: return "VAR_INC_DEC"; + case Icode_NAME_INC_DEC: return "NAME_INC_DEC"; + case Icode_PROP_INC_DEC: return "PROP_INC_DEC"; + case Icode_ELEM_INC_DEC: return "ELEM_INC_DEC"; + case Icode_REF_INC_DEC: return "REF_INC_DEC"; + case Icode_SCOPE_LOAD: return "SCOPE_LOAD"; + case Icode_SCOPE_SAVE: return "SCOPE_SAVE"; + case Icode_TYPEOFNAME: return "TYPEOFNAME"; + case Icode_NAME_AND_THIS: return "NAME_AND_THIS"; + case Icode_PROP_AND_THIS: return "PROP_AND_THIS"; + case Icode_ELEM_AND_THIS: return "ELEM_AND_THIS"; + case Icode_VALUE_AND_THIS: return "VALUE_AND_THIS"; + case Icode_CLOSURE_EXPR: return "CLOSURE_EXPR"; + case Icode_CLOSURE_STMT: return "CLOSURE_STMT"; + case Icode_CALLSPECIAL: return "CALLSPECIAL"; + case Icode_RETUNDEF: return "RETUNDEF"; + case Icode_GOSUB: return "GOSUB"; + case Icode_STARTSUB: return "STARTSUB"; + case Icode_RETSUB: return "RETSUB"; + case Icode_LINE: return "LINE"; + case Icode_SHORTNUMBER: return "SHORTNUMBER"; + case Icode_INTNUMBER: return "INTNUMBER"; + case Icode_LITERAL_NEW: return "LITERAL_NEW"; + case Icode_LITERAL_SET: return "LITERAL_SET"; + case Icode_SPARE_ARRAYLIT: return "SPARE_ARRAYLIT"; + case Icode_REG_IND_C0: return "REG_IND_C0"; + case Icode_REG_IND_C1: return "REG_IND_C1"; + case Icode_REG_IND_C2: return "REG_IND_C2"; + case Icode_REG_IND_C3: return "REG_IND_C3"; + case Icode_REG_IND_C4: return "REG_IND_C4"; + case Icode_REG_IND_C5: return "REG_IND_C5"; + case Icode_REG_IND1: return "LOAD_IND1"; + case Icode_REG_IND2: return "LOAD_IND2"; + case Icode_REG_IND4: return "LOAD_IND4"; + case Icode_REG_STR_C0: return "REG_STR_C0"; + case Icode_REG_STR_C1: return "REG_STR_C1"; + case Icode_REG_STR_C2: return "REG_STR_C2"; + case Icode_REG_STR_C3: return "REG_STR_C3"; + case Icode_REG_STR1: return "LOAD_STR1"; + case Icode_REG_STR2: return "LOAD_STR2"; + case Icode_REG_STR4: return "LOAD_STR4"; + case Icode_GETVAR1: return "GETVAR1"; + case Icode_SETVAR1: return "SETVAR1"; + case Icode_UNDEF: return "UNDEF"; + case Icode_ZERO: return "ZERO"; + case Icode_ONE: return "ONE"; + case Icode_ENTERDQ: return "ENTERDQ"; + case Icode_LEAVEDQ: return "LEAVEDQ"; + case Icode_TAIL_CALL: return "TAIL_CALL"; + case Icode_LOCAL_CLEAR: return "LOCAL_CLEAR"; + case Icode_LITERAL_GETTER: return "LITERAL_GETTER"; + case Icode_LITERAL_SETTER: return "LITERAL_SETTER"; + case Icode_SETCONST: return "SETCONST"; + case Icode_SETCONSTVAR: return "SETCONSTVAR"; + case Icode_SETCONSTVAR1: return "SETCONSTVAR1"; + case Icode_GENERATOR: return "GENERATOR"; + case Icode_GENERATOR_END: return "GENERATOR_END"; + case Icode_DEBUGGER: return "DEBUGGER"; + } + + // icode without name + throw new IllegalStateException(String.valueOf(bytecode)); + } + + private static boolean validIcode(int icode) + { + return MIN_ICODE <= icode && icode <= -1; + } + + private static boolean validTokenCode(int token) + { + return Token.FIRST_BYTECODE_TOKEN <= token + && token <= Token.LAST_BYTECODE_TOKEN; + } + + private static boolean validBytecode(int bytecode) + { + return validIcode(bytecode) || validTokenCode(bytecode); + } + + public Object compile(CompilerEnvirons compilerEnv, + ScriptOrFnNode tree, + String encodedSource, + boolean returnFunction) + { + this.compilerEnv = compilerEnv; + new NodeTransformer().transform(tree); + + if (Token.printTrees) { + /*APPJET*///System.out.println(tree.toStringTree(tree)); + } + + if (returnFunction) { + tree = tree.getFunctionNode(0); + } + + scriptOrFn = tree; + itsData = new InterpreterData(compilerEnv.getLanguageVersion(), + scriptOrFn.getSourceName(), + encodedSource); + itsData.topLevel = true; + + if (returnFunction) { + generateFunctionICode(); + } else { + generateICodeFromTree(scriptOrFn); + } + + return itsData; + } + + public Script createScriptObject(Object bytecode, Object staticSecurityDomain) + { + if(bytecode != itsData) + { + Kit.codeBug(); + } + return InterpretedFunction.createScript(itsData, + staticSecurityDomain); + } + + public void setEvalScriptFlag(Script script) { + ((InterpretedFunction)script).idata.evalScriptFlag = true; + } + + + public Function createFunctionObject(Context cx, Scriptable scope, + Object bytecode, Object staticSecurityDomain) + { + if(bytecode != itsData) + { + Kit.codeBug(); + } + return InterpretedFunction.createFunction(cx, scope, itsData, + staticSecurityDomain); + } + + private void generateFunctionICode() + { + itsInFunctionFlag = true; + + FunctionNode theFunction = (FunctionNode)scriptOrFn; + + itsData.itsFunctionType = theFunction.getFunctionType(); + itsData.itsNeedsActivation = theFunction.requiresActivation(); + itsData.itsName = theFunction.getFunctionName(); + if (!theFunction.getIgnoreDynamicScope()) { + if (compilerEnv.isUseDynamicScope()) { + itsData.useDynamicScope = true; + } + } + if (theFunction.isGenerator()) { + addIcode(Icode_GENERATOR); + addUint16(theFunction.getBaseLineno() & 0xFFFF); + } + + generateICodeFromTree(theFunction.getLastChild()); + } + + private void generateICodeFromTree(Node tree) + { + generateNestedFunctions(); + + generateRegExpLiterals(); + + visitStatement(tree, 0); + fixLabelGotos(); + // add RETURN_RESULT only to scripts as function always ends with RETURN + if (itsData.itsFunctionType == 0) { + addToken(Token.RETURN_RESULT); + } + + if (itsData.itsICode.length != itsICodeTop) { + // Make itsData.itsICode length exactly itsICodeTop to save memory + // and catch bugs with jumps beyond icode as early as possible + byte[] tmp = new byte[itsICodeTop]; + System.arraycopy(itsData.itsICode, 0, tmp, 0, itsICodeTop); + itsData.itsICode = tmp; + } + if (itsStrings.size() == 0) { + itsData.itsStringTable = null; + } else { + itsData.itsStringTable = new String[itsStrings.size()]; + ObjToIntMap.Iterator iter = itsStrings.newIterator(); + for (iter.start(); !iter.done(); iter.next()) { + String str = (String)iter.getKey(); + int index = iter.getValue(); + if (itsData.itsStringTable[index] != null) Kit.codeBug(); + itsData.itsStringTable[index] = str; + } + } + if (itsDoubleTableTop == 0) { + itsData.itsDoubleTable = null; + } else if (itsData.itsDoubleTable.length != itsDoubleTableTop) { + double[] tmp = new double[itsDoubleTableTop]; + System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0, + itsDoubleTableTop); + itsData.itsDoubleTable = tmp; + } + if (itsExceptionTableTop != 0 + && itsData.itsExceptionTable.length != itsExceptionTableTop) + { + int[] tmp = new int[itsExceptionTableTop]; + System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0, + itsExceptionTableTop); + itsData.itsExceptionTable = tmp; + } + + itsData.itsMaxVars = scriptOrFn.getParamAndVarCount(); + // itsMaxFrameArray: interpret method needs this amount for its + // stack and sDbl arrays + itsData.itsMaxFrameArray = itsData.itsMaxVars + + itsData.itsMaxLocals + + itsData.itsMaxStack; + + itsData.argNames = scriptOrFn.getParamAndVarNames(); + itsData.argIsConst = scriptOrFn.getParamAndVarConst(); + itsData.argCount = scriptOrFn.getParamCount(); + + itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart(); + itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd(); + + if (itsLiteralIds.size() != 0) { + itsData.literalIds = itsLiteralIds.toArray(); + } + + if (Token.printICode) dumpICode(itsData); + } + + private void generateNestedFunctions() + { + int functionCount = scriptOrFn.getFunctionCount(); + if (functionCount == 0) return; + + InterpreterData[] array = new InterpreterData[functionCount]; + for (int i = 0; i != functionCount; i++) { + FunctionNode def = scriptOrFn.getFunctionNode(i); + Interpreter jsi = new Interpreter(); + jsi.compilerEnv = compilerEnv; + jsi.scriptOrFn = def; + jsi.itsData = new InterpreterData(itsData); + jsi.generateFunctionICode(); + array[i] = jsi.itsData; + } + itsData.itsNestedFunctions = array; + } + + private void generateRegExpLiterals() + { + int N = scriptOrFn.getRegexpCount(); + if (N == 0) return; + + Context cx = Context.getContext(); + RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx); + Object[] array = new Object[N]; + for (int i = 0; i != N; i++) { + String string = scriptOrFn.getRegexpString(i); + String flags = scriptOrFn.getRegexpFlags(i); + array[i] = rep.compileRegExp(cx, string, flags); + } + itsData.itsRegExpLiterals = array; + } + + private void updateLineNumber(Node node) + { + int lineno = node.getLineno(); + if (lineno != itsLineNumber && lineno >= 0) { + if (itsData.firstLinePC < 0) { + itsData.firstLinePC = lineno; + } + itsLineNumber = lineno; + addIcode(Icode_LINE); + addUint16(lineno & 0xFFFF); + } + } + + private RuntimeException badTree(Node node) + { + throw new RuntimeException(node.toString()); + } + + private void visitStatement(Node node, int initialStackDepth) + { + int type = node.getType(); + Node child = node.getFirstChild(); + switch (type) { + + case Token.FUNCTION: + { + int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP); + int fnType = scriptOrFn.getFunctionNode(fnIndex). + getFunctionType(); + // Only function expressions or function expression + // statements need closure code creating new function + // object on stack as function statements are initialized + // at script/function start. + // In addition, function expressions can not be present here + // at statement level, they must only be present as expressions. + if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { + addIndexOp(Icode_CLOSURE_STMT, fnIndex); + } else { + if (fnType != FunctionNode.FUNCTION_STATEMENT) { + throw Kit.codeBug(); + } + } + // For function statements or function expression statements + // in scripts, we need to ensure that the result of the script + // is the function if it is the last statement in the script. + // For example, eval("function () {}") should return a + // function, not undefined. + if (!itsInFunctionFlag) { + addIndexOp(Icode_CLOSURE_EXPR, fnIndex); + stackChange(1); + addIcode(Icode_POP_RESULT); + stackChange(-1); + } + } + break; + + case Token.LABEL: + case Token.LOOP: + case Token.BLOCK: + case Token.EMPTY: + case Token.WITH: + updateLineNumber(node); + case Token.SCRIPT: + // fall through + while (child != null) { + visitStatement(child, initialStackDepth); + child = child.getNext(); + } + break; + + case Token.ENTERWITH: + visitExpression(child, 0); + addToken(Token.ENTERWITH); + stackChange(-1); + break; + + case Token.LEAVEWITH: + addToken(Token.LEAVEWITH); + break; + + case Token.LOCAL_BLOCK: + { + int local = allocLocal(); + node.putIntProp(Node.LOCAL_PROP, local); + updateLineNumber(node); + while (child != null) { + visitStatement(child, initialStackDepth); + child = child.getNext(); + } + addIndexOp(Icode_LOCAL_CLEAR, local); + releaseLocal(local); + } + break; + + case Token.DEBUGGER: + addIcode(Icode_DEBUGGER); + break; + + case Token.SWITCH: + updateLineNumber(node); + // See comments in IRFactory.createSwitch() for description + // of SWITCH node + { + visitExpression(child, 0); + for (Node.Jump caseNode = (Node.Jump)child.getNext(); + caseNode != null; + caseNode = (Node.Jump)caseNode.getNext()) + { + if (caseNode.getType() != Token.CASE) + throw badTree(caseNode); + Node test = caseNode.getFirstChild(); + addIcode(Icode_DUP); + stackChange(1); + visitExpression(test, 0); + addToken(Token.SHEQ); + stackChange(-1); + // If true, Icode_IFEQ_POP will jump and remove case + // value from stack + addGoto(caseNode.target, Icode_IFEQ_POP); + stackChange(-1); + } + addIcode(Icode_POP); + stackChange(-1); + } + break; + + case Token.TARGET: + markTargetLabel(node); + break; + + case Token.IFEQ : + case Token.IFNE : + { + Node target = ((Node.Jump)node).target; + visitExpression(child, 0); + addGoto(target, type); + stackChange(-1); + } + break; + + case Token.GOTO: + { + Node target = ((Node.Jump)node).target; + addGoto(target, type); + } + break; + + case Token.JSR: + { + Node target = ((Node.Jump)node).target; + addGoto(target, Icode_GOSUB); + } + break; + + case Token.FINALLY: + { + // Account for incomming GOTOSUB address + stackChange(1); + int finallyRegister = getLocalBlockRef(node); + addIndexOp(Icode_STARTSUB, finallyRegister); + stackChange(-1); + while (child != null) { + visitStatement(child, initialStackDepth); + child = child.getNext(); + } + addIndexOp(Icode_RETSUB, finallyRegister); + } + break; + + case Token.EXPR_VOID: + case Token.EXPR_RESULT: + updateLineNumber(node); + visitExpression(child, 0); + addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT); + stackChange(-1); + break; + + case Token.TRY: + { + Node.Jump tryNode = (Node.Jump)node; + int exceptionObjectLocal = getLocalBlockRef(tryNode); + int scopeLocal = allocLocal(); + + addIndexOp(Icode_SCOPE_SAVE, scopeLocal); + + int tryStart = itsICodeTop; + boolean savedFlag = itsInTryFlag; + itsInTryFlag = true; + while (child != null) { + visitStatement(child, initialStackDepth); + child = child.getNext(); + } + itsInTryFlag = savedFlag; + + Node catchTarget = tryNode.target; + if (catchTarget != null) { + int catchStartPC + = itsLabelTable[getTargetLabel(catchTarget)]; + addExceptionHandler( + tryStart, catchStartPC, catchStartPC, + false, exceptionObjectLocal, scopeLocal); + } + Node finallyTarget = tryNode.getFinally(); + if (finallyTarget != null) { + int finallyStartPC + = itsLabelTable[getTargetLabel(finallyTarget)]; + addExceptionHandler( + tryStart, finallyStartPC, finallyStartPC, + true, exceptionObjectLocal, scopeLocal); + } + + addIndexOp(Icode_LOCAL_CLEAR, scopeLocal); + releaseLocal(scopeLocal); + } + break; + + case Token.CATCH_SCOPE: + { + int localIndex = getLocalBlockRef(node); + int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP); + String name = child.getString(); + child = child.getNext(); + visitExpression(child, 0); // load expression object + addStringPrefix(name); + addIndexPrefix(localIndex); + addToken(Token.CATCH_SCOPE); + addUint8(scopeIndex != 0 ? 1 : 0); + stackChange(-1); + } + break; + + case Token.THROW: + updateLineNumber(node); + visitExpression(child, 0); + addToken(Token.THROW); + addUint16(itsLineNumber & 0xFFFF); + stackChange(-1); + break; + + case Token.RETHROW: + updateLineNumber(node); + addIndexOp(Token.RETHROW, getLocalBlockRef(node)); + break; + + case Token.RETURN: + updateLineNumber(node); + if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) { + // We're in a generator, so change RETURN to GENERATOR_END + addIcode(Icode_GENERATOR_END); + addUint16(itsLineNumber & 0xFFFF); + } else if (child != null) { + visitExpression(child, ECF_TAIL); + addToken(Token.RETURN); + stackChange(-1); + } else { + addIcode(Icode_RETUNDEF); + } + break; + + case Token.RETURN_RESULT: + updateLineNumber(node); + addToken(Token.RETURN_RESULT); + break; + + case Token.ENUM_INIT_KEYS: + case Token.ENUM_INIT_VALUES: + case Token.ENUM_INIT_ARRAY: + visitExpression(child, 0); + addIndexOp(type, getLocalBlockRef(node)); + stackChange(-1); + break; + + case Icode_GENERATOR: + break; + + default: + throw badTree(node); + } + + if (itsStackDepth != initialStackDepth) { + throw Kit.codeBug(); + } + } + + private void visitExpression(Node node, int contextFlags) + { + int type = node.getType(); + Node child = node.getFirstChild(); + int savedStackDepth = itsStackDepth; + switch (type) { + + case Token.FUNCTION: + { + int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP); + FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex); + // See comments in visitStatement for Token.FUNCTION case + if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION) { + throw Kit.codeBug(); + } + addIndexOp(Icode_CLOSURE_EXPR, fnIndex); + stackChange(1); + } + break; + + case Token.LOCAL_LOAD: + { + int localIndex = getLocalBlockRef(node); + addIndexOp(Token.LOCAL_LOAD, localIndex); + stackChange(1); + } + break; + + case Token.COMMA: + { + Node lastChild = node.getLastChild(); + while (child != lastChild) { + visitExpression(child, 0); + addIcode(Icode_POP); + stackChange(-1); + child = child.getNext(); + } + // Preserve tail context flag if any + visitExpression(child, contextFlags & ECF_TAIL); + } + break; + + case Token.USE_STACK: + // Indicates that stack was modified externally, + // like placed catch object + stackChange(1); + break; + + case Token.REF_CALL: + case Token.CALL: + case Token.NEW: + { + if (type == Token.NEW) { + visitExpression(child, 0); + } else { + generateCallFunAndThis(child); + } + int argCount = 0; + while ((child = child.getNext()) != null) { + visitExpression(child, 0); + ++argCount; + } + int callType = node.getIntProp(Node.SPECIALCALL_PROP, + Node.NON_SPECIALCALL); + if (callType != Node.NON_SPECIALCALL) { + // embed line number and source filename + addIndexOp(Icode_CALLSPECIAL, argCount); + addUint8(callType); + addUint8(type == Token.NEW ? 1 : 0); + addUint16(itsLineNumber & 0xFFFF); + } else { + // Only use the tail call optimization if we're not in a try + // or we're not generating debug info (since the + // optimization will confuse the debugger) + if (type == Token.CALL && (contextFlags & ECF_TAIL) != 0 && + !compilerEnv.isGenerateDebugInfo() && !itsInTryFlag) + { + type = Icode_TAIL_CALL; + } + addIndexOp(type, argCount); + } + // adjust stack + if (type == Token.NEW) { + // new: f, args -> result + stackChange(-argCount); + } else { + // call: f, thisObj, args -> result + // ref_call: f, thisObj, args -> ref + stackChange(-1 - argCount); + } + if (argCount > itsData.itsMaxCalleeArgs) { + itsData.itsMaxCalleeArgs = argCount; + } + } + break; + + case Token.AND: + case Token.OR: + { + visitExpression(child, 0); + addIcode(Icode_DUP); + stackChange(1); + int afterSecondJumpStart = itsICodeTop; + int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ; + addGotoOp(jump); + stackChange(-1); + addIcode(Icode_POP); + stackChange(-1); + child = child.getNext(); + // Preserve tail context flag if any + visitExpression(child, contextFlags & ECF_TAIL); + resolveForwardGoto(afterSecondJumpStart); + } + break; + + case Token.HOOK: + { + Node ifThen = child.getNext(); + Node ifElse = ifThen.getNext(); + visitExpression(child, 0); + int elseJumpStart = itsICodeTop; + addGotoOp(Token.IFNE); + stackChange(-1); + // Preserve tail context flag if any + visitExpression(ifThen, contextFlags & ECF_TAIL); + int afterElseJumpStart = itsICodeTop; + addGotoOp(Token.GOTO); + resolveForwardGoto(elseJumpStart); + itsStackDepth = savedStackDepth; + // Preserve tail context flag if any + visitExpression(ifElse, contextFlags & ECF_TAIL); + resolveForwardGoto(afterElseJumpStart); + } + break; + + case Token.GETPROP: + case Token.GETPROPNOWARN: + visitExpression(child, 0); + child = child.getNext(); + addStringOp(type, child.getString()); + break; + + case Token.GETELEM: + case Token.DELPROP: + case Token.BITAND: + case Token.BITOR: + case Token.BITXOR: + case Token.LSH: + case Token.RSH: + case Token.URSH: + case Token.ADD: + case Token.SUB: + case Token.MOD: + case Token.DIV: + case Token.MUL: + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + case Token.IN: + case Token.INSTANCEOF: + case Token.LE: + case Token.LT: + case Token.GE: + case Token.GT: + visitExpression(child, 0); + child = child.getNext(); + visitExpression(child, 0); + addToken(type); + stackChange(-1); + break; + + case Token.POS: + case Token.NEG: + case Token.NOT: + case Token.BITNOT: + case Token.TYPEOF: + case Token.VOID: + visitExpression(child, 0); + if (type == Token.VOID) { + addIcode(Icode_POP); + addIcode(Icode_UNDEF); + } else { + addToken(type); + } + break; + + case Token.GET_REF: + case Token.DEL_REF: + visitExpression(child, 0); + addToken(type); + break; + + case Token.SETPROP: + case Token.SETPROP_OP: + { + visitExpression(child, 0); + child = child.getNext(); + String property = child.getString(); + child = child.getNext(); + if (type == Token.SETPROP_OP) { + addIcode(Icode_DUP); + stackChange(1); + addStringOp(Token.GETPROP, property); + // Compensate for the following USE_STACK + stackChange(-1); + } + visitExpression(child, 0); + addStringOp(Token.SETPROP, property); + stackChange(-1); + } + break; + + case Token.SETELEM: + case Token.SETELEM_OP: + visitExpression(child, 0); + child = child.getNext(); + visitExpression(child, 0); + child = child.getNext(); + if (type == Token.SETELEM_OP) { + addIcode(Icode_DUP2); + stackChange(2); + addToken(Token.GETELEM); + stackChange(-1); + // Compensate for the following USE_STACK + stackChange(-1); + } + visitExpression(child, 0); + addToken(Token.SETELEM); + stackChange(-2); + break; + + case Token.SET_REF: + case Token.SET_REF_OP: + visitExpression(child, 0); + child = child.getNext(); + if (type == Token.SET_REF_OP) { + addIcode(Icode_DUP); + stackChange(1); + addToken(Token.GET_REF); + // Compensate for the following USE_STACK + stackChange(-1); + } + visitExpression(child, 0); + addToken(Token.SET_REF); + stackChange(-1); + break; + + case Token.SETNAME: + { + String name = child.getString(); + visitExpression(child, 0); + child = child.getNext(); + visitExpression(child, 0); + addStringOp(Token.SETNAME, name); + stackChange(-1); + } + break; + + case Token.SETCONST: + { + String name = child.getString(); + visitExpression(child, 0); + child = child.getNext(); + visitExpression(child, 0); + addStringOp(Icode_SETCONST, name); + stackChange(-1); + } + break; + + case Token.TYPEOFNAME: + { + int index = -1; + // use typeofname if an activation frame exists + // since the vars all exist there instead of in jregs + if (itsInFunctionFlag && !itsData.itsNeedsActivation) + index = scriptOrFn.getIndexForNameNode(node); + if (index == -1) { + addStringOp(Icode_TYPEOFNAME, node.getString()); + stackChange(1); + } else { + addVarOp(Token.GETVAR, index); + stackChange(1); + addToken(Token.TYPEOF); + } + } + break; + + case Token.BINDNAME: + case Token.NAME: + case Token.STRING: + addStringOp(type, node.getString()); + stackChange(1); + break; + + case Token.INC: + case Token.DEC: + visitIncDec(node, child); + break; + + case Token.NUMBER: + { + double num = node.getDouble(); + int inum = (int)num; + if (inum == num) { + if (inum == 0) { + addIcode(Icode_ZERO); + // Check for negative zero + if (1.0 / num < 0.0) { + addToken(Token.NEG); + } + } else if (inum == 1) { + addIcode(Icode_ONE); + } else if ((short)inum == inum) { + addIcode(Icode_SHORTNUMBER); + // write short as uin16 bit pattern + addUint16(inum & 0xFFFF); + } else { + addIcode(Icode_INTNUMBER); + addInt(inum); + } + } else { + int index = getDoubleIndex(num); + addIndexOp(Token.NUMBER, index); + } + stackChange(1); + } + break; + + case Token.GETVAR: + { + if (itsData.itsNeedsActivation) Kit.codeBug(); + int index = scriptOrFn.getIndexForNameNode(node); + addVarOp(Token.GETVAR, index); + stackChange(1); + } + break; + + case Token.SETVAR: + { + if (itsData.itsNeedsActivation) Kit.codeBug(); + int index = scriptOrFn.getIndexForNameNode(child); + child = child.getNext(); + visitExpression(child, 0); + addVarOp(Token.SETVAR, index); + } + break; + + case Token.SETCONSTVAR: + { + if (itsData.itsNeedsActivation) Kit.codeBug(); + int index = scriptOrFn.getIndexForNameNode(child); + child = child.getNext(); + visitExpression(child, 0); + addVarOp(Token.SETCONSTVAR, index); + } + break; + + case Token.NULL: + case Token.THIS: + case Token.THISFN: + case Token.FALSE: + case Token.TRUE: + addToken(type); + stackChange(1); + break; + + case Token.ENUM_NEXT: + case Token.ENUM_ID: + addIndexOp(type, getLocalBlockRef(node)); + stackChange(1); + break; + + case Token.REGEXP: + { + int index = node.getExistingIntProp(Node.REGEXP_PROP); + addIndexOp(Token.REGEXP, index); + stackChange(1); + } + break; + + case Token.ARRAYLIT: + case Token.OBJECTLIT: + visitLiteral(node, child); + break; + + case Token.ARRAYCOMP: + visitArrayComprehension(node, child, child.getNext()); + break; + + case Token.REF_SPECIAL: + visitExpression(child, 0); + addStringOp(type, (String)node.getProp(Node.NAME_PROP)); + break; + + case Token.REF_MEMBER: + case Token.REF_NS_MEMBER: + case Token.REF_NAME: + case Token.REF_NS_NAME: + { + int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0); + // generate possible target, possible namespace and member + int childCount = 0; + do { + visitExpression(child, 0); + ++childCount; + child = child.getNext(); + } while (child != null); + addIndexOp(type, memberTypeFlags); + stackChange(1 - childCount); + } + break; + + case Token.DOTQUERY: + { + int queryPC; + updateLineNumber(node); + visitExpression(child, 0); + addIcode(Icode_ENTERDQ); + stackChange(-1); + queryPC = itsICodeTop; + visitExpression(child.getNext(), 0); + addBackwardGoto(Icode_LEAVEDQ, queryPC); + } + break; + + case Token.DEFAULTNAMESPACE : + case Token.ESCXMLATTR : + case Token.ESCXMLTEXT : + visitExpression(child, 0); + addToken(type); + break; + + case Token.YIELD: + if (child != null) { + visitExpression(child, 0); + } else { + addIcode(Icode_UNDEF); + stackChange(1); + } + addToken(Token.YIELD); + addUint16(node.getLineno() & 0xFFFF); + break; + + case Token.WITHEXPR: { + Node enterWith = node.getFirstChild(); + Node with = enterWith.getNext(); + visitExpression(enterWith.getFirstChild(), 0); + addToken(Token.ENTERWITH); + stackChange(-1); + visitExpression(with.getFirstChild(), 0); + addToken(Token.LEAVEWITH); + break; + } + + default: + throw badTree(node); + } + if (savedStackDepth + 1 != itsStackDepth) { + Kit.codeBug(); + } + } + + private void generateCallFunAndThis(Node left) + { + // Generate code to place on stack function and thisObj + int type = left.getType(); + switch (type) { + case Token.NAME: { + String name = left.getString(); + // stack: ... -> ... function thisObj + addStringOp(Icode_NAME_AND_THIS, name); + stackChange(2); + break; + } + case Token.GETPROP: + case Token.GETELEM: { + Node target = left.getFirstChild(); + visitExpression(target, 0); + Node id = target.getNext(); + if (type == Token.GETPROP) { + String property = id.getString(); + // stack: ... target -> ... function thisObj + addStringOp(Icode_PROP_AND_THIS, property); + stackChange(1); + } else { + visitExpression(id, 0); + // stack: ... target id -> ... function thisObj + addIcode(Icode_ELEM_AND_THIS); + } + break; + } + default: + // Including Token.GETVAR + visitExpression(left, 0); + // stack: ... value -> ... function thisObj + addIcode(Icode_VALUE_AND_THIS); + stackChange(1); + break; + } + } + + private void visitIncDec(Node node, Node child) + { + int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP); + int childType = child.getType(); + switch (childType) { + case Token.GETVAR : { + if (itsData.itsNeedsActivation) Kit.codeBug(); + int i = scriptOrFn.getIndexForNameNode(child); + addVarOp(Icode_VAR_INC_DEC, i); + addUint8(incrDecrMask); + stackChange(1); + break; + } + case Token.NAME : { + String name = child.getString(); + addStringOp(Icode_NAME_INC_DEC, name); + addUint8(incrDecrMask); + stackChange(1); + break; + } + case Token.GETPROP : { + Node object = child.getFirstChild(); + visitExpression(object, 0); + String property = object.getNext().getString(); + addStringOp(Icode_PROP_INC_DEC, property); + addUint8(incrDecrMask); + break; + } + case Token.GETELEM : { + Node object = child.getFirstChild(); + visitExpression(object, 0); + Node index = object.getNext(); + visitExpression(index, 0); + addIcode(Icode_ELEM_INC_DEC); + addUint8(incrDecrMask); + stackChange(-1); + break; + } + case Token.GET_REF : { + Node ref = child.getFirstChild(); + visitExpression(ref, 0); + addIcode(Icode_REF_INC_DEC); + addUint8(incrDecrMask); + break; + } + default : { + throw badTree(node); + } + } + } + + private void visitLiteral(Node node, Node child) + { + int type = node.getType(); + int count; + Object[] propertyIds = null; + if (type == Token.ARRAYLIT) { + count = 0; + for (Node n = child; n != null; n = n.getNext()) { + ++count; + } + } else if (type == Token.OBJECTLIT) { + propertyIds = (Object[])node.getProp(Node.OBJECT_IDS_PROP); + count = propertyIds.length; + } else { + throw badTree(node); + } + addIndexOp(Icode_LITERAL_NEW, count); + stackChange(2); + while (child != null) { + int childType = child.getType(); + if (childType == Token.GET) { + visitExpression(child.getFirstChild(), 0); + addIcode(Icode_LITERAL_GETTER); + } else if (childType == Token.SET) { + visitExpression(child.getFirstChild(), 0); + addIcode(Icode_LITERAL_SETTER); + } else { + visitExpression(child, 0); + addIcode(Icode_LITERAL_SET); + } + stackChange(-1); + child = child.getNext(); + } + if (type == Token.ARRAYLIT) { + int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP); + if (skipIndexes == null) { + addToken(Token.ARRAYLIT); + } else { + int index = itsLiteralIds.size(); + itsLiteralIds.add(skipIndexes); + addIndexOp(Icode_SPARE_ARRAYLIT, index); + } + } else { + int index = itsLiteralIds.size(); + itsLiteralIds.add(propertyIds); + addIndexOp(Token.OBJECTLIT, index); + } + stackChange(-1); + } + + private void visitArrayComprehension(Node node, Node initStmt, Node expr) + { + // A bit of a hack: array comprehensions are implemented using + // statement nodes for the iteration, yet they appear in an + // expression context. So we pass the current stack depth to + // visitStatement so it can check that the depth is not altered + // by statements. + visitStatement(initStmt, itsStackDepth); + visitExpression(expr, 0); + } + + private int getLocalBlockRef(Node node) + { + Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP); + return localBlock.getExistingIntProp(Node.LOCAL_PROP); + } + + private int getTargetLabel(Node target) + { + int label = target.labelId(); + if (label != -1) { + return label; + } + label = itsLabelTableTop; + if (itsLabelTable == null || label == itsLabelTable.length) { + if (itsLabelTable == null) { + itsLabelTable = new int[MIN_LABEL_TABLE_SIZE]; + }else { + int[] tmp = new int[itsLabelTable.length * 2]; + System.arraycopy(itsLabelTable, 0, tmp, 0, label); + itsLabelTable = tmp; + } + } + itsLabelTableTop = label + 1; + itsLabelTable[label] = -1; + + target.labelId(label); + return label; + } + + private void markTargetLabel(Node target) + { + int label = getTargetLabel(target); + if (itsLabelTable[label] != -1) { + // Can mark label only once + Kit.codeBug(); + } + itsLabelTable[label] = itsICodeTop; + } + + private void addGoto(Node target, int gotoOp) + { + int label = getTargetLabel(target); + if (!(label < itsLabelTableTop)) Kit.codeBug(); + int targetPC = itsLabelTable[label]; + + if (targetPC != -1) { + addBackwardGoto(gotoOp, targetPC); + } else { + int gotoPC = itsICodeTop; + addGotoOp(gotoOp); + int top = itsFixupTableTop; + if (itsFixupTable == null || top == itsFixupTable.length) { + if (itsFixupTable == null) { + itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE]; + } else { + long[] tmp = new long[itsFixupTable.length * 2]; + System.arraycopy(itsFixupTable, 0, tmp, 0, top); + itsFixupTable = tmp; + } + } + itsFixupTableTop = top + 1; + itsFixupTable[top] = ((long)label << 32) | gotoPC; + } + } + + private void fixLabelGotos() + { + for (int i = 0; i < itsFixupTableTop; i++) { + long fixup = itsFixupTable[i]; + int label = (int)(fixup >> 32); + int jumpSource = (int)fixup; + int pc = itsLabelTable[label]; + if (pc == -1) { + // Unlocated label + throw Kit.codeBug(); + } + resolveGoto(jumpSource, pc); + } + itsFixupTableTop = 0; + } + + private void addBackwardGoto(int gotoOp, int jumpPC) + { + int fromPC = itsICodeTop; + // Ensure that this is a jump backward + if (fromPC <= jumpPC) throw Kit.codeBug(); + addGotoOp(gotoOp); + resolveGoto(fromPC, jumpPC); + } + + private void resolveForwardGoto(int fromPC) + { + // Ensure that forward jump skips at least self bytecode + if (itsICodeTop < fromPC + 3) throw Kit.codeBug(); + resolveGoto(fromPC, itsICodeTop); + } + + private void resolveGoto(int fromPC, int jumpPC) + { + int offset = jumpPC - fromPC; + // Ensure that jumps do not overlap + if (0 <= offset && offset <= 2) throw Kit.codeBug(); + int offsetSite = fromPC + 1; + if (offset != (short)offset) { + if (itsData.longJumps == null) { + itsData.longJumps = new UintMap(); + } + itsData.longJumps.put(offsetSite, jumpPC); + offset = 0; + } + byte[] array = itsData.itsICode; + array[offsetSite] = (byte)(offset >> 8); + array[offsetSite + 1] = (byte)offset; + } + + private void addToken(int token) + { + if (!validTokenCode(token)) throw Kit.codeBug(); + addUint8(token); + } + + private void addIcode(int icode) + { + if (!validIcode(icode)) throw Kit.codeBug(); + // Write negative icode as uint8 bits + addUint8(icode & 0xFF); + } + + private void addUint8(int value) + { + if ((value & ~0xFF) != 0) throw Kit.codeBug(); + byte[] array = itsData.itsICode; + int top = itsICodeTop; + if (top == array.length) { + array = increaseICodeCapacity(1); + } + array[top] = (byte)value; + itsICodeTop = top + 1; + } + + private void addUint16(int value) + { + if ((value & ~0xFFFF) != 0) throw Kit.codeBug(); + byte[] array = itsData.itsICode; + int top = itsICodeTop; + if (top + 2 > array.length) { + array = increaseICodeCapacity(2); + } + array[top] = (byte)(value >>> 8); + array[top + 1] = (byte)value; + itsICodeTop = top + 2; + } + + private void addInt(int i) + { + byte[] array = itsData.itsICode; + int top = itsICodeTop; + if (top + 4 > array.length) { + array = increaseICodeCapacity(4); + } + array[top] = (byte)(i >>> 24); + array[top + 1] = (byte)(i >>> 16); + array[top + 2] = (byte)(i >>> 8); + array[top + 3] = (byte)i; + itsICodeTop = top + 4; + } + + private int getDoubleIndex(double num) + { + int index = itsDoubleTableTop; + if (index == 0) { + itsData.itsDoubleTable = new double[64]; + } else if (itsData.itsDoubleTable.length == index) { + double[] na = new double[index * 2]; + System.arraycopy(itsData.itsDoubleTable, 0, na, 0, index); + itsData.itsDoubleTable = na; + } + itsData.itsDoubleTable[index] = num; + itsDoubleTableTop = index + 1; + return index; + } + + private void addGotoOp(int gotoOp) + { + byte[] array = itsData.itsICode; + int top = itsICodeTop; + if (top + 3 > array.length) { + array = increaseICodeCapacity(3); + } + array[top] = (byte)gotoOp; + // Offset would written later + itsICodeTop = top + 1 + 2; + } + + private void addVarOp(int op, int varIndex) + { + switch (op) { + case Token.SETCONSTVAR: + if (varIndex < 128) { + addIcode(Icode_SETCONSTVAR1); + addUint8(varIndex); + return; + } + addIndexOp(Icode_SETCONSTVAR, varIndex); + return; + case Token.GETVAR: + case Token.SETVAR: + if (varIndex < 128) { + addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1); + addUint8(varIndex); + return; + } + // fallthrough + case Icode_VAR_INC_DEC: + addIndexOp(op, varIndex); + return; + } + throw Kit.codeBug(); + } + + private void addStringOp(int op, String str) + { + addStringPrefix(str); + if (validIcode(op)) { + addIcode(op); + } else { + addToken(op); + } + } + + private void addIndexOp(int op, int index) + { + addIndexPrefix(index); + if (validIcode(op)) { + addIcode(op); + } else { + addToken(op); + } + } + + private void addStringPrefix(String str) + { + int index = itsStrings.get(str, -1); + if (index == -1) { + index = itsStrings.size(); + itsStrings.put(str, index); + } + if (index < 4) { + addIcode(Icode_REG_STR_C0 - index); + } else if (index <= 0xFF) { + addIcode(Icode_REG_STR1); + addUint8(index); + } else if (index <= 0xFFFF) { + addIcode(Icode_REG_STR2); + addUint16(index); + } else { + addIcode(Icode_REG_STR4); + addInt(index); + } + } + + private void addIndexPrefix(int index) + { + if (index < 0) Kit.codeBug(); + if (index < 6) { + addIcode(Icode_REG_IND_C0 - index); + } else if (index <= 0xFF) { + addIcode(Icode_REG_IND1); + addUint8(index); + } else if (index <= 0xFFFF) { + addIcode(Icode_REG_IND2); + addUint16(index); + } else { + addIcode(Icode_REG_IND4); + addInt(index); + } + } + + private void addExceptionHandler(int icodeStart, int icodeEnd, + int handlerStart, boolean isFinally, + int exceptionObjectLocal, int scopeLocal) + { + int top = itsExceptionTableTop; + int[] table = itsData.itsExceptionTable; + if (table == null) { + if (top != 0) Kit.codeBug(); + table = new int[EXCEPTION_SLOT_SIZE * 2]; + itsData.itsExceptionTable = table; + } else if (table.length == top) { + table = new int[table.length * 2]; + System.arraycopy(itsData.itsExceptionTable, 0, table, 0, top); + itsData.itsExceptionTable = table; + } + table[top + EXCEPTION_TRY_START_SLOT] = icodeStart; + table[top + EXCEPTION_TRY_END_SLOT] = icodeEnd; + table[top + EXCEPTION_HANDLER_SLOT] = handlerStart; + table[top + EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0; + table[top + EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal; + table[top + EXCEPTION_SCOPE_SLOT] = scopeLocal; + + itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE; + } + + private byte[] increaseICodeCapacity(int extraSize) + { + int capacity = itsData.itsICode.length; + int top = itsICodeTop; + if (top + extraSize <= capacity) throw Kit.codeBug(); + capacity *= 2; + if (top + extraSize > capacity) { + capacity = top + extraSize; + } + byte[] array = new byte[capacity]; + System.arraycopy(itsData.itsICode, 0, array, 0, top); + itsData.itsICode = array; + return array; + } + + private void stackChange(int change) + { + if (change <= 0) { + itsStackDepth += change; + } else { + int newDepth = itsStackDepth + change; + if (newDepth > itsData.itsMaxStack) { + itsData.itsMaxStack = newDepth; + } + itsStackDepth = newDepth; + } + } + + private int allocLocal() + { + int localSlot = itsLocalTop; + ++itsLocalTop; + if (itsLocalTop > itsData.itsMaxLocals) { + itsData.itsMaxLocals = itsLocalTop; + } + return localSlot; + } + + private void releaseLocal(int localSlot) + { + --itsLocalTop; + if (localSlot != itsLocalTop) Kit.codeBug(); + } + + private static int getShort(byte[] iCode, int pc) { + return (iCode[pc] << 8) | (iCode[pc + 1] & 0xFF); + } + + private static int getIndex(byte[] iCode, int pc) { + return ((iCode[pc] & 0xFF) << 8) | (iCode[pc + 1] & 0xFF); + } + + private static int getInt(byte[] iCode, int pc) { + return (iCode[pc] << 24) | ((iCode[pc + 1] & 0xFF) << 16) + | ((iCode[pc + 2] & 0xFF) << 8) | (iCode[pc + 3] & 0xFF); + } + + private static int getExceptionHandler(CallFrame frame, + boolean onlyFinally) + { + int[] exceptionTable = frame.idata.itsExceptionTable; + if (exceptionTable == null) { + // No exception handlers + return -1; + } + + // Icode switch in the interpreter increments PC immediately + // and it is necessary to subtract 1 from the saved PC + // to point it before the start of the next instruction. + int pc = frame.pc - 1; + + // OPT: use binary search + int best = -1, bestStart = 0, bestEnd = 0; + for (int i = 0; i != exceptionTable.length; i += EXCEPTION_SLOT_SIZE) { + int start = exceptionTable[i + EXCEPTION_TRY_START_SLOT]; + int end = exceptionTable[i + EXCEPTION_TRY_END_SLOT]; + if (!(start <= pc && pc < end)) { + continue; + } + if (onlyFinally && exceptionTable[i + EXCEPTION_TYPE_SLOT] != 1) { + continue; + } + if (best >= 0) { + // Since handlers always nest and they never have shared end + // although they can share start it is sufficient to compare + // handlers ends + if (bestEnd < end) { + continue; + } + // Check the above assumption + if (bestStart > start) Kit.codeBug(); // should be nested + if (bestEnd == end) Kit.codeBug(); // no ens sharing + } + best = i; + bestStart = start; + bestEnd = end; + } + return best; + } + + private static void dumpICode(InterpreterData idata) + { + if (!Token.printICode) { + return; + } + + byte iCode[] = idata.itsICode; + int iCodeLength = iCode.length; + String[] strings = idata.itsStringTable; + PrintStream out = System.out; + out.println("ICode dump, for " + idata.itsName + + ", length = " + iCodeLength); + out.println("MaxStack = " + idata.itsMaxStack); + + int indexReg = 0; + for (int pc = 0; pc < iCodeLength; ) { + out.flush(); + out.print(" [" + pc + "] "); + int token = iCode[pc]; + int icodeLength = bytecodeSpan(token); + String tname = bytecodeName(token); + int old_pc = pc; + ++pc; + switch (token) { + default: + if (icodeLength != 1) Kit.codeBug(); + out.println(tname); + break; + + case Icode_GOSUB : + case Token.GOTO : + case Token.IFEQ : + case Token.IFNE : + case Icode_IFEQ_POP : + case Icode_LEAVEDQ : { + int newPC = pc + getShort(iCode, pc) - 1; + out.println(tname + " " + newPC); + pc += 2; + break; + } + case Icode_VAR_INC_DEC : + case Icode_NAME_INC_DEC : + case Icode_PROP_INC_DEC : + case Icode_ELEM_INC_DEC : + case Icode_REF_INC_DEC: { + int incrDecrType = iCode[pc]; + out.println(tname + " " + incrDecrType); + ++pc; + break; + } + + case Icode_CALLSPECIAL : { + int callType = iCode[pc] & 0xFF; + boolean isNew = (iCode[pc + 1] != 0); + int line = getIndex(iCode, pc+2); + out.println(tname+" "+callType+" "+isNew+" "+indexReg+" "+line); + pc += 4; + break; + } + + case Token.CATCH_SCOPE: + { + boolean afterFisrtFlag = (iCode[pc] != 0); + out.println(tname+" "+afterFisrtFlag); + ++pc; + } + break; + case Token.REGEXP : + out.println(tname+" "+idata.itsRegExpLiterals[indexReg]); + break; + case Token.OBJECTLIT : + case Icode_SPARE_ARRAYLIT : + out.println(tname+" "+idata.literalIds[indexReg]); + break; + case Icode_CLOSURE_EXPR : + case Icode_CLOSURE_STMT : + out.println(tname+" "+idata.itsNestedFunctions[indexReg]); + break; + case Token.CALL : + case Icode_TAIL_CALL : + case Token.REF_CALL : + case Token.NEW : + out.println(tname+' '+indexReg); + break; + case Token.THROW : + case Token.YIELD : + case Icode_GENERATOR : + case Icode_GENERATOR_END : + { + int line = getIndex(iCode, pc); + out.println(tname + " : " + line); + pc += 2; + break; + } + case Icode_SHORTNUMBER : { + int value = getShort(iCode, pc); + out.println(tname + " " + value); + pc += 2; + break; + } + case Icode_INTNUMBER : { + int value = getInt(iCode, pc); + out.println(tname + " " + value); + pc += 4; + break; + } + case Token.NUMBER : { + double value = idata.itsDoubleTable[indexReg]; + out.println(tname + " " + value); + break; + } + case Icode_LINE : { + int line = getIndex(iCode, pc); + out.println(tname + " : " + line); + pc += 2; + break; + } + case Icode_REG_STR1: { + String str = strings[0xFF & iCode[pc]]; + out.println(tname + " \"" + str + '"'); + ++pc; + break; + } + case Icode_REG_STR2: { + String str = strings[getIndex(iCode, pc)]; + out.println(tname + " \"" + str + '"'); + pc += 2; + break; + } + case Icode_REG_STR4: { + String str = strings[getInt(iCode, pc)]; + out.println(tname + " \"" + str + '"'); + pc += 4; + break; + } + case Icode_REG_IND_C0: + indexReg = 0; + out.println(tname); + break; + case Icode_REG_IND_C1: + indexReg = 1; + out.println(tname); + break; + case Icode_REG_IND_C2: + indexReg = 2; + out.println(tname); + break; + case Icode_REG_IND_C3: + indexReg = 3; + out.println(tname); + break; + case Icode_REG_IND_C4: + indexReg = 4; + out.println(tname); + break; + case Icode_REG_IND_C5: + indexReg = 5; + out.println(tname); + break; + case Icode_REG_IND1: { + indexReg = 0xFF & iCode[pc]; + out.println(tname+" "+indexReg); + ++pc; + break; + } + case Icode_REG_IND2: { + indexReg = getIndex(iCode, pc); + out.println(tname+" "+indexReg); + pc += 2; + break; + } + case Icode_REG_IND4: { + indexReg = getInt(iCode, pc); + out.println(tname+" "+indexReg); + pc += 4; + break; + } + case Icode_GETVAR1: + case Icode_SETVAR1: + case Icode_SETCONSTVAR1: + indexReg = iCode[pc]; + out.println(tname+" "+indexReg); + ++pc; + break; + } + if (old_pc + icodeLength != pc) Kit.codeBug(); + } + + int[] table = idata.itsExceptionTable; + if (table != null) { + out.println("Exception handlers: " + +table.length / EXCEPTION_SLOT_SIZE); + for (int i = 0; i != table.length; + i += EXCEPTION_SLOT_SIZE) + { + int tryStart = table[i + EXCEPTION_TRY_START_SLOT]; + int tryEnd = table[i + EXCEPTION_TRY_END_SLOT]; + int handlerStart = table[i + EXCEPTION_HANDLER_SLOT]; + int type = table[i + EXCEPTION_TYPE_SLOT]; + int exceptionLocal = table[i + EXCEPTION_LOCAL_SLOT]; + int scopeLocal = table[i + EXCEPTION_SCOPE_SLOT]; + + out.println(" tryStart="+tryStart+" tryEnd="+tryEnd + +" handlerStart="+handlerStart + +" type="+(type == 0 ? "catch" : "finally") + +" exceptionLocal="+exceptionLocal); + } + } + out.flush(); + } + + private static int bytecodeSpan(int bytecode) + { + switch (bytecode) { + case Token.THROW : + case Token.YIELD: + case Icode_GENERATOR: + case Icode_GENERATOR_END: + // source line + return 1 + 2; + + case Icode_GOSUB : + case Token.GOTO : + case Token.IFEQ : + case Token.IFNE : + case Icode_IFEQ_POP : + case Icode_LEAVEDQ : + // target pc offset + return 1 + 2; + + case Icode_CALLSPECIAL : + // call type + // is new + // line number + return 1 + 1 + 1 + 2; + + case Token.CATCH_SCOPE: + // scope flag + return 1 + 1; + + case Icode_VAR_INC_DEC: + case Icode_NAME_INC_DEC: + case Icode_PROP_INC_DEC: + case Icode_ELEM_INC_DEC: + case Icode_REF_INC_DEC: + // type of ++/-- + return 1 + 1; + + case Icode_SHORTNUMBER : + // short number + return 1 + 2; + + case Icode_INTNUMBER : + // int number + return 1 + 4; + + case Icode_REG_IND1: + // ubyte index + return 1 + 1; + + case Icode_REG_IND2: + // ushort index + return 1 + 2; + + case Icode_REG_IND4: + // int index + return 1 + 4; + + case Icode_REG_STR1: + // ubyte string index + return 1 + 1; + + case Icode_REG_STR2: + // ushort string index + return 1 + 2; + + case Icode_REG_STR4: + // int string index + return 1 + 4; + + case Icode_GETVAR1: + case Icode_SETVAR1: + case Icode_SETCONSTVAR1: + // byte var index + return 1 + 1; + + case Icode_LINE : + // line number + return 1 + 2; + } + if (!validBytecode(bytecode)) throw Kit.codeBug(); + return 1; + } + + static int[] getLineNumbers(InterpreterData data) + { + UintMap presentLines = new UintMap(); + + byte[] iCode = data.itsICode; + int iCodeLength = iCode.length; + for (int pc = 0; pc != iCodeLength;) { + int bytecode = iCode[pc]; + int span = bytecodeSpan(bytecode); + if (bytecode == Icode_LINE) { + if (span != 3) Kit.codeBug(); + int line = getIndex(iCode, pc + 1); + presentLines.put(line, 0); + } + pc += span; + } + + return presentLines.getKeys(); + } + + public void captureStackInfo(RhinoException ex) + { + Context cx = Context.getCurrentContext(); + if (cx == null || cx.lastInterpreterFrame == null) { + // No interpreter invocations + ex.interpreterStackInfo = null; + ex.interpreterLineData = null; + return; + } + // has interpreter frame on the stack + CallFrame[] array; + if (cx.previousInterpreterInvocations == null + || cx.previousInterpreterInvocations.size() == 0) + { + array = new CallFrame[1]; + } else { + int previousCount = cx.previousInterpreterInvocations.size(); + if (cx.previousInterpreterInvocations.peek() + == cx.lastInterpreterFrame) + { + // It can happen if exception was generated after + // frame was pushed to cx.previousInterpreterInvocations + // but before assignment to cx.lastInterpreterFrame. + // In this case frames has to be ignored. + --previousCount; + } + array = new CallFrame[previousCount + 1]; + cx.previousInterpreterInvocations.toArray(array); + } + array[array.length - 1] = (CallFrame)cx.lastInterpreterFrame; + + int interpreterFrameCount = 0; + for (int i = 0; i != array.length; ++i) { + interpreterFrameCount += 1 + array[i].frameIndex; + } + + int[] linePC = new int[interpreterFrameCount]; + // Fill linePC with pc positions from all interpreter frames. + // Start from the most nested frame + int linePCIndex = interpreterFrameCount; + for (int i = array.length; i != 0;) { + --i; + CallFrame frame = array[i]; + while (frame != null) { + --linePCIndex; + linePC[linePCIndex] = frame.pcSourceLineStart; + frame = frame.parentFrame; + } + } + if (linePCIndex != 0) Kit.codeBug(); + + ex.interpreterStackInfo = array; + ex.interpreterLineData = linePC; + } + + public String getSourcePositionFromStack(Context cx, int[] linep) + { + CallFrame frame = (CallFrame)cx.lastInterpreterFrame; + InterpreterData idata = frame.idata; + if (frame.pcSourceLineStart >= 0) { + linep[0] = getIndex(idata.itsICode, frame.pcSourceLineStart); + } else { + linep[0] = 0; + } + return idata.itsSourceFile; + } + + public String getPatchedStack(RhinoException ex, + String nativeStackTrace) + { + String tag = "org.mozilla.javascript.Interpreter.interpretLoop"; + StringBuffer sb = new StringBuffer(nativeStackTrace.length() + 1000); + String lineSeparator = SecurityUtilities.getSystemProperty("line.separator"); + + CallFrame[] array = (CallFrame[])ex.interpreterStackInfo; + int[] linePC = ex.interpreterLineData; + int arrayIndex = array.length; + int linePCIndex = linePC.length; + int offset = 0; + while (arrayIndex != 0) { + --arrayIndex; + int pos = nativeStackTrace.indexOf(tag, offset); + if (pos < 0) { + break; + } + + // Skip tag length + pos += tag.length(); + // Skip until the end of line + for (; pos != nativeStackTrace.length(); ++pos) { + char c = nativeStackTrace.charAt(pos); + if (c == '\n' || c == '\r') { + break; + } + } + sb.append(nativeStackTrace.substring(offset, pos)); + offset = pos; + + CallFrame frame = array[arrayIndex]; + while (frame != null) { + if (linePCIndex == 0) Kit.codeBug(); + --linePCIndex; + InterpreterData idata = frame.idata; + sb.append(lineSeparator); + sb.append("\tat script"); + if (idata.itsName != null && idata.itsName.length() != 0) { + sb.append('.'); + sb.append(idata.itsName); + } + sb.append('('); + sb.append(idata.itsSourceFile); + int pc = linePC[linePCIndex]; + if (pc >= 0) { + // Include line info only if available + sb.append(':'); + sb.append(getIndex(idata.itsICode, pc)); + } + sb.append(')'); + frame = frame.parentFrame; + } + } + sb.append(nativeStackTrace.substring(offset)); + + return sb.toString(); + } + + public List getScriptStack(RhinoException ex) + { + if (ex.interpreterStackInfo == null) { + return null; + } + + List list = new ArrayList(); + String lineSeparator = + SecurityUtilities.getSystemProperty("line.separator"); + + CallFrame[] array = (CallFrame[])ex.interpreterStackInfo; + int[] linePC = ex.interpreterLineData; + int arrayIndex = array.length; + int linePCIndex = linePC.length; + while (arrayIndex != 0) { + --arrayIndex; + StringBuffer sb = new StringBuffer(); + CallFrame frame = array[arrayIndex]; + while (frame != null) { + if (linePCIndex == 0) Kit.codeBug(); + --linePCIndex; + InterpreterData idata = frame.idata; + sb.append("\tat "); + sb.append(idata.itsSourceFile); + int pc = linePC[linePCIndex]; + if (pc >= 0) { + // Include line info only if available + sb.append(':'); + sb.append(getIndex(idata.itsICode, pc)); + } + if (idata.itsName != null && idata.itsName.length() != 0) { + sb.append(" ("); + sb.append(idata.itsName); + sb.append(')'); + } + sb.append(lineSeparator); + frame = frame.parentFrame; + } + list.add(sb.toString()); + } + return list; + } + + static String getEncodedSource(InterpreterData idata) + { + if (idata.encodedSource == null) { + return null; + } + return idata.encodedSource.substring(idata.encodedSourceStart, + idata.encodedSourceEnd); + } + + private static void initFunction(Context cx, Scriptable scope, + InterpretedFunction parent, int index) + { + InterpretedFunction fn; + fn = InterpretedFunction.createFunction(cx, scope, parent, index); + ScriptRuntime.initFunction(cx, scope, fn, fn.idata.itsFunctionType, + parent.idata.evalScriptFlag); + } + + static Object interpret(InterpretedFunction ifun, + Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!ScriptRuntime.hasTopCall(cx)) Kit.codeBug(); + + if (cx.interpreterSecurityDomain != ifun.securityDomain) { + Object savedDomain = cx.interpreterSecurityDomain; + cx.interpreterSecurityDomain = ifun.securityDomain; + try { + return ifun.securityController.callWithDomain( + ifun.securityDomain, cx, ifun, scope, thisObj, args); + } finally { + cx.interpreterSecurityDomain = savedDomain; + } + } + + CallFrame frame = new CallFrame(); + initFrame(cx, scope, thisObj, args, null, 0, args.length, + ifun, null, frame); + + return interpretLoop(cx, frame, null); + } + + static class GeneratorState { + GeneratorState(int operation, Object value) { + this.operation = operation; + this.value = value; + } + int operation; + Object value; + RuntimeException returnedException; + } + + public static Object resumeGenerator(Context cx, + Scriptable scope, + int operation, + Object savedState, + Object value) + { + CallFrame frame = (CallFrame) savedState; + GeneratorState generatorState = new GeneratorState(operation, value); + if (operation == NativeGenerator.GENERATOR_CLOSE) { + try { + return interpretLoop(cx, frame, generatorState); + } catch (RuntimeException e) { + // Only propagate exceptions other than closingException + if (e != value) + throw e; + } + return Undefined.instance; + } + Object result = interpretLoop(cx, frame, generatorState); + if (generatorState.returnedException != null) + throw generatorState.returnedException; + return result; + } + + public static Object restartContinuation(Continuation c, Context cx, + Scriptable scope, Object[] args) + { + if (!ScriptRuntime.hasTopCall(cx)) { + return ScriptRuntime.doTopCall(c, cx, scope, null, args); + } + + Object arg; + if (args.length == 0) { + arg = Undefined.instance; + } else { + arg = args[0]; + } + + CallFrame capturedFrame = (CallFrame)c.getImplementation(); + if (capturedFrame == null) { + // No frames to restart + return arg; + } + + ContinuationJump cjump = new ContinuationJump(c, null); + + cjump.result = arg; + return interpretLoop(cx, null, cjump); + } + + private 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 + + final Object DBL_MRK = UniqueTag.DOUBLE_MARK; + final Object undefined = Undefined.instance; + + final boolean instructionCounting = (cx.instructionThreshold != 0); + // arbitrary number to add to instructionCount when calling + // other functions + final int INVOCATION_COST = 100; + // arbitrary exception cost for instruction counting + final int EXCEPTION_COST = 100; + + String stringReg = null; + int indexReg = -1; + + if (cx.lastInterpreterFrame != null) { + // save the top frame from the previous interpretLoop + // invocation on the stack + if (cx.previousInterpreterInvocations == null) { + cx.previousInterpreterInvocations = new ObjArray(); + } + cx.previousInterpreterInvocations.push(cx.lastInterpreterFrame); + } + + // When restarting continuation throwable is not null and to jump + // to the code that rewind continuation state indexReg should be set + // to -1. + // With the normal call throable == null and indexReg == -1 allows to + // catch bugs with using indeReg to access array eleemnts before + // initializing indexReg. + + GeneratorState generatorState = null; + if (throwable != null) { + if (throwable instanceof GeneratorState) { + generatorState = (GeneratorState) throwable; + + // reestablish this call frame + enterFrame(cx, frame, ScriptRuntime.emptyArgs, true); + throwable = null; + } else if (!(throwable instanceof ContinuationJump)) { + // It should be continuation + Kit.codeBug(); + } + } + + Object interpreterResult = null; + double interpreterResultDbl = 0.0; + + StateLoop: for (;;) { + withoutExceptions: try { + + if (throwable != null) { + // Need to return both 'frame' and 'throwable' from + // 'processThrowable', so just added a 'throwable' + // member in 'frame'. + frame = processThrowable(cx, throwable, frame, indexReg, + instructionCounting); + throwable = frame.throwable; + frame.throwable = null; + } else { + if (generatorState == null && frame.frozen) Kit.codeBug(); + } + + // Use local variables for constant values in frame + // for faster access + Object[] stack = frame.stack; + double[] sDbl = frame.sDbl; + Object[] vars = frame.varSource.stack; + double[] varDbls = frame.varSource.sDbl; + int[] varAttributes = frame.varSource.stackAttributes; + byte[] iCode = frame.idata.itsICode; + String[] strings = frame.idata.itsStringTable; + + // Use local for stackTop as well. Since execption handlers + // can only exist at statement level where stack is empty, + // it is necessary to save/restore stackTop only across + // function calls and normal returns. + int stackTop = frame.savedStackTop; + + // Store new frame in cx which is used for error reporting etc. + cx.lastInterpreterFrame = frame; + + Loop: for (;;) { + + // Exception handler assumes that PC is already incremented + // pass the instruction start when it searches the + // exception handler + int op = iCode[frame.pc++]; + jumplessRun: { + + // Back indent to ease implementation reading +switch (op) { + case Icode_GENERATOR: { + if (!frame.frozen) { + // First time encountering this opcode: create new generator + // object and return + frame.pc--; // we want to come back here when we resume + CallFrame generatorFrame = captureFrameForGenerator(frame); + generatorFrame.frozen = true; + NativeGenerator generator = new NativeGenerator(frame.scope, + generatorFrame.fnOrScript, generatorFrame); + frame.result = generator; + break Loop; + } else { + // We are now resuming execution. Fall through to YIELD case. + } + } + // fall through... + case Token.YIELD: { + if (!frame.frozen) { + return freezeGenerator(cx, frame, stackTop, generatorState); + } else { + Object obj = thawGenerator(frame, stackTop, generatorState, op); + if (obj != Scriptable.NOT_FOUND) { + throwable = obj; + break withoutExceptions; + } + continue Loop; + } + } + case Icode_GENERATOR_END: { + // throw StopIteration + frame.frozen = true; + int sourceLine = getIndex(iCode, frame.pc); + generatorState.returnedException = new JavaScriptException( + NativeIterator.getStopIterationObject(frame.scope), + frame.idata.itsSourceFile, sourceLine); + break Loop; + } + case Token.THROW: { + Object value = stack[stackTop]; + if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + + int sourceLine = getIndex(iCode, frame.pc); + throwable = new JavaScriptException(value, + frame.idata.itsSourceFile, + sourceLine); + break withoutExceptions; + } + case Token.RETHROW: { + indexReg += frame.localShift; + throwable = stack[indexReg]; + break withoutExceptions; + } + case Token.GE : + case Token.LE : + case Token.GT : + case Token.LT : { + --stackTop; + Object rhs = stack[stackTop + 1]; + Object lhs = stack[stackTop]; + boolean valBln; + object_compare: + { + number_compare: + { + double rDbl, lDbl; + if (rhs == DBL_MRK) { + rDbl = sDbl[stackTop + 1]; + lDbl = stack_double(frame, stackTop); + } else if (lhs == DBL_MRK) { + rDbl = ScriptRuntime.toNumber(rhs); + lDbl = sDbl[stackTop]; + } else { + break number_compare; + } + switch (op) { + case Token.GE: + valBln = (lDbl >= rDbl); + break object_compare; + case Token.LE: + valBln = (lDbl <= rDbl); + break object_compare; + case Token.GT: + valBln = (lDbl > rDbl); + break object_compare; + case Token.LT: + valBln = (lDbl < rDbl); + break object_compare; + default: + throw Kit.codeBug(); + } + } + switch (op) { + case Token.GE: + valBln = ScriptRuntime.cmp_LE(rhs, lhs); + break; + case Token.LE: + valBln = ScriptRuntime.cmp_LE(lhs, rhs); + break; + case Token.GT: + valBln = ScriptRuntime.cmp_LT(rhs, lhs); + break; + case Token.LT: + valBln = ScriptRuntime.cmp_LT(lhs, rhs); + break; + default: + throw Kit.codeBug(); + } + } + stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); + continue Loop; + } + case Token.IN : + case Token.INSTANCEOF : { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + boolean valBln; + if (op == Token.IN) { + valBln = ScriptRuntime.in(lhs, rhs, cx); + } else { + valBln = ScriptRuntime.instanceOf(lhs, rhs, cx); + } + stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); + continue Loop; + } + case Token.EQ : + case Token.NE : { + --stackTop; + boolean 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] = ScriptRuntime.wrapBoolean(valBln); + continue Loop; + } + case Token.SHEQ : + case Token.SHNE : { + --stackTop; + Object rhs = stack[stackTop + 1]; + Object lhs = stack[stackTop]; + boolean valBln; + shallow_compare: { + double rdbl, ldbl; + if (rhs == DBL_MRK) { + rdbl = sDbl[stackTop + 1]; + if (lhs == DBL_MRK) { + ldbl = sDbl[stackTop]; + } else if (lhs instanceof Number) { + ldbl = ((Number)lhs).doubleValue(); + } else { + valBln = false; + break shallow_compare; + } + } else if (lhs == DBL_MRK) { + ldbl = sDbl[stackTop]; + if (rhs == DBL_MRK) { + rdbl = sDbl[stackTop + 1]; + } else if (rhs instanceof Number) { + rdbl = ((Number)rhs).doubleValue(); + } else { + valBln = false; + break shallow_compare; + } + } else { + valBln = ScriptRuntime.shallowEq(lhs, rhs); + break shallow_compare; + } + valBln = (ldbl == rdbl); + } + valBln ^= (op == Token.SHNE); + stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); + continue Loop; + } + case Token.IFNE : + if (stack_boolean(frame, stackTop--)) { + frame.pc += 2; + continue Loop; + } + break jumplessRun; + case Token.IFEQ : + if (!stack_boolean(frame, stackTop--)) { + frame.pc += 2; + continue Loop; + } + break jumplessRun; + case Icode_IFEQ_POP : + if (!stack_boolean(frame, stackTop--)) { + frame.pc += 2; + continue Loop; + } + stack[stackTop--] = null; + break jumplessRun; + case Token.GOTO : + break jumplessRun; + case Icode_GOSUB : + ++stackTop; + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = frame.pc + 2; + break jumplessRun; + case Icode_STARTSUB : + if (stackTop == frame.emptyStackTop + 1) { + // Call from Icode_GOSUB: store return PC address in the local + indexReg += frame.localShift; + stack[indexReg] = stack[stackTop]; + sDbl[indexReg] = sDbl[stackTop]; + --stackTop; + } else { + // Call from exception handler: exception object is already stored + // in the local + if (stackTop != frame.emptyStackTop) Kit.codeBug(); + } + continue Loop; + 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; + break withoutExceptions; + } + // Normal return from GOSUB + frame.pc = (int)sDbl[indexReg]; + if (instructionCounting) { + frame.pcPrevBranch = frame.pc; + } + continue Loop; + } + case Icode_POP : + stack[stackTop] = null; + stackTop--; + continue Loop; + case Icode_POP_RESULT : + frame.result = stack[stackTop]; + frame.resultDbl = sDbl[stackTop]; + stack[stackTop] = null; + --stackTop; + continue Loop; + case Icode_DUP : + stack[stackTop + 1] = stack[stackTop]; + sDbl[stackTop + 1] = sDbl[stackTop]; + stackTop++; + continue Loop; + 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; + continue Loop; + 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; + continue Loop; + } + case Token.RETURN : + frame.result = stack[stackTop]; + frame.resultDbl = sDbl[stackTop]; + --stackTop; + break Loop; + case Token.RETURN_RESULT : + break Loop; + case Icode_RETUNDEF : + frame.result = undefined; + break Loop; + case Token.BITNOT : { + int rIntValue = stack_int32(frame, stackTop); + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = ~rIntValue; + continue Loop; + } + case Token.BITAND : + case Token.BITOR : + case Token.BITXOR : + case Token.LSH : + case Token.RSH : { + int lIntValue = stack_int32(frame, stackTop-1); + int rIntValue = 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; + continue Loop; + } + case Token.URSH : { + double lDbl = stack_double(frame, stackTop-1); + int rIntValue = stack_int32(frame, stackTop) & 0x1F; + stack[--stackTop] = DBL_MRK; + sDbl[stackTop] = ScriptRuntime.toUint32(lDbl) >>> rIntValue; + continue Loop; + } + 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; + continue Loop; + } + case Token.ADD : + --stackTop; + do_add(stack, sDbl, stackTop, cx); + continue Loop; + 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; + continue Loop; + } + case Token.NOT : + stack[stackTop] = ScriptRuntime.wrapBoolean( + !stack_boolean(frame, stackTop)); + continue Loop; + case Token.BINDNAME : + stack[++stackTop] = ScriptRuntime.bind(cx, frame.scope, stringReg); + continue Loop; + case Token.SETNAME : { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Scriptable lhs = (Scriptable)stack[stackTop]; + stack[stackTop] = ScriptRuntime.setName(lhs, rhs, cx, + frame.scope, stringReg); + continue Loop; + } + case Icode_SETCONST: { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Scriptable lhs = (Scriptable)stack[stackTop]; + stack[stackTop] = ScriptRuntime.setConst(lhs, rhs, cx, stringReg); + continue Loop; + } + case Token.DELPROP : { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx); + continue Loop; + } + case Token.GETPROPNOWARN : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.getObjectPropNoWarn(lhs, stringReg, cx); + continue Loop; + } + case Token.GETPROP : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.getObjectProp(lhs, stringReg, cx); + continue Loop; + } + case Token.SETPROP : { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.setObjectProp(lhs, stringReg, rhs, + cx); + continue Loop; + } + case Icode_PROP_INC_DEC : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.propIncrDecr(lhs, stringReg, + cx, iCode[frame.pc]); + ++frame.pc; + continue Loop; + } + case Token.GETELEM : { + --stackTop; + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) { + lhs = ScriptRuntime.wrapNumber(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; + continue Loop; + } + case Token.SETELEM : { + stackTop -= 2; + Object rhs = stack[stackTop + 2]; + if (rhs == DBL_MRK) { + rhs = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]); + } + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) { + lhs = ScriptRuntime.wrapNumber(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; + continue Loop; + } + case Icode_ELEM_INC_DEC: { + Object rhs = stack[stackTop]; + if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, + iCode[frame.pc]); + ++frame.pc; + continue Loop; + } + case Token.GET_REF : { + Ref ref = (Ref)stack[stackTop]; + stack[stackTop] = ScriptRuntime.refGet(ref, cx); + continue Loop; + } + case Token.SET_REF : { + Object value = stack[stackTop]; + if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Ref ref = (Ref)stack[stackTop]; + stack[stackTop] = ScriptRuntime.refSet(ref, value, cx); + continue Loop; + } + case Token.DEL_REF : { + Ref ref = (Ref)stack[stackTop]; + stack[stackTop] = ScriptRuntime.refDel(ref, cx); + continue Loop; + } + case Icode_REF_INC_DEC : { + Ref ref = (Ref)stack[stackTop]; + stack[stackTop] = ScriptRuntime.refIncrDecr(ref, cx, iCode[frame.pc]); + ++frame.pc; + continue Loop; + } + case Token.LOCAL_LOAD : + ++stackTop; + indexReg += frame.localShift; + stack[stackTop] = stack[indexReg]; + sDbl[stackTop] = sDbl[indexReg]; + continue Loop; + case Icode_LOCAL_CLEAR : + indexReg += frame.localShift; + stack[indexReg] = null; + continue Loop; + case Icode_NAME_AND_THIS : + // stringReg: name + ++stackTop; + stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(stringReg, + cx, frame.scope); + ++stackTop; + stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + continue Loop; + case Icode_PROP_AND_THIS: { + Object obj = stack[stackTop]; + if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); + // stringReg: property + stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(obj, stringReg, + cx); + ++stackTop; + stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + continue Loop; + } + case Icode_ELEM_AND_THIS: { + Object obj = stack[stackTop - 1]; + if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]); + Object id = stack[stackTop]; + if (id == DBL_MRK) id = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(obj, id, cx); + stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + continue Loop; + } + case Icode_VALUE_AND_THIS : { + Object value = stack[stackTop]; + if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.getValueFunctionAndThis(value, cx); + ++stackTop; + stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + continue Loop; + } + case Icode_CALLSPECIAL : { + if (instructionCounting) { + cx.instructionCount += INVOCATION_COST; + } + int callType = iCode[frame.pc] & 0xFF; + boolean 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 = ScriptRuntime.wrapNumber(sDbl[stackTop]); + Object[] outArgs = getArgsArray( + stack, sDbl, stackTop + 1, indexReg); + stack[stackTop] = ScriptRuntime.newSpecial( + cx, function, outArgs, frame.scope, callType); + } else { + // stack change: function thisObj arg0 .. argN -> result + stackTop -= 1 + indexReg; + + // Call code generation ensure that stack here + // is ... Callable Scriptable + Scriptable functionThis = (Scriptable)stack[stackTop + 1]; + Callable function = (Callable)stack[stackTop]; + Object[] outArgs = getArgsArray( + stack, sDbl, stackTop + 2, indexReg); + stack[stackTop] = ScriptRuntime.callSpecial( + cx, function, functionThis, outArgs, + frame.scope, frame.thisObj, callType, + frame.idata.itsSourceFile, sourceLine); + } + frame.pc += 4; + continue Loop; + } + case Token.CALL : + case Icode_TAIL_CALL : + case Token.REF_CALL : { + if (instructionCounting) { + cx.instructionCount += INVOCATION_COST; + } + // stack change: function thisObj arg0 .. argN -> result + // indexReg: number of arguments + stackTop -= 1 + indexReg; + + // CALL generation ensures that fun and funThisObj + // are already Scriptable and Callable objects respectively + Callable fun = (Callable)stack[stackTop]; + Scriptable funThisObj = (Scriptable)stack[stackTop + 1]; + if (op == Token.REF_CALL) { + Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, + indexReg); + stack[stackTop] = ScriptRuntime.callRef(fun, funThisObj, + outArgs, cx); + continue Loop; + } + Scriptable calleeScope = frame.scope; + if (frame.useActivation) { + calleeScope = ScriptableObject.getTopLevelScope(frame.scope); + } + if (fun instanceof 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 interaction with GC. + callParentFrame = frame.parentFrame; + // Release the current frame. See Bug #344501 to see why + // it is being done here. + exitFrame(cx, frame, null); + } + initFrame(cx, calleeScope, funThisObj, stack, sDbl, + stackTop + 2, indexReg, ifun, callParentFrame, + calleeFrame); + if (op != Icode_TAIL_CALL) { + frame.savedStackTop = stackTop; + frame.savedCallOp = op; + } + frame = calleeFrame; + continue StateLoop; + } + } + + if (fun instanceof 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 withoutExceptions; + } + + if (fun instanceof IdFunctionObject) { + IdFunctionObject ifun = (IdFunctionObject)fun; + if (Continuation.isContinuationConstructor(ifun)) { + captureContinuation(cx, frame, stackTop); + continue Loop; + } + // Bug 405654 -- make best effort to keep Function.apply and + // Function.call within this interpreter loop invocation + if(BaseFunction.isApplyOrCall(ifun)) { + Callable applyCallable = ScriptRuntime.getCallable(funThisObj); + if(applyCallable instanceof InterpretedFunction) { + InterpretedFunction iApplyCallable = (InterpretedFunction)applyCallable; + if(frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) { + frame = initFrameForApplyOrCall(cx, frame, indexReg, + stack, sDbl, stackTop, op, calleeScope, ifun, + iApplyCallable); + continue StateLoop; + } + } + } + } + + stack[stackTop] = fun.call(cx, calleeScope, funThisObj, + getArgsArray(stack, sDbl, stackTop + 2, indexReg)); + + continue Loop; + } + 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 instanceof InterpretedFunction) { + InterpretedFunction f = (InterpretedFunction)lhs; + if (frame.fnOrScript.securityDomain == f.securityDomain) { + Scriptable 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; + continue StateLoop; + } + } + if (!(lhs instanceof Function)) { + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + throw ScriptRuntime.notFunctionError(lhs); + } + Function fun = (Function)lhs; + + if (fun instanceof IdFunctionObject) { + IdFunctionObject ifun = (IdFunctionObject)fun; + if (Continuation.isContinuationConstructor(ifun)) { + captureContinuation(cx, frame, stackTop); + continue Loop; + } + } + + Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg); + stack[stackTop] = fun.construct(cx, frame.scope, outArgs); + continue Loop; + } + case Token.TYPEOF : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.typeof(lhs); + continue Loop; + } + case Icode_TYPEOFNAME : + stack[++stackTop] = ScriptRuntime.typeofName(frame.scope, stringReg); + continue Loop; + case Token.STRING : + stack[++stackTop] = stringReg; + continue Loop; + case Icode_SHORTNUMBER : + ++stackTop; + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = getShort(iCode, frame.pc); + frame.pc += 2; + continue Loop; + case Icode_INTNUMBER : + ++stackTop; + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = getInt(iCode, frame.pc); + frame.pc += 4; + continue Loop; + case Token.NUMBER : + ++stackTop; + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg]; + continue Loop; + case Token.NAME : + stack[++stackTop] = ScriptRuntime.name(cx, frame.scope, stringReg); + continue Loop; + case Icode_NAME_INC_DEC : + stack[++stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, stringReg, + cx, iCode[frame.pc]); + ++frame.pc; + continue Loop; + case Icode_SETCONSTVAR1: + indexReg = iCode[frame.pc++]; + // fallthrough + case Token.SETCONSTVAR : + if (!frame.useActivation) { + if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { + throw Context.reportRuntimeError1("msg.var.redecl", + frame.idata.argNames[indexReg]); + } + if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST) + != 0) + { + vars[indexReg] = stack[stackTop]; + varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST; + varDbls[indexReg] = sDbl[stackTop]; + } + } else { + Object val = stack[stackTop]; + if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stringReg = frame.idata.argNames[indexReg]; + if (frame.scope instanceof ConstProperties) { + ConstProperties cp = (ConstProperties)frame.scope; + cp.putConst(stringReg, frame.scope, val); + } else + throw Kit.codeBug(); + } + continue Loop; + case Icode_SETVAR1: + indexReg = iCode[frame.pc++]; + // fallthrough + case Token.SETVAR : + if (!frame.useActivation) { + if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { + vars[indexReg] = stack[stackTop]; + varDbls[indexReg] = sDbl[stackTop]; + } + } else { + Object val = stack[stackTop]; + if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stringReg = frame.idata.argNames[indexReg]; + frame.scope.put(stringReg, frame.scope, val); + } + continue Loop; + case Icode_GETVAR1: + indexReg = iCode[frame.pc++]; + // fallthrough + 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); + } + continue Loop; + 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 = ScriptRuntime.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, + cx, incrDecrMask); + } + ++frame.pc; + continue Loop; + } + case Icode_ZERO : + ++stackTop; + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = 0; + continue Loop; + case Icode_ONE : + ++stackTop; + stack[stackTop] = DBL_MRK; + sDbl[stackTop] = 1; + continue Loop; + case Token.NULL : + stack[++stackTop] = null; + continue Loop; + case Token.THIS : + stack[++stackTop] = frame.thisObj; + continue Loop; + case Token.THISFN : + stack[++stackTop] = frame.fnOrScript; + continue Loop; + case Token.FALSE : + stack[++stackTop] = Boolean.FALSE; + continue Loop; + case Token.TRUE : + stack[++stackTop] = Boolean.TRUE; + continue Loop; + case Icode_UNDEF : + stack[++stackTop] = undefined; + continue Loop; + case Token.ENTERWITH : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + frame.scope = ScriptRuntime.enterWith(lhs, cx, frame.scope); + continue Loop; + } + case Token.LEAVEWITH : + frame.scope = ScriptRuntime.leaveWith(frame.scope); + continue Loop; + case Token.CATCH_SCOPE : { + // stack top: exception object + // stringReg: name of exception variable + // indexReg: local for exception scope + --stackTop; + indexReg += frame.localShift; + + boolean afterFirstScope = (frame.idata.itsICode[frame.pc] != 0); + Throwable caughtException = (Throwable)stack[stackTop + 1]; + Scriptable lastCatchScope; + if (!afterFirstScope) { + lastCatchScope = null; + } else { + lastCatchScope = (Scriptable)stack[indexReg]; + } + stack[indexReg] = ScriptRuntime.newCatchScope(caughtException, + lastCatchScope, stringReg, + cx, frame.scope); + ++frame.pc; + continue Loop; + } + case Token.ENUM_INIT_KEYS : + case Token.ENUM_INIT_VALUES : + case Token.ENUM_INIT_ARRAY : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + indexReg += frame.localShift; + int enumType = op == Token.ENUM_INIT_KEYS + ? ScriptRuntime.ENUMERATE_KEYS : + op == Token.ENUM_INIT_VALUES + ? ScriptRuntime.ENUMERATE_VALUES : + ScriptRuntime.ENUMERATE_ARRAY; + stack[indexReg] = ScriptRuntime.enumInit(lhs, cx, enumType); + continue Loop; + } + case Token.ENUM_NEXT : + case Token.ENUM_ID : { + indexReg += frame.localShift; + Object val = stack[indexReg]; + ++stackTop; + stack[stackTop] = (op == Token.ENUM_NEXT) + ? (Object)ScriptRuntime.enumNext(val) + : (Object)ScriptRuntime.enumId(val, cx); + continue Loop; + } + case Token.REF_SPECIAL : { + //stringReg: name of special property + Object obj = stack[stackTop]; + if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg, cx); + continue Loop; + } + case Token.REF_MEMBER: { + //indexReg: flags + Object elem = stack[stackTop]; + if (elem == DBL_MRK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object obj = stack[stackTop]; + if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.memberRef(obj, elem, cx, indexReg); + continue Loop; + } + case Token.REF_NS_MEMBER: { + //indexReg: flags + Object elem = stack[stackTop]; + if (elem == DBL_MRK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object ns = stack[stackTop]; + if (ns == DBL_MRK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object obj = stack[stackTop]; + if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.memberRef(obj, ns, elem, cx, indexReg); + continue Loop; + } + case Token.REF_NAME: { + //indexReg: flags + Object name = stack[stackTop]; + if (name == DBL_MRK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.nameRef(name, cx, frame.scope, + indexReg); + continue Loop; + } + case Token.REF_NS_NAME: { + //indexReg: flags + Object name = stack[stackTop]; + if (name == DBL_MRK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + Object ns = stack[stackTop]; + if (ns == DBL_MRK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.nameRef(ns, name, cx, frame.scope, + indexReg); + continue Loop; + } + case Icode_SCOPE_LOAD : + indexReg += frame.localShift; + frame.scope = (Scriptable)stack[indexReg]; + continue Loop; + case Icode_SCOPE_SAVE : + indexReg += frame.localShift; + stack[indexReg] = frame.scope; + continue Loop; + case Icode_CLOSURE_EXPR : + stack[++stackTop] = InterpretedFunction.createFunction(cx, frame.scope, + frame.fnOrScript, + indexReg); + continue Loop; + case Icode_CLOSURE_STMT : + initFunction(cx, frame.scope, frame.fnOrScript, indexReg); + continue Loop; + case Token.REGEXP : + stack[++stackTop] = frame.scriptRegExps[indexReg]; + continue Loop; + case Icode_LITERAL_NEW : + // indexReg: number of values in the literal + ++stackTop; + stack[stackTop] = new int[indexReg]; + ++stackTop; + stack[stackTop] = new Object[indexReg]; + sDbl[stackTop] = 0; + continue Loop; + case Icode_LITERAL_SET : { + Object value = stack[stackTop]; + if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + int i = (int)sDbl[stackTop]; + ((Object[])stack[stackTop])[i] = value; + sDbl[stackTop] = i + 1; + continue Loop; + } + case Icode_LITERAL_GETTER : { + Object value = stack[stackTop]; + --stackTop; + int i = (int)sDbl[stackTop]; + ((Object[])stack[stackTop])[i] = value; + ((int[])stack[stackTop - 1])[i] = -1; + sDbl[stackTop] = i + 1; + continue Loop; + } + case Icode_LITERAL_SETTER : { + Object value = stack[stackTop]; + --stackTop; + int i = (int)sDbl[stackTop]; + ((Object[])stack[stackTop])[i] = value; + ((int[])stack[stackTop - 1])[i] = +1; + sDbl[stackTop] = i + 1; + continue Loop; + } + case Token.ARRAYLIT : + case Icode_SPARE_ARRAYLIT : + case Token.OBJECTLIT : { + Object[] data = (Object[])stack[stackTop]; + --stackTop; + int[] getterSetters = (int[])stack[stackTop]; + Object val; + if (op == Token.OBJECTLIT) { + Object[] ids = (Object[])frame.idata.literalIds[indexReg]; + val = ScriptRuntime.newObjectLiteral(ids, data, getterSetters, cx, + frame.scope); + } else { + int[] skipIndexces = null; + if (op == Icode_SPARE_ARRAYLIT) { + skipIndexces = (int[])frame.idata.literalIds[indexReg]; + } + val = ScriptRuntime.newArrayLiteral(data, skipIndexces, cx, + frame.scope); + } + stack[stackTop] = val; + continue Loop; + } + case Icode_ENTERDQ : { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + --stackTop; + frame.scope = ScriptRuntime.enterDotQuery(lhs, frame.scope); + continue Loop; + } + case Icode_LEAVEDQ : { + boolean 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; + continue Loop; + } + // reset stack and PC to code after ENTERDQ + --stackTop; + break jumplessRun; + } + case Token.DEFAULTNAMESPACE : { + Object value = stack[stackTop]; + if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = ScriptRuntime.setDefaultNamespace(value, cx); + continue Loop; + } + case Token.ESCXMLATTR : { + Object value = stack[stackTop]; + if (value != DBL_MRK) { + stack[stackTop] = ScriptRuntime.escapeAttributeValue(value, cx); + } + continue Loop; + } + case Token.ESCXMLTEXT : { + Object value = stack[stackTop]; + if (value != DBL_MRK) { + stack[stackTop] = ScriptRuntime.escapeTextValue(value, cx); + } + continue Loop; + } + case Icode_DEBUGGER: + if (frame.debuggerFrame != null) { + frame.debuggerFrame.onDebuggerStatement(cx); + } + break Loop; + 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; + continue Loop; + case Icode_REG_IND_C0: + indexReg = 0; + continue Loop; + case Icode_REG_IND_C1: + indexReg = 1; + continue Loop; + case Icode_REG_IND_C2: + indexReg = 2; + continue Loop; + case Icode_REG_IND_C3: + indexReg = 3; + continue Loop; + case Icode_REG_IND_C4: + indexReg = 4; + continue Loop; + case Icode_REG_IND_C5: + indexReg = 5; + continue Loop; + case Icode_REG_IND1: + indexReg = 0xFF & iCode[frame.pc]; + ++frame.pc; + continue Loop; + case Icode_REG_IND2: + indexReg = getIndex(iCode, frame.pc); + frame.pc += 2; + continue Loop; + case Icode_REG_IND4: + indexReg = getInt(iCode, frame.pc); + frame.pc += 4; + continue Loop; + case Icode_REG_STR_C0: + stringReg = strings[0]; + continue Loop; + case Icode_REG_STR_C1: + stringReg = strings[1]; + continue Loop; + case Icode_REG_STR_C2: + stringReg = strings[2]; + continue Loop; + case Icode_REG_STR_C3: + stringReg = strings[3]; + continue Loop; + case Icode_REG_STR1: + stringReg = strings[0xFF & iCode[frame.pc]]; + ++frame.pc; + continue Loop; + case Icode_REG_STR2: + stringReg = strings[getIndex(iCode, frame.pc)]; + frame.pc += 2; + continue Loop; + case Icode_REG_STR4: + stringReg = strings[getInt(iCode, frame.pc)]; + frame.pc += 4; + continue Loop; + default : + dumpICode(frame.idata); + throw new RuntimeException( + "Unknown icode : "+op+" @ pc : "+(frame.pc-1)); +} // end of interpreter switch + + } // end of jumplessRun label block + + // This should be reachable only for jump implementation + // when pc points to encoded target offset + if (instructionCounting) { + addInstructionCount(cx, frame, 2); + } + int offset = getShort(iCode, frame.pc); + if (offset != 0) { + // -1 accounts for pc pointing to jump opcode + 1 + frame.pc += offset - 1; + } else { + frame.pc = frame.idata.longJumps. + getExistingInt(frame.pc); + } + if (instructionCounting) { + frame.pcPrevBranch = frame.pc; + } + continue Loop; + + } // end of Loop: for + + exitFrame(cx, frame, null); + interpreterResult = frame.result; + interpreterResultDbl = frame.resultDbl; + if (frame.parentFrame != null) { + frame = frame.parentFrame; + if (frame.frozen) { + frame = frame.cloneFrozen(); + } + setCallResult( + frame, interpreterResult, interpreterResultDbl); + interpreterResult = null; // Help GC + continue StateLoop; + } + break StateLoop; + + } // end of interpreter withoutExceptions: try + catch (Throwable ex) { + if (throwable != null) { + // This is serious bug and it is better to track it ASAP + ex.printStackTrace(System.err); + throw new IllegalStateException(); + } + throwable = ex; + } + + // This should be reachable only after above catch or from + // finally when it needs to propagate exception or from + // explicit throw + if (throwable == null) Kit.codeBug(); + + // Exception type + 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; + ContinuationJump cjump = null; + + if (generatorState != null && + generatorState.operation == NativeGenerator.GENERATOR_CLOSE && + throwable == generatorState.value) + { + exState = EX_FINALLY_STATE; + } else 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 = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) + ? EX_CATCH_STATE + : EX_FINALLY_STATE; + } else if (throwable instanceof Error) { + exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) + ? EX_CATCH_STATE + : EX_NO_JS_STATE; + } else if (throwable instanceof ContinuationJump) { + // It must be ContinuationJump + exState = EX_FINALLY_STATE; + cjump = (ContinuationJump)throwable; + } else { + exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) + ? EX_CATCH_STATE + : EX_FINALLY_STATE; + } + + if (instructionCounting) { + try { + addInstructionCount(cx, frame, EXCEPTION_COST); + } catch (RuntimeException ex) { + throwable = ex; + exState = EX_FINALLY_STATE; + } catch (Error ex) { + // Error from instruction counting + // => unconditionally terminate JS + throwable = ex; + cjump = null; + exState = EX_NO_JS_STATE; + } + } + if (frame.debuggerFrame != null + && throwable instanceof RuntimeException) + { + // Call debugger only for RuntimeException + RuntimeException rex = (RuntimeException)throwable; + try { + frame.debuggerFrame.onExceptionThrown(cx, rex); + } catch (Throwable ex) { + // Any exception from debugger + // => unconditionally terminate JS + throwable = ex; + cjump = null; + exState = EX_NO_JS_STATE; + } + } + + for (;;) { + if (exState != EX_NO_JS_STATE) { + boolean 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 + continue StateLoop; + } + } + // No allowed exception handlers in this frame, unwind + // to parent and try to look there + + exitFrame(cx, frame, throwable); + + frame = frame.parentFrame; + if (frame == null) { break; } + if (cjump != null && cjump.branchFrame == frame) { + // Continuation branch point was hit, + // restart the state loop to reenter continuation + indexReg = -1; + continue StateLoop; + } + } + + // No more frames, rethrow the exception or deal with continuation + if (cjump != null) { + if (cjump.branchFrame != null) { + // The above loop should locate the top frame + Kit.codeBug(); + } + if (cjump.capturedFrame != null) { + // Restarting detached continuation + indexReg = -1; + continue StateLoop; + } + // Return continuation result to the caller + interpreterResult = cjump.result; + interpreterResultDbl = cjump.resultDbl; + throwable = null; + } + break StateLoop; + + } // 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 instanceof RuntimeException) { + throw (RuntimeException)throwable; + } else { + // Must be instance of Error or code bug + throw (Error)throwable; + } + } + + return (interpreterResult != DBL_MRK) + ? interpreterResult + : ScriptRuntime.wrapNumber(interpreterResultDbl); + } + + private static CallFrame processThrowable(Context cx, Object throwable, + CallFrame frame, int indexReg, + boolean instructionCounting) + { + // Recovering from exception, indexReg contains + // the index of handler + + if (indexReg >= 0) { + // Normal exception handler, transfer + // control appropriately + + if (frame.frozen) { + // XXX Deal with exceptios!!! + frame = frame.cloneFrozen(); + } + + int[] table = frame.idata.itsExceptionTable; + + frame.pc = table[indexReg + EXCEPTION_HANDLER_SLOT]; + if (instructionCounting) { + frame.pcPrevBranch = frame.pc; + } + + frame.savedStackTop = frame.emptyStackTop; + int scopeLocal = frame.localShift + + table[indexReg + + EXCEPTION_SCOPE_SLOT]; + int exLocal = frame.localShift + + table[indexReg + + EXCEPTION_LOCAL_SLOT]; + frame.scope = (Scriptable)frame.stack[scopeLocal]; + frame.stack[exLocal] = throwable; + + throwable = null; + } else { + // Continuation restoration + ContinuationJump cjump = (ContinuationJump)throwable; + + // Clear throwable to indicate that exceptions are OK + throwable = null; + + if (cjump.branchFrame != frame) Kit.codeBug(); + + // Check that we have at least one frozen frame + // in the case of detached continuation restoration: + // unwind code ensure that + if (cjump.capturedFrame == null) Kit.codeBug(); + + // Need to rewind branchFrame, capturedFrame + // and all frames in between + int rewindCount = cjump.capturedFrame.frameIndex + 1; + if (cjump.branchFrame != null) { + rewindCount -= cjump.branchFrame.frameIndex; + } + + int enterCount = 0; + CallFrame[] enterFrames = null; + + CallFrame x = cjump.capturedFrame; + for (int i = 0; i != rewindCount; ++i) { + if (!x.frozen) Kit.codeBug(); + if (isFrameEnterExitRequired(x)) { + if (enterFrames == null) { + // Allocate enough space to store the rest + // of rewind frames in case all of them + // would require to enter + enterFrames = new CallFrame[rewindCount + - i]; + } + enterFrames[enterCount] = x; + ++enterCount; + } + x = x.parentFrame; + } + + while (enterCount != 0) { + // execute enter: walk enterFrames in the reverse + // order since they were stored starting from + // the capturedFrame, not branchFrame + --enterCount; + x = enterFrames[enterCount]; + enterFrame(cx, x, ScriptRuntime.emptyArgs, true); + } + + // Continuation jump is almost done: capturedFrame + // points to the call to the function that captured + // continuation, so clone capturedFrame and + // emulate return that function with the suplied result + frame = cjump.capturedFrame.cloneFrozen(); + setCallResult(frame, cjump.result, cjump.resultDbl); + // restart the execution + } + frame.throwable = throwable; + return frame; + } + + private static Object freezeGenerator(Context cx, CallFrame frame, + int stackTop, + GeneratorState generatorState) + { + if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) { + // Error: no yields when generator is closing + throw ScriptRuntime.typeError0("msg.yield.closing"); + } + // return to our caller (which should be a method of NativeGenerator) + frame.frozen = true; + frame.result = frame.stack[stackTop]; + frame.resultDbl = frame.sDbl[stackTop]; + frame.savedStackTop = stackTop; + frame.pc--; // we want to come back here when we resume + ScriptRuntime.exitActivationFunction(cx); + return (frame.result != UniqueTag.DOUBLE_MARK) + ? frame.result + : ScriptRuntime.wrapNumber(frame.resultDbl); + } + + private static Object thawGenerator(CallFrame frame, int stackTop, + GeneratorState generatorState, int op) + { + // we are resuming execution + frame.frozen = false; + int sourceLine = getIndex(frame.idata.itsICode, frame.pc); + frame.pc += 2; // skip line number data + if (generatorState.operation == NativeGenerator.GENERATOR_THROW) { + // processing a call to <generator>.throw(exception): must + // act as if exception was thrown from resumption point + return new JavaScriptException(generatorState.value, + frame.idata.itsSourceFile, + sourceLine); + } + if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) { + return generatorState.value; + } + if (generatorState.operation != NativeGenerator.GENERATOR_SEND) + throw Kit.codeBug(); + if (op == Token.YIELD) + frame.stack[stackTop] = generatorState.value; + return Scriptable.NOT_FOUND; + } + + private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame, + int indexReg, Object[] stack, double[] sDbl, int stackTop, int op, + Scriptable calleeScope, IdFunctionObject ifun, + InterpretedFunction iApplyCallable) + { + Scriptable applyThis; + if (indexReg != 0) { + applyThis = ScriptRuntime.toObjectOrNull(cx, stack[stackTop + 2]); + } + else { + applyThis = null; + } + if (applyThis == null) { + // This covers the case of args[0] == (null|undefined) as well. + applyThis = ScriptRuntime.getTopCallScope(cx); + } + if(op == Icode_TAIL_CALL) { + exitFrame(cx, frame, null); + frame = frame.parentFrame; + } + else { + frame.savedStackTop = stackTop; + frame.savedCallOp = op; + } + CallFrame calleeFrame = new CallFrame(); + if(BaseFunction.isApply(ifun)) { + Object[] callArgs = indexReg < 2 ? ScriptRuntime.emptyArgs : + ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]); + initFrame(cx, calleeScope, applyThis, callArgs, null, 0, + callArgs.length, iApplyCallable, frame, calleeFrame); + } + else { + // Shift args left + for(int i = 1; i < indexReg; ++i) { + stack[stackTop + 1 + i] = stack[stackTop + 2 + i]; + sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i]; + } + int argCount = indexReg < 2 ? 0 : indexReg - 1; + initFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2, + argCount, iApplyCallable, frame, calleeFrame); + } + + frame = calleeFrame; + return frame; + } + + private static void initFrame(Context cx, Scriptable callerScope, + Scriptable thisObj, + Object[] args, double[] argsDbl, + int argShift, int argCount, + InterpretedFunction fnOrScript, + CallFrame parentFrame, CallFrame frame) + { + InterpreterData idata = fnOrScript.idata; + + boolean useActivation = idata.itsNeedsActivation; + DebugFrame debuggerFrame = null; + if (cx.debugger != null) { + debuggerFrame = cx.debugger.getFrame(cx, idata); + if (debuggerFrame != null) { + useActivation = true; + } + } + + if (useActivation) { + // Copy args to new array to pass to enterActivationFunction + // or debuggerFrame.onEnter + if (argsDbl != null) { + args = getArgsArray(args, argsDbl, argShift, argCount); + } + argShift = 0; + argsDbl = null; + } + + Scriptable scope; + if (idata.itsFunctionType != 0) { + if (!idata.useDynamicScope) { + scope = fnOrScript.getParentScope(); + } else { + scope = callerScope; + } + + if (useActivation) { + scope = ScriptRuntime.createFunctionActivation( + fnOrScript, scope, args); + } + } else { + scope = callerScope; + ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope, + fnOrScript.idata.evalScriptFlag); + } + + if (idata.itsNestedFunctions != null) { + if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) + Kit.codeBug(); + for (int i = 0; i < idata.itsNestedFunctions.length; i++) { + InterpreterData fdata = idata.itsNestedFunctions[i]; + if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) { + initFunction(cx, scope, fnOrScript, i); + } + } + } + + 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); + } + } + + // Initialize args, vars, locals and stack + + int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1; + int maxFrameArray = idata.itsMaxFrameArray; + if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1) + Kit.codeBug(); + + Object[] stack; + int[] stackAttributes; + double[] sDbl; + boolean stackReuse; + if (frame.stack != null && maxFrameArray <= frame.stack.length) { + // Reuse stacks from old frame + stackReuse = true; + stack = frame.stack; + stackAttributes = frame.stackAttributes; + sDbl = frame.sDbl; + } else { + stackReuse = false; + stack = new Object[maxFrameArray]; + stackAttributes = new int[maxFrameArray]; + sDbl = new double[maxFrameArray]; + } + + int varCount = idata.getParamAndVarCount(); + for (int i = 0; i < varCount; i++) { + if (idata.getParamOrVarConst(i)) + stackAttributes[i] = ScriptableObject.CONST; + } + int definedArgs = idata.argCount; + if (definedArgs > argCount) { definedArgs = argCount; } + + // Fill the frame structure + + frame.parentFrame = parentFrame; + frame.frameIndex = (parentFrame == null) + ? 0 : parentFrame.frameIndex + 1; + if(frame.frameIndex > cx.getMaximumInterpreterStackDepth()) + { + throw Context.reportRuntimeError("Exceeded maximum stack depth"); + } + frame.frozen = false; + + frame.fnOrScript = fnOrScript; + frame.idata = idata; + + frame.stack = stack; + frame.stackAttributes = stackAttributes; + frame.sDbl = sDbl; + frame.varSource = frame; + frame.localShift = idata.itsMaxVars; + frame.emptyStackTop = emptyStackTop; + + frame.debuggerFrame = debuggerFrame; + frame.useActivation = useActivation; + + frame.thisObj = thisObj; + frame.scriptRegExps = scriptRegExps; + + // Initialize initial values of variables that change during + // interpretation. + frame.result = Undefined.instance; + frame.pc = 0; + frame.pcPrevBranch = 0; + frame.pcSourceLineStart = idata.firstLinePC; + frame.scope = scope; + + frame.savedStackTop = emptyStackTop; + frame.savedCallOp = 0; + + System.arraycopy(args, argShift, stack, 0, definedArgs); + if (argsDbl != null) { + System.arraycopy(argsDbl, argShift, sDbl, 0, definedArgs); + } + for (int i = definedArgs; i != idata.itsMaxVars; ++i) { + stack[i] = Undefined.instance; + } + if (stackReuse) { + // Clean the stack part and space beyond stack if any + // of the old array to allow to GC objects there + for (int i = emptyStackTop + 1; i != stack.length; ++i) { + stack[i] = null; + } + } + + enterFrame(cx, frame, args, false); + } + + private static boolean isFrameEnterExitRequired(CallFrame frame) + { + return frame.debuggerFrame != null || frame.idata.itsNeedsActivation; + } + + private static void enterFrame(Context cx, CallFrame frame, Object[] args, + boolean continuationRestart) + { + boolean usesActivation = frame.idata.itsNeedsActivation; + boolean isDebugged = frame.debuggerFrame != null; + if(usesActivation || isDebugged) { + Scriptable scope = frame.scope; + if(scope == null) { + Kit.codeBug(); + } else if (continuationRestart) { + // Walk the parent chain of frame.scope until a NativeCall is + // found. Normally, frame.scope is a NativeCall when called + // from initFrame() for a debugged or activatable function. + // However, when called from interpretLoop() as part of + // restarting a continuation, it can also be a NativeWith if + // the continuation was captured within a "with" or "catch" + // block ("catch" implicitly uses NativeWith to create a scope + // to expose the exception variable). + for(;;) { + if(scope instanceof NativeWith) { + scope = scope.getParentScope(); + if (scope == null || (frame.parentFrame != null && + frame.parentFrame.scope == scope)) + { + // If we get here, we didn't find a NativeCall in + // the call chain before reaching parent frame's + // scope. This should not be possible. + Kit.codeBug(); + break; // Never reached, but keeps the static analyzer happy about "scope" not being null 5 lines above. + } + } + else { + break; + } + } + } + if (isDebugged) { + frame.debuggerFrame.onEnter(cx, scope, frame.thisObj, args); + } + // Enter activation only when itsNeedsActivation true, + // since debugger should not interfere with activation + // chaining + if (usesActivation) { + ScriptRuntime.enterActivationFunction(cx, scope); + } + } + } + + private static void exitFrame(Context cx, CallFrame frame, + Object throwable) + { + if (frame.idata.itsNeedsActivation) { + ScriptRuntime.exitActivationFunction(cx); + } + + if (frame.debuggerFrame != null) { + try { + if (throwable instanceof Throwable) { + frame.debuggerFrame.onExit(cx, true, throwable); + } else { + Object result; + ContinuationJump cjump = (ContinuationJump)throwable; + if (cjump == null) { + result = frame.result; + } else { + result = cjump.result; + } + if (result == UniqueTag.DOUBLE_MARK) { + double resultDbl; + if (cjump == null) { + resultDbl = frame.resultDbl; + } else { + resultDbl = cjump.resultDbl; + } + result = ScriptRuntime.wrapNumber(resultDbl); + } + frame.debuggerFrame.onExit(cx, false, result); + } + } catch (Throwable ex) { + System.err.println( +"RHINO USAGE WARNING: onExit terminated with exception"); + ex.printStackTrace(System.err); + } + } + } + + private static void setCallResult(CallFrame frame, + Object callResult, + double callResultDbl) + { + if (frame.savedCallOp == Token.CALL) { + frame.stack[frame.savedStackTop] = callResult; + frame.sDbl[frame.savedStackTop] = callResultDbl; + } else if (frame.savedCallOp == Token.NEW) { + // If construct returns scriptable, + // then it replaces on stack top saved original instance + // of the object. + if (callResult instanceof Scriptable) { + frame.stack[frame.savedStackTop] = callResult; + } + } else { + Kit.codeBug(); + } + frame.savedCallOp = 0; + } + + private static void captureContinuation(Context cx, CallFrame frame, + int stackTop) + { + Continuation c = new Continuation(); + ScriptRuntime.setObjectProtoAndParent( + c, ScriptRuntime.getTopCallScope(cx)); + + // Make sure that all frames upstack frames are frozen + CallFrame x = frame.parentFrame; + while (x != null && !x.frozen) { + x.frozen = true; + // Allow to GC unused stack space + for (int i = x.savedStackTop + 1; i != x.stack.length; ++i) { + // Allow to GC unused stack space + x.stack[i] = null; + x.stackAttributes[i] = ScriptableObject.EMPTY; + } + if (x.savedCallOp == Token.CALL) { + // the call will always overwrite the stack top with the result + x.stack[x.savedStackTop] = null; + } else { + if (x.savedCallOp != Token.NEW) Kit.codeBug(); + // the new operator uses stack top to store the constructed + // object so it shall not be cleared: see comments in + // setCallResult + } + x = x.parentFrame; + } + + c.initImplementation(frame.parentFrame); + frame.stack[stackTop] = c; + } + + private static int stack_int32(CallFrame frame, int i) + { + Object x = frame.stack[i]; + double value; + if (x == UniqueTag.DOUBLE_MARK) { + value = frame.sDbl[i]; + } else { + value = ScriptRuntime.toNumber(x); + } + return ScriptRuntime.toInt32(value); + } + + private static double stack_double(CallFrame frame, int i) + { + Object x = frame.stack[i]; + if (x != UniqueTag.DOUBLE_MARK) { + return ScriptRuntime.toNumber(x); + } else { + return frame.sDbl[i]; + } + } + + private static boolean stack_boolean(CallFrame frame, int i) + { + Object x = frame.stack[i]; + if (x == Boolean.TRUE) { + return true; + } else if (x == Boolean.FALSE) { + return false; + } else if (x == UniqueTag.DOUBLE_MARK) { + double d = frame.sDbl[i]; + return d == d && d != 0.0; + } else if (x == null || x == Undefined.instance) { + return false; + } else if (x instanceof Number) { + double d = ((Number)x).doubleValue(); + return (d == d && d != 0.0); + } else if (x instanceof Boolean) { + return ((Boolean)x).booleanValue(); + } else { + return ScriptRuntime.toBoolean(x); + } + } + + private static void do_add(Object[] stack, double[] sDbl, int stackTop, + Context cx) + { + Object rhs = stack[stackTop + 1]; + Object lhs = stack[stackTop]; + double d; + boolean leftRightOrder; + if (rhs == UniqueTag.DOUBLE_MARK) { + d = sDbl[stackTop + 1]; + if (lhs == UniqueTag.DOUBLE_MARK) { + sDbl[stackTop] += d; + return; + } + leftRightOrder = true; + // fallthrough to object + number code + } else if (lhs == UniqueTag.DOUBLE_MARK) { + d = sDbl[stackTop]; + lhs = rhs; + leftRightOrder = false; + // fallthrough to object + number code + } else { + if (lhs instanceof Scriptable || rhs instanceof Scriptable) { + stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx); + } else if (lhs instanceof String) { + String lstr = (String)lhs; + String rstr = ScriptRuntime.toString(rhs); + stack[stackTop] = lstr.concat(rstr); + } else if (rhs instanceof String) { + String lstr = ScriptRuntime.toString(lhs); + String rstr = (String)rhs; + stack[stackTop] = lstr.concat(rstr); + } else { + double lDbl = (lhs instanceof Number) + ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs); + double rDbl = (rhs instanceof Number) + ? ((Number)rhs).doubleValue() : ScriptRuntime.toNumber(rhs); + stack[stackTop] = UniqueTag.DOUBLE_MARK; + sDbl[stackTop] = lDbl + rDbl; + } + return; + } + + // handle object(lhs) + number(d) code + if (lhs instanceof Scriptable) { + rhs = ScriptRuntime.wrapNumber(d); + if (!leftRightOrder) { + Object tmp = lhs; + lhs = rhs; + rhs = tmp; + } + stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx); + } else if (lhs instanceof String) { + String lstr = (String)lhs; + String rstr = ScriptRuntime.toString(d); + if (leftRightOrder) { + stack[stackTop] = lstr.concat(rstr); + } else { + stack[stackTop] = rstr.concat(lstr); + } + } else { + double lDbl = (lhs instanceof Number) + ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs); + stack[stackTop] = UniqueTag.DOUBLE_MARK; + sDbl[stackTop] = lDbl + d; + } + } + + private static Object[] getArgsArray(Object[] stack, double[] sDbl, + int shift, int count) + { + if (count == 0) { + return ScriptRuntime.emptyArgs; + } + Object[] args = new Object[count]; + for (int i = 0; i != count; ++i, ++shift) { + Object val = stack[shift]; + if (val == UniqueTag.DOUBLE_MARK) { + val = ScriptRuntime.wrapNumber(sDbl[shift]); + } + args[i] = val; + } + return args; + } + + private static void addInstructionCount(Context cx, CallFrame frame, + int extra) + { + cx.instructionCount += frame.pc - frame.pcPrevBranch + extra; + if (cx.instructionCount > cx.instructionThreshold) { + cx.observeInstructionCount(cx.instructionCount); + cx.instructionCount = 0; + } + } +} |