0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Patrick Beard
0026: * Norris Boyd
0027: * Igor Bukanov
0028: * Ethan Hugg
0029: * Bob Jervis
0030: * Terry Lucas
0031: * Roger Lawrence
0032: * Milen Nankov
0033: * Hannes Wallnoefer
0034: *
0035: * Alternatively, the contents of this file may be used under the terms of
0036: * the GNU General Public License Version 2 or later (the "GPL"), in which
0037: * case the provisions of the GPL are applicable instead of those above. If
0038: * you wish to allow use of your version of this file only under the terms of
0039: * the GPL and not to allow others to use your version of this file under the
0040: * MPL, indicate your decision by deleting the provisions above and replacing
0041: * them with the notice and other provisions required by the GPL. If you do
0042: * not delete the provisions above, a recipient may use your version of this
0043: * file under either the MPL or the GPL.
0044: *
0045: * ***** END LICENSE BLOCK ***** */
0046:
0047: package org.mozilla.javascript;
0048:
0049: import java.io.PrintStream;
0050: import java.io.Serializable;
0051: import java.util.List;
0052: import java.util.ArrayList;
0053:
0054: import org.mozilla.javascript.continuations.Continuation;
0055: import org.mozilla.javascript.debug.DebugFrame;
0056:
0057: public class Interpreter implements Evaluator {
0058:
0059: // Additional interpreter-specific codes
0060:
0061: private static final int
0062:
0063: // Stack: ... value1 -> ... value1 value1
0064: Icode_DUP = -1,
0065:
0066: // Stack: ... value2 value1 -> ... value2 value1 value2 value1
0067: Icode_DUP2 = -2,
0068:
0069: // Stack: ... value2 value1 -> ... value1 value2
0070: Icode_SWAP = -3,
0071:
0072: // Stack: ... value1 -> ...
0073: Icode_POP = -4,
0074:
0075: // Store stack top into return register and then pop it
0076: Icode_POP_RESULT = -5,
0077:
0078: // To jump conditionally and pop additional stack value
0079: Icode_IFEQ_POP = -6,
0080:
0081: // various types of ++/--
0082: Icode_VAR_INC_DEC = -7,
0083: Icode_NAME_INC_DEC = -8,
0084: Icode_PROP_INC_DEC = -9,
0085: Icode_ELEM_INC_DEC = -10,
0086: Icode_REF_INC_DEC = -11,
0087:
0088: // load/save scope from/to local
0089: Icode_SCOPE_LOAD = -12,
0090: Icode_SCOPE_SAVE = -13,
0091:
0092: Icode_TYPEOFNAME = -14,
0093:
0094: // helper for function calls
0095: Icode_NAME_AND_THIS = -15,
0096: Icode_PROP_AND_THIS = -16,
0097: Icode_ELEM_AND_THIS = -17,
0098: Icode_VALUE_AND_THIS = -18,
0099:
0100: // Create closure object for nested functions
0101: Icode_CLOSURE_EXPR = -19,
0102: Icode_CLOSURE_STMT = -20,
0103:
0104: // Special calls
0105: Icode_CALLSPECIAL = -21,
0106:
0107: // To return undefined value
0108: Icode_RETUNDEF = -22,
0109:
0110: // Exception handling implementation
0111: Icode_GOSUB = -23,
0112: Icode_STARTSUB = -24,
0113: Icode_RETSUB = -25,
0114:
0115: // To indicating a line number change in icodes.
0116: Icode_LINE = -26,
0117:
0118: // To store shorts and ints inline
0119: Icode_SHORTNUMBER = -27,
0120: Icode_INTNUMBER = -28,
0121:
0122: // To create and populate array to hold values for [] and {} literals
0123: Icode_LITERAL_NEW = -29,
0124: Icode_LITERAL_SET = -30,
0125:
0126: // Array literal with skipped index like [1,,2]
0127: Icode_SPARE_ARRAYLIT = -31,
0128:
0129: // Load index register to prepare for the following index operation
0130: Icode_REG_IND_C0 = -32,
0131: Icode_REG_IND_C1 = -33,
0132: Icode_REG_IND_C2 = -34,
0133: Icode_REG_IND_C3 = -35,
0134: Icode_REG_IND_C4 = -36,
0135: Icode_REG_IND_C5 = -37,
0136: Icode_REG_IND1 = -38,
0137: Icode_REG_IND2 = -39,
0138: Icode_REG_IND4 = -40,
0139:
0140: // Load string register to prepare for the following string operation
0141: Icode_REG_STR_C0 = -41,
0142: Icode_REG_STR_C1 = -42,
0143: Icode_REG_STR_C2 = -43,
0144: Icode_REG_STR_C3 = -44,
0145: Icode_REG_STR1 = -45,
0146: Icode_REG_STR2 = -46,
0147: Icode_REG_STR4 = -47,
0148:
0149: // Version of getvar/setvar that read var index directly from bytecode
0150: Icode_GETVAR1 = -48, Icode_SETVAR1 = -49,
0151:
0152: // Load unefined
0153: Icode_UNDEF = -50, Icode_ZERO = -51, Icode_ONE = -52,
0154:
0155: // entrance and exit from .()
0156: Icode_ENTERDQ = -53, Icode_LEAVEDQ = -54,
0157:
0158: Icode_TAIL_CALL = -55,
0159:
0160: // Clear local to allow GC its context
0161: Icode_LOCAL_CLEAR = -56,
0162:
0163: // Literal get/set
0164: Icode_LITERAL_GETTER = -57, Icode_LITERAL_SETTER = -58,
0165:
0166: // const
0167: Icode_SETCONST = -59,
0168: Icode_SETCONSTVAR = -60,
0169: Icode_SETCONSTVAR1 = -61,
0170:
0171: // Generator opcodes (along with Token.YIELD)
0172: Icode_GENERATOR = -62, Icode_GENERATOR_END = -63,
0173:
0174: Icode_DEBUGGER = -64,
0175:
0176: // Last icode
0177: MIN_ICODE = -64;
0178:
0179: // data for parsing
0180:
0181: private CompilerEnvirons compilerEnv;
0182:
0183: private boolean itsInFunctionFlag;
0184: private boolean itsInTryFlag;
0185:
0186: private InterpreterData itsData;
0187: private ScriptOrFnNode scriptOrFn;
0188: private int itsICodeTop;
0189: private int itsStackDepth;
0190: private int itsLineNumber;
0191: private int itsDoubleTableTop;
0192: private ObjToIntMap itsStrings = new ObjToIntMap(20);
0193: private int itsLocalTop;
0194:
0195: private static final int MIN_LABEL_TABLE_SIZE = 32;
0196: private static final int MIN_FIXUP_TABLE_SIZE = 40;
0197: private int[] itsLabelTable;
0198: private int itsLabelTableTop;
0199: // itsFixupTable[i] = (label_index << 32) | fixup_site
0200: private long[] itsFixupTable;
0201: private int itsFixupTableTop;
0202: private ObjArray itsLiteralIds = new ObjArray();
0203:
0204: private int itsExceptionTableTop;
0205: private static final int EXCEPTION_TRY_START_SLOT = 0;
0206: private static final int EXCEPTION_TRY_END_SLOT = 1;
0207: private static final int EXCEPTION_HANDLER_SLOT = 2;
0208: private static final int EXCEPTION_TYPE_SLOT = 3;
0209: private static final int EXCEPTION_LOCAL_SLOT = 4;
0210: private static final int EXCEPTION_SCOPE_SLOT = 5;
0211: // SLOT_SIZE: space for try start/end, handler, start, handler type,
0212: // exception local and scope local
0213: private static final int EXCEPTION_SLOT_SIZE = 6;
0214:
0215: // ECF_ or Expression Context Flags constants: for now only TAIL is available
0216: private static final int ECF_TAIL = 1 << 0;
0217:
0218: /**
0219: * Class to hold data corresponding to one interpreted call stack frame.
0220: */
0221: private static class CallFrame implements Cloneable, Serializable {
0222: static final long serialVersionUID = -2843792508994958978L;
0223:
0224: CallFrame parentFrame;
0225: // amount of stack frames before this one on the interpretation stack
0226: int frameIndex;
0227: // If true indicates read-only frame that is a part of continuation
0228: boolean frozen;
0229:
0230: InterpretedFunction fnOrScript;
0231: InterpreterData idata;
0232:
0233: // Stack structure
0234: // stack[0 <= i < localShift]: arguments and local variables
0235: // stack[localShift <= i <= emptyStackTop]: used for local temporaries
0236: // stack[emptyStackTop < i < stack.length]: stack data
0237: // sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value
0238:
0239: Object[] stack;
0240: int[] stackAttributes;
0241: double[] sDbl;
0242: CallFrame varSource; // defaults to this unless continuation frame
0243: int localShift;
0244: int emptyStackTop;
0245:
0246: DebugFrame debuggerFrame;
0247: boolean useActivation;
0248:
0249: Scriptable this Obj;
0250: Scriptable[] scriptRegExps;
0251:
0252: // The values that change during interpretation
0253:
0254: Object result;
0255: double resultDbl;
0256: int pc;
0257: int pcPrevBranch;
0258: int pcSourceLineStart;
0259: Scriptable scope;
0260:
0261: int savedStackTop;
0262: int savedCallOp;
0263: Object throwable;
0264:
0265: CallFrame cloneFrozen() {
0266: if (!frozen)
0267: Kit.codeBug();
0268:
0269: CallFrame copy;
0270: try {
0271: copy = (CallFrame) clone();
0272: } catch (CloneNotSupportedException ex) {
0273: throw new IllegalStateException();
0274: }
0275:
0276: // clone stack but keep varSource to point to values
0277: // from this frame to share variables.
0278:
0279: copy.stack = stack.clone();
0280: copy.stackAttributes = stackAttributes.clone();
0281: copy.sDbl = sDbl.clone();
0282:
0283: copy.frozen = false;
0284: return copy;
0285: }
0286: }
0287:
0288: private static final class ContinuationJump implements Serializable {
0289: static final long serialVersionUID = 7687739156004308247L;
0290:
0291: CallFrame capturedFrame;
0292: CallFrame branchFrame;
0293: Object result;
0294: double resultDbl;
0295:
0296: ContinuationJump(Continuation c, CallFrame current) {
0297: this .capturedFrame = (CallFrame) c.getImplementation();
0298: if (this .capturedFrame == null || current == null) {
0299: // Continuation and current execution does not share
0300: // any frames if there is nothing to capture or
0301: // if there is no currently executed frames
0302: this .branchFrame = null;
0303: } else {
0304: // Search for branch frame where parent frame chains starting
0305: // from captured and current meet.
0306: CallFrame chain1 = this .capturedFrame;
0307: CallFrame chain2 = current;
0308:
0309: // First work parents of chain1 or chain2 until the same
0310: // frame depth.
0311: int diff = chain1.frameIndex - chain2.frameIndex;
0312: if (diff != 0) {
0313: if (diff < 0) {
0314: // swap to make sure that
0315: // chain1.frameIndex > chain2.frameIndex and diff > 0
0316: chain1 = current;
0317: chain2 = this .capturedFrame;
0318: diff = -diff;
0319: }
0320: do {
0321: chain1 = chain1.parentFrame;
0322: } while (--diff != 0);
0323: if (chain1.frameIndex != chain2.frameIndex)
0324: Kit.codeBug();
0325: }
0326:
0327: // Now walk parents in parallel until a shared frame is found
0328: // or until the root is reached.
0329: while (chain1 != chain2 && chain1 != null) {
0330: chain1 = chain1.parentFrame;
0331: chain2 = chain2.parentFrame;
0332: }
0333:
0334: this .branchFrame = chain1;
0335: if (this .branchFrame != null
0336: && !this .branchFrame.frozen)
0337: Kit.codeBug();
0338: }
0339: }
0340: }
0341:
0342: private static CallFrame captureFrameForGenerator(CallFrame frame) {
0343: frame.frozen = true;
0344: CallFrame result = frame.cloneFrozen();
0345: frame.frozen = false;
0346:
0347: // now isolate this frame from its previous context
0348: result.parentFrame = null;
0349: result.frameIndex = 0;
0350:
0351: return result;
0352: }
0353:
0354: static {
0355: // Checks for byte code consistencies, good compiler can eliminate them
0356:
0357: if (Token.LAST_BYTECODE_TOKEN > 127) {
0358: String str = "Violation of Token.LAST_BYTECODE_TOKEN <= 127";
0359: System.err.println(str);
0360: throw new IllegalStateException(str);
0361: }
0362: if (MIN_ICODE < -128) {
0363: String str = "Violation of Interpreter.MIN_ICODE >= -128";
0364: System.err.println(str);
0365: throw new IllegalStateException(str);
0366: }
0367: }
0368:
0369: private static String bytecodeName(int bytecode) {
0370: if (!validBytecode(bytecode)) {
0371: throw new IllegalArgumentException(String.valueOf(bytecode));
0372: }
0373:
0374: if (!Token.printICode) {
0375: return String.valueOf(bytecode);
0376: }
0377:
0378: if (validTokenCode(bytecode)) {
0379: return Token.name(bytecode);
0380: }
0381:
0382: switch (bytecode) {
0383: case Icode_DUP:
0384: return "DUP";
0385: case Icode_DUP2:
0386: return "DUP2";
0387: case Icode_SWAP:
0388: return "SWAP";
0389: case Icode_POP:
0390: return "POP";
0391: case Icode_POP_RESULT:
0392: return "POP_RESULT";
0393: case Icode_IFEQ_POP:
0394: return "IFEQ_POP";
0395: case Icode_VAR_INC_DEC:
0396: return "VAR_INC_DEC";
0397: case Icode_NAME_INC_DEC:
0398: return "NAME_INC_DEC";
0399: case Icode_PROP_INC_DEC:
0400: return "PROP_INC_DEC";
0401: case Icode_ELEM_INC_DEC:
0402: return "ELEM_INC_DEC";
0403: case Icode_REF_INC_DEC:
0404: return "REF_INC_DEC";
0405: case Icode_SCOPE_LOAD:
0406: return "SCOPE_LOAD";
0407: case Icode_SCOPE_SAVE:
0408: return "SCOPE_SAVE";
0409: case Icode_TYPEOFNAME:
0410: return "TYPEOFNAME";
0411: case Icode_NAME_AND_THIS:
0412: return "NAME_AND_THIS";
0413: case Icode_PROP_AND_THIS:
0414: return "PROP_AND_THIS";
0415: case Icode_ELEM_AND_THIS:
0416: return "ELEM_AND_THIS";
0417: case Icode_VALUE_AND_THIS:
0418: return "VALUE_AND_THIS";
0419: case Icode_CLOSURE_EXPR:
0420: return "CLOSURE_EXPR";
0421: case Icode_CLOSURE_STMT:
0422: return "CLOSURE_STMT";
0423: case Icode_CALLSPECIAL:
0424: return "CALLSPECIAL";
0425: case Icode_RETUNDEF:
0426: return "RETUNDEF";
0427: case Icode_GOSUB:
0428: return "GOSUB";
0429: case Icode_STARTSUB:
0430: return "STARTSUB";
0431: case Icode_RETSUB:
0432: return "RETSUB";
0433: case Icode_LINE:
0434: return "LINE";
0435: case Icode_SHORTNUMBER:
0436: return "SHORTNUMBER";
0437: case Icode_INTNUMBER:
0438: return "INTNUMBER";
0439: case Icode_LITERAL_NEW:
0440: return "LITERAL_NEW";
0441: case Icode_LITERAL_SET:
0442: return "LITERAL_SET";
0443: case Icode_SPARE_ARRAYLIT:
0444: return "SPARE_ARRAYLIT";
0445: case Icode_REG_IND_C0:
0446: return "REG_IND_C0";
0447: case Icode_REG_IND_C1:
0448: return "REG_IND_C1";
0449: case Icode_REG_IND_C2:
0450: return "REG_IND_C2";
0451: case Icode_REG_IND_C3:
0452: return "REG_IND_C3";
0453: case Icode_REG_IND_C4:
0454: return "REG_IND_C4";
0455: case Icode_REG_IND_C5:
0456: return "REG_IND_C5";
0457: case Icode_REG_IND1:
0458: return "LOAD_IND1";
0459: case Icode_REG_IND2:
0460: return "LOAD_IND2";
0461: case Icode_REG_IND4:
0462: return "LOAD_IND4";
0463: case Icode_REG_STR_C0:
0464: return "REG_STR_C0";
0465: case Icode_REG_STR_C1:
0466: return "REG_STR_C1";
0467: case Icode_REG_STR_C2:
0468: return "REG_STR_C2";
0469: case Icode_REG_STR_C3:
0470: return "REG_STR_C3";
0471: case Icode_REG_STR1:
0472: return "LOAD_STR1";
0473: case Icode_REG_STR2:
0474: return "LOAD_STR2";
0475: case Icode_REG_STR4:
0476: return "LOAD_STR4";
0477: case Icode_GETVAR1:
0478: return "GETVAR1";
0479: case Icode_SETVAR1:
0480: return "SETVAR1";
0481: case Icode_UNDEF:
0482: return "UNDEF";
0483: case Icode_ZERO:
0484: return "ZERO";
0485: case Icode_ONE:
0486: return "ONE";
0487: case Icode_ENTERDQ:
0488: return "ENTERDQ";
0489: case Icode_LEAVEDQ:
0490: return "LEAVEDQ";
0491: case Icode_TAIL_CALL:
0492: return "TAIL_CALL";
0493: case Icode_LOCAL_CLEAR:
0494: return "LOCAL_CLEAR";
0495: case Icode_LITERAL_GETTER:
0496: return "LITERAL_GETTER";
0497: case Icode_LITERAL_SETTER:
0498: return "LITERAL_SETTER";
0499: case Icode_SETCONST:
0500: return "SETCONST";
0501: case Icode_SETCONSTVAR:
0502: return "SETCONSTVAR";
0503: case Icode_SETCONSTVAR1:
0504: return "SETCONSTVAR1";
0505: case Icode_GENERATOR:
0506: return "GENERATOR";
0507: case Icode_GENERATOR_END:
0508: return "GENERATOR_END";
0509: case Icode_DEBUGGER:
0510: return "DEBUGGER";
0511: }
0512:
0513: // icode without name
0514: throw new IllegalStateException(String.valueOf(bytecode));
0515: }
0516:
0517: private static boolean validIcode(int icode) {
0518: return MIN_ICODE <= icode && icode <= -1;
0519: }
0520:
0521: private static boolean validTokenCode(int token) {
0522: return Token.FIRST_BYTECODE_TOKEN <= token
0523: && token <= Token.LAST_BYTECODE_TOKEN;
0524: }
0525:
0526: private static boolean validBytecode(int bytecode) {
0527: return validIcode(bytecode) || validTokenCode(bytecode);
0528: }
0529:
0530: public Object compile(CompilerEnvirons compilerEnv,
0531: ScriptOrFnNode tree, String encodedSource,
0532: boolean returnFunction) {
0533: this .compilerEnv = compilerEnv;
0534: new NodeTransformer().transform(tree);
0535:
0536: if (Token.printTrees) {
0537: System.out.println(tree.toStringTree(tree));
0538: }
0539:
0540: if (returnFunction) {
0541: tree = tree.getFunctionNode(0);
0542: }
0543:
0544: scriptOrFn = tree;
0545: itsData = new InterpreterData(compilerEnv.getLanguageVersion(),
0546: scriptOrFn.getSourceName(), encodedSource);
0547: itsData.topLevel = true;
0548:
0549: if (returnFunction) {
0550: generateFunctionICode();
0551: } else {
0552: generateICodeFromTree(scriptOrFn);
0553: }
0554:
0555: return itsData;
0556: }
0557:
0558: public Script createScriptObject(Object bytecode,
0559: Object staticSecurityDomain) {
0560: if (bytecode != itsData) {
0561: Kit.codeBug();
0562: }
0563: return InterpretedFunction.createScript(itsData,
0564: staticSecurityDomain);
0565: }
0566:
0567: public void setEvalScriptFlag(Script script) {
0568: ((InterpretedFunction) script).idata.evalScriptFlag = true;
0569: }
0570:
0571: public Function createFunctionObject(Context cx, Scriptable scope,
0572: Object bytecode, Object staticSecurityDomain) {
0573: if (bytecode != itsData) {
0574: Kit.codeBug();
0575: }
0576: return InterpretedFunction.createFunction(cx, scope, itsData,
0577: staticSecurityDomain);
0578: }
0579:
0580: private void generateFunctionICode() {
0581: itsInFunctionFlag = true;
0582:
0583: FunctionNode theFunction = (FunctionNode) scriptOrFn;
0584:
0585: itsData.itsFunctionType = theFunction.getFunctionType();
0586: itsData.itsNeedsActivation = theFunction.requiresActivation();
0587: itsData.itsName = theFunction.getFunctionName();
0588: if (!theFunction.getIgnoreDynamicScope()) {
0589: if (compilerEnv.isUseDynamicScope()) {
0590: itsData.useDynamicScope = true;
0591: }
0592: }
0593: if (theFunction.isGenerator()) {
0594: addIcode(Icode_GENERATOR);
0595: addUint16(theFunction.getBaseLineno() & 0xFFFF);
0596: }
0597:
0598: generateICodeFromTree(theFunction.getLastChild());
0599: }
0600:
0601: private void generateICodeFromTree(Node tree) {
0602: generateNestedFunctions();
0603:
0604: generateRegExpLiterals();
0605:
0606: visitStatement(tree, 0);
0607: fixLabelGotos();
0608: // add RETURN_RESULT only to scripts as function always ends with RETURN
0609: if (itsData.itsFunctionType == 0) {
0610: addToken(Token.RETURN_RESULT);
0611: }
0612:
0613: if (itsData.itsICode.length != itsICodeTop) {
0614: // Make itsData.itsICode length exactly itsICodeTop to save memory
0615: // and catch bugs with jumps beyond icode as early as possible
0616: byte[] tmp = new byte[itsICodeTop];
0617: System.arraycopy(itsData.itsICode, 0, tmp, 0, itsICodeTop);
0618: itsData.itsICode = tmp;
0619: }
0620: if (itsStrings.size() == 0) {
0621: itsData.itsStringTable = null;
0622: } else {
0623: itsData.itsStringTable = new String[itsStrings.size()];
0624: ObjToIntMap.Iterator iter = itsStrings.newIterator();
0625: for (iter.start(); !iter.done(); iter.next()) {
0626: String str = (String) iter.getKey();
0627: int index = iter.getValue();
0628: if (itsData.itsStringTable[index] != null)
0629: Kit.codeBug();
0630: itsData.itsStringTable[index] = str;
0631: }
0632: }
0633: if (itsDoubleTableTop == 0) {
0634: itsData.itsDoubleTable = null;
0635: } else if (itsData.itsDoubleTable.length != itsDoubleTableTop) {
0636: double[] tmp = new double[itsDoubleTableTop];
0637: System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0,
0638: itsDoubleTableTop);
0639: itsData.itsDoubleTable = tmp;
0640: }
0641: if (itsExceptionTableTop != 0
0642: && itsData.itsExceptionTable.length != itsExceptionTableTop) {
0643: int[] tmp = new int[itsExceptionTableTop];
0644: System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0,
0645: itsExceptionTableTop);
0646: itsData.itsExceptionTable = tmp;
0647: }
0648:
0649: itsData.itsMaxVars = scriptOrFn.getParamAndVarCount();
0650: // itsMaxFrameArray: interpret method needs this amount for its
0651: // stack and sDbl arrays
0652: itsData.itsMaxFrameArray = itsData.itsMaxVars
0653: + itsData.itsMaxLocals + itsData.itsMaxStack;
0654:
0655: itsData.argNames = scriptOrFn.getParamAndVarNames();
0656: itsData.argIsConst = scriptOrFn.getParamAndVarConst();
0657: itsData.argCount = scriptOrFn.getParamCount();
0658:
0659: itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart();
0660: itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd();
0661:
0662: if (itsLiteralIds.size() != 0) {
0663: itsData.literalIds = itsLiteralIds.toArray();
0664: }
0665:
0666: if (Token.printICode)
0667: dumpICode(itsData);
0668: }
0669:
0670: private void generateNestedFunctions() {
0671: int functionCount = scriptOrFn.getFunctionCount();
0672: if (functionCount == 0)
0673: return;
0674:
0675: InterpreterData[] array = new InterpreterData[functionCount];
0676: for (int i = 0; i != functionCount; i++) {
0677: FunctionNode def = scriptOrFn.getFunctionNode(i);
0678: Interpreter jsi = new Interpreter();
0679: jsi.compilerEnv = compilerEnv;
0680: jsi.scriptOrFn = def;
0681: jsi.itsData = new InterpreterData(itsData);
0682: jsi.generateFunctionICode();
0683: array[i] = jsi.itsData;
0684: }
0685: itsData.itsNestedFunctions = array;
0686: }
0687:
0688: private void generateRegExpLiterals() {
0689: int N = scriptOrFn.getRegexpCount();
0690: if (N == 0)
0691: return;
0692:
0693: Context cx = Context.getContext();
0694: RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
0695: Object[] array = new Object[N];
0696: for (int i = 0; i != N; i++) {
0697: String string = scriptOrFn.getRegexpString(i);
0698: String flags = scriptOrFn.getRegexpFlags(i);
0699: array[i] = rep.compileRegExp(cx, string, flags);
0700: }
0701: itsData.itsRegExpLiterals = array;
0702: }
0703:
0704: private void updateLineNumber(Node node) {
0705: int lineno = node.getLineno();
0706: if (lineno != itsLineNumber && lineno >= 0) {
0707: if (itsData.firstLinePC < 0) {
0708: itsData.firstLinePC = lineno;
0709: }
0710: itsLineNumber = lineno;
0711: addIcode(Icode_LINE);
0712: addUint16(lineno & 0xFFFF);
0713: }
0714: }
0715:
0716: private RuntimeException badTree(Node node) {
0717: throw new RuntimeException(node.toString());
0718: }
0719:
0720: private void visitStatement(Node node, int initialStackDepth) {
0721: int type = node.getType();
0722: Node child = node.getFirstChild();
0723: switch (type) {
0724:
0725: case Token.FUNCTION: {
0726: int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
0727: int fnType = scriptOrFn.getFunctionNode(fnIndex)
0728: .getFunctionType();
0729: // Only function expressions or function expression
0730: // statements need closure code creating new function
0731: // object on stack as function statements are initialized
0732: // at script/function start.
0733: // In addition, function expressions can not be present here
0734: // at statement level, they must only be present as expressions.
0735: if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
0736: addIndexOp(Icode_CLOSURE_STMT, fnIndex);
0737: } else {
0738: if (fnType != FunctionNode.FUNCTION_STATEMENT) {
0739: throw Kit.codeBug();
0740: }
0741: }
0742: // For function statements or function expression statements
0743: // in scripts, we need to ensure that the result of the script
0744: // is the function if it is the last statement in the script.
0745: // For example, eval("function () {}") should return a
0746: // function, not undefined.
0747: if (!itsInFunctionFlag) {
0748: addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
0749: stackChange(1);
0750: addIcode(Icode_POP_RESULT);
0751: stackChange(-1);
0752: }
0753: }
0754: break;
0755:
0756: case Token.LABEL:
0757: case Token.LOOP:
0758: case Token.BLOCK:
0759: case Token.EMPTY:
0760: case Token.WITH:
0761: updateLineNumber(node);
0762: case Token.SCRIPT:
0763: // fall through
0764: while (child != null) {
0765: visitStatement(child, initialStackDepth);
0766: child = child.getNext();
0767: }
0768: break;
0769:
0770: case Token.ENTERWITH:
0771: visitExpression(child, 0);
0772: addToken(Token.ENTERWITH);
0773: stackChange(-1);
0774: break;
0775:
0776: case Token.LEAVEWITH:
0777: addToken(Token.LEAVEWITH);
0778: break;
0779:
0780: case Token.LOCAL_BLOCK: {
0781: int local = allocLocal();
0782: node.putIntProp(Node.LOCAL_PROP, local);
0783: updateLineNumber(node);
0784: while (child != null) {
0785: visitStatement(child, initialStackDepth);
0786: child = child.getNext();
0787: }
0788: addIndexOp(Icode_LOCAL_CLEAR, local);
0789: releaseLocal(local);
0790: }
0791: break;
0792:
0793: case Token.DEBUGGER:
0794: addIcode(Icode_DEBUGGER);
0795: break;
0796:
0797: case Token.SWITCH:
0798: updateLineNumber(node);
0799: // See comments in IRFactory.createSwitch() for description
0800: // of SWITCH node
0801: {
0802: visitExpression(child, 0);
0803: for (Node.Jump caseNode = (Node.Jump) child.getNext(); caseNode != null; caseNode = (Node.Jump) caseNode
0804: .getNext()) {
0805: if (caseNode.getType() != Token.CASE)
0806: throw badTree(caseNode);
0807: Node test = caseNode.getFirstChild();
0808: addIcode(Icode_DUP);
0809: stackChange(1);
0810: visitExpression(test, 0);
0811: addToken(Token.SHEQ);
0812: stackChange(-1);
0813: // If true, Icode_IFEQ_POP will jump and remove case
0814: // value from stack
0815: addGoto(caseNode.target, Icode_IFEQ_POP);
0816: stackChange(-1);
0817: }
0818: addIcode(Icode_POP);
0819: stackChange(-1);
0820: }
0821: break;
0822:
0823: case Token.TARGET:
0824: markTargetLabel(node);
0825: break;
0826:
0827: case Token.IFEQ:
0828: case Token.IFNE: {
0829: Node target = ((Node.Jump) node).target;
0830: visitExpression(child, 0);
0831: addGoto(target, type);
0832: stackChange(-1);
0833: }
0834: break;
0835:
0836: case Token.GOTO: {
0837: Node target = ((Node.Jump) node).target;
0838: addGoto(target, type);
0839: }
0840: break;
0841:
0842: case Token.JSR: {
0843: Node target = ((Node.Jump) node).target;
0844: addGoto(target, Icode_GOSUB);
0845: }
0846: break;
0847:
0848: case Token.FINALLY: {
0849: // Account for incomming GOTOSUB address
0850: stackChange(1);
0851: int finallyRegister = getLocalBlockRef(node);
0852: addIndexOp(Icode_STARTSUB, finallyRegister);
0853: stackChange(-1);
0854: while (child != null) {
0855: visitStatement(child, initialStackDepth);
0856: child = child.getNext();
0857: }
0858: addIndexOp(Icode_RETSUB, finallyRegister);
0859: }
0860: break;
0861:
0862: case Token.EXPR_VOID:
0863: case Token.EXPR_RESULT:
0864: updateLineNumber(node);
0865: visitExpression(child, 0);
0866: addIcode((type == Token.EXPR_VOID) ? Icode_POP
0867: : Icode_POP_RESULT);
0868: stackChange(-1);
0869: break;
0870:
0871: case Token.TRY: {
0872: Node.Jump tryNode = (Node.Jump) node;
0873: int exceptionObjectLocal = getLocalBlockRef(tryNode);
0874: int scopeLocal = allocLocal();
0875:
0876: addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
0877:
0878: int tryStart = itsICodeTop;
0879: boolean savedFlag = itsInTryFlag;
0880: itsInTryFlag = true;
0881: while (child != null) {
0882: visitStatement(child, initialStackDepth);
0883: child = child.getNext();
0884: }
0885: itsInTryFlag = savedFlag;
0886:
0887: Node catchTarget = tryNode.target;
0888: if (catchTarget != null) {
0889: int catchStartPC = itsLabelTable[getTargetLabel(catchTarget)];
0890: addExceptionHandler(tryStart, catchStartPC,
0891: catchStartPC, false, exceptionObjectLocal,
0892: scopeLocal);
0893: }
0894: Node finallyTarget = tryNode.getFinally();
0895: if (finallyTarget != null) {
0896: int finallyStartPC = itsLabelTable[getTargetLabel(finallyTarget)];
0897: addExceptionHandler(tryStart, finallyStartPC,
0898: finallyStartPC, true, exceptionObjectLocal,
0899: scopeLocal);
0900: }
0901:
0902: addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
0903: releaseLocal(scopeLocal);
0904: }
0905: break;
0906:
0907: case Token.CATCH_SCOPE: {
0908: int localIndex = getLocalBlockRef(node);
0909: int scopeIndex = node
0910: .getExistingIntProp(Node.CATCH_SCOPE_PROP);
0911: String name = child.getString();
0912: child = child.getNext();
0913: visitExpression(child, 0); // load expression object
0914: addStringPrefix(name);
0915: addIndexPrefix(localIndex);
0916: addToken(Token.CATCH_SCOPE);
0917: addUint8(scopeIndex != 0 ? 1 : 0);
0918: stackChange(-1);
0919: }
0920: break;
0921:
0922: case Token.THROW:
0923: updateLineNumber(node);
0924: visitExpression(child, 0);
0925: addToken(Token.THROW);
0926: addUint16(itsLineNumber & 0xFFFF);
0927: stackChange(-1);
0928: break;
0929:
0930: case Token.RETHROW:
0931: updateLineNumber(node);
0932: addIndexOp(Token.RETHROW, getLocalBlockRef(node));
0933: break;
0934:
0935: case Token.RETURN:
0936: updateLineNumber(node);
0937: if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
0938: // We're in a generator, so change RETURN to GENERATOR_END
0939: addIcode(Icode_GENERATOR_END);
0940: addUint16(itsLineNumber & 0xFFFF);
0941: } else if (child != null) {
0942: visitExpression(child, ECF_TAIL);
0943: addToken(Token.RETURN);
0944: stackChange(-1);
0945: } else {
0946: addIcode(Icode_RETUNDEF);
0947: }
0948: break;
0949:
0950: case Token.RETURN_RESULT:
0951: updateLineNumber(node);
0952: addToken(Token.RETURN_RESULT);
0953: break;
0954:
0955: case Token.ENUM_INIT_KEYS:
0956: case Token.ENUM_INIT_VALUES:
0957: case Token.ENUM_INIT_ARRAY:
0958: visitExpression(child, 0);
0959: addIndexOp(type, getLocalBlockRef(node));
0960: stackChange(-1);
0961: break;
0962:
0963: case Icode_GENERATOR:
0964: break;
0965:
0966: default:
0967: throw badTree(node);
0968: }
0969:
0970: if (itsStackDepth != initialStackDepth) {
0971: throw Kit.codeBug();
0972: }
0973: }
0974:
0975: private void visitExpression(Node node, int contextFlags) {
0976: int type = node.getType();
0977: Node child = node.getFirstChild();
0978: int savedStackDepth = itsStackDepth;
0979: switch (type) {
0980:
0981: case Token.FUNCTION: {
0982: int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
0983: FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
0984: // See comments in visitStatement for Token.FUNCTION case
0985: if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION) {
0986: throw Kit.codeBug();
0987: }
0988: addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
0989: stackChange(1);
0990: }
0991: break;
0992:
0993: case Token.LOCAL_LOAD: {
0994: int localIndex = getLocalBlockRef(node);
0995: addIndexOp(Token.LOCAL_LOAD, localIndex);
0996: stackChange(1);
0997: }
0998: break;
0999:
1000: case Token.COMMA: {
1001: Node lastChild = node.getLastChild();
1002: while (child != lastChild) {
1003: visitExpression(child, 0);
1004: addIcode(Icode_POP);
1005: stackChange(-1);
1006: child = child.getNext();
1007: }
1008: // Preserve tail context flag if any
1009: visitExpression(child, contextFlags & ECF_TAIL);
1010: }
1011: break;
1012:
1013: case Token.USE_STACK:
1014: // Indicates that stack was modified externally,
1015: // like placed catch object
1016: stackChange(1);
1017: break;
1018:
1019: case Token.REF_CALL:
1020: case Token.CALL:
1021: case Token.NEW: {
1022: if (type == Token.NEW) {
1023: visitExpression(child, 0);
1024: } else {
1025: generateCallFunAndThis(child);
1026: }
1027: int argCount = 0;
1028: while ((child = child.getNext()) != null) {
1029: visitExpression(child, 0);
1030: ++argCount;
1031: }
1032: int callType = node.getIntProp(Node.SPECIALCALL_PROP,
1033: Node.NON_SPECIALCALL);
1034: if (callType != Node.NON_SPECIALCALL) {
1035: // embed line number and source filename
1036: addIndexOp(Icode_CALLSPECIAL, argCount);
1037: addUint8(callType);
1038: addUint8(type == Token.NEW ? 1 : 0);
1039: addUint16(itsLineNumber & 0xFFFF);
1040: } else {
1041: // Only use the tail call optimization if we're not in a try
1042: // or we're not generating debug info (since the
1043: // optimization will confuse the debugger)
1044: if (type == Token.CALL
1045: && (contextFlags & ECF_TAIL) != 0
1046: && !compilerEnv.isGenerateDebugInfo()
1047: && !itsInTryFlag) {
1048: type = Icode_TAIL_CALL;
1049: }
1050: addIndexOp(type, argCount);
1051: }
1052: // adjust stack
1053: if (type == Token.NEW) {
1054: // new: f, args -> result
1055: stackChange(-argCount);
1056: } else {
1057: // call: f, thisObj, args -> result
1058: // ref_call: f, thisObj, args -> ref
1059: stackChange(-1 - argCount);
1060: }
1061: if (argCount > itsData.itsMaxCalleeArgs) {
1062: itsData.itsMaxCalleeArgs = argCount;
1063: }
1064: }
1065: break;
1066:
1067: case Token.AND:
1068: case Token.OR: {
1069: visitExpression(child, 0);
1070: addIcode(Icode_DUP);
1071: stackChange(1);
1072: int afterSecondJumpStart = itsICodeTop;
1073: int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
1074: addGotoOp(jump);
1075: stackChange(-1);
1076: addIcode(Icode_POP);
1077: stackChange(-1);
1078: child = child.getNext();
1079: // Preserve tail context flag if any
1080: visitExpression(child, contextFlags & ECF_TAIL);
1081: resolveForwardGoto(afterSecondJumpStart);
1082: }
1083: break;
1084:
1085: case Token.HOOK: {
1086: Node ifThen = child.getNext();
1087: Node ifElse = ifThen.getNext();
1088: visitExpression(child, 0);
1089: int elseJumpStart = itsICodeTop;
1090: addGotoOp(Token.IFNE);
1091: stackChange(-1);
1092: // Preserve tail context flag if any
1093: visitExpression(ifThen, contextFlags & ECF_TAIL);
1094: int afterElseJumpStart = itsICodeTop;
1095: addGotoOp(Token.GOTO);
1096: resolveForwardGoto(elseJumpStart);
1097: itsStackDepth = savedStackDepth;
1098: // Preserve tail context flag if any
1099: visitExpression(ifElse, contextFlags & ECF_TAIL);
1100: resolveForwardGoto(afterElseJumpStart);
1101: }
1102: break;
1103:
1104: case Token.GETPROP:
1105: case Token.GETPROPNOWARN:
1106: visitExpression(child, 0);
1107: child = child.getNext();
1108: addStringOp(type, child.getString());
1109: break;
1110:
1111: case Token.GETELEM:
1112: case Token.DELPROP:
1113: case Token.BITAND:
1114: case Token.BITOR:
1115: case Token.BITXOR:
1116: case Token.LSH:
1117: case Token.RSH:
1118: case Token.URSH:
1119: case Token.ADD:
1120: case Token.SUB:
1121: case Token.MOD:
1122: case Token.DIV:
1123: case Token.MUL:
1124: case Token.EQ:
1125: case Token.NE:
1126: case Token.SHEQ:
1127: case Token.SHNE:
1128: case Token.IN:
1129: case Token.INSTANCEOF:
1130: case Token.LE:
1131: case Token.LT:
1132: case Token.GE:
1133: case Token.GT:
1134: visitExpression(child, 0);
1135: child = child.getNext();
1136: visitExpression(child, 0);
1137: addToken(type);
1138: stackChange(-1);
1139: break;
1140:
1141: case Token.POS:
1142: case Token.NEG:
1143: case Token.NOT:
1144: case Token.BITNOT:
1145: case Token.TYPEOF:
1146: case Token.VOID:
1147: visitExpression(child, 0);
1148: if (type == Token.VOID) {
1149: addIcode(Icode_POP);
1150: addIcode(Icode_UNDEF);
1151: } else {
1152: addToken(type);
1153: }
1154: break;
1155:
1156: case Token.GET_REF:
1157: case Token.DEL_REF:
1158: visitExpression(child, 0);
1159: addToken(type);
1160: break;
1161:
1162: case Token.SETPROP:
1163: case Token.SETPROP_OP: {
1164: visitExpression(child, 0);
1165: child = child.getNext();
1166: String property = child.getString();
1167: child = child.getNext();
1168: if (type == Token.SETPROP_OP) {
1169: addIcode(Icode_DUP);
1170: stackChange(1);
1171: addStringOp(Token.GETPROP, property);
1172: // Compensate for the following USE_STACK
1173: stackChange(-1);
1174: }
1175: visitExpression(child, 0);
1176: addStringOp(Token.SETPROP, property);
1177: stackChange(-1);
1178: }
1179: break;
1180:
1181: case Token.SETELEM:
1182: case Token.SETELEM_OP:
1183: visitExpression(child, 0);
1184: child = child.getNext();
1185: visitExpression(child, 0);
1186: child = child.getNext();
1187: if (type == Token.SETELEM_OP) {
1188: addIcode(Icode_DUP2);
1189: stackChange(2);
1190: addToken(Token.GETELEM);
1191: stackChange(-1);
1192: // Compensate for the following USE_STACK
1193: stackChange(-1);
1194: }
1195: visitExpression(child, 0);
1196: addToken(Token.SETELEM);
1197: stackChange(-2);
1198: break;
1199:
1200: case Token.SET_REF:
1201: case Token.SET_REF_OP:
1202: visitExpression(child, 0);
1203: child = child.getNext();
1204: if (type == Token.SET_REF_OP) {
1205: addIcode(Icode_DUP);
1206: stackChange(1);
1207: addToken(Token.GET_REF);
1208: // Compensate for the following USE_STACK
1209: stackChange(-1);
1210: }
1211: visitExpression(child, 0);
1212: addToken(Token.SET_REF);
1213: stackChange(-1);
1214: break;
1215:
1216: case Token.SETNAME: {
1217: String name = child.getString();
1218: visitExpression(child, 0);
1219: child = child.getNext();
1220: visitExpression(child, 0);
1221: addStringOp(Token.SETNAME, name);
1222: stackChange(-1);
1223: }
1224: break;
1225:
1226: case Token.SETCONST: {
1227: String name = child.getString();
1228: visitExpression(child, 0);
1229: child = child.getNext();
1230: visitExpression(child, 0);
1231: addStringOp(Icode_SETCONST, name);
1232: stackChange(-1);
1233: }
1234: break;
1235:
1236: case Token.TYPEOFNAME: {
1237: int index = -1;
1238: // use typeofname if an activation frame exists
1239: // since the vars all exist there instead of in jregs
1240: if (itsInFunctionFlag && !itsData.itsNeedsActivation)
1241: index = scriptOrFn.getIndexForNameNode(node);
1242: if (index == -1) {
1243: addStringOp(Icode_TYPEOFNAME, node.getString());
1244: stackChange(1);
1245: } else {
1246: addVarOp(Token.GETVAR, index);
1247: stackChange(1);
1248: addToken(Token.TYPEOF);
1249: }
1250: }
1251: break;
1252:
1253: case Token.BINDNAME:
1254: case Token.NAME:
1255: case Token.STRING:
1256: addStringOp(type, node.getString());
1257: stackChange(1);
1258: break;
1259:
1260: case Token.INC:
1261: case Token.DEC:
1262: visitIncDec(node, child);
1263: break;
1264:
1265: case Token.NUMBER: {
1266: double num = node.getDouble();
1267: int inum = (int) num;
1268: if (inum == num) {
1269: if (inum == 0) {
1270: addIcode(Icode_ZERO);
1271: // Check for negative zero
1272: if (1.0 / num < 0.0) {
1273: addToken(Token.NEG);
1274: }
1275: } else if (inum == 1) {
1276: addIcode(Icode_ONE);
1277: } else if ((short) inum == inum) {
1278: addIcode(Icode_SHORTNUMBER);
1279: // write short as uin16 bit pattern
1280: addUint16(inum & 0xFFFF);
1281: } else {
1282: addIcode(Icode_INTNUMBER);
1283: addInt(inum);
1284: }
1285: } else {
1286: int index = getDoubleIndex(num);
1287: addIndexOp(Token.NUMBER, index);
1288: }
1289: stackChange(1);
1290: }
1291: break;
1292:
1293: case Token.GETVAR: {
1294: if (itsData.itsNeedsActivation)
1295: Kit.codeBug();
1296: int index = scriptOrFn.getIndexForNameNode(node);
1297: addVarOp(Token.GETVAR, index);
1298: stackChange(1);
1299: }
1300: break;
1301:
1302: case Token.SETVAR: {
1303: if (itsData.itsNeedsActivation)
1304: Kit.codeBug();
1305: int index = scriptOrFn.getIndexForNameNode(child);
1306: child = child.getNext();
1307: visitExpression(child, 0);
1308: addVarOp(Token.SETVAR, index);
1309: }
1310: break;
1311:
1312: case Token.SETCONSTVAR: {
1313: if (itsData.itsNeedsActivation)
1314: Kit.codeBug();
1315: int index = scriptOrFn.getIndexForNameNode(child);
1316: child = child.getNext();
1317: visitExpression(child, 0);
1318: addVarOp(Token.SETCONSTVAR, index);
1319: }
1320: break;
1321:
1322: case Token.NULL:
1323: case Token.THIS:
1324: case Token.THISFN:
1325: case Token.FALSE:
1326: case Token.TRUE:
1327: addToken(type);
1328: stackChange(1);
1329: break;
1330:
1331: case Token.ENUM_NEXT:
1332: case Token.ENUM_ID:
1333: addIndexOp(type, getLocalBlockRef(node));
1334: stackChange(1);
1335: break;
1336:
1337: case Token.REGEXP: {
1338: int index = node.getExistingIntProp(Node.REGEXP_PROP);
1339: addIndexOp(Token.REGEXP, index);
1340: stackChange(1);
1341: }
1342: break;
1343:
1344: case Token.ARRAYLIT:
1345: case Token.OBJECTLIT:
1346: visitLiteral(node, child);
1347: break;
1348:
1349: case Token.ARRAYCOMP:
1350: visitArrayComprehension(node, child, child.getNext());
1351: break;
1352:
1353: case Token.REF_SPECIAL:
1354: visitExpression(child, 0);
1355: addStringOp(type, (String) node.getProp(Node.NAME_PROP));
1356: break;
1357:
1358: case Token.REF_MEMBER:
1359: case Token.REF_NS_MEMBER:
1360: case Token.REF_NAME:
1361: case Token.REF_NS_NAME: {
1362: int memberTypeFlags = node.getIntProp(
1363: Node.MEMBER_TYPE_PROP, 0);
1364: // generate possible target, possible namespace and member
1365: int childCount = 0;
1366: do {
1367: visitExpression(child, 0);
1368: ++childCount;
1369: child = child.getNext();
1370: } while (child != null);
1371: addIndexOp(type, memberTypeFlags);
1372: stackChange(1 - childCount);
1373: }
1374: break;
1375:
1376: case Token.DOTQUERY: {
1377: int queryPC;
1378: updateLineNumber(node);
1379: visitExpression(child, 0);
1380: addIcode(Icode_ENTERDQ);
1381: stackChange(-1);
1382: queryPC = itsICodeTop;
1383: visitExpression(child.getNext(), 0);
1384: addBackwardGoto(Icode_LEAVEDQ, queryPC);
1385: }
1386: break;
1387:
1388: case Token.DEFAULTNAMESPACE:
1389: case Token.ESCXMLATTR:
1390: case Token.ESCXMLTEXT:
1391: visitExpression(child, 0);
1392: addToken(type);
1393: break;
1394:
1395: case Token.YIELD:
1396: if (child != null) {
1397: visitExpression(child, 0);
1398: } else {
1399: addIcode(Icode_UNDEF);
1400: stackChange(1);
1401: }
1402: addToken(Token.YIELD);
1403: addUint16(node.getLineno() & 0xFFFF);
1404: break;
1405:
1406: case Token.WITHEXPR: {
1407: Node enterWith = node.getFirstChild();
1408: Node with = enterWith.getNext();
1409: visitExpression(enterWith.getFirstChild(), 0);
1410: addToken(Token.ENTERWITH);
1411: stackChange(-1);
1412: visitExpression(with.getFirstChild(), 0);
1413: addToken(Token.LEAVEWITH);
1414: break;
1415: }
1416:
1417: default:
1418: throw badTree(node);
1419: }
1420: if (savedStackDepth + 1 != itsStackDepth) {
1421: Kit.codeBug();
1422: }
1423: }
1424:
1425: private void generateCallFunAndThis(Node left) {
1426: // Generate code to place on stack function and thisObj
1427: int type = left.getType();
1428: switch (type) {
1429: case Token.NAME: {
1430: String name = left.getString();
1431: // stack: ... -> ... function thisObj
1432: addStringOp(Icode_NAME_AND_THIS, name);
1433: stackChange(2);
1434: break;
1435: }
1436: case Token.GETPROP:
1437: case Token.GETELEM: {
1438: Node target = left.getFirstChild();
1439: visitExpression(target, 0);
1440: Node id = target.getNext();
1441: if (type == Token.GETPROP) {
1442: String property = id.getString();
1443: // stack: ... target -> ... function thisObj
1444: addStringOp(Icode_PROP_AND_THIS, property);
1445: stackChange(1);
1446: } else {
1447: visitExpression(id, 0);
1448: // stack: ... target id -> ... function thisObj
1449: addIcode(Icode_ELEM_AND_THIS);
1450: }
1451: break;
1452: }
1453: default:
1454: // Including Token.GETVAR
1455: visitExpression(left, 0);
1456: // stack: ... value -> ... function thisObj
1457: addIcode(Icode_VALUE_AND_THIS);
1458: stackChange(1);
1459: break;
1460: }
1461: }
1462:
1463: private void visitIncDec(Node node, Node child) {
1464: int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
1465: int childType = child.getType();
1466: switch (childType) {
1467: case Token.GETVAR: {
1468: if (itsData.itsNeedsActivation)
1469: Kit.codeBug();
1470: int i = scriptOrFn.getIndexForNameNode(child);
1471: addVarOp(Icode_VAR_INC_DEC, i);
1472: addUint8(incrDecrMask);
1473: stackChange(1);
1474: break;
1475: }
1476: case Token.NAME: {
1477: String name = child.getString();
1478: addStringOp(Icode_NAME_INC_DEC, name);
1479: addUint8(incrDecrMask);
1480: stackChange(1);
1481: break;
1482: }
1483: case Token.GETPROP: {
1484: Node object = child.getFirstChild();
1485: visitExpression(object, 0);
1486: String property = object.getNext().getString();
1487: addStringOp(Icode_PROP_INC_DEC, property);
1488: addUint8(incrDecrMask);
1489: break;
1490: }
1491: case Token.GETELEM: {
1492: Node object = child.getFirstChild();
1493: visitExpression(object, 0);
1494: Node index = object.getNext();
1495: visitExpression(index, 0);
1496: addIcode(Icode_ELEM_INC_DEC);
1497: addUint8(incrDecrMask);
1498: stackChange(-1);
1499: break;
1500: }
1501: case Token.GET_REF: {
1502: Node ref = child.getFirstChild();
1503: visitExpression(ref, 0);
1504: addIcode(Icode_REF_INC_DEC);
1505: addUint8(incrDecrMask);
1506: break;
1507: }
1508: default: {
1509: throw badTree(node);
1510: }
1511: }
1512: }
1513:
1514: private void visitLiteral(Node node, Node child) {
1515: int type = node.getType();
1516: int count;
1517: Object[] propertyIds = null;
1518: if (type == Token.ARRAYLIT) {
1519: count = 0;
1520: for (Node n = child; n != null; n = n.getNext()) {
1521: ++count;
1522: }
1523: } else if (type == Token.OBJECTLIT) {
1524: propertyIds = (Object[]) node.getProp(Node.OBJECT_IDS_PROP);
1525: count = propertyIds.length;
1526: } else {
1527: throw badTree(node);
1528: }
1529: addIndexOp(Icode_LITERAL_NEW, count);
1530: stackChange(2);
1531: while (child != null) {
1532: int childType = child.getType();
1533: if (childType == Token.GET) {
1534: visitExpression(child.getFirstChild(), 0);
1535: addIcode(Icode_LITERAL_GETTER);
1536: } else if (childType == Token.SET) {
1537: visitExpression(child.getFirstChild(), 0);
1538: addIcode(Icode_LITERAL_SETTER);
1539: } else {
1540: visitExpression(child, 0);
1541: addIcode(Icode_LITERAL_SET);
1542: }
1543: stackChange(-1);
1544: child = child.getNext();
1545: }
1546: if (type == Token.ARRAYLIT) {
1547: int[] skipIndexes = (int[]) node
1548: .getProp(Node.SKIP_INDEXES_PROP);
1549: if (skipIndexes == null) {
1550: addToken(Token.ARRAYLIT);
1551: } else {
1552: int index = itsLiteralIds.size();
1553: itsLiteralIds.add(skipIndexes);
1554: addIndexOp(Icode_SPARE_ARRAYLIT, index);
1555: }
1556: } else {
1557: int index = itsLiteralIds.size();
1558: itsLiteralIds.add(propertyIds);
1559: addIndexOp(Token.OBJECTLIT, index);
1560: }
1561: stackChange(-1);
1562: }
1563:
1564: private void visitArrayComprehension(Node node, Node initStmt,
1565: Node expr) {
1566: // A bit of a hack: array comprehensions are implemented using
1567: // statement nodes for the iteration, yet they appear in an
1568: // expression context. So we pass the current stack depth to
1569: // visitStatement so it can check that the depth is not altered
1570: // by statements.
1571: visitStatement(initStmt, itsStackDepth);
1572: visitExpression(expr, 0);
1573: }
1574:
1575: private int getLocalBlockRef(Node node) {
1576: Node localBlock = (Node) node.getProp(Node.LOCAL_BLOCK_PROP);
1577: return localBlock.getExistingIntProp(Node.LOCAL_PROP);
1578: }
1579:
1580: private int getTargetLabel(Node target) {
1581: int label = target.labelId();
1582: if (label != -1) {
1583: return label;
1584: }
1585: label = itsLabelTableTop;
1586: if (itsLabelTable == null || label == itsLabelTable.length) {
1587: if (itsLabelTable == null) {
1588: itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
1589: } else {
1590: int[] tmp = new int[itsLabelTable.length * 2];
1591: System.arraycopy(itsLabelTable, 0, tmp, 0, label);
1592: itsLabelTable = tmp;
1593: }
1594: }
1595: itsLabelTableTop = label + 1;
1596: itsLabelTable[label] = -1;
1597:
1598: target.labelId(label);
1599: return label;
1600: }
1601:
1602: private void markTargetLabel(Node target) {
1603: int label = getTargetLabel(target);
1604: if (itsLabelTable[label] != -1) {
1605: // Can mark label only once
1606: Kit.codeBug();
1607: }
1608: itsLabelTable[label] = itsICodeTop;
1609: }
1610:
1611: private void addGoto(Node target, int gotoOp) {
1612: int label = getTargetLabel(target);
1613: if (!(label < itsLabelTableTop))
1614: Kit.codeBug();
1615: int targetPC = itsLabelTable[label];
1616:
1617: if (targetPC != -1) {
1618: addBackwardGoto(gotoOp, targetPC);
1619: } else {
1620: int gotoPC = itsICodeTop;
1621: addGotoOp(gotoOp);
1622: int top = itsFixupTableTop;
1623: if (itsFixupTable == null || top == itsFixupTable.length) {
1624: if (itsFixupTable == null) {
1625: itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
1626: } else {
1627: long[] tmp = new long[itsFixupTable.length * 2];
1628: System.arraycopy(itsFixupTable, 0, tmp, 0, top);
1629: itsFixupTable = tmp;
1630: }
1631: }
1632: itsFixupTableTop = top + 1;
1633: itsFixupTable[top] = ((long) label << 32) | gotoPC;
1634: }
1635: }
1636:
1637: private void fixLabelGotos() {
1638: for (int i = 0; i < itsFixupTableTop; i++) {
1639: long fixup = itsFixupTable[i];
1640: int label = (int) (fixup >> 32);
1641: int jumpSource = (int) fixup;
1642: int pc = itsLabelTable[label];
1643: if (pc == -1) {
1644: // Unlocated label
1645: throw Kit.codeBug();
1646: }
1647: resolveGoto(jumpSource, pc);
1648: }
1649: itsFixupTableTop = 0;
1650: }
1651:
1652: private void addBackwardGoto(int gotoOp, int jumpPC) {
1653: int fromPC = itsICodeTop;
1654: // Ensure that this is a jump backward
1655: if (fromPC <= jumpPC)
1656: throw Kit.codeBug();
1657: addGotoOp(gotoOp);
1658: resolveGoto(fromPC, jumpPC);
1659: }
1660:
1661: private void resolveForwardGoto(int fromPC) {
1662: // Ensure that forward jump skips at least self bytecode
1663: if (itsICodeTop < fromPC + 3)
1664: throw Kit.codeBug();
1665: resolveGoto(fromPC, itsICodeTop);
1666: }
1667:
1668: private void resolveGoto(int fromPC, int jumpPC) {
1669: int offset = jumpPC - fromPC;
1670: // Ensure that jumps do not overlap
1671: if (0 <= offset && offset <= 2)
1672: throw Kit.codeBug();
1673: int offsetSite = fromPC + 1;
1674: if (offset != (short) offset) {
1675: if (itsData.longJumps == null) {
1676: itsData.longJumps = new UintMap();
1677: }
1678: itsData.longJumps.put(offsetSite, jumpPC);
1679: offset = 0;
1680: }
1681: byte[] array = itsData.itsICode;
1682: array[offsetSite] = (byte) (offset >> 8);
1683: array[offsetSite + 1] = (byte) offset;
1684: }
1685:
1686: private void addToken(int token) {
1687: if (!validTokenCode(token))
1688: throw Kit.codeBug();
1689: addUint8(token);
1690: }
1691:
1692: private void addIcode(int icode) {
1693: if (!validIcode(icode))
1694: throw Kit.codeBug();
1695: // Write negative icode as uint8 bits
1696: addUint8(icode & 0xFF);
1697: }
1698:
1699: private void addUint8(int value) {
1700: if ((value & ~0xFF) != 0)
1701: throw Kit.codeBug();
1702: byte[] array = itsData.itsICode;
1703: int top = itsICodeTop;
1704: if (top == array.length) {
1705: array = increaseICodeCapacity(1);
1706: }
1707: array[top] = (byte) value;
1708: itsICodeTop = top + 1;
1709: }
1710:
1711: private void addUint16(int value) {
1712: if ((value & ~0xFFFF) != 0)
1713: throw Kit.codeBug();
1714: byte[] array = itsData.itsICode;
1715: int top = itsICodeTop;
1716: if (top + 2 > array.length) {
1717: array = increaseICodeCapacity(2);
1718: }
1719: array[top] = (byte) (value >>> 8);
1720: array[top + 1] = (byte) value;
1721: itsICodeTop = top + 2;
1722: }
1723:
1724: private void addInt(int i) {
1725: byte[] array = itsData.itsICode;
1726: int top = itsICodeTop;
1727: if (top + 4 > array.length) {
1728: array = increaseICodeCapacity(4);
1729: }
1730: array[top] = (byte) (i >>> 24);
1731: array[top + 1] = (byte) (i >>> 16);
1732: array[top + 2] = (byte) (i >>> 8);
1733: array[top + 3] = (byte) i;
1734: itsICodeTop = top + 4;
1735: }
1736:
1737: private int getDoubleIndex(double num) {
1738: int index = itsDoubleTableTop;
1739: if (index == 0) {
1740: itsData.itsDoubleTable = new double[64];
1741: } else if (itsData.itsDoubleTable.length == index) {
1742: double[] na = new double[index * 2];
1743: System.arraycopy(itsData.itsDoubleTable, 0, na, 0, index);
1744: itsData.itsDoubleTable = na;
1745: }
1746: itsData.itsDoubleTable[index] = num;
1747: itsDoubleTableTop = index + 1;
1748: return index;
1749: }
1750:
1751: private void addGotoOp(int gotoOp) {
1752: byte[] array = itsData.itsICode;
1753: int top = itsICodeTop;
1754: if (top + 3 > array.length) {
1755: array = increaseICodeCapacity(3);
1756: }
1757: array[top] = (byte) gotoOp;
1758: // Offset would written later
1759: itsICodeTop = top + 1 + 2;
1760: }
1761:
1762: private void addVarOp(int op, int varIndex) {
1763: switch (op) {
1764: case Token.SETCONSTVAR:
1765: if (varIndex < 128) {
1766: addIcode(Icode_SETCONSTVAR1);
1767: addUint8(varIndex);
1768: return;
1769: }
1770: addIndexOp(Icode_SETCONSTVAR, varIndex);
1771: return;
1772: case Token.GETVAR:
1773: case Token.SETVAR:
1774: if (varIndex < 128) {
1775: addIcode(op == Token.GETVAR ? Icode_GETVAR1
1776: : Icode_SETVAR1);
1777: addUint8(varIndex);
1778: return;
1779: }
1780: // fallthrough
1781: case Icode_VAR_INC_DEC:
1782: addIndexOp(op, varIndex);
1783: return;
1784: }
1785: throw Kit.codeBug();
1786: }
1787:
1788: private void addStringOp(int op, String str) {
1789: addStringPrefix(str);
1790: if (validIcode(op)) {
1791: addIcode(op);
1792: } else {
1793: addToken(op);
1794: }
1795: }
1796:
1797: private void addIndexOp(int op, int index) {
1798: addIndexPrefix(index);
1799: if (validIcode(op)) {
1800: addIcode(op);
1801: } else {
1802: addToken(op);
1803: }
1804: }
1805:
1806: private void addStringPrefix(String str) {
1807: int index = itsStrings.get(str, -1);
1808: if (index == -1) {
1809: index = itsStrings.size();
1810: itsStrings.put(str, index);
1811: }
1812: if (index < 4) {
1813: addIcode(Icode_REG_STR_C0 - index);
1814: } else if (index <= 0xFF) {
1815: addIcode(Icode_REG_STR1);
1816: addUint8(index);
1817: } else if (index <= 0xFFFF) {
1818: addIcode(Icode_REG_STR2);
1819: addUint16(index);
1820: } else {
1821: addIcode(Icode_REG_STR4);
1822: addInt(index);
1823: }
1824: }
1825:
1826: private void addIndexPrefix(int index) {
1827: if (index < 0)
1828: Kit.codeBug();
1829: if (index < 6) {
1830: addIcode(Icode_REG_IND_C0 - index);
1831: } else if (index <= 0xFF) {
1832: addIcode(Icode_REG_IND1);
1833: addUint8(index);
1834: } else if (index <= 0xFFFF) {
1835: addIcode(Icode_REG_IND2);
1836: addUint16(index);
1837: } else {
1838: addIcode(Icode_REG_IND4);
1839: addInt(index);
1840: }
1841: }
1842:
1843: private void addExceptionHandler(int icodeStart, int icodeEnd,
1844: int handlerStart, boolean isFinally,
1845: int exceptionObjectLocal, int scopeLocal) {
1846: int top = itsExceptionTableTop;
1847: int[] table = itsData.itsExceptionTable;
1848: if (table == null) {
1849: if (top != 0)
1850: Kit.codeBug();
1851: table = new int[EXCEPTION_SLOT_SIZE * 2];
1852: itsData.itsExceptionTable = table;
1853: } else if (table.length == top) {
1854: table = new int[table.length * 2];
1855: System.arraycopy(itsData.itsExceptionTable, 0, table, 0,
1856: top);
1857: itsData.itsExceptionTable = table;
1858: }
1859: table[top + EXCEPTION_TRY_START_SLOT] = icodeStart;
1860: table[top + EXCEPTION_TRY_END_SLOT] = icodeEnd;
1861: table[top + EXCEPTION_HANDLER_SLOT] = handlerStart;
1862: table[top + EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0;
1863: table[top + EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal;
1864: table[top + EXCEPTION_SCOPE_SLOT] = scopeLocal;
1865:
1866: itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE;
1867: }
1868:
1869: private byte[] increaseICodeCapacity(int extraSize) {
1870: int capacity = itsData.itsICode.length;
1871: int top = itsICodeTop;
1872: if (top + extraSize <= capacity)
1873: throw Kit.codeBug();
1874: capacity *= 2;
1875: if (top + extraSize > capacity) {
1876: capacity = top + extraSize;
1877: }
1878: byte[] array = new byte[capacity];
1879: System.arraycopy(itsData.itsICode, 0, array, 0, top);
1880: itsData.itsICode = array;
1881: return array;
1882: }
1883:
1884: private void stackChange(int change) {
1885: if (change <= 0) {
1886: itsStackDepth += change;
1887: } else {
1888: int newDepth = itsStackDepth + change;
1889: if (newDepth > itsData.itsMaxStack) {
1890: itsData.itsMaxStack = newDepth;
1891: }
1892: itsStackDepth = newDepth;
1893: }
1894: }
1895:
1896: private int allocLocal() {
1897: int localSlot = itsLocalTop;
1898: ++itsLocalTop;
1899: if (itsLocalTop > itsData.itsMaxLocals) {
1900: itsData.itsMaxLocals = itsLocalTop;
1901: }
1902: return localSlot;
1903: }
1904:
1905: private void releaseLocal(int localSlot) {
1906: --itsLocalTop;
1907: if (localSlot != itsLocalTop)
1908: Kit.codeBug();
1909: }
1910:
1911: private static int getShort(byte[] iCode, int pc) {
1912: return (iCode[pc] << 8) | (iCode[pc + 1] & 0xFF);
1913: }
1914:
1915: private static int getIndex(byte[] iCode, int pc) {
1916: return ((iCode[pc] & 0xFF) << 8) | (iCode[pc + 1] & 0xFF);
1917: }
1918:
1919: private static int getInt(byte[] iCode, int pc) {
1920: return (iCode[pc] << 24) | ((iCode[pc + 1] & 0xFF) << 16)
1921: | ((iCode[pc + 2] & 0xFF) << 8)
1922: | (iCode[pc + 3] & 0xFF);
1923: }
1924:
1925: private static int getExceptionHandler(CallFrame frame,
1926: boolean onlyFinally) {
1927: int[] exceptionTable = frame.idata.itsExceptionTable;
1928: if (exceptionTable == null) {
1929: // No exception handlers
1930: return -1;
1931: }
1932:
1933: // Icode switch in the interpreter increments PC immediately
1934: // and it is necessary to subtract 1 from the saved PC
1935: // to point it before the start of the next instruction.
1936: int pc = frame.pc - 1;
1937:
1938: // OPT: use binary search
1939: int best = -1, bestStart = 0, bestEnd = 0;
1940: for (int i = 0; i != exceptionTable.length; i += EXCEPTION_SLOT_SIZE) {
1941: int start = exceptionTable[i + EXCEPTION_TRY_START_SLOT];
1942: int end = exceptionTable[i + EXCEPTION_TRY_END_SLOT];
1943: if (!(start <= pc && pc < end)) {
1944: continue;
1945: }
1946: if (onlyFinally
1947: && exceptionTable[i + EXCEPTION_TYPE_SLOT] != 1) {
1948: continue;
1949: }
1950: if (best >= 0) {
1951: // Since handlers always nest and they never have shared end
1952: // although they can share start it is sufficient to compare
1953: // handlers ends
1954: if (bestEnd < end) {
1955: continue;
1956: }
1957: // Check the above assumption
1958: if (bestStart > start)
1959: Kit.codeBug(); // should be nested
1960: if (bestEnd == end)
1961: Kit.codeBug(); // no ens sharing
1962: }
1963: best = i;
1964: bestStart = start;
1965: bestEnd = end;
1966: }
1967: return best;
1968: }
1969:
1970: private static void dumpICode(InterpreterData idata) {
1971: if (!Token.printICode) {
1972: return;
1973: }
1974:
1975: byte iCode[] = idata.itsICode;
1976: int iCodeLength = iCode.length;
1977: String[] strings = idata.itsStringTable;
1978: PrintStream out = System.out;
1979: out.println("ICode dump, for " + idata.itsName + ", length = "
1980: + iCodeLength);
1981: out.println("MaxStack = " + idata.itsMaxStack);
1982:
1983: int indexReg = 0;
1984: for (int pc = 0; pc < iCodeLength;) {
1985: out.flush();
1986: out.print(" [" + pc + "] ");
1987: int token = iCode[pc];
1988: int icodeLength = bytecodeSpan(token);
1989: String tname = bytecodeName(token);
1990: int old_pc = pc;
1991: ++pc;
1992: switch (token) {
1993: default:
1994: if (icodeLength != 1)
1995: Kit.codeBug();
1996: out.println(tname);
1997: break;
1998:
1999: case Icode_GOSUB:
2000: case Token.GOTO:
2001: case Token.IFEQ:
2002: case Token.IFNE:
2003: case Icode_IFEQ_POP:
2004: case Icode_LEAVEDQ: {
2005: int newPC = pc + getShort(iCode, pc) - 1;
2006: out.println(tname + " " + newPC);
2007: pc += 2;
2008: break;
2009: }
2010: case Icode_VAR_INC_DEC:
2011: case Icode_NAME_INC_DEC:
2012: case Icode_PROP_INC_DEC:
2013: case Icode_ELEM_INC_DEC:
2014: case Icode_REF_INC_DEC: {
2015: int incrDecrType = iCode[pc];
2016: out.println(tname + " " + incrDecrType);
2017: ++pc;
2018: break;
2019: }
2020:
2021: case Icode_CALLSPECIAL: {
2022: int callType = iCode[pc] & 0xFF;
2023: boolean isNew = (iCode[pc + 1] != 0);
2024: int line = getIndex(iCode, pc + 2);
2025: out.println(tname + " " + callType + " " + isNew + " "
2026: + indexReg + " " + line);
2027: pc += 4;
2028: break;
2029: }
2030:
2031: case Token.CATCH_SCOPE: {
2032: boolean afterFisrtFlag = (iCode[pc] != 0);
2033: out.println(tname + " " + afterFisrtFlag);
2034: ++pc;
2035: }
2036: break;
2037: case Token.REGEXP:
2038: out.println(tname + " "
2039: + idata.itsRegExpLiterals[indexReg]);
2040: break;
2041: case Token.OBJECTLIT:
2042: case Icode_SPARE_ARRAYLIT:
2043: out.println(tname + " " + idata.literalIds[indexReg]);
2044: break;
2045: case Icode_CLOSURE_EXPR:
2046: case Icode_CLOSURE_STMT:
2047: out.println(tname + " "
2048: + idata.itsNestedFunctions[indexReg]);
2049: break;
2050: case Token.CALL:
2051: case Icode_TAIL_CALL:
2052: case Token.REF_CALL:
2053: case Token.NEW:
2054: out.println(tname + ' ' + indexReg);
2055: break;
2056: case Token.THROW:
2057: case Token.YIELD:
2058: case Icode_GENERATOR:
2059: case Icode_GENERATOR_END: {
2060: int line = getIndex(iCode, pc);
2061: out.println(tname + " : " + line);
2062: pc += 2;
2063: break;
2064: }
2065: case Icode_SHORTNUMBER: {
2066: int value = getShort(iCode, pc);
2067: out.println(tname + " " + value);
2068: pc += 2;
2069: break;
2070: }
2071: case Icode_INTNUMBER: {
2072: int value = getInt(iCode, pc);
2073: out.println(tname + " " + value);
2074: pc += 4;
2075: break;
2076: }
2077: case Token.NUMBER: {
2078: double value = idata.itsDoubleTable[indexReg];
2079: out.println(tname + " " + value);
2080: break;
2081: }
2082: case Icode_LINE: {
2083: int line = getIndex(iCode, pc);
2084: out.println(tname + " : " + line);
2085: pc += 2;
2086: break;
2087: }
2088: case Icode_REG_STR1: {
2089: String str = strings[0xFF & iCode[pc]];
2090: out.println(tname + " \"" + str + '"');
2091: ++pc;
2092: break;
2093: }
2094: case Icode_REG_STR2: {
2095: String str = strings[getIndex(iCode, pc)];
2096: out.println(tname + " \"" + str + '"');
2097: pc += 2;
2098: break;
2099: }
2100: case Icode_REG_STR4: {
2101: String str = strings[getInt(iCode, pc)];
2102: out.println(tname + " \"" + str + '"');
2103: pc += 4;
2104: break;
2105: }
2106: case Icode_REG_IND_C0:
2107: indexReg = 0;
2108: out.println(tname);
2109: break;
2110: case Icode_REG_IND_C1:
2111: indexReg = 1;
2112: out.println(tname);
2113: break;
2114: case Icode_REG_IND_C2:
2115: indexReg = 2;
2116: out.println(tname);
2117: break;
2118: case Icode_REG_IND_C3:
2119: indexReg = 3;
2120: out.println(tname);
2121: break;
2122: case Icode_REG_IND_C4:
2123: indexReg = 4;
2124: out.println(tname);
2125: break;
2126: case Icode_REG_IND_C5:
2127: indexReg = 5;
2128: out.println(tname);
2129: break;
2130: case Icode_REG_IND1: {
2131: indexReg = 0xFF & iCode[pc];
2132: out.println(tname + " " + indexReg);
2133: ++pc;
2134: break;
2135: }
2136: case Icode_REG_IND2: {
2137: indexReg = getIndex(iCode, pc);
2138: out.println(tname + " " + indexReg);
2139: pc += 2;
2140: break;
2141: }
2142: case Icode_REG_IND4: {
2143: indexReg = getInt(iCode, pc);
2144: out.println(tname + " " + indexReg);
2145: pc += 4;
2146: break;
2147: }
2148: case Icode_GETVAR1:
2149: case Icode_SETVAR1:
2150: case Icode_SETCONSTVAR1:
2151: indexReg = iCode[pc];
2152: out.println(tname + " " + indexReg);
2153: ++pc;
2154: break;
2155: }
2156: if (old_pc + icodeLength != pc)
2157: Kit.codeBug();
2158: }
2159:
2160: int[] table = idata.itsExceptionTable;
2161: if (table != null) {
2162: out.println("Exception handlers: " + table.length
2163: / EXCEPTION_SLOT_SIZE);
2164: for (int i = 0; i != table.length; i += EXCEPTION_SLOT_SIZE) {
2165: int tryStart = table[i + EXCEPTION_TRY_START_SLOT];
2166: int tryEnd = table[i + EXCEPTION_TRY_END_SLOT];
2167: int handlerStart = table[i + EXCEPTION_HANDLER_SLOT];
2168: int type = table[i + EXCEPTION_TYPE_SLOT];
2169: int exceptionLocal = table[i + EXCEPTION_LOCAL_SLOT];
2170: int scopeLocal = table[i + EXCEPTION_SCOPE_SLOT];
2171:
2172: out.println(" tryStart=" + tryStart + " tryEnd="
2173: + tryEnd + " handlerStart=" + handlerStart
2174: + " type=" + (type == 0 ? "catch" : "finally")
2175: + " exceptionLocal=" + exceptionLocal);
2176: }
2177: }
2178: out.flush();
2179: }
2180:
2181: private static int bytecodeSpan(int bytecode) {
2182: switch (bytecode) {
2183: case Token.THROW:
2184: case Token.YIELD:
2185: case Icode_GENERATOR:
2186: case Icode_GENERATOR_END:
2187: // source line
2188: return 1 + 2;
2189:
2190: case Icode_GOSUB:
2191: case Token.GOTO:
2192: case Token.IFEQ:
2193: case Token.IFNE:
2194: case Icode_IFEQ_POP:
2195: case Icode_LEAVEDQ:
2196: // target pc offset
2197: return 1 + 2;
2198:
2199: case Icode_CALLSPECIAL:
2200: // call type
2201: // is new
2202: // line number
2203: return 1 + 1 + 1 + 2;
2204:
2205: case Token.CATCH_SCOPE:
2206: // scope flag
2207: return 1 + 1;
2208:
2209: case Icode_VAR_INC_DEC:
2210: case Icode_NAME_INC_DEC:
2211: case Icode_PROP_INC_DEC:
2212: case Icode_ELEM_INC_DEC:
2213: case Icode_REF_INC_DEC:
2214: // type of ++/--
2215: return 1 + 1;
2216:
2217: case Icode_SHORTNUMBER:
2218: // short number
2219: return 1 + 2;
2220:
2221: case Icode_INTNUMBER:
2222: // int number
2223: return 1 + 4;
2224:
2225: case Icode_REG_IND1:
2226: // ubyte index
2227: return 1 + 1;
2228:
2229: case Icode_REG_IND2:
2230: // ushort index
2231: return 1 + 2;
2232:
2233: case Icode_REG_IND4:
2234: // int index
2235: return 1 + 4;
2236:
2237: case Icode_REG_STR1:
2238: // ubyte string index
2239: return 1 + 1;
2240:
2241: case Icode_REG_STR2:
2242: // ushort string index
2243: return 1 + 2;
2244:
2245: case Icode_REG_STR4:
2246: // int string index
2247: return 1 + 4;
2248:
2249: case Icode_GETVAR1:
2250: case Icode_SETVAR1:
2251: case Icode_SETCONSTVAR1:
2252: // byte var index
2253: return 1 + 1;
2254:
2255: case Icode_LINE:
2256: // line number
2257: return 1 + 2;
2258: }
2259: if (!validBytecode(bytecode))
2260: throw Kit.codeBug();
2261: return 1;
2262: }
2263:
2264: static int[] getLineNumbers(InterpreterData data) {
2265: UintMap presentLines = new UintMap();
2266:
2267: byte[] iCode = data.itsICode;
2268: int iCodeLength = iCode.length;
2269: for (int pc = 0; pc != iCodeLength;) {
2270: int bytecode = iCode[pc];
2271: int span = bytecodeSpan(bytecode);
2272: if (bytecode == Icode_LINE) {
2273: if (span != 3)
2274: Kit.codeBug();
2275: int line = getIndex(iCode, pc + 1);
2276: presentLines.put(line, 0);
2277: }
2278: pc += span;
2279: }
2280:
2281: return presentLines.getKeys();
2282: }
2283:
2284: public void captureStackInfo(RhinoException ex) {
2285: Context cx = Context.getCurrentContext();
2286: if (cx == null || cx.lastInterpreterFrame == null) {
2287: // No interpreter invocations
2288: ex.interpreterStackInfo = null;
2289: ex.interpreterLineData = null;
2290: return;
2291: }
2292: // has interpreter frame on the stack
2293: CallFrame[] array;
2294: if (cx.previousInterpreterInvocations == null
2295: || cx.previousInterpreterInvocations.size() == 0) {
2296: array = new CallFrame[1];
2297: } else {
2298: int previousCount = cx.previousInterpreterInvocations
2299: .size();
2300: if (cx.previousInterpreterInvocations.peek() == cx.lastInterpreterFrame) {
2301: // It can happen if exception was generated after
2302: // frame was pushed to cx.previousInterpreterInvocations
2303: // but before assignment to cx.lastInterpreterFrame.
2304: // In this case frames has to be ignored.
2305: --previousCount;
2306: }
2307: array = new CallFrame[previousCount + 1];
2308: cx.previousInterpreterInvocations.toArray(array);
2309: }
2310: array[array.length - 1] = (CallFrame) cx.lastInterpreterFrame;
2311:
2312: int interpreterFrameCount = 0;
2313: for (int i = 0; i != array.length; ++i) {
2314: interpreterFrameCount += 1 + array[i].frameIndex;
2315: }
2316:
2317: int[] linePC = new int[interpreterFrameCount];
2318: // Fill linePC with pc positions from all interpreter frames.
2319: // Start from the most nested frame
2320: int linePCIndex = interpreterFrameCount;
2321: for (int i = array.length; i != 0;) {
2322: --i;
2323: CallFrame frame = array[i];
2324: while (frame != null) {
2325: --linePCIndex;
2326: linePC[linePCIndex] = frame.pcSourceLineStart;
2327: frame = frame.parentFrame;
2328: }
2329: }
2330: if (linePCIndex != 0)
2331: Kit.codeBug();
2332:
2333: ex.interpreterStackInfo = array;
2334: ex.interpreterLineData = linePC;
2335: }
2336:
2337: public String getSourcePositionFromStack(Context cx, int[] linep) {
2338: CallFrame frame = (CallFrame) cx.lastInterpreterFrame;
2339: InterpreterData idata = frame.idata;
2340: if (frame.pcSourceLineStart >= 0) {
2341: linep[0] = getIndex(idata.itsICode, frame.pcSourceLineStart);
2342: } else {
2343: linep[0] = 0;
2344: }
2345: return idata.itsSourceFile;
2346: }
2347:
2348: public String getPatchedStack(RhinoException ex,
2349: String nativeStackTrace) {
2350: String tag = "org.mozilla.javascript.Interpreter.interpretLoop";
2351: StringBuffer sb = new StringBuffer(
2352: nativeStackTrace.length() + 1000);
2353: String lineSeparator = SecurityUtilities
2354: .getSystemProperty("line.separator");
2355:
2356: CallFrame[] array = (CallFrame[]) ex.interpreterStackInfo;
2357: int[] linePC = ex.interpreterLineData;
2358: int arrayIndex = array.length;
2359: int linePCIndex = linePC.length;
2360: int offset = 0;
2361: while (arrayIndex != 0) {
2362: --arrayIndex;
2363: int pos = nativeStackTrace.indexOf(tag, offset);
2364: if (pos < 0) {
2365: break;
2366: }
2367:
2368: // Skip tag length
2369: pos += tag.length();
2370: // Skip until the end of line
2371: for (; pos != nativeStackTrace.length(); ++pos) {
2372: char c = nativeStackTrace.charAt(pos);
2373: if (c == '\n' || c == '\r') {
2374: break;
2375: }
2376: }
2377: sb.append(nativeStackTrace.substring(offset, pos));
2378: offset = pos;
2379:
2380: CallFrame frame = array[arrayIndex];
2381: while (frame != null) {
2382: if (linePCIndex == 0)
2383: Kit.codeBug();
2384: --linePCIndex;
2385: InterpreterData idata = frame.idata;
2386: sb.append(lineSeparator);
2387: sb.append("\tat script");
2388: if (idata.itsName != null
2389: && idata.itsName.length() != 0) {
2390: sb.append('.');
2391: sb.append(idata.itsName);
2392: }
2393: sb.append('(');
2394: sb.append(idata.itsSourceFile);
2395: int pc = linePC[linePCIndex];
2396: if (pc >= 0) {
2397: // Include line info only if available
2398: sb.append(':');
2399: sb.append(getIndex(idata.itsICode, pc));
2400: }
2401: sb.append(')');
2402: frame = frame.parentFrame;
2403: }
2404: }
2405: sb.append(nativeStackTrace.substring(offset));
2406:
2407: return sb.toString();
2408: }
2409:
2410: public List getScriptStack(RhinoException ex) {
2411: if (ex.interpreterStackInfo == null) {
2412: return null;
2413: }
2414:
2415: List list = new ArrayList();
2416: String lineSeparator = SecurityUtilities
2417: .getSystemProperty("line.separator");
2418:
2419: CallFrame[] array = (CallFrame[]) ex.interpreterStackInfo;
2420: int[] linePC = ex.interpreterLineData;
2421: int arrayIndex = array.length;
2422: int linePCIndex = linePC.length;
2423: while (arrayIndex != 0) {
2424: --arrayIndex;
2425: StringBuffer sb = new StringBuffer();
2426: CallFrame frame = array[arrayIndex];
2427: while (frame != null) {
2428: if (linePCIndex == 0)
2429: Kit.codeBug();
2430: --linePCIndex;
2431: InterpreterData idata = frame.idata;
2432: sb.append("\tat ");
2433: sb.append(idata.itsSourceFile);
2434: int pc = linePC[linePCIndex];
2435: if (pc >= 0) {
2436: // Include line info only if available
2437: sb.append(':');
2438: sb.append(getIndex(idata.itsICode, pc));
2439: }
2440: if (idata.itsName != null
2441: && idata.itsName.length() != 0) {
2442: sb.append(" (");
2443: sb.append(idata.itsName);
2444: sb.append(')');
2445: }
2446: sb.append(lineSeparator);
2447: frame = frame.parentFrame;
2448: }
2449: list.add(sb.toString());
2450: }
2451: return list;
2452: }
2453:
2454: static String getEncodedSource(InterpreterData idata) {
2455: if (idata.encodedSource == null) {
2456: return null;
2457: }
2458: return idata.encodedSource.substring(idata.encodedSourceStart,
2459: idata.encodedSourceEnd);
2460: }
2461:
2462: private static void initFunction(Context cx, Scriptable scope,
2463: InterpretedFunction parent, int index) {
2464: InterpretedFunction fn;
2465: fn = InterpretedFunction.createFunction(cx, scope, parent,
2466: index);
2467: ScriptRuntime.initFunction(cx, scope, fn,
2468: fn.idata.itsFunctionType, parent.idata.evalScriptFlag);
2469: }
2470:
2471: static Object interpret(InterpretedFunction ifun, Context cx,
2472: Scriptable scope, Scriptable this Obj, Object[] args) {
2473: if (!ScriptRuntime.hasTopCall(cx))
2474: Kit.codeBug();
2475:
2476: if (cx.interpreterSecurityDomain != ifun.securityDomain) {
2477: Object savedDomain = cx.interpreterSecurityDomain;
2478: cx.interpreterSecurityDomain = ifun.securityDomain;
2479: try {
2480: return ifun.securityController.callWithDomain(
2481: ifun.securityDomain, cx, ifun, scope, this Obj,
2482: args);
2483: } finally {
2484: cx.interpreterSecurityDomain = savedDomain;
2485: }
2486: }
2487:
2488: CallFrame frame = new CallFrame();
2489: initFrame(cx, scope, this Obj, args, null, 0, args.length, ifun,
2490: null, frame);
2491:
2492: return interpretLoop(cx, frame, null);
2493: }
2494:
2495: static class GeneratorState {
2496: GeneratorState(int operation, Object value) {
2497: this .operation = operation;
2498: this .value = value;
2499: }
2500:
2501: int operation;
2502: Object value;
2503: RuntimeException returnedException;
2504: }
2505:
2506: public static Object resumeGenerator(Context cx, Scriptable scope,
2507: int operation, Object savedState, Object value) {
2508: CallFrame frame = (CallFrame) savedState;
2509: GeneratorState generatorState = new GeneratorState(operation,
2510: value);
2511: if (operation == NativeGenerator.GENERATOR_CLOSE) {
2512: try {
2513: return interpretLoop(cx, frame, generatorState);
2514: } catch (RuntimeException e) {
2515: // Only propagate exceptions other than closingException
2516: if (e != value)
2517: throw e;
2518: }
2519: return Undefined.instance;
2520: }
2521: Object result = interpretLoop(cx, frame, generatorState);
2522: if (generatorState.returnedException != null)
2523: throw generatorState.returnedException;
2524: return result;
2525: }
2526:
2527: public static Object restartContinuation(Continuation c,
2528: Context cx, Scriptable scope, Object[] args) {
2529: if (!ScriptRuntime.hasTopCall(cx)) {
2530: return ScriptRuntime.doTopCall(c, cx, scope, null, args);
2531: }
2532:
2533: Object arg;
2534: if (args.length == 0) {
2535: arg = Undefined.instance;
2536: } else {
2537: arg = args[0];
2538: }
2539:
2540: CallFrame capturedFrame = (CallFrame) c.getImplementation();
2541: if (capturedFrame == null) {
2542: // No frames to restart
2543: return arg;
2544: }
2545:
2546: ContinuationJump cjump = new ContinuationJump(c, null);
2547:
2548: cjump.result = arg;
2549: return interpretLoop(cx, null, cjump);
2550: }
2551:
2552: private static Object interpretLoop(Context cx, CallFrame frame,
2553: Object throwable) {
2554: // throwable holds exception object to rethrow or catch
2555: // It is also used for continuation restart in which case
2556: // it holds ContinuationJump
2557:
2558: final Object DBL_MRK = UniqueTag.DOUBLE_MARK;
2559: final Object undefined = Undefined.instance;
2560:
2561: final boolean instructionCounting = (cx.instructionThreshold != 0);
2562: // arbitrary number to add to instructionCount when calling
2563: // other functions
2564: final int INVOCATION_COST = 100;
2565: // arbitrary exception cost for instruction counting
2566: final int EXCEPTION_COST = 100;
2567:
2568: String stringReg = null;
2569: int indexReg = -1;
2570:
2571: if (cx.lastInterpreterFrame != null) {
2572: // save the top frame from the previous interpretLoop
2573: // invocation on the stack
2574: if (cx.previousInterpreterInvocations == null) {
2575: cx.previousInterpreterInvocations = new ObjArray();
2576: }
2577: cx.previousInterpreterInvocations
2578: .push(cx.lastInterpreterFrame);
2579: }
2580:
2581: // When restarting continuation throwable is not null and to jump
2582: // to the code that rewind continuation state indexReg should be set
2583: // to -1.
2584: // With the normal call throable == null and indexReg == -1 allows to
2585: // catch bugs with using indeReg to access array eleemnts before
2586: // initializing indexReg.
2587:
2588: GeneratorState generatorState = null;
2589: if (throwable != null) {
2590: if (throwable instanceof GeneratorState) {
2591: generatorState = (GeneratorState) throwable;
2592:
2593: // reestablish this call frame
2594: enterFrame(cx, frame, ScriptRuntime.emptyArgs, true);
2595: throwable = null;
2596: } else if (!(throwable instanceof ContinuationJump)) {
2597: // It should be continuation
2598: Kit.codeBug();
2599: }
2600: }
2601:
2602: Object interpreterResult = null;
2603: double interpreterResultDbl = 0.0;
2604:
2605: StateLoop: for (;;) {
2606: withoutExceptions: try {
2607:
2608: if (throwable != null) {
2609: // Need to return both 'frame' and 'throwable' from
2610: // 'processThrowable', so just added a 'throwable'
2611: // member in 'frame'.
2612: frame = processThrowable(cx, throwable, frame,
2613: indexReg, instructionCounting);
2614: throwable = frame.throwable;
2615: frame.throwable = null;
2616: } else {
2617: if (generatorState == null && frame.frozen)
2618: Kit.codeBug();
2619: }
2620:
2621: // Use local variables for constant values in frame
2622: // for faster access
2623: Object[] stack = frame.stack;
2624: double[] sDbl = frame.sDbl;
2625: Object[] vars = frame.varSource.stack;
2626: double[] varDbls = frame.varSource.sDbl;
2627: int[] varAttributes = frame.varSource.stackAttributes;
2628: byte[] iCode = frame.idata.itsICode;
2629: String[] strings = frame.idata.itsStringTable;
2630:
2631: // Use local for stackTop as well. Since execption handlers
2632: // can only exist at statement level where stack is empty,
2633: // it is necessary to save/restore stackTop only across
2634: // function calls and normal returns.
2635: int stackTop = frame.savedStackTop;
2636:
2637: // Store new frame in cx which is used for error reporting etc.
2638: cx.lastInterpreterFrame = frame;
2639:
2640: Loop: for (;;) {
2641:
2642: // Exception handler assumes that PC is already incremented
2643: // pass the instruction start when it searches the
2644: // exception handler
2645: int op = iCode[frame.pc++];
2646: jumplessRun: {
2647:
2648: // Back indent to ease implementation reading
2649: switch (op) {
2650: case Icode_GENERATOR: {
2651: if (!frame.frozen) {
2652: // First time encountering this opcode: create new generator
2653: // object and return
2654: frame.pc--; // we want to come back here when we resume
2655: CallFrame generatorFrame = captureFrameForGenerator(frame);
2656: generatorFrame.frozen = true;
2657: NativeGenerator generator = new NativeGenerator(
2658: frame.scope,
2659: generatorFrame.fnOrScript,
2660: generatorFrame);
2661: frame.result = generator;
2662: break Loop;
2663: } else {
2664: // We are now resuming execution. Fall through to YIELD case.
2665: }
2666: }
2667: // fall through...
2668: case Token.YIELD: {
2669: if (!frame.frozen) {
2670: return freezeGenerator(cx, frame,
2671: stackTop, generatorState);
2672: } else {
2673: Object obj = thawGenerator(frame,
2674: stackTop, generatorState, op);
2675: if (obj != Scriptable.NOT_FOUND) {
2676: throwable = obj;
2677: break withoutExceptions;
2678: }
2679: continue Loop;
2680: }
2681: }
2682: case Icode_GENERATOR_END: {
2683: // throw StopIteration
2684: frame.frozen = true;
2685: int sourceLine = getIndex(iCode, frame.pc);
2686: generatorState.returnedException = new JavaScriptException(
2687: NativeIterator
2688: .getStopIterationObject(frame.scope),
2689: frame.idata.itsSourceFile,
2690: sourceLine);
2691: break Loop;
2692: }
2693: case Token.THROW: {
2694: Object value = stack[stackTop];
2695: if (value == DBL_MRK)
2696: value = ScriptRuntime
2697: .wrapNumber(sDbl[stackTop]);
2698: --stackTop;
2699:
2700: int sourceLine = getIndex(iCode, frame.pc);
2701: throwable = new JavaScriptException(value,
2702: frame.idata.itsSourceFile,
2703: sourceLine);
2704: break withoutExceptions;
2705: }
2706: case Token.RETHROW: {
2707: indexReg += frame.localShift;
2708: throwable = stack[indexReg];
2709: break withoutExceptions;
2710: }
2711: case Token.GE:
2712: case Token.LE:
2713: case Token.GT:
2714: case Token.LT: {
2715: --stackTop;
2716: Object rhs = stack[stackTop + 1];
2717: Object lhs = stack[stackTop];
2718: boolean valBln;
2719: object_compare: {
2720: number_compare: {
2721: double rDbl, lDbl;
2722: if (rhs == DBL_MRK) {
2723: rDbl = sDbl[stackTop + 1];
2724: lDbl = stack_double(frame,
2725: stackTop);
2726: } else if (lhs == DBL_MRK) {
2727: rDbl = ScriptRuntime
2728: .toNumber(rhs);
2729: lDbl = sDbl[stackTop];
2730: } else {
2731: break number_compare;
2732: }
2733: switch (op) {
2734: case Token.GE:
2735: valBln = (lDbl >= rDbl);
2736: break object_compare;
2737: case Token.LE:
2738: valBln = (lDbl <= rDbl);
2739: break object_compare;
2740: case Token.GT:
2741: valBln = (lDbl > rDbl);
2742: break object_compare;
2743: case Token.LT:
2744: valBln = (lDbl < rDbl);
2745: break object_compare;
2746: default:
2747: throw Kit.codeBug();
2748: }
2749: }
2750: switch (op) {
2751: case Token.GE:
2752: valBln = ScriptRuntime.cmp_LE(rhs,
2753: lhs);
2754: break;
2755: case Token.LE:
2756: valBln = ScriptRuntime.cmp_LE(lhs,
2757: rhs);
2758: break;
2759: case Token.GT:
2760: valBln = ScriptRuntime.cmp_LT(rhs,
2761: lhs);
2762: break;
2763: case Token.LT:
2764: valBln = ScriptRuntime.cmp_LT(lhs,
2765: rhs);
2766: break;
2767: default:
2768: throw Kit.codeBug();
2769: }
2770: }
2771: stack[stackTop] = ScriptRuntime
2772: .wrapBoolean(valBln);
2773: continue Loop;
2774: }
2775: case Token.IN:
2776: case Token.INSTANCEOF: {
2777: Object rhs = stack[stackTop];
2778: if (rhs == DBL_MRK)
2779: rhs = ScriptRuntime
2780: .wrapNumber(sDbl[stackTop]);
2781: --stackTop;
2782: Object lhs = stack[stackTop];
2783: if (lhs == DBL_MRK)
2784: lhs = ScriptRuntime
2785: .wrapNumber(sDbl[stackTop]);
2786: boolean valBln;
2787: if (op == Token.IN) {
2788: valBln = ScriptRuntime.in(lhs, rhs, cx);
2789: } else {
2790: valBln = ScriptRuntime.instanceOf(lhs,
2791: rhs, cx);
2792: }
2793: stack[stackTop] = ScriptRuntime
2794: .wrapBoolean(valBln);
2795: continue Loop;
2796: }
2797: case Token.EQ:
2798: case Token.NE: {
2799: --stackTop;
2800: boolean valBln;
2801: Object rhs = stack[stackTop + 1];
2802: Object lhs = stack[stackTop];
2803: if (rhs == DBL_MRK) {
2804: if (lhs == DBL_MRK) {
2805: valBln = (sDbl[stackTop] == sDbl[stackTop + 1]);
2806: } else {
2807: valBln = ScriptRuntime.eqNumber(
2808: sDbl[stackTop + 1], lhs);
2809: }
2810: } else {
2811: if (lhs == DBL_MRK) {
2812: valBln = ScriptRuntime.eqNumber(
2813: sDbl[stackTop], rhs);
2814: } else {
2815: valBln = ScriptRuntime.eq(lhs, rhs);
2816: }
2817: }
2818: valBln ^= (op == Token.NE);
2819: stack[stackTop] = ScriptRuntime
2820: .wrapBoolean(valBln);
2821: continue Loop;
2822: }
2823: case Token.SHEQ:
2824: case Token.SHNE: {
2825: --stackTop;
2826: Object rhs = stack[stackTop + 1];
2827: Object lhs = stack[stackTop];
2828: boolean valBln;
2829: shallow_compare: {
2830: double rdbl, ldbl;
2831: if (rhs == DBL_MRK) {
2832: rdbl = sDbl[stackTop + 1];
2833: if (lhs == DBL_MRK) {
2834: ldbl = sDbl[stackTop];
2835: } else if (lhs instanceof Number) {
2836: ldbl = ((Number) lhs)
2837: .doubleValue();
2838: } else {
2839: valBln = false;
2840: break shallow_compare;
2841: }
2842: } else if (lhs == DBL_MRK) {
2843: ldbl = sDbl[stackTop];
2844: if (rhs == DBL_MRK) {
2845: rdbl = sDbl[stackTop + 1];
2846: } else if (rhs instanceof Number) {
2847: rdbl = ((Number) rhs)
2848: .doubleValue();
2849: } else {
2850: valBln = false;
2851: break shallow_compare;
2852: }
2853: } else {
2854: valBln = ScriptRuntime.shallowEq(
2855: lhs, rhs);
2856: break shallow_compare;
2857: }
2858: valBln = (ldbl == rdbl);
2859: }
2860: valBln ^= (op == Token.SHNE);
2861: stack[stackTop] = ScriptRuntime
2862: .wrapBoolean(valBln);
2863: continue Loop;
2864: }
2865: case Token.IFNE:
2866: if (stack_boolean(frame, stackTop--)) {
2867: frame.pc += 2;
2868: continue Loop;
2869: }
2870: break jumplessRun;
2871: case Token.IFEQ:
2872: if (!stack_boolean(frame, stackTop--)) {
2873: frame.pc += 2;
2874: continue Loop;
2875: }
2876: break jumplessRun;
2877: case Icode_IFEQ_POP:
2878: if (!stack_boolean(frame, stackTop--)) {
2879: frame.pc += 2;
2880: continue Loop;
2881: }
2882: stack[stackTop--] = null;
2883: break jumplessRun;
2884: case Token.GOTO:
2885: break jumplessRun;
2886: case Icode_GOSUB:
2887: ++stackTop;
2888: stack[stackTop] = DBL_MRK;
2889: sDbl[stackTop] = frame.pc + 2;
2890: break jumplessRun;
2891: case Icode_STARTSUB:
2892: if (stackTop == frame.emptyStackTop + 1) {
2893: // Call from Icode_GOSUB: store return PC address in the local
2894: indexReg += frame.localShift;
2895: stack[indexReg] = stack[stackTop];
2896: sDbl[indexReg] = sDbl[stackTop];
2897: --stackTop;
2898: } else {
2899: // Call from exception handler: exception object is already stored
2900: // in the local
2901: if (stackTop != frame.emptyStackTop)
2902: Kit.codeBug();
2903: }
2904: continue Loop;
2905: case Icode_RETSUB: {
2906: // indexReg: local to store return address
2907: if (instructionCounting) {
2908: addInstructionCount(cx, frame, 0);
2909: }
2910: indexReg += frame.localShift;
2911: Object value = stack[indexReg];
2912: if (value != DBL_MRK) {
2913: // Invocation from exception handler, restore object to rethrow
2914: throwable = value;
2915: break withoutExceptions;
2916: }
2917: // Normal return from GOSUB
2918: frame.pc = (int) sDbl[indexReg];
2919: if (instructionCounting) {
2920: frame.pcPrevBranch = frame.pc;
2921: }
2922: continue Loop;
2923: }
2924: case Icode_POP:
2925: stack[stackTop] = null;
2926: stackTop--;
2927: continue Loop;
2928: case Icode_POP_RESULT:
2929: frame.result = stack[stackTop];
2930: frame.resultDbl = sDbl[stackTop];
2931: stack[stackTop] = null;
2932: --stackTop;
2933: continue Loop;
2934: case Icode_DUP:
2935: stack[stackTop + 1] = stack[stackTop];
2936: sDbl[stackTop + 1] = sDbl[stackTop];
2937: stackTop++;
2938: continue Loop;
2939: case Icode_DUP2:
2940: stack[stackTop + 1] = stack[stackTop - 1];
2941: sDbl[stackTop + 1] = sDbl[stackTop - 1];
2942: stack[stackTop + 2] = stack[stackTop];
2943: sDbl[stackTop + 2] = sDbl[stackTop];
2944: stackTop += 2;
2945: continue Loop;
2946: case Icode_SWAP: {
2947: Object o = stack[stackTop];
2948: stack[stackTop] = stack[stackTop - 1];
2949: stack[stackTop - 1] = o;
2950: double d = sDbl[stackTop];
2951: sDbl[stackTop] = sDbl[stackTop - 1];
2952: sDbl[stackTop - 1] = d;
2953: continue Loop;
2954: }
2955: case Token.RETURN:
2956: frame.result = stack[stackTop];
2957: frame.resultDbl = sDbl[stackTop];
2958: --stackTop;
2959: break Loop;
2960: case Token.RETURN_RESULT:
2961: break Loop;
2962: case Icode_RETUNDEF:
2963: frame.result = undefined;
2964: break Loop;
2965: case Token.BITNOT: {
2966: int rIntValue = stack_int32(frame, stackTop);
2967: stack[stackTop] = DBL_MRK;
2968: sDbl[stackTop] = ~rIntValue;
2969: continue Loop;
2970: }
2971: case Token.BITAND:
2972: case Token.BITOR:
2973: case Token.BITXOR:
2974: case Token.LSH:
2975: case Token.RSH: {
2976: int lIntValue = stack_int32(frame,
2977: stackTop - 1);
2978: int rIntValue = stack_int32(frame, stackTop);
2979: stack[--stackTop] = DBL_MRK;
2980: switch (op) {
2981: case Token.BITAND:
2982: lIntValue &= rIntValue;
2983: break;
2984: case Token.BITOR:
2985: lIntValue |= rIntValue;
2986: break;
2987: case Token.BITXOR:
2988: lIntValue ^= rIntValue;
2989: break;
2990: case Token.LSH:
2991: lIntValue <<= rIntValue;
2992: break;
2993: case Token.RSH:
2994: lIntValue >>= rIntValue;
2995: break;
2996: }
2997: sDbl[stackTop] = lIntValue;
2998: continue Loop;
2999: }
3000: case Token.URSH: {
3001: double lDbl = stack_double(frame,
3002: stackTop - 1);
3003: int rIntValue = stack_int32(frame, stackTop) & 0x1F;
3004: stack[--stackTop] = DBL_MRK;
3005: sDbl[stackTop] = ScriptRuntime
3006: .toUint32(lDbl) >>> rIntValue;
3007: continue Loop;
3008: }
3009: case Token.NEG:
3010: case Token.POS: {
3011: double rDbl = stack_double(frame, stackTop);
3012: stack[stackTop] = DBL_MRK;
3013: if (op == Token.NEG) {
3014: rDbl = -rDbl;
3015: }
3016: sDbl[stackTop] = rDbl;
3017: continue Loop;
3018: }
3019: case Token.ADD:
3020: --stackTop;
3021: do_add(stack, sDbl, stackTop, cx);
3022: continue Loop;
3023: case Token.SUB:
3024: case Token.MUL:
3025: case Token.DIV:
3026: case Token.MOD: {
3027: double rDbl = stack_double(frame, stackTop);
3028: --stackTop;
3029: double lDbl = stack_double(frame, stackTop);
3030: stack[stackTop] = DBL_MRK;
3031: switch (op) {
3032: case Token.SUB:
3033: lDbl -= rDbl;
3034: break;
3035: case Token.MUL:
3036: lDbl *= rDbl;
3037: break;
3038: case Token.DIV:
3039: lDbl /= rDbl;
3040: break;
3041: case Token.MOD:
3042: lDbl %= rDbl;
3043: break;
3044: }
3045: sDbl[stackTop] = lDbl;
3046: continue Loop;
3047: }
3048: case Token.NOT:
3049: stack[stackTop] = ScriptRuntime
3050: .wrapBoolean(!stack_boolean(frame,
3051: stackTop));
3052: continue Loop;
3053: case Token.BINDNAME:
3054: stack[++stackTop] = ScriptRuntime.bind(cx,
3055: frame.scope, stringReg);
3056: continue Loop;
3057: case Token.SETNAME: {
3058: Object rhs = stack[stackTop];
3059: if (rhs == DBL_MRK)
3060: rhs = ScriptRuntime
3061: .wrapNumber(sDbl[stackTop]);
3062: --stackTop;
3063: Scriptable lhs = (Scriptable) stack[stackTop];
3064: stack[stackTop] = ScriptRuntime.setName(
3065: lhs, rhs, cx, frame.scope,
3066: stringReg);
3067: continue Loop;
3068: }
3069: case Icode_SETCONST: {
3070: Object rhs = stack[stackTop];
3071: if (rhs == DBL_MRK)
3072: rhs = ScriptRuntime
3073: .wrapNumber(sDbl[stackTop]);
3074: --stackTop;
3075: Scriptable lhs = (Scriptable) stack[stackTop];
3076: stack[stackTop] = ScriptRuntime.setConst(
3077: lhs, rhs, cx, stringReg);
3078: continue Loop;
3079: }
3080: case Token.DELPROP: {
3081: Object rhs = stack[stackTop];
3082: if (rhs == DBL_MRK)
3083: rhs = ScriptRuntime
3084: .wrapNumber(sDbl[stackTop]);
3085: --stackTop;
3086: Object lhs = stack[stackTop];
3087: if (lhs == DBL_MRK)
3088: lhs = ScriptRuntime
3089: .wrapNumber(sDbl[stackTop]);
3090: stack[stackTop] = ScriptRuntime.delete(lhs,
3091: rhs, cx);
3092: continue Loop;
3093: }
3094: case Token.GETPROPNOWARN: {
3095: Object lhs = stack[stackTop];
3096: if (lhs == DBL_MRK)
3097: lhs = ScriptRuntime
3098: .wrapNumber(sDbl[stackTop]);
3099: stack[stackTop] = ScriptRuntime
3100: .getObjectPropNoWarn(lhs,
3101: stringReg, cx);
3102: continue Loop;
3103: }
3104: case Token.GETPROP: {
3105: Object lhs = stack[stackTop];
3106: if (lhs == DBL_MRK)
3107: lhs = ScriptRuntime
3108: .wrapNumber(sDbl[stackTop]);
3109: stack[stackTop] = ScriptRuntime
3110: .getObjectProp(lhs, stringReg, cx);
3111: continue Loop;
3112: }
3113: case Token.SETPROP: {
3114: Object rhs = stack[stackTop];
3115: if (rhs == DBL_MRK)
3116: rhs = ScriptRuntime
3117: .wrapNumber(sDbl[stackTop]);
3118: --stackTop;
3119: Object lhs = stack[stackTop];
3120: if (lhs == DBL_MRK)
3121: lhs = ScriptRuntime
3122: .wrapNumber(sDbl[stackTop]);
3123: stack[stackTop] = ScriptRuntime
3124: .setObjectProp(lhs, stringReg, rhs,
3125: cx);
3126: continue Loop;
3127: }
3128: case Icode_PROP_INC_DEC: {
3129: Object lhs = stack[stackTop];
3130: if (lhs == DBL_MRK)
3131: lhs = ScriptRuntime
3132: .wrapNumber(sDbl[stackTop]);
3133: stack[stackTop] = ScriptRuntime
3134: .propIncrDecr(lhs, stringReg, cx,
3135: iCode[frame.pc]);
3136: ++frame.pc;
3137: continue Loop;
3138: }
3139: case Token.GETELEM: {
3140: --stackTop;
3141: Object lhs = stack[stackTop];
3142: if (lhs == DBL_MRK) {
3143: lhs = ScriptRuntime
3144: .wrapNumber(sDbl[stackTop]);
3145: }
3146: Object value;
3147: Object id = stack[stackTop + 1];
3148: if (id != DBL_MRK) {
3149: value = ScriptRuntime.getObjectElem(
3150: lhs, id, cx);
3151: } else {
3152: double d = sDbl[stackTop + 1];
3153: value = ScriptRuntime.getObjectIndex(
3154: lhs, d, cx);
3155: }
3156: stack[stackTop] = value;
3157: continue Loop;
3158: }
3159: case Token.SETELEM: {
3160: stackTop -= 2;
3161: Object rhs = stack[stackTop + 2];
3162: if (rhs == DBL_MRK) {
3163: rhs = ScriptRuntime
3164: .wrapNumber(sDbl[stackTop + 2]);
3165: }
3166: Object lhs = stack[stackTop];
3167: if (lhs == DBL_MRK) {
3168: lhs = ScriptRuntime
3169: .wrapNumber(sDbl[stackTop]);
3170: }
3171: Object value;
3172: Object id = stack[stackTop + 1];
3173: if (id != DBL_MRK) {
3174: value = ScriptRuntime.setObjectElem(
3175: lhs, id, rhs, cx);
3176: } else {
3177: double d = sDbl[stackTop + 1];
3178: value = ScriptRuntime.setObjectIndex(
3179: lhs, d, rhs, cx);
3180: }
3181: stack[stackTop] = value;
3182: continue Loop;
3183: }
3184: case Icode_ELEM_INC_DEC: {
3185: Object rhs = stack[stackTop];
3186: if (rhs == DBL_MRK)
3187: rhs = ScriptRuntime
3188: .wrapNumber(sDbl[stackTop]);
3189: --stackTop;
3190: Object lhs = stack[stackTop];
3191: if (lhs == DBL_MRK)
3192: lhs = ScriptRuntime
3193: .wrapNumber(sDbl[stackTop]);
3194: stack[stackTop] = ScriptRuntime
3195: .elemIncrDecr(lhs, rhs, cx,
3196: iCode[frame.pc]);
3197: ++frame.pc;
3198: continue Loop;
3199: }
3200: case Token.GET_REF: {
3201: Ref ref = (Ref) stack[stackTop];
3202: stack[stackTop] = ScriptRuntime.refGet(ref,
3203: cx);
3204: continue Loop;
3205: }
3206: case Token.SET_REF: {
3207: Object value = stack[stackTop];
3208: if (value == DBL_MRK)
3209: value = ScriptRuntime
3210: .wrapNumber(sDbl[stackTop]);
3211: --stackTop;
3212: Ref ref = (Ref) stack[stackTop];
3213: stack[stackTop] = ScriptRuntime.refSet(ref,
3214: value, cx);
3215: continue Loop;
3216: }
3217: case Token.DEL_REF: {
3218: Ref ref = (Ref) stack[stackTop];
3219: stack[stackTop] = ScriptRuntime.refDel(ref,
3220: cx);
3221: continue Loop;
3222: }
3223: case Icode_REF_INC_DEC: {
3224: Ref ref = (Ref) stack[stackTop];
3225: stack[stackTop] = ScriptRuntime
3226: .refIncrDecr(ref, cx,
3227: iCode[frame.pc]);
3228: ++frame.pc;
3229: continue Loop;
3230: }
3231: case Token.LOCAL_LOAD:
3232: ++stackTop;
3233: indexReg += frame.localShift;
3234: stack[stackTop] = stack[indexReg];
3235: sDbl[stackTop] = sDbl[indexReg];
3236: continue Loop;
3237: case Icode_LOCAL_CLEAR:
3238: indexReg += frame.localShift;
3239: stack[indexReg] = null;
3240: continue Loop;
3241: case Icode_NAME_AND_THIS:
3242: // stringReg: name
3243: ++stackTop;
3244: stack[stackTop] = ScriptRuntime
3245: .getNameFunctionAndThis(stringReg,
3246: cx, frame.scope);
3247: ++stackTop;
3248: stack[stackTop] = ScriptRuntime
3249: .lastStoredScriptable(cx);
3250: continue Loop;
3251: case Icode_PROP_AND_THIS: {
3252: Object obj = stack[stackTop];
3253: if (obj == DBL_MRK)
3254: obj = ScriptRuntime
3255: .wrapNumber(sDbl[stackTop]);
3256: // stringReg: property
3257: stack[stackTop] = ScriptRuntime
3258: .getPropFunctionAndThis(obj,
3259: stringReg, cx);
3260: ++stackTop;
3261: stack[stackTop] = ScriptRuntime
3262: .lastStoredScriptable(cx);
3263: continue Loop;
3264: }
3265: case Icode_ELEM_AND_THIS: {
3266: Object obj = stack[stackTop - 1];
3267: if (obj == DBL_MRK)
3268: obj = ScriptRuntime
3269: .wrapNumber(sDbl[stackTop - 1]);
3270: Object id = stack[stackTop];
3271: if (id == DBL_MRK)
3272: id = ScriptRuntime
3273: .wrapNumber(sDbl[stackTop]);
3274: stack[stackTop - 1] = ScriptRuntime
3275: .getElemFunctionAndThis(obj, id, cx);
3276: stack[stackTop] = ScriptRuntime
3277: .lastStoredScriptable(cx);
3278: continue Loop;
3279: }
3280: case Icode_VALUE_AND_THIS: {
3281: Object value = stack[stackTop];
3282: if (value == DBL_MRK)
3283: value = ScriptRuntime
3284: .wrapNumber(sDbl[stackTop]);
3285: stack[stackTop] = ScriptRuntime
3286: .getValueFunctionAndThis(value, cx);
3287: ++stackTop;
3288: stack[stackTop] = ScriptRuntime
3289: .lastStoredScriptable(cx);
3290: continue Loop;
3291: }
3292: case Icode_CALLSPECIAL: {
3293: if (instructionCounting) {
3294: cx.instructionCount += INVOCATION_COST;
3295: }
3296: int callType = iCode[frame.pc] & 0xFF;
3297: boolean isNew = (iCode[frame.pc + 1] != 0);
3298: int sourceLine = getIndex(iCode,
3299: frame.pc + 2);
3300:
3301: // indexReg: number of arguments
3302: if (isNew) {
3303: // stack change: function arg0 .. argN -> newResult
3304: stackTop -= indexReg;
3305:
3306: Object function = stack[stackTop];
3307: if (function == DBL_MRK)
3308: function = ScriptRuntime
3309: .wrapNumber(sDbl[stackTop]);
3310: Object[] outArgs = getArgsArray(stack,
3311: sDbl, stackTop + 1, indexReg);
3312: stack[stackTop] = ScriptRuntime
3313: .newSpecial(cx, function,
3314: outArgs, frame.scope,
3315: callType);
3316: } else {
3317: // stack change: function thisObj arg0 .. argN -> result
3318: stackTop -= 1 + indexReg;
3319:
3320: // Call code generation ensure that stack here
3321: // is ... Callable Scriptable
3322: Scriptable functionThis = (Scriptable) stack[stackTop + 1];
3323: Callable function = (Callable) stack[stackTop];
3324: Object[] outArgs = getArgsArray(stack,
3325: sDbl, stackTop + 2, indexReg);
3326: stack[stackTop] = ScriptRuntime
3327: .callSpecial(
3328: cx,
3329: function,
3330: functionThis,
3331: outArgs,
3332: frame.scope,
3333: frame.this Obj,
3334: callType,
3335: frame.idata.itsSourceFile,
3336: sourceLine);
3337: }
3338: frame.pc += 4;
3339: continue Loop;
3340: }
3341: case Token.CALL:
3342: case Icode_TAIL_CALL:
3343: case Token.REF_CALL: {
3344: if (instructionCounting) {
3345: cx.instructionCount += INVOCATION_COST;
3346: }
3347: // stack change: function thisObj arg0 .. argN -> result
3348: // indexReg: number of arguments
3349: stackTop -= 1 + indexReg;
3350:
3351: // CALL generation ensures that fun and funThisObj
3352: // are already Scriptable and Callable objects respectively
3353: Callable fun = (Callable) stack[stackTop];
3354: Scriptable funThisObj = (Scriptable) stack[stackTop + 1];
3355: if (op == Token.REF_CALL) {
3356: Object[] outArgs = getArgsArray(stack,
3357: sDbl, stackTop + 2, indexReg);
3358: stack[stackTop] = ScriptRuntime
3359: .callRef(fun, funThisObj,
3360: outArgs, cx);
3361: continue Loop;
3362: }
3363: Scriptable calleeScope = frame.scope;
3364: if (frame.useActivation) {
3365: calleeScope = ScriptableObject
3366: .getTopLevelScope(frame.scope);
3367: }
3368: if (fun instanceof InterpretedFunction) {
3369: InterpretedFunction ifun = (InterpretedFunction) fun;
3370: if (frame.fnOrScript.securityDomain == ifun.securityDomain) {
3371: CallFrame callParentFrame = frame;
3372: CallFrame calleeFrame = new CallFrame();
3373: if (op == Icode_TAIL_CALL) {
3374: // In principle tail call can re-use the current
3375: // frame and its stack arrays but it is hard to
3376: // do properly. Any exceptions that can legally
3377: // happen during frame re-initialization including
3378: // StackOverflowException during innocent looking
3379: // System.arraycopy may leave the current frame
3380: // data corrupted leading to undefined behaviour
3381: // in the catch code bellow that unwinds JS stack
3382: // on exceptions. Then there is issue about frame release
3383: // end exceptions there.
3384: // To avoid frame allocation a released frame
3385: // can be cached for re-use which would also benefit
3386: // non-tail calls but it is not clear that this caching
3387: // would gain in performance due to potentially
3388: // bad interaction with GC.
3389: callParentFrame = frame.parentFrame;
3390: // Release the current frame. See Bug #344501 to see why
3391: // it is being done here.
3392: exitFrame(cx, frame, null);
3393: }
3394: initFrame(cx, calleeScope,
3395: funThisObj, stack, sDbl,
3396: stackTop + 2, indexReg,
3397: ifun, callParentFrame,
3398: calleeFrame);
3399: if (op != Icode_TAIL_CALL) {
3400: frame.savedStackTop = stackTop;
3401: frame.savedCallOp = op;
3402: }
3403: frame = calleeFrame;
3404: continue StateLoop;
3405: }
3406: }
3407:
3408: if (fun instanceof Continuation) {
3409: // Jump to the captured continuation
3410: ContinuationJump cjump;
3411: cjump = new ContinuationJump(
3412: (Continuation) fun, frame);
3413:
3414: // continuation result is the first argument if any
3415: // of contination call
3416: if (indexReg == 0) {
3417: cjump.result = undefined;
3418: } else {
3419: cjump.result = stack[stackTop + 2];
3420: cjump.resultDbl = sDbl[stackTop + 2];
3421: }
3422:
3423: // Start the real unwind job
3424: throwable = cjump;
3425: break withoutExceptions;
3426: }
3427:
3428: if (fun instanceof IdFunctionObject) {
3429: IdFunctionObject ifun = (IdFunctionObject) fun;
3430: if (Continuation
3431: .isContinuationConstructor(ifun)) {
3432: captureContinuation(cx, frame,
3433: stackTop);
3434: continue Loop;
3435: }
3436: // Bug 405654 -- make best effort to keep Function.apply and
3437: // Function.call within this interpreter loop invocation
3438: if (BaseFunction.isApplyOrCall(ifun)) {
3439: Callable applyCallable = ScriptRuntime
3440: .getCallable(funThisObj);
3441: if (applyCallable instanceof InterpretedFunction) {
3442: InterpretedFunction iApplyCallable = (InterpretedFunction) applyCallable;
3443: if (frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) {
3444: frame = initFrameForApplyOrCall(
3445: cx, frame,
3446: indexReg, stack,
3447: sDbl, stackTop, op,
3448: calleeScope, ifun,
3449: iApplyCallable);
3450: continue StateLoop;
3451: }
3452: }
3453: }
3454: }
3455:
3456: stack[stackTop] = fun.call(cx, calleeScope,
3457: funThisObj, getArgsArray(stack,
3458: sDbl, stackTop + 2,
3459: indexReg));
3460:
3461: continue Loop;
3462: }
3463: case Token.NEW: {
3464: if (instructionCounting) {
3465: cx.instructionCount += INVOCATION_COST;
3466: }
3467: // stack change: function arg0 .. argN -> newResult
3468: // indexReg: number of arguments
3469: stackTop -= indexReg;
3470:
3471: Object lhs = stack[stackTop];
3472: if (lhs instanceof InterpretedFunction) {
3473: InterpretedFunction f = (InterpretedFunction) lhs;
3474: if (frame.fnOrScript.securityDomain == f.securityDomain) {
3475: Scriptable newInstance = f
3476: .createObject(cx,
3477: frame.scope);
3478: CallFrame calleeFrame = new CallFrame();
3479: initFrame(cx, frame.scope,
3480: newInstance, stack, sDbl,
3481: stackTop + 1, indexReg, f,
3482: frame, calleeFrame);
3483:
3484: stack[stackTop] = newInstance;
3485: frame.savedStackTop = stackTop;
3486: frame.savedCallOp = op;
3487: frame = calleeFrame;
3488: continue StateLoop;
3489: }
3490: }
3491: if (!(lhs instanceof Function)) {
3492: if (lhs == DBL_MRK)
3493: lhs = ScriptRuntime
3494: .wrapNumber(sDbl[stackTop]);
3495: throw ScriptRuntime
3496: .notFunctionError(lhs);
3497: }
3498: Function fun = (Function) lhs;
3499:
3500: if (fun instanceof IdFunctionObject) {
3501: IdFunctionObject ifun = (IdFunctionObject) fun;
3502: if (Continuation
3503: .isContinuationConstructor(ifun)) {
3504: captureContinuation(cx, frame,
3505: stackTop);
3506: continue Loop;
3507: }
3508: }
3509:
3510: Object[] outArgs = getArgsArray(stack,
3511: sDbl, stackTop + 1, indexReg);
3512: stack[stackTop] = fun.construct(cx,
3513: frame.scope, outArgs);
3514: continue Loop;
3515: }
3516: case Token.TYPEOF: {
3517: Object lhs = stack[stackTop];
3518: if (lhs == DBL_MRK)
3519: lhs = ScriptRuntime
3520: .wrapNumber(sDbl[stackTop]);
3521: stack[stackTop] = ScriptRuntime.typeof(lhs);
3522: continue Loop;
3523: }
3524: case Icode_TYPEOFNAME:
3525: stack[++stackTop] = ScriptRuntime
3526: .typeofName(frame.scope, stringReg);
3527: continue Loop;
3528: case Token.STRING:
3529: stack[++stackTop] = stringReg;
3530: continue Loop;
3531: case Icode_SHORTNUMBER:
3532: ++stackTop;
3533: stack[stackTop] = DBL_MRK;
3534: sDbl[stackTop] = getShort(iCode, frame.pc);
3535: frame.pc += 2;
3536: continue Loop;
3537: case Icode_INTNUMBER:
3538: ++stackTop;
3539: stack[stackTop] = DBL_MRK;
3540: sDbl[stackTop] = getInt(iCode, frame.pc);
3541: frame.pc += 4;
3542: continue Loop;
3543: case Token.NUMBER:
3544: ++stackTop;
3545: stack[stackTop] = DBL_MRK;
3546: sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg];
3547: continue Loop;
3548: case Token.NAME:
3549: stack[++stackTop] = ScriptRuntime.name(cx,
3550: frame.scope, stringReg);
3551: continue Loop;
3552: case Icode_NAME_INC_DEC:
3553: stack[++stackTop] = ScriptRuntime
3554: .nameIncrDecr(frame.scope,
3555: stringReg, cx,
3556: iCode[frame.pc]);
3557: ++frame.pc;
3558: continue Loop;
3559: case Icode_SETCONSTVAR1:
3560: indexReg = iCode[frame.pc++];
3561: // fallthrough
3562: case Token.SETCONSTVAR:
3563: if (!frame.useActivation) {
3564: if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
3565: throw Context
3566: .reportRuntimeError1(
3567: "msg.var.redecl",
3568: frame.idata.argNames[indexReg]);
3569: }
3570: if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST) != 0) {
3571: vars[indexReg] = stack[stackTop];
3572: varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST;
3573: varDbls[indexReg] = sDbl[stackTop];
3574: }
3575: } else {
3576: Object val = stack[stackTop];
3577: if (val == DBL_MRK)
3578: val = ScriptRuntime
3579: .wrapNumber(sDbl[stackTop]);
3580: stringReg = frame.idata.argNames[indexReg];
3581: if (frame.scope instanceof ConstProperties) {
3582: ConstProperties cp = (ConstProperties) frame.scope;
3583: cp.putConst(stringReg, frame.scope,
3584: val);
3585: } else
3586: throw Kit.codeBug();
3587: }
3588: continue Loop;
3589: case Icode_SETVAR1:
3590: indexReg = iCode[frame.pc++];
3591: // fallthrough
3592: case Token.SETVAR:
3593: if (!frame.useActivation) {
3594: if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
3595: vars[indexReg] = stack[stackTop];
3596: varDbls[indexReg] = sDbl[stackTop];
3597: }
3598: } else {
3599: Object val = stack[stackTop];
3600: if (val == DBL_MRK)
3601: val = ScriptRuntime
3602: .wrapNumber(sDbl[stackTop]);
3603: stringReg = frame.idata.argNames[indexReg];
3604: frame.scope.put(stringReg, frame.scope,
3605: val);
3606: }
3607: continue Loop;
3608: case Icode_GETVAR1:
3609: indexReg = iCode[frame.pc++];
3610: // fallthrough
3611: case Token.GETVAR:
3612: ++stackTop;
3613: if (!frame.useActivation) {
3614: stack[stackTop] = vars[indexReg];
3615: sDbl[stackTop] = varDbls[indexReg];
3616: } else {
3617: stringReg = frame.idata.argNames[indexReg];
3618: stack[stackTop] = frame.scope.get(
3619: stringReg, frame.scope);
3620: }
3621: continue Loop;
3622: case Icode_VAR_INC_DEC: {
3623: // indexReg : varindex
3624: ++stackTop;
3625: int incrDecrMask = iCode[frame.pc];
3626: if (!frame.useActivation) {
3627: stack[stackTop] = DBL_MRK;
3628: Object varValue = vars[indexReg];
3629: double d;
3630: if (varValue == DBL_MRK) {
3631: d = varDbls[indexReg];
3632: } else {
3633: d = ScriptRuntime
3634: .toNumber(varValue);
3635: vars[indexReg] = DBL_MRK;
3636: }
3637: double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0) ? d + 1.0
3638: : d - 1.0;
3639: varDbls[indexReg] = d2;
3640: sDbl[stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2
3641: : d;
3642: } else {
3643: String varName = frame.idata.argNames[indexReg];
3644: stack[stackTop] = ScriptRuntime
3645: .nameIncrDecr(frame.scope,
3646: varName, cx,
3647: incrDecrMask);
3648: }
3649: ++frame.pc;
3650: continue Loop;
3651: }
3652: case Icode_ZERO:
3653: ++stackTop;
3654: stack[stackTop] = DBL_MRK;
3655: sDbl[stackTop] = 0;
3656: continue Loop;
3657: case Icode_ONE:
3658: ++stackTop;
3659: stack[stackTop] = DBL_MRK;
3660: sDbl[stackTop] = 1;
3661: continue Loop;
3662: case Token.NULL:
3663: stack[++stackTop] = null;
3664: continue Loop;
3665: case Token.THIS:
3666: stack[++stackTop] = frame.this Obj;
3667: continue Loop;
3668: case Token.THISFN:
3669: stack[++stackTop] = frame.fnOrScript;
3670: continue Loop;
3671: case Token.FALSE:
3672: stack[++stackTop] = Boolean.FALSE;
3673: continue Loop;
3674: case Token.TRUE:
3675: stack[++stackTop] = Boolean.TRUE;
3676: continue Loop;
3677: case Icode_UNDEF:
3678: stack[++stackTop] = undefined;
3679: continue Loop;
3680: case Token.ENTERWITH: {
3681: Object lhs = stack[stackTop];
3682: if (lhs == DBL_MRK)
3683: lhs = ScriptRuntime
3684: .wrapNumber(sDbl[stackTop]);
3685: --stackTop;
3686: frame.scope = ScriptRuntime.enterWith(lhs,
3687: cx, frame.scope);
3688: continue Loop;
3689: }
3690: case Token.LEAVEWITH:
3691: frame.scope = ScriptRuntime
3692: .leaveWith(frame.scope);
3693: continue Loop;
3694: case Token.CATCH_SCOPE: {
3695: // stack top: exception object
3696: // stringReg: name of exception variable
3697: // indexReg: local for exception scope
3698: --stackTop;
3699: indexReg += frame.localShift;
3700:
3701: boolean afterFirstScope = (frame.idata.itsICode[frame.pc] != 0);
3702: Throwable caughtException = (Throwable) stack[stackTop + 1];
3703: Scriptable lastCatchScope;
3704: if (!afterFirstScope) {
3705: lastCatchScope = null;
3706: } else {
3707: lastCatchScope = (Scriptable) stack[indexReg];
3708: }
3709: stack[indexReg] = ScriptRuntime
3710: .newCatchScope(caughtException,
3711: lastCatchScope, stringReg,
3712: cx, frame.scope);
3713: ++frame.pc;
3714: continue Loop;
3715: }
3716: case Token.ENUM_INIT_KEYS:
3717: case Token.ENUM_INIT_VALUES:
3718: case Token.ENUM_INIT_ARRAY: {
3719: Object lhs = stack[stackTop];
3720: if (lhs == DBL_MRK)
3721: lhs = ScriptRuntime
3722: .wrapNumber(sDbl[stackTop]);
3723: --stackTop;
3724: indexReg += frame.localShift;
3725: int enumType = op == Token.ENUM_INIT_KEYS ? ScriptRuntime.ENUMERATE_KEYS
3726: : op == Token.ENUM_INIT_VALUES ? ScriptRuntime.ENUMERATE_VALUES
3727: : ScriptRuntime.ENUMERATE_ARRAY;
3728: stack[indexReg] = ScriptRuntime.enumInit(
3729: lhs, cx, enumType);
3730: continue Loop;
3731: }
3732: case Token.ENUM_NEXT:
3733: case Token.ENUM_ID: {
3734: indexReg += frame.localShift;
3735: Object val = stack[indexReg];
3736: ++stackTop;
3737: stack[stackTop] = (op == Token.ENUM_NEXT) ? (Object) ScriptRuntime
3738: .enumNext(val)
3739: : (Object) ScriptRuntime.enumId(
3740: val, cx);
3741: continue Loop;
3742: }
3743: case Token.REF_SPECIAL: {
3744: //stringReg: name of special property
3745: Object obj = stack[stackTop];
3746: if (obj == DBL_MRK)
3747: obj = ScriptRuntime
3748: .wrapNumber(sDbl[stackTop]);
3749: stack[stackTop] = ScriptRuntime.specialRef(
3750: obj, stringReg, cx);
3751: continue Loop;
3752: }
3753: case Token.REF_MEMBER: {
3754: //indexReg: flags
3755: Object elem = stack[stackTop];
3756: if (elem == DBL_MRK)
3757: elem = ScriptRuntime
3758: .wrapNumber(sDbl[stackTop]);
3759: --stackTop;
3760: Object obj = stack[stackTop];
3761: if (obj == DBL_MRK)
3762: obj = ScriptRuntime
3763: .wrapNumber(sDbl[stackTop]);
3764: stack[stackTop] = ScriptRuntime.memberRef(
3765: obj, elem, cx, indexReg);
3766: continue Loop;
3767: }
3768: case Token.REF_NS_MEMBER: {
3769: //indexReg: flags
3770: Object elem = stack[stackTop];
3771: if (elem == DBL_MRK)
3772: elem = ScriptRuntime
3773: .wrapNumber(sDbl[stackTop]);
3774: --stackTop;
3775: Object ns = stack[stackTop];
3776: if (ns == DBL_MRK)
3777: ns = ScriptRuntime
3778: .wrapNumber(sDbl[stackTop]);
3779: --stackTop;
3780: Object obj = stack[stackTop];
3781: if (obj == DBL_MRK)
3782: obj = ScriptRuntime
3783: .wrapNumber(sDbl[stackTop]);
3784: stack[stackTop] = ScriptRuntime.memberRef(
3785: obj, ns, elem, cx, indexReg);
3786: continue Loop;
3787: }
3788: case Token.REF_NAME: {
3789: //indexReg: flags
3790: Object name = stack[stackTop];
3791: if (name == DBL_MRK)
3792: name = ScriptRuntime
3793: .wrapNumber(sDbl[stackTop]);
3794: stack[stackTop] = ScriptRuntime.nameRef(
3795: name, cx, frame.scope, indexReg);
3796: continue Loop;
3797: }
3798: case Token.REF_NS_NAME: {
3799: //indexReg: flags
3800: Object name = stack[stackTop];
3801: if (name == DBL_MRK)
3802: name = ScriptRuntime
3803: .wrapNumber(sDbl[stackTop]);
3804: --stackTop;
3805: Object ns = stack[stackTop];
3806: if (ns == DBL_MRK)
3807: ns = ScriptRuntime
3808: .wrapNumber(sDbl[stackTop]);
3809: stack[stackTop] = ScriptRuntime.nameRef(ns,
3810: name, cx, frame.scope, indexReg);
3811: continue Loop;
3812: }
3813: case Icode_SCOPE_LOAD:
3814: indexReg += frame.localShift;
3815: frame.scope = (Scriptable) stack[indexReg];
3816: continue Loop;
3817: case Icode_SCOPE_SAVE:
3818: indexReg += frame.localShift;
3819: stack[indexReg] = frame.scope;
3820: continue Loop;
3821: case Icode_CLOSURE_EXPR:
3822: stack[++stackTop] = InterpretedFunction
3823: .createFunction(cx, frame.scope,
3824: frame.fnOrScript, indexReg);
3825: continue Loop;
3826: case Icode_CLOSURE_STMT:
3827: initFunction(cx, frame.scope,
3828: frame.fnOrScript, indexReg);
3829: continue Loop;
3830: case Token.REGEXP:
3831: stack[++stackTop] = frame.scriptRegExps[indexReg];
3832: continue Loop;
3833: case Icode_LITERAL_NEW:
3834: // indexReg: number of values in the literal
3835: ++stackTop;
3836: stack[stackTop] = new int[indexReg];
3837: ++stackTop;
3838: stack[stackTop] = new Object[indexReg];
3839: sDbl[stackTop] = 0;
3840: continue Loop;
3841: case Icode_LITERAL_SET: {
3842: Object value = stack[stackTop];
3843: if (value == DBL_MRK)
3844: value = ScriptRuntime
3845: .wrapNumber(sDbl[stackTop]);
3846: --stackTop;
3847: int i = (int) sDbl[stackTop];
3848: ((Object[]) stack[stackTop])[i] = value;
3849: sDbl[stackTop] = i + 1;
3850: continue Loop;
3851: }
3852: case Icode_LITERAL_GETTER: {
3853: Object value = stack[stackTop];
3854: --stackTop;
3855: int i = (int) sDbl[stackTop];
3856: ((Object[]) stack[stackTop])[i] = value;
3857: ((int[]) stack[stackTop - 1])[i] = -1;
3858: sDbl[stackTop] = i + 1;
3859: continue Loop;
3860: }
3861: case Icode_LITERAL_SETTER: {
3862: Object value = stack[stackTop];
3863: --stackTop;
3864: int i = (int) sDbl[stackTop];
3865: ((Object[]) stack[stackTop])[i] = value;
3866: ((int[]) stack[stackTop - 1])[i] = +1;
3867: sDbl[stackTop] = i + 1;
3868: continue Loop;
3869: }
3870: case Token.ARRAYLIT:
3871: case Icode_SPARE_ARRAYLIT:
3872: case Token.OBJECTLIT: {
3873: Object[] data = (Object[]) stack[stackTop];
3874: --stackTop;
3875: int[] getterSetters = (int[]) stack[stackTop];
3876: Object val;
3877: if (op == Token.OBJECTLIT) {
3878: Object[] ids = (Object[]) frame.idata.literalIds[indexReg];
3879: val = ScriptRuntime.newObjectLiteral(
3880: ids, data, getterSetters, cx,
3881: frame.scope);
3882: } else {
3883: int[] skipIndexces = null;
3884: if (op == Icode_SPARE_ARRAYLIT) {
3885: skipIndexces = (int[]) frame.idata.literalIds[indexReg];
3886: }
3887: val = ScriptRuntime.newArrayLiteral(
3888: data, skipIndexces, cx,
3889: frame.scope);
3890: }
3891: stack[stackTop] = val;
3892: continue Loop;
3893: }
3894: case Icode_ENTERDQ: {
3895: Object lhs = stack[stackTop];
3896: if (lhs == DBL_MRK)
3897: lhs = ScriptRuntime
3898: .wrapNumber(sDbl[stackTop]);
3899: --stackTop;
3900: frame.scope = ScriptRuntime.enterDotQuery(
3901: lhs, frame.scope);
3902: continue Loop;
3903: }
3904: case Icode_LEAVEDQ: {
3905: boolean valBln = stack_boolean(frame,
3906: stackTop);
3907: Object x = ScriptRuntime.updateDotQuery(
3908: valBln, frame.scope);
3909: if (x != null) {
3910: stack[stackTop] = x;
3911: frame.scope = ScriptRuntime
3912: .leaveDotQuery(frame.scope);
3913: frame.pc += 2;
3914: continue Loop;
3915: }
3916: // reset stack and PC to code after ENTERDQ
3917: --stackTop;
3918: break jumplessRun;
3919: }
3920: case Token.DEFAULTNAMESPACE: {
3921: Object value = stack[stackTop];
3922: if (value == DBL_MRK)
3923: value = ScriptRuntime
3924: .wrapNumber(sDbl[stackTop]);
3925: stack[stackTop] = ScriptRuntime
3926: .setDefaultNamespace(value, cx);
3927: continue Loop;
3928: }
3929: case Token.ESCXMLATTR: {
3930: Object value = stack[stackTop];
3931: if (value != DBL_MRK) {
3932: stack[stackTop] = ScriptRuntime
3933: .escapeAttributeValue(value, cx);
3934: }
3935: continue Loop;
3936: }
3937: case Token.ESCXMLTEXT: {
3938: Object value = stack[stackTop];
3939: if (value != DBL_MRK) {
3940: stack[stackTop] = ScriptRuntime
3941: .escapeTextValue(value, cx);
3942: }
3943: continue Loop;
3944: }
3945: case Icode_DEBUGGER:
3946: if (frame.debuggerFrame != null) {
3947: frame.debuggerFrame
3948: .onDebuggerStatement(cx);
3949: }
3950: break Loop;
3951: case Icode_LINE:
3952: frame.pcSourceLineStart = frame.pc;
3953: if (frame.debuggerFrame != null) {
3954: int line = getIndex(iCode, frame.pc);
3955: frame.debuggerFrame.onLineChange(cx,
3956: line);
3957: }
3958: frame.pc += 2;
3959: continue Loop;
3960: case Icode_REG_IND_C0:
3961: indexReg = 0;
3962: continue Loop;
3963: case Icode_REG_IND_C1:
3964: indexReg = 1;
3965: continue Loop;
3966: case Icode_REG_IND_C2:
3967: indexReg = 2;
3968: continue Loop;
3969: case Icode_REG_IND_C3:
3970: indexReg = 3;
3971: continue Loop;
3972: case Icode_REG_IND_C4:
3973: indexReg = 4;
3974: continue Loop;
3975: case Icode_REG_IND_C5:
3976: indexReg = 5;
3977: continue Loop;
3978: case Icode_REG_IND1:
3979: indexReg = 0xFF & iCode[frame.pc];
3980: ++frame.pc;
3981: continue Loop;
3982: case Icode_REG_IND2:
3983: indexReg = getIndex(iCode, frame.pc);
3984: frame.pc += 2;
3985: continue Loop;
3986: case Icode_REG_IND4:
3987: indexReg = getInt(iCode, frame.pc);
3988: frame.pc += 4;
3989: continue Loop;
3990: case Icode_REG_STR_C0:
3991: stringReg = strings[0];
3992: continue Loop;
3993: case Icode_REG_STR_C1:
3994: stringReg = strings[1];
3995: continue Loop;
3996: case Icode_REG_STR_C2:
3997: stringReg = strings[2];
3998: continue Loop;
3999: case Icode_REG_STR_C3:
4000: stringReg = strings[3];
4001: continue Loop;
4002: case Icode_REG_STR1:
4003: stringReg = strings[0xFF & iCode[frame.pc]];
4004: ++frame.pc;
4005: continue Loop;
4006: case Icode_REG_STR2:
4007: stringReg = strings[getIndex(iCode,
4008: frame.pc)];
4009: frame.pc += 2;
4010: continue Loop;
4011: case Icode_REG_STR4:
4012: stringReg = strings[getInt(iCode, frame.pc)];
4013: frame.pc += 4;
4014: continue Loop;
4015: default:
4016: dumpICode(frame.idata);
4017: throw new RuntimeException(
4018: "Unknown icode : " + op
4019: + " @ pc : "
4020: + (frame.pc - 1));
4021: } // end of interpreter switch
4022:
4023: } // end of jumplessRun label block
4024:
4025: // This should be reachable only for jump implementation
4026: // when pc points to encoded target offset
4027: if (instructionCounting) {
4028: addInstructionCount(cx, frame, 2);
4029: }
4030: int offset = getShort(iCode, frame.pc);
4031: if (offset != 0) {
4032: // -1 accounts for pc pointing to jump opcode + 1
4033: frame.pc += offset - 1;
4034: } else {
4035: frame.pc = frame.idata.longJumps
4036: .getExistingInt(frame.pc);
4037: }
4038: if (instructionCounting) {
4039: frame.pcPrevBranch = frame.pc;
4040: }
4041: continue Loop;
4042:
4043: } // end of Loop: for
4044:
4045: exitFrame(cx, frame, null);
4046: interpreterResult = frame.result;
4047: interpreterResultDbl = frame.resultDbl;
4048: if (frame.parentFrame != null) {
4049: frame = frame.parentFrame;
4050: if (frame.frozen) {
4051: frame = frame.cloneFrozen();
4052: }
4053: setCallResult(frame, interpreterResult,
4054: interpreterResultDbl);
4055: interpreterResult = null; // Help GC
4056: continue StateLoop;
4057: }
4058: break StateLoop;
4059:
4060: } // end of interpreter withoutExceptions: try
4061: catch (Throwable ex) {
4062: if (throwable != null) {
4063: // This is serious bug and it is better to track it ASAP
4064: ex.printStackTrace(System.err);
4065: throw new IllegalStateException();
4066: }
4067: throwable = ex;
4068: }
4069:
4070: // This should be reachable only after above catch or from
4071: // finally when it needs to propagate exception or from
4072: // explicit throw
4073: if (throwable == null)
4074: Kit.codeBug();
4075:
4076: // Exception type
4077: final int EX_CATCH_STATE = 2; // Can execute JS catch
4078: final int EX_FINALLY_STATE = 1; // Can execute JS finally
4079: final int EX_NO_JS_STATE = 0; // Terminate JS execution
4080:
4081: int exState;
4082: ContinuationJump cjump = null;
4083:
4084: if (generatorState != null
4085: && generatorState.operation == NativeGenerator.GENERATOR_CLOSE
4086: && throwable == generatorState.value) {
4087: exState = EX_FINALLY_STATE;
4088: } else if (throwable instanceof JavaScriptException) {
4089: exState = EX_CATCH_STATE;
4090: } else if (throwable instanceof EcmaError) {
4091: // an offical ECMA error object,
4092: exState = EX_CATCH_STATE;
4093: } else if (throwable instanceof EvaluatorException) {
4094: exState = EX_CATCH_STATE;
4095: } else if (throwable instanceof RuntimeException) {
4096: exState = cx
4097: .hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) ? EX_CATCH_STATE
4098: : EX_FINALLY_STATE;
4099: } else if (throwable instanceof Error) {
4100: exState = cx
4101: .hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) ? EX_CATCH_STATE
4102: : EX_NO_JS_STATE;
4103: } else if (throwable instanceof ContinuationJump) {
4104: // It must be ContinuationJump
4105: exState = EX_FINALLY_STATE;
4106: cjump = (ContinuationJump) throwable;
4107: } else {
4108: exState = cx
4109: .hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) ? EX_CATCH_STATE
4110: : EX_FINALLY_STATE;
4111: }
4112:
4113: if (instructionCounting) {
4114: try {
4115: addInstructionCount(cx, frame, EXCEPTION_COST);
4116: } catch (RuntimeException ex) {
4117: throwable = ex;
4118: exState = EX_FINALLY_STATE;
4119: } catch (Error ex) {
4120: // Error from instruction counting
4121: // => unconditionally terminate JS
4122: throwable = ex;
4123: cjump = null;
4124: exState = EX_NO_JS_STATE;
4125: }
4126: }
4127: if (frame.debuggerFrame != null
4128: && throwable instanceof RuntimeException) {
4129: // Call debugger only for RuntimeException
4130: RuntimeException rex = (RuntimeException) throwable;
4131: try {
4132: frame.debuggerFrame.onExceptionThrown(cx, rex);
4133: } catch (Throwable ex) {
4134: // Any exception from debugger
4135: // => unconditionally terminate JS
4136: throwable = ex;
4137: cjump = null;
4138: exState = EX_NO_JS_STATE;
4139: }
4140: }
4141:
4142: for (;;) {
4143: if (exState != EX_NO_JS_STATE) {
4144: boolean onlyFinally = (exState != EX_CATCH_STATE);
4145: indexReg = getExceptionHandler(frame, onlyFinally);
4146: if (indexReg >= 0) {
4147: // We caught an exception, restart the loop
4148: // with exception pending the processing at the loop
4149: // start
4150: continue StateLoop;
4151: }
4152: }
4153: // No allowed exception handlers in this frame, unwind
4154: // to parent and try to look there
4155:
4156: exitFrame(cx, frame, throwable);
4157:
4158: frame = frame.parentFrame;
4159: if (frame == null) {
4160: break;
4161: }
4162: if (cjump != null && cjump.branchFrame == frame) {
4163: // Continuation branch point was hit,
4164: // restart the state loop to reenter continuation
4165: indexReg = -1;
4166: continue StateLoop;
4167: }
4168: }
4169:
4170: // No more frames, rethrow the exception or deal with continuation
4171: if (cjump != null) {
4172: if (cjump.branchFrame != null) {
4173: // The above loop should locate the top frame
4174: Kit.codeBug();
4175: }
4176: if (cjump.capturedFrame != null) {
4177: // Restarting detached continuation
4178: indexReg = -1;
4179: continue StateLoop;
4180: }
4181: // Return continuation result to the caller
4182: interpreterResult = cjump.result;
4183: interpreterResultDbl = cjump.resultDbl;
4184: throwable = null;
4185: }
4186: break StateLoop;
4187:
4188: } // end of StateLoop: for(;;)
4189:
4190: // Do cleanups/restorations before the final return or throw
4191:
4192: if (cx.previousInterpreterInvocations != null
4193: && cx.previousInterpreterInvocations.size() != 0) {
4194: cx.lastInterpreterFrame = cx.previousInterpreterInvocations
4195: .pop();
4196: } else {
4197: // It was the last interpreter frame on the stack
4198: cx.lastInterpreterFrame = null;
4199: // Force GC of the value cx.previousInterpreterInvocations
4200: cx.previousInterpreterInvocations = null;
4201: }
4202:
4203: if (throwable != null) {
4204: if (throwable instanceof RuntimeException) {
4205: throw (RuntimeException) throwable;
4206: } else {
4207: // Must be instance of Error or code bug
4208: throw (Error) throwable;
4209: }
4210: }
4211:
4212: return (interpreterResult != DBL_MRK) ? interpreterResult
4213: : ScriptRuntime.wrapNumber(interpreterResultDbl);
4214: }
4215:
4216: private static CallFrame processThrowable(Context cx,
4217: Object throwable, CallFrame frame, int indexReg,
4218: boolean instructionCounting) {
4219: // Recovering from exception, indexReg contains
4220: // the index of handler
4221:
4222: if (indexReg >= 0) {
4223: // Normal exception handler, transfer
4224: // control appropriately
4225:
4226: if (frame.frozen) {
4227: // XXX Deal with exceptios!!!
4228: frame = frame.cloneFrozen();
4229: }
4230:
4231: int[] table = frame.idata.itsExceptionTable;
4232:
4233: frame.pc = table[indexReg + EXCEPTION_HANDLER_SLOT];
4234: if (instructionCounting) {
4235: frame.pcPrevBranch = frame.pc;
4236: }
4237:
4238: frame.savedStackTop = frame.emptyStackTop;
4239: int scopeLocal = frame.localShift
4240: + table[indexReg + EXCEPTION_SCOPE_SLOT];
4241: int exLocal = frame.localShift
4242: + table[indexReg + EXCEPTION_LOCAL_SLOT];
4243: frame.scope = (Scriptable) frame.stack[scopeLocal];
4244: frame.stack[exLocal] = throwable;
4245:
4246: throwable = null;
4247: } else {
4248: // Continuation restoration
4249: ContinuationJump cjump = (ContinuationJump) throwable;
4250:
4251: // Clear throwable to indicate that exceptions are OK
4252: throwable = null;
4253:
4254: if (cjump.branchFrame != frame)
4255: Kit.codeBug();
4256:
4257: // Check that we have at least one frozen frame
4258: // in the case of detached continuation restoration:
4259: // unwind code ensure that
4260: if (cjump.capturedFrame == null)
4261: Kit.codeBug();
4262:
4263: // Need to rewind branchFrame, capturedFrame
4264: // and all frames in between
4265: int rewindCount = cjump.capturedFrame.frameIndex + 1;
4266: if (cjump.branchFrame != null) {
4267: rewindCount -= cjump.branchFrame.frameIndex;
4268: }
4269:
4270: int enterCount = 0;
4271: CallFrame[] enterFrames = null;
4272:
4273: CallFrame x = cjump.capturedFrame;
4274: for (int i = 0; i != rewindCount; ++i) {
4275: if (!x.frozen)
4276: Kit.codeBug();
4277: if (isFrameEnterExitRequired(x)) {
4278: if (enterFrames == null) {
4279: // Allocate enough space to store the rest
4280: // of rewind frames in case all of them
4281: // would require to enter
4282: enterFrames = new CallFrame[rewindCount - i];
4283: }
4284: enterFrames[enterCount] = x;
4285: ++enterCount;
4286: }
4287: x = x.parentFrame;
4288: }
4289:
4290: while (enterCount != 0) {
4291: // execute enter: walk enterFrames in the reverse
4292: // order since they were stored starting from
4293: // the capturedFrame, not branchFrame
4294: --enterCount;
4295: x = enterFrames[enterCount];
4296: enterFrame(cx, x, ScriptRuntime.emptyArgs, true);
4297: }
4298:
4299: // Continuation jump is almost done: capturedFrame
4300: // points to the call to the function that captured
4301: // continuation, so clone capturedFrame and
4302: // emulate return that function with the suplied result
4303: frame = cjump.capturedFrame.cloneFrozen();
4304: setCallResult(frame, cjump.result, cjump.resultDbl);
4305: // restart the execution
4306: }
4307: frame.throwable = throwable;
4308: return frame;
4309: }
4310:
4311: private static Object freezeGenerator(Context cx, CallFrame frame,
4312: int stackTop, GeneratorState generatorState) {
4313: if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
4314: // Error: no yields when generator is closing
4315: throw ScriptRuntime.typeError0("msg.yield.closing");
4316: }
4317: // return to our caller (which should be a method of NativeGenerator)
4318: frame.frozen = true;
4319: frame.result = frame.stack[stackTop];
4320: frame.resultDbl = frame.sDbl[stackTop];
4321: frame.savedStackTop = stackTop;
4322: frame.pc--; // we want to come back here when we resume
4323: ScriptRuntime.exitActivationFunction(cx);
4324: return (frame.result != UniqueTag.DOUBLE_MARK) ? frame.result
4325: : ScriptRuntime.wrapNumber(frame.resultDbl);
4326: }
4327:
4328: private static Object thawGenerator(CallFrame frame, int stackTop,
4329: GeneratorState generatorState, int op) {
4330: // we are resuming execution
4331: frame.frozen = false;
4332: int sourceLine = getIndex(frame.idata.itsICode, frame.pc);
4333: frame.pc += 2; // skip line number data
4334: if (generatorState.operation == NativeGenerator.GENERATOR_THROW) {
4335: // processing a call to <generator>.throw(exception): must
4336: // act as if exception was thrown from resumption point
4337: return new JavaScriptException(generatorState.value,
4338: frame.idata.itsSourceFile, sourceLine);
4339: }
4340: if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
4341: return generatorState.value;
4342: }
4343: if (generatorState.operation != NativeGenerator.GENERATOR_SEND)
4344: throw Kit.codeBug();
4345: if (op == Token.YIELD)
4346: frame.stack[stackTop] = generatorState.value;
4347: return Scriptable.NOT_FOUND;
4348: }
4349:
4350: private static CallFrame initFrameForApplyOrCall(Context cx,
4351: CallFrame frame, int indexReg, Object[] stack,
4352: double[] sDbl, int stackTop, int op,
4353: Scriptable calleeScope, IdFunctionObject ifun,
4354: InterpretedFunction iApplyCallable) {
4355: Scriptable applyThis;
4356: if (indexReg != 0) {
4357: applyThis = ScriptRuntime.toObjectOrNull(cx,
4358: stack[stackTop + 2]);
4359: } else {
4360: applyThis = null;
4361: }
4362: if (applyThis == null) {
4363: // This covers the case of args[0] == (null|undefined) as well.
4364: applyThis = ScriptRuntime.getTopCallScope(cx);
4365: }
4366: if (op == Icode_TAIL_CALL) {
4367: exitFrame(cx, frame, null);
4368: frame = frame.parentFrame;
4369: } else {
4370: frame.savedStackTop = stackTop;
4371: frame.savedCallOp = op;
4372: }
4373: CallFrame calleeFrame = new CallFrame();
4374: if (BaseFunction.isApply(ifun)) {
4375: Object[] callArgs = indexReg < 2 ? ScriptRuntime.emptyArgs
4376: : ScriptRuntime.getApplyArguments(cx,
4377: stack[stackTop + 3]);
4378: initFrame(cx, calleeScope, applyThis, callArgs, null, 0,
4379: callArgs.length, iApplyCallable, frame, calleeFrame);
4380: } else {
4381: // Shift args left
4382: for (int i = 1; i < indexReg; ++i) {
4383: stack[stackTop + 1 + i] = stack[stackTop + 2 + i];
4384: sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i];
4385: }
4386: int argCount = indexReg < 2 ? 0 : indexReg - 1;
4387: initFrame(cx, calleeScope, applyThis, stack, sDbl,
4388: stackTop + 2, argCount, iApplyCallable, frame,
4389: calleeFrame);
4390: }
4391:
4392: frame = calleeFrame;
4393: return frame;
4394: }
4395:
4396: private static void initFrame(Context cx, Scriptable callerScope,
4397: Scriptable this Obj, Object[] args, double[] argsDbl,
4398: int argShift, int argCount, InterpretedFunction fnOrScript,
4399: CallFrame parentFrame, CallFrame frame) {
4400: InterpreterData idata = fnOrScript.idata;
4401:
4402: boolean useActivation = idata.itsNeedsActivation;
4403: DebugFrame debuggerFrame = null;
4404: if (cx.debugger != null) {
4405: debuggerFrame = cx.debugger.getFrame(cx, idata);
4406: if (debuggerFrame != null) {
4407: useActivation = true;
4408: }
4409: }
4410:
4411: if (useActivation) {
4412: // Copy args to new array to pass to enterActivationFunction
4413: // or debuggerFrame.onEnter
4414: if (argsDbl != null) {
4415: args = getArgsArray(args, argsDbl, argShift, argCount);
4416: }
4417: argShift = 0;
4418: argsDbl = null;
4419: }
4420:
4421: Scriptable scope;
4422: if (idata.itsFunctionType != 0) {
4423: if (!idata.useDynamicScope) {
4424: scope = fnOrScript.getParentScope();
4425: } else {
4426: scope = callerScope;
4427: }
4428:
4429: if (useActivation) {
4430: scope = ScriptRuntime.createFunctionActivation(
4431: fnOrScript, scope, args);
4432: }
4433: } else {
4434: scope = callerScope;
4435: ScriptRuntime.initScript(fnOrScript, this Obj, cx, scope,
4436: fnOrScript.idata.evalScriptFlag);
4437: }
4438:
4439: if (idata.itsNestedFunctions != null) {
4440: if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation)
4441: Kit.codeBug();
4442: for (int i = 0; i < idata.itsNestedFunctions.length; i++) {
4443: InterpreterData fdata = idata.itsNestedFunctions[i];
4444: if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) {
4445: initFunction(cx, scope, fnOrScript, i);
4446: }
4447: }
4448: }
4449:
4450: Scriptable[] scriptRegExps = null;
4451: if (idata.itsRegExpLiterals != null) {
4452: // Wrapped regexps for functions are stored in
4453: // InterpretedFunction
4454: // but for script which should not contain references to scope
4455: // the regexps re-wrapped during each script execution
4456: if (idata.itsFunctionType != 0) {
4457: scriptRegExps = fnOrScript.functionRegExps;
4458: } else {
4459: scriptRegExps = fnOrScript.createRegExpWraps(cx, scope);
4460: }
4461: }
4462:
4463: // Initialize args, vars, locals and stack
4464:
4465: int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1;
4466: int maxFrameArray = idata.itsMaxFrameArray;
4467: if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1)
4468: Kit.codeBug();
4469:
4470: Object[] stack;
4471: int[] stackAttributes;
4472: double[] sDbl;
4473: boolean stackReuse;
4474: if (frame.stack != null && maxFrameArray <= frame.stack.length) {
4475: // Reuse stacks from old frame
4476: stackReuse = true;
4477: stack = frame.stack;
4478: stackAttributes = frame.stackAttributes;
4479: sDbl = frame.sDbl;
4480: } else {
4481: stackReuse = false;
4482: stack = new Object[maxFrameArray];
4483: stackAttributes = new int[maxFrameArray];
4484: sDbl = new double[maxFrameArray];
4485: }
4486:
4487: int varCount = idata.getParamAndVarCount();
4488: for (int i = 0; i < varCount; i++) {
4489: if (idata.getParamOrVarConst(i))
4490: stackAttributes[i] = ScriptableObject.CONST;
4491: }
4492: int definedArgs = idata.argCount;
4493: if (definedArgs > argCount) {
4494: definedArgs = argCount;
4495: }
4496:
4497: // Fill the frame structure
4498:
4499: frame.parentFrame = parentFrame;
4500: frame.frameIndex = (parentFrame == null) ? 0
4501: : parentFrame.frameIndex + 1;
4502: if (frame.frameIndex > cx.getMaximumInterpreterStackDepth()) {
4503: throw Context
4504: .reportRuntimeError("Exceeded maximum stack depth");
4505: }
4506: frame.frozen = false;
4507:
4508: frame.fnOrScript = fnOrScript;
4509: frame.idata = idata;
4510:
4511: frame.stack = stack;
4512: frame.stackAttributes = stackAttributes;
4513: frame.sDbl = sDbl;
4514: frame.varSource = frame;
4515: frame.localShift = idata.itsMaxVars;
4516: frame.emptyStackTop = emptyStackTop;
4517:
4518: frame.debuggerFrame = debuggerFrame;
4519: frame.useActivation = useActivation;
4520:
4521: frame.this Obj = this Obj;
4522: frame.scriptRegExps = scriptRegExps;
4523:
4524: // Initialize initial values of variables that change during
4525: // interpretation.
4526: frame.result = Undefined.instance;
4527: frame.pc = 0;
4528: frame.pcPrevBranch = 0;
4529: frame.pcSourceLineStart = idata.firstLinePC;
4530: frame.scope = scope;
4531:
4532: frame.savedStackTop = emptyStackTop;
4533: frame.savedCallOp = 0;
4534:
4535: System.arraycopy(args, argShift, stack, 0, definedArgs);
4536: if (argsDbl != null) {
4537: System.arraycopy(argsDbl, argShift, sDbl, 0, definedArgs);
4538: }
4539: for (int i = definedArgs; i != idata.itsMaxVars; ++i) {
4540: stack[i] = Undefined.instance;
4541: }
4542: if (stackReuse) {
4543: // Clean the stack part and space beyond stack if any
4544: // of the old array to allow to GC objects there
4545: for (int i = emptyStackTop + 1; i != stack.length; ++i) {
4546: stack[i] = null;
4547: }
4548: }
4549:
4550: enterFrame(cx, frame, args, false);
4551: }
4552:
4553: private static boolean isFrameEnterExitRequired(CallFrame frame) {
4554: return frame.debuggerFrame != null
4555: || frame.idata.itsNeedsActivation;
4556: }
4557:
4558: private static void enterFrame(Context cx, CallFrame frame,
4559: Object[] args, boolean continuationRestart) {
4560: boolean usesActivation = frame.idata.itsNeedsActivation;
4561: boolean isDebugged = frame.debuggerFrame != null;
4562: if (usesActivation || isDebugged) {
4563: Scriptable scope = frame.scope;
4564: if (scope == null) {
4565: Kit.codeBug();
4566: } else if (continuationRestart) {
4567: // Walk the parent chain of frame.scope until a NativeCall is
4568: // found. Normally, frame.scope is a NativeCall when called
4569: // from initFrame() for a debugged or activatable function.
4570: // However, when called from interpretLoop() as part of
4571: // restarting a continuation, it can also be a NativeWith if
4572: // the continuation was captured within a "with" or "catch"
4573: // block ("catch" implicitly uses NativeWith to create a scope
4574: // to expose the exception variable).
4575: for (;;) {
4576: if (scope instanceof NativeWith) {
4577: scope = scope.getParentScope();
4578: if (scope == null
4579: || (frame.parentFrame != null && frame.parentFrame.scope == scope)) {
4580: // If we get here, we didn't find a NativeCall in
4581: // the call chain before reaching parent frame's
4582: // scope. This should not be possible.
4583: Kit.codeBug();
4584: break; // Never reached, but keeps the static analyzer happy about "scope" not being null 5 lines above.
4585: }
4586: } else {
4587: break;
4588: }
4589: }
4590: }
4591: if (isDebugged) {
4592: frame.debuggerFrame.onEnter(cx, scope, frame.this Obj,
4593: args);
4594: }
4595: // Enter activation only when itsNeedsActivation true,
4596: // since debugger should not interfere with activation
4597: // chaining
4598: if (usesActivation) {
4599: ScriptRuntime.enterActivationFunction(cx, scope);
4600: }
4601: }
4602: }
4603:
4604: private static void exitFrame(Context cx, CallFrame frame,
4605: Object throwable) {
4606: if (frame.idata.itsNeedsActivation) {
4607: ScriptRuntime.exitActivationFunction(cx);
4608: }
4609:
4610: if (frame.debuggerFrame != null) {
4611: try {
4612: if (throwable instanceof Throwable) {
4613: frame.debuggerFrame.onExit(cx, true, throwable);
4614: } else {
4615: Object result;
4616: ContinuationJump cjump = (ContinuationJump) throwable;
4617: if (cjump == null) {
4618: result = frame.result;
4619: } else {
4620: result = cjump.result;
4621: }
4622: if (result == UniqueTag.DOUBLE_MARK) {
4623: double resultDbl;
4624: if (cjump == null) {
4625: resultDbl = frame.resultDbl;
4626: } else {
4627: resultDbl = cjump.resultDbl;
4628: }
4629: result = ScriptRuntime.wrapNumber(resultDbl);
4630: }
4631: frame.debuggerFrame.onExit(cx, false, result);
4632: }
4633: } catch (Throwable ex) {
4634: System.err
4635: .println("RHINO USAGE WARNING: onExit terminated with exception");
4636: ex.printStackTrace(System.err);
4637: }
4638: }
4639: }
4640:
4641: private static void setCallResult(CallFrame frame,
4642: Object callResult, double callResultDbl) {
4643: if (frame.savedCallOp == Token.CALL) {
4644: frame.stack[frame.savedStackTop] = callResult;
4645: frame.sDbl[frame.savedStackTop] = callResultDbl;
4646: } else if (frame.savedCallOp == Token.NEW) {
4647: // If construct returns scriptable,
4648: // then it replaces on stack top saved original instance
4649: // of the object.
4650: if (callResult instanceof Scriptable) {
4651: frame.stack[frame.savedStackTop] = callResult;
4652: }
4653: } else {
4654: Kit.codeBug();
4655: }
4656: frame.savedCallOp = 0;
4657: }
4658:
4659: private static void captureContinuation(Context cx,
4660: CallFrame frame, int stackTop) {
4661: Continuation c = new Continuation();
4662: ScriptRuntime.setObjectProtoAndParent(c, ScriptRuntime
4663: .getTopCallScope(cx));
4664:
4665: // Make sure that all frames upstack frames are frozen
4666: CallFrame x = frame.parentFrame;
4667: while (x != null && !x.frozen) {
4668: x.frozen = true;
4669: // Allow to GC unused stack space
4670: for (int i = x.savedStackTop + 1; i != x.stack.length; ++i) {
4671: // Allow to GC unused stack space
4672: x.stack[i] = null;
4673: x.stackAttributes[i] = ScriptableObject.EMPTY;
4674: }
4675: if (x.savedCallOp == Token.CALL) {
4676: // the call will always overwrite the stack top with the result
4677: x.stack[x.savedStackTop] = null;
4678: } else {
4679: if (x.savedCallOp != Token.NEW)
4680: Kit.codeBug();
4681: // the new operator uses stack top to store the constructed
4682: // object so it shall not be cleared: see comments in
4683: // setCallResult
4684: }
4685: x = x.parentFrame;
4686: }
4687:
4688: c.initImplementation(frame.parentFrame);
4689: frame.stack[stackTop] = c;
4690: }
4691:
4692: private static int stack_int32(CallFrame frame, int i) {
4693: Object x = frame.stack[i];
4694: double value;
4695: if (x == UniqueTag.DOUBLE_MARK) {
4696: value = frame.sDbl[i];
4697: } else {
4698: value = ScriptRuntime.toNumber(x);
4699: }
4700: return ScriptRuntime.toInt32(value);
4701: }
4702:
4703: private static double stack_double(CallFrame frame, int i) {
4704: Object x = frame.stack[i];
4705: if (x != UniqueTag.DOUBLE_MARK) {
4706: return ScriptRuntime.toNumber(x);
4707: } else {
4708: return frame.sDbl[i];
4709: }
4710: }
4711:
4712: private static boolean stack_boolean(CallFrame frame, int i) {
4713: Object x = frame.stack[i];
4714: if (x == Boolean.TRUE) {
4715: return true;
4716: } else if (x == Boolean.FALSE) {
4717: return false;
4718: } else if (x == UniqueTag.DOUBLE_MARK) {
4719: double d = frame.sDbl[i];
4720: return d == d && d != 0.0;
4721: } else if (x == null || x == Undefined.instance) {
4722: return false;
4723: } else if (x instanceof Number) {
4724: double d = ((Number) x).doubleValue();
4725: return (d == d && d != 0.0);
4726: } else if (x instanceof Boolean) {
4727: return ((Boolean) x).booleanValue();
4728: } else {
4729: return ScriptRuntime.toBoolean(x);
4730: }
4731: }
4732:
4733: private static void do_add(Object[] stack, double[] sDbl,
4734: int stackTop, Context cx) {
4735: Object rhs = stack[stackTop + 1];
4736: Object lhs = stack[stackTop];
4737: double d;
4738: boolean leftRightOrder;
4739: if (rhs == UniqueTag.DOUBLE_MARK) {
4740: d = sDbl[stackTop + 1];
4741: if (lhs == UniqueTag.DOUBLE_MARK) {
4742: sDbl[stackTop] += d;
4743: return;
4744: }
4745: leftRightOrder = true;
4746: // fallthrough to object + number code
4747: } else if (lhs == UniqueTag.DOUBLE_MARK) {
4748: d = sDbl[stackTop];
4749: lhs = rhs;
4750: leftRightOrder = false;
4751: // fallthrough to object + number code
4752: } else {
4753: if (lhs instanceof Scriptable || rhs instanceof Scriptable) {
4754: stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx);
4755: } else if (lhs instanceof String) {
4756: String lstr = (String) lhs;
4757: String rstr = ScriptRuntime.toString(rhs);
4758: stack[stackTop] = lstr.concat(rstr);
4759: } else if (rhs instanceof String) {
4760: String lstr = ScriptRuntime.toString(lhs);
4761: String rstr = (String) rhs;
4762: stack[stackTop] = lstr.concat(rstr);
4763: } else {
4764: double lDbl = (lhs instanceof Number) ? ((Number) lhs)
4765: .doubleValue() : ScriptRuntime.toNumber(lhs);
4766: double rDbl = (rhs instanceof Number) ? ((Number) rhs)
4767: .doubleValue() : ScriptRuntime.toNumber(rhs);
4768: stack[stackTop] = UniqueTag.DOUBLE_MARK;
4769: sDbl[stackTop] = lDbl + rDbl;
4770: }
4771: return;
4772: }
4773:
4774: // handle object(lhs) + number(d) code
4775: if (lhs instanceof Scriptable) {
4776: rhs = ScriptRuntime.wrapNumber(d);
4777: if (!leftRightOrder) {
4778: Object tmp = lhs;
4779: lhs = rhs;
4780: rhs = tmp;
4781: }
4782: stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx);
4783: } else if (lhs instanceof String) {
4784: String lstr = (String) lhs;
4785: String rstr = ScriptRuntime.toString(d);
4786: if (leftRightOrder) {
4787: stack[stackTop] = lstr.concat(rstr);
4788: } else {
4789: stack[stackTop] = rstr.concat(lstr);
4790: }
4791: } else {
4792: double lDbl = (lhs instanceof Number) ? ((Number) lhs)
4793: .doubleValue() : ScriptRuntime.toNumber(lhs);
4794: stack[stackTop] = UniqueTag.DOUBLE_MARK;
4795: sDbl[stackTop] = lDbl + d;
4796: }
4797: }
4798:
4799: private static Object[] getArgsArray(Object[] stack, double[] sDbl,
4800: int shift, int count) {
4801: if (count == 0) {
4802: return ScriptRuntime.emptyArgs;
4803: }
4804: Object[] args = new Object[count];
4805: for (int i = 0; i != count; ++i, ++shift) {
4806: Object val = stack[shift];
4807: if (val == UniqueTag.DOUBLE_MARK) {
4808: val = ScriptRuntime.wrapNumber(sDbl[shift]);
4809: }
4810: args[i] = val;
4811: }
4812: return args;
4813: }
4814:
4815: private static void addInstructionCount(Context cx,
4816: CallFrame frame, int extra) {
4817: cx.instructionCount += frame.pc - frame.pcPrevBranch + extra;
4818: if (cx.instructionCount > cx.instructionThreshold) {
4819: cx.observeInstructionCount(cx.instructionCount);
4820: cx.instructionCount = 0;
4821: }
4822: }
4823: }
|