private static void toStringTreeHelper(ScriptOrFnNode treeTop, Node n, ObjToIntMap printIds, int level, System.Text.StringBuilder sb) { if (Token.printTrees) { if (printIds == null) { printIds = new ObjToIntMap(); generatePrintIds(treeTop, printIds); } for (int i = 0; i != level; ++i) { sb.Append(" "); } n.toString(printIds, sb); sb.Append('\n'); for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { if (cursor.Type == Token.FUNCTION) { int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP); FunctionNode fn = treeTop.getFunctionNode(fnIndex); toStringTreeHelper(fn, fn, null, level + 1, sb); } else { toStringTreeHelper(treeTop, cursor, printIds, level + 1, sb); } } } }
void VisitStatement (Node node) { int type = node.Type; Node child = node.FirstChild; switch (type) { case Token.FUNCTION: { int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); int fnType = scriptOrFn.getFunctionNode (fnIndex).FunctionType; // Only function expressions or function expression // statements needs closure code creating new function // object on stack as function statements are initialized // at script/function start // In addition function expression can not present here // at statement level, they must only present as expressions. if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { addIndexOp (Icode_CLOSURE_STMT, fnIndex); } else { if (fnType != FunctionNode.FUNCTION_STATEMENT) { throw Context.CodeBug (); } } } break; case Token.SCRIPT: case Token.LABEL: case Token.LOOP: case Token.BLOCK: case Token.EMPTY: case Token.WITH: updateLineNumber (node); while (child != null) { VisitStatement (child); child = child.Next; } break; case Token.ENTERWITH: VisitExpression (child, 0); addToken (Token.ENTERWITH); stackChange (-1); break; case Token.LEAVEWITH: addToken (Token.LEAVEWITH); break; case Token.LOCAL_BLOCK: { int local = allocLocal (); node.putIntProp (Node.LOCAL_PROP, local); updateLineNumber (node); while (child != null) { VisitStatement (child); child = child.Next; } addIndexOp (Icode_LOCAL_CLEAR, local); releaseLocal (local); } break; case Token.DEBUGGER: updateLineNumber (node); addIcode (Icode_DEBUGGER); break; case Token.SWITCH: updateLineNumber (node); { // See comments in IRFactory.createSwitch() for description // of SWITCH node Node switchNode = (Node.Jump)node; VisitExpression (child, 0); for (Node.Jump caseNode = (Node.Jump)child.Next; caseNode != null; caseNode = (Node.Jump)caseNode.Next) { if (caseNode.Type != Token.CASE) throw badTree (caseNode); Node test = caseNode.FirstChild; addIcode (Icode_DUP); stackChange (1); VisitExpression (test, 0); addToken (Token.SHEQ); stackChange (-1); // If true, Icode_IFEQ_POP will jump and remove case // value from stack addGoto (caseNode.target, Icode_IFEQ_POP); stackChange (-1); } addIcode (Icode_POP); stackChange (-1); } break; case Token.TARGET: markTargetLabel (node); break; case Token.IFEQ: case Token.IFNE: { Node target = ((Node.Jump)node).target; VisitExpression (child, 0); addGoto (target, type); stackChange (-1); } break; case Token.GOTO: { Node target = ((Node.Jump)node).target; addGoto (target, type); } break; case Token.JSR: { Node target = ((Node.Jump)node).target; addGoto (target, Icode_GOSUB); } break; case Token.FINALLY: { // Account for incomming GOTOSUB address stackChange (1); int finallyRegister = getLocalBlockRef (node); addIndexOp (Icode_STARTSUB, finallyRegister); stackChange (-1); while (child != null) { VisitStatement (child); child = child.Next; } addIndexOp (Icode_RETSUB, finallyRegister); } break; case Token.EXPR_VOID: case Token.EXPR_RESULT: updateLineNumber (node); VisitExpression (child, 0); addIcode ((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT); stackChange (-1); break; case Token.TRY: { Node.Jump tryNode = (Node.Jump)node; int exceptionObjectLocal = getLocalBlockRef (tryNode); int scopeLocal = allocLocal (); addIndexOp (Icode_SCOPE_SAVE, scopeLocal); int tryStart = itsICodeTop; while (child != null) { VisitStatement (child); child = child.Next; } Node catchTarget = tryNode.target; if (catchTarget != null) { int catchStartPC = itsLabelTable [getTargetLabel (catchTarget)]; addExceptionHandler (tryStart, catchStartPC, catchStartPC, false, exceptionObjectLocal, scopeLocal); } Node finallyTarget = tryNode.Finally; if (finallyTarget != null) { int finallyStartPC = itsLabelTable [getTargetLabel (finallyTarget)]; addExceptionHandler (tryStart, finallyStartPC, finallyStartPC, true, exceptionObjectLocal, scopeLocal); } addIndexOp (Icode_LOCAL_CLEAR, scopeLocal); releaseLocal (scopeLocal); } break; case Token.CATCH_SCOPE: { int localIndex = getLocalBlockRef (node); int scopeIndex = node.getExistingIntProp (Node.CATCH_SCOPE_PROP); string name = child.String; child = child.Next; VisitExpression (child, 0); // load expression object addStringPrefix (name); addIndexPrefix (localIndex); addToken (Token.CATCH_SCOPE); addUint8 (scopeIndex != 0 ? 1 : 0); stackChange (-1); } break; case Token.THROW: updateLineNumber (node); VisitExpression (child, 0); addToken (Token.THROW); addUint16 (itsLineNumber & 0xFFFF); stackChange (-1); break; case Token.RETHROW: updateLineNumber (node); addIndexOp (Token.RETHROW, getLocalBlockRef (node)); break; case Token.RETURN: updateLineNumber (node); if (child != null) { VisitExpression (child, ECF_TAIL); addToken (Token.RETURN); stackChange (-1); } else { addIcode (Icode_RETUNDEF); } break; case Token.RETURN_RESULT: updateLineNumber (node); addToken (Token.RETURN_RESULT); break; case Token.ENUM_INIT_KEYS: case Token.ENUM_INIT_VALUES: VisitExpression (child, 0); addIndexOp (type, getLocalBlockRef (node)); stackChange (-1); break; default: throw badTree (node); } if (itsStackDepth != 0) { throw Context.CodeBug (); } }
void VisitExpression (Node node, int contextFlags) { if (VisitExpressionOptimized (node, contextFlags)) return; int type = node.Type; Node child = node.FirstChild; int savedStackDepth = itsStackDepth; switch (type) { case Token.FUNCTION: { int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); FunctionNode fn = scriptOrFn.getFunctionNode (fnIndex); // See comments in visitStatement for Token.FUNCTION case switch (fn.FunctionType) { case FunctionNode.FUNCTION_EXPRESSION: addIndexOp (Icode_CLOSURE_EXPR, fnIndex); break; default: throw Context.CodeBug (); } stackChange (1); } break; case Token.LOCAL_LOAD: { int localIndex = getLocalBlockRef (node); addIndexOp (Token.LOCAL_LOAD, localIndex); stackChange (1); } break; case Token.COMMA: { Node lastChild = node.LastChild; while (child != lastChild) { VisitExpression (child, 0); addIcode (Icode_POP); stackChange (-1); child = child.Next; } // Preserve tail context flag if any VisitExpression (child, contextFlags & ECF_TAIL); } break; case Token.USE_STACK: // Indicates that stack was modified externally, // like placed catch object stackChange (1); break; case Token.REF_CALL: case Token.CALL: case Token.NEW: { if (type == Token.NEW) { VisitExpression (child, 0); } else { generateCallFunAndThis (child); } int argCount = 0; while ((child = child.Next) != null) { VisitExpression (child, 0); ++argCount; } int callType = node.getIntProp (Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL); if (callType != Node.NON_SPECIALCALL) { // embed line number and source filename addIndexOp (Icode_CALLSPECIAL, argCount); addUint8 (callType); addUint8 (type == Token.NEW ? 1 : 0); addUint16 (itsLineNumber & 0xFFFF); } else { if (type == Token.CALL) { if ((contextFlags & ECF_TAIL) != 0) { type = Icode_TAIL_CALL; } } addIndexOp (type, argCount); } // adjust stack if (type == Token.NEW) { // new: f, args -> result stackChange (-argCount); } else { // call: f, thisObj, args -> result // ref_call: f, thisObj, args -> ref stackChange (-1 - argCount); } if (argCount > itsData.itsMaxCalleeArgs) { itsData.itsMaxCalleeArgs = argCount; } } break; case Token.AND: case Token.OR: { VisitExpression (child, 0); addIcode (Icode_DUP); stackChange (1); int afterSecondJumpStart = itsICodeTop; int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ; addGotoOp (jump); stackChange (-1); addIcode (Icode_POP); stackChange (-1); child = child.Next; // Preserve tail context flag if any VisitExpression (child, contextFlags & ECF_TAIL); resolveForwardGoto (afterSecondJumpStart); } break; case Token.HOOK: { Node ifThen = child.Next; Node ifElse = ifThen.Next; VisitExpression (child, 0); int elseJumpStart = itsICodeTop; addGotoOp (Token.IFNE); ; stackChange (-1); // Preserve tail context flag if any VisitExpression (ifThen, contextFlags & ECF_TAIL); int afterElseJumpStart = itsICodeTop; addGotoOp (Token.GOTO); resolveForwardGoto (elseJumpStart); itsStackDepth = savedStackDepth; // Preserve tail context flag if any VisitExpression (ifElse, contextFlags & ECF_TAIL); resolveForwardGoto (afterElseJumpStart); } break; case Token.GETPROP: VisitExpression (child, 0); child = child.Next; addStringOp (Token.GETPROP, child.String); break; case Token.ADD: case Token.GETELEM: case Token.DELPROP: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MOD: case Token.DIV: case Token.MUL: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LT: case Token.GE: case Token.GT: VisitExpression (child, 0); VisitExpression (child.Next, 0); addToken (type); stackChange (-1); break; case Token.POS: case Token.NEG: case Token.NOT: case Token.BITNOT: case Token.TYPEOF: case Token.VOID: VisitExpression (child, 0); if (type == Token.VOID) { addIcode (Icode_POP); addIcode (Icode_UNDEF); } else { addToken (type); } break; case Token.GET_REF: case Token.DEL_REF: VisitExpression (child, 0); addToken (type); break; case Token.SETPROP: case Token.SETPROP_OP: case Token.SETPROP_GETTER: case Token.SETPROP_SETTER: { VisitExpression (child, 0); child = child.Next; string property = child.String; child = child.Next; if (type == Token.SETPROP_OP) { addIcode (Icode_DUP); stackChange (1); addStringOp (Token.GETPROP, property); // Compensate for the following USE_STACK stackChange (-1); } VisitExpression (child, 0); addStringOp ((type == Token.SETPROP_OP) ? Token.SETPROP : type, property); stackChange (-1); } break; case Token.SETELEM: case Token.SETELEM_OP: VisitExpression (child, 0); child = child.Next; VisitExpression (child, 0); child = child.Next; if (type == Token.SETELEM_OP) { addIcode (Icode_DUP2); stackChange (2); addToken (Token.GETELEM); stackChange (-1); // Compensate for the following USE_STACK stackChange (-1); } VisitExpression (child, 0); addToken (Token.SETELEM); stackChange (-2); break; case Token.SET_REF: case Token.SET_REF_OP: VisitExpression (child, 0); child = child.Next; if (type == Token.SET_REF_OP) { addIcode (Icode_DUP); stackChange (1); addToken (Token.GET_REF); // Compensate for the following USE_STACK stackChange (-1); } VisitExpression (child, 0); addToken (Token.SET_REF); stackChange (-1); break; case Token.SETNAME: { string name = child.String; VisitExpression (child, 0); child = child.Next; VisitExpression (child, 0); addStringOp (Token.SETNAME, name); stackChange (-1); } break; case Token.TYPEOFNAME: { string name = node.String; int index = -1; // use typeofname if an activation frame exists // since the vars all exist there instead of in jregs if (itsInFunctionFlag && !itsData.itsNeedsActivation) index = scriptOrFn.getParamOrVarIndex (name); if (index == -1) { addStringOp (Icode_TYPEOFNAME, name); stackChange (1); } else { addVarOp (Token.GETVAR, index); stackChange (1); addToken (Token.TYPEOF); } } break; case Token.BINDNAME: case Token.NAME: case Token.STRING: addStringOp (type, node.String); stackChange (1); break; case Token.INC: case Token.DEC: VisitIncDec (node, child); break; case Token.NUMBER: { double num = node.Double; int inum = (int)num; if (inum == num) { if (inum == 0) { addIcode (Icode_ZERO); // Check for negative zero if (1.0 / num < 0.0) { addToken (Token.NEG); } } else if (inum == 1) { addIcode (Icode_ONE); } else if ((short)inum == inum) { addIcode (Icode_SHORTNUMBER); // write short as uin16 bit pattern addUint16 (inum & 0xFFFF); } else { addIcode (Icode_INTNUMBER); addInt (inum); } } else { int index = GetDoubleIndex (num); addIndexOp (Token.NUMBER, index); } stackChange (1); } break; case Token.GETVAR: { if (itsData.itsNeedsActivation) Context.CodeBug (); string name = node.String; int index = scriptOrFn.getParamOrVarIndex (name); addVarOp (Token.GETVAR, index); stackChange (1); } break; case Token.SETVAR: { if (itsData.itsNeedsActivation) Context.CodeBug (); string name = child.String; child = child.Next; VisitExpression (child, 0); int index = scriptOrFn.getParamOrVarIndex (name); addVarOp (Token.SETVAR, index); } break; case Token.NULL: case Token.THIS: case Token.THISFN: case Token.FALSE: case Token.TRUE: addToken (type); stackChange (1); break; case Token.ENUM_NEXT: case Token.ENUM_ID: addIndexOp (type, getLocalBlockRef (node)); stackChange (1); break; case Token.REGEXP: { int index = node.getExistingIntProp (Node.REGEXP_PROP); addIndexOp (Token.REGEXP, index); stackChange (1); } break; case Token.ARRAYLIT: case Token.OBJECTLIT: VisitLiteral (node, child); break; case Token.REF_SPECIAL: VisitExpression (child, 0); addStringOp (type, (string)node.getProp (Node.NAME_PROP)); break; case Token.REF_MEMBER: case Token.REF_NS_MEMBER: case Token.REF_NAME: case Token.REF_NS_NAME: { int memberTypeFlags = node.getIntProp (Node.MEMBER_TYPE_PROP, 0); // generate possible target, possible namespace and member int childCount = 0; do { VisitExpression (child, 0); ++childCount; child = child.Next; } while (child != null); addIndexOp (type, memberTypeFlags); stackChange (1 - childCount); } break; case Token.DOTQUERY: { int queryPC; updateLineNumber (node); VisitExpression (child, 0); addIcode (Icode_ENTERDQ); stackChange (-1); queryPC = itsICodeTop; VisitExpression (child.Next, 0); addBackwardGoto (Icode_LEAVEDQ, queryPC); } break; case Token.DEFAULTNAMESPACE: case Token.ESCXMLATTR: case Token.ESCXMLTEXT: VisitExpression (child, 0); addToken (type); break; default: throw badTree (node); } //if (savedStackDepth + 1 != itsStackDepth) { // EcmaScriptHelper.CodeBug(); //} }
void VisitIncDec (Node node, Node child) { int incrDecrMask = node.getExistingIntProp (Node.INCRDECR_PROP); int childType = child.Type; switch (childType) { case Token.GETVAR: { if (itsData.itsNeedsActivation) Context.CodeBug (); string name = child.String; int i = scriptOrFn.getParamOrVarIndex (name); addVarOp (Icode_VAR_INC_DEC, i); addUint8 (incrDecrMask); stackChange (1); break; } case Token.NAME: { string name = child.String; addStringOp (Icode_NAME_INC_DEC, name); addUint8 (incrDecrMask); stackChange (1); break; } case Token.GETPROP: { Node obj = child.FirstChild; VisitExpression (obj, 0); string property = obj.Next.String; addStringOp (Icode_PROP_INC_DEC, property); addUint8 (incrDecrMask); break; } case Token.GETELEM: { Node obj = child.FirstChild; VisitExpression (obj, 0); Node index = obj.Next; VisitExpression (index, 0); addIcode (Icode_ELEM_INC_DEC); addUint8 (incrDecrMask); stackChange (-1); break; } case Token.GET_REF: { Node rf = child.FirstChild; VisitExpression (rf, 0); addIcode (Icode_REF_INC_DEC); addUint8 (incrDecrMask); break; } default: { throw badTree (node); } } }