private void GenerateExpression(Node node, Node parent) { int type = node.GetType(); Node child = node.GetFirstChild(); switch (type) { case Token.USE_STACK: { break; } case Token.FUNCTION: { if (fnCurrent != null || parent.GetType() != Token.SCRIPT) { int fnIndex = node.GetExistingIntProp(Node.FUNCTION_PROP); OptFunctionNode ofn = OptFunctionNode.Get(scriptOrFn, fnIndex); int t = ofn.fnode.GetFunctionType(); if (t != FunctionNode.FUNCTION_EXPRESSION) { throw Codegen.BadTree(); } VisitFunction(ofn, t); } break; } case Token.NAME: { cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); cfw.AddPush(node.GetString()); AddScriptRuntimeInvoke("name", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/String;" + ")Ljava/lang/Object;"); break; } case Token.CALL: case Token.NEW: { int specialType = node.GetIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL); if (specialType == Node.NON_SPECIALCALL) { OptFunctionNode target; target = (OptFunctionNode)node.GetProp(Node.DIRECTCALL_PROP); if (target != null) { VisitOptimizedCall(node, target, type, child); } else { if (type == Token.CALL) { VisitStandardCall(node, child); } else { VisitStandardNew(node, child); } } } else { VisitSpecialCall(node, type, specialType, child); } break; } case Token.REF_CALL: { GenerateFunctionAndThisObj(child, node); // stack: ... functionObj thisObj child = child.GetNext(); GenerateCallArgArray(node, child, false); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("callRef", "(Lorg/mozilla/javascript/Callable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/Ref;"); break; } case Token.NUMBER: { double num = node.GetDouble(); if (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { cfw.AddPush(num); } else { codegen.PushNumberAsObject(cfw, num); } break; } case Token.STRING: { cfw.AddPush(node.GetString()); break; } case Token.THIS: { cfw.AddALoad(thisObjLocal); break; } case Token.THISFN: { cfw.Add(ByteCode.ALOAD_0); break; } case Token.NULL: { cfw.Add(ByteCode.ACONST_NULL); break; } case Token.TRUE: { cfw.Add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); break; } case Token.FALSE: { cfw.Add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); break; } case Token.REGEXP: { // Create a new wrapper around precompiled regexp cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); int i = node.GetExistingIntProp(Node.REGEXP_PROP); cfw.Add(ByteCode.GETSTATIC, codegen.mainClassName, codegen.GetCompiledRegexpName(scriptOrFn, i), "Ljava/lang/Object;"); cfw.AddInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "wrapRegExp", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/Object;" + ")Lorg/mozilla/javascript/Scriptable;"); break; } case Token.COMMA: { Node next = child.GetNext(); while (next != null) { GenerateExpression(child, node); cfw.Add(ByteCode.POP); child = next; next = next.GetNext(); } GenerateExpression(child, node); break; } case Token.ENUM_NEXT: case Token.ENUM_ID: { int local = GetLocalBlockRegister(node); cfw.AddALoad(local); if (type == Token.ENUM_NEXT) { AddScriptRuntimeInvoke("enumNext", "(Ljava/lang/Object;)Ljava/lang/Boolean;"); } else { cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("enumId", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } break; } case Token.ARRAYLIT: { VisitArrayLiteral(node, child, false); break; } case Token.OBJECTLIT: { VisitObjectLiteral(node, child, false); break; } case Token.NOT: { int trueTarget = cfw.AcquireLabel(); int falseTarget = cfw.AcquireLabel(); int beyond = cfw.AcquireLabel(); GenerateIfJump(child, node, trueTarget, falseTarget); cfw.MarkLabel(trueTarget); cfw.Add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); cfw.Add(ByteCode.GOTO, beyond); cfw.MarkLabel(falseTarget); cfw.Add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); cfw.MarkLabel(beyond); cfw.AdjustStackTop(-1); break; } case Token.BITNOT: { GenerateExpression(child, node); AddScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I"); cfw.AddPush(-1); // implement ~a as (a ^ -1) cfw.Add(ByteCode.IXOR); cfw.Add(ByteCode.I2D); AddDoubleWrap(); break; } case Token.VOID: { GenerateExpression(child, node); cfw.Add(ByteCode.POP); Codegen.PushUndefined(cfw); break; } case Token.TYPEOF: { GenerateExpression(child, node); AddScriptRuntimeInvoke("typeof", "(Ljava/lang/Object;" + ")Ljava/lang/String;"); break; } case Token.TYPEOFNAME: { VisitTypeofname(node); break; } case Token.INC: case Token.DEC: { VisitIncDec(node); break; } case Token.OR: case Token.AND: { GenerateExpression(child, node); cfw.Add(ByteCode.DUP); AddScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z"); int falseTarget = cfw.AcquireLabel(); if (type == Token.AND) { cfw.Add(ByteCode.IFEQ, falseTarget); } else { cfw.Add(ByteCode.IFNE, falseTarget); } cfw.Add(ByteCode.POP); GenerateExpression(child.GetNext(), node); cfw.MarkLabel(falseTarget); break; } case Token.HOOK: { Node ifThen = child.GetNext(); Node ifElse = ifThen.GetNext(); GenerateExpression(child, node); AddScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z"); int elseTarget = cfw.AcquireLabel(); cfw.Add(ByteCode.IFEQ, elseTarget); short stack = cfw.GetStackTop(); GenerateExpression(ifThen, node); int afterHook = cfw.AcquireLabel(); cfw.Add(ByteCode.GOTO, afterHook); cfw.MarkLabel(elseTarget, stack); GenerateExpression(ifElse, node); cfw.MarkLabel(afterHook); break; } case Token.ADD: { GenerateExpression(child, node); GenerateExpression(child.GetNext(), node); switch (node.GetIntProp(Node.ISNUMBER_PROP, -1)) { case Node.BOTH: { cfw.Add(ByteCode.DADD); break; } case Node.LEFT: { AddOptRuntimeInvoke("add", "(DLjava/lang/Object;)Ljava/lang/Object;"); break; } case Node.RIGHT: { AddOptRuntimeInvoke("add", "(Ljava/lang/Object;D)Ljava/lang/Object;"); break; } default: { if (child.GetType() == Token.STRING) { AddScriptRuntimeInvoke("add", "(Ljava/lang/CharSequence;" + "Ljava/lang/Object;" + ")Ljava/lang/CharSequence;"); } else { if (child.GetNext().GetType() == Token.STRING) { AddScriptRuntimeInvoke("add", "(Ljava/lang/Object;" + "Ljava/lang/CharSequence;" + ")Ljava/lang/CharSequence;"); } else { cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("add", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } } break; } } break; } case Token.MUL: { VisitArithmetic(node, ByteCode.DMUL, child, parent); break; } case Token.SUB: { VisitArithmetic(node, ByteCode.DSUB, child, parent); break; } case Token.DIV: case Token.MOD: { VisitArithmetic(node, type == Token.DIV ? ByteCode.DDIV : ByteCode.DREM, child, parent); break; } case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: { VisitBitOp(node, type, child); break; } case Token.POS: case Token.NEG: { GenerateExpression(child, node); AddObjectToDouble(); if (type == Token.NEG) { cfw.Add(ByteCode.DNEG); } AddDoubleWrap(); break; } case Token.TO_DOUBLE: { // cnvt to double (not Double) GenerateExpression(child, node); AddObjectToDouble(); break; } case Token.TO_OBJECT: { // convert from double int prop = -1; if (child.GetType() == Token.NUMBER) { prop = child.GetIntProp(Node.ISNUMBER_PROP, -1); } if (prop != -1) { child.RemoveProp(Node.ISNUMBER_PROP); GenerateExpression(child, node); child.PutIntProp(Node.ISNUMBER_PROP, prop); } else { GenerateExpression(child, node); AddDoubleWrap(); } break; } case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LT: case Token.GE: case Token.GT: { int trueGOTO = cfw.AcquireLabel(); int falseGOTO = cfw.AcquireLabel(); VisitIfJumpRelOp(node, child, trueGOTO, falseGOTO); AddJumpedBooleanWrap(trueGOTO, falseGOTO); break; } case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: { int trueGOTO = cfw.AcquireLabel(); int falseGOTO = cfw.AcquireLabel(); VisitIfJumpEqOp(node, child, trueGOTO, falseGOTO); AddJumpedBooleanWrap(trueGOTO, falseGOTO); break; } case Token.GETPROP: case Token.GETPROPNOWARN: { VisitGetProp(node, child); break; } case Token.GETELEM: { GenerateExpression(child, node); // object GenerateExpression(child.GetNext(), node); // id cfw.AddALoad(contextLocal); if (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { AddScriptRuntimeInvoke("getObjectIndex", "(Ljava/lang/Object;D" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } else { cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("getObjectElem", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;"); } break; } case Token.GET_REF: { GenerateExpression(child, node); // reference cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("refGet", "(Lorg/mozilla/javascript/Ref;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); break; } case Token.GETVAR: { VisitGetVar(node); break; } case Token.SETVAR: { VisitSetVar(node, child, true); break; } case Token.SETNAME: { VisitSetName(node, child); break; } case Token.STRICT_SETNAME: { VisitStrictSetName(node, child); break; } case Token.SETCONST: { VisitSetConst(node, child); break; } case Token.SETCONSTVAR: { VisitSetConstVar(node, child, true); break; } case Token.SETPROP: case Token.SETPROP_OP: { VisitSetProp(type, node, child); break; } case Token.SETELEM: case Token.SETELEM_OP: { VisitSetElem(type, node, child); break; } case Token.SET_REF: case Token.SET_REF_OP: { GenerateExpression(child, node); child = child.GetNext(); if (type == Token.SET_REF_OP) { cfw.Add(ByteCode.DUP); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("refGet", "(Lorg/mozilla/javascript/Ref;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } GenerateExpression(child, node); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("refSet", "(Lorg/mozilla/javascript/Ref;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); break; } case Token.DEL_REF: { GenerateExpression(child, node); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("refDel", "(Lorg/mozilla/javascript/Ref;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); break; } case Token.DELPROP: { bool isName = child.GetType() == Token.BINDNAME; GenerateExpression(child, node); child = child.GetNext(); GenerateExpression(child, node); cfw.AddALoad(contextLocal); cfw.AddPush(isName); AddScriptRuntimeInvoke("delete", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "Z)Ljava/lang/Object;"); break; } case Token.BINDNAME: { while (child != null) { GenerateExpression(child, node); child = child.GetNext(); } // Generate code for "ScriptRuntime.bind(varObj, "s")" cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); cfw.AddPush(node.GetString()); AddScriptRuntimeInvoke("bind", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/String;" + ")Lorg/mozilla/javascript/Scriptable;"); break; } case Token.LOCAL_LOAD: { cfw.AddALoad(GetLocalBlockRegister(node)); break; } case Token.REF_SPECIAL: { string special = (string)node.GetProp(Node.NAME_PROP); GenerateExpression(child, node); cfw.AddPush(special); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("specialRef", "(Ljava/lang/Object;" + "Ljava/lang/String;" + "Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/Ref;"); 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); do { // generate possible target, possible namespace and member GenerateExpression(child, node); child = child.GetNext(); } while (child != null); cfw.AddALoad(contextLocal); string methodName; string signature; switch (type) { case Token.REF_MEMBER: { methodName = "memberRef"; signature = "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "I" + ")Lorg/mozilla/javascript/Ref;"; break; } case Token.REF_NS_MEMBER: { methodName = "memberRef"; signature = "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "I" + ")Lorg/mozilla/javascript/Ref;"; break; } case Token.REF_NAME: { methodName = "nameRef"; signature = "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "I" + ")Lorg/mozilla/javascript/Ref;"; cfw.AddALoad(variableObjectLocal); break; } case Token.REF_NS_NAME: { methodName = "nameRef"; signature = "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "I" + ")Lorg/mozilla/javascript/Ref;"; cfw.AddALoad(variableObjectLocal); break; } default: { throw Kit.CodeBug(); } } cfw.AddPush(memberTypeFlags); AddScriptRuntimeInvoke(methodName, signature); break; } case Token.DOTQUERY: { VisitDotQuery(node, child); break; } case Token.ESCXMLATTR: { GenerateExpression(child, node); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("escapeAttributeValue", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/String;"); break; } case Token.ESCXMLTEXT: { GenerateExpression(child, node); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("escapeTextValue", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/String;"); break; } case Token.DEFAULTNAMESPACE: { GenerateExpression(child, node); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("setDefaultNamespace", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); break; } case Token.YIELD: { GenerateYieldPoint(node, true); break; } case Token.WITHEXPR: { Node enterWith = child; Node with = enterWith.GetNext(); Node leaveWith = with.GetNext(); GenerateStatement(enterWith); GenerateExpression(with.GetFirstChild(), with); GenerateStatement(leaveWith); break; } case Token.ARRAYCOMP: { Node initStmt = child; Node expr = child.GetNext(); GenerateStatement(initStmt); GenerateExpression(expr, node); break; } default: { throw new Exception("Unexpected node type " + type); } } }
private void VisitExpression(Node node, int contextFlags) { int type = node.GetType(); Node child = node.GetFirstChild(); int savedStackDepth = stackDepth; switch (type) { case Token.FUNCTION: { int fnIndex = node.GetExistingIntProp(Node.FUNCTION_PROP); FunctionNode fn = scriptOrFn.GetFunctionNode(fnIndex); // See comments in visitStatement for Token.FUNCTION case if (fn.GetFunctionType() != FunctionNode.FUNCTION_EXPRESSION) { throw Kit.CodeBug(); } AddIndexOp(Icode_CLOSURE_EXPR, fnIndex); StackChange(1); break; } case Token.LOCAL_LOAD: { int localIndex = GetLocalBlockRef(node); AddIndexOp(Token.LOCAL_LOAD, localIndex); StackChange(1); break; } case Token.COMMA: { Node lastChild = node.GetLastChild(); while (child != lastChild) { VisitExpression(child, 0); AddIcode(Icode_POP); StackChange(-1); child = child.GetNext(); } // Preserve tail context flag if any VisitExpression(child, contextFlags & ECF_TAIL); break; } case Token.USE_STACK: { // Indicates that stack was modified externally, // like placed catch object StackChange(1); break; } case Token.REF_CALL: case Token.CALL: case Token.NEW: { if (type == Token.NEW) { VisitExpression(child, 0); } else { GenerateCallFunAndThis(child); } int argCount = 0; while ((child = child.GetNext()) != null) { VisitExpression(child, 0); ++argCount; } int callType = node.GetIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL); if (type != Token.REF_CALL && callType != Node.NON_SPECIALCALL) { // embed line number and source filename AddIndexOp(Icode_CALLSPECIAL, argCount); AddUint8(callType); AddUint8(type == Token.NEW ? 1 : 0); AddUint16(lineNumber & unchecked((int)(0xFFFF))); } else { // Only use the tail call optimization if we're not in a try // or we're not generating debug info (since the // optimization will confuse the debugger) if (type == Token.CALL && (contextFlags & ECF_TAIL) != 0 && !compilerEnv.IsGenerateDebugInfo() && !itsInTryFlag) { type = Icode_TAIL_CALL; } AddIndexOp(type, argCount); } // adjust stack if (type == Token.NEW) { // new: f, args -> result StackChange(-argCount); } else { // call: f, thisObj, args -> result // ref_call: f, thisObj, args -> ref StackChange(-1 - argCount); } if (argCount > itsData.itsMaxCalleeArgs) { itsData.itsMaxCalleeArgs = argCount; } break; } case Token.AND: case Token.OR: { VisitExpression(child, 0); AddIcode(Icode_DUP); StackChange(1); int afterSecondJumpStart = iCodeTop; int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ; AddGotoOp(jump); StackChange(-1); AddIcode(Icode_POP); StackChange(-1); child = child.GetNext(); // Preserve tail context flag if any VisitExpression(child, contextFlags & ECF_TAIL); ResolveForwardGoto(afterSecondJumpStart); break; } case Token.HOOK: { Node ifThen = child.GetNext(); Node ifElse = ifThen.GetNext(); VisitExpression(child, 0); int elseJumpStart = iCodeTop; AddGotoOp(Token.IFNE); StackChange(-1); // Preserve tail context flag if any VisitExpression(ifThen, contextFlags & ECF_TAIL); int afterElseJumpStart = iCodeTop; AddGotoOp(Token.GOTO); ResolveForwardGoto(elseJumpStart); stackDepth = savedStackDepth; // Preserve tail context flag if any VisitExpression(ifElse, contextFlags & ECF_TAIL); ResolveForwardGoto(afterElseJumpStart); break; } case Token.GETPROP: case Token.GETPROPNOWARN: { VisitExpression(child, 0); child = child.GetNext(); AddStringOp(type, child.GetString()); break; } case Token.DELPROP: { bool isName = child.GetType() == Token.BINDNAME; VisitExpression(child, 0); child = child.GetNext(); VisitExpression(child, 0); if (isName) { // special handling for delete name AddIcode(Icode_DELNAME); } else { AddToken(Token.DELPROP); } StackChange(-1); break; } case Token.GETELEM: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.LSH: case Token.RSH: case Token.URSH: case Token.ADD: case Token.SUB: case Token.MOD: case Token.DIV: case Token.MUL: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LT: case Token.GE: case Token.GT: { VisitExpression(child, 0); child = child.GetNext(); VisitExpression(child, 0); AddToken(type); StackChange(-1); break; } case Token.POS: case Token.NEG: case Token.NOT: case Token.BITNOT: case Token.TYPEOF: case Token.VOID: { VisitExpression(child, 0); if (type == Token.VOID) { AddIcode(Icode_POP); AddIcode(Icode_UNDEF); } else { AddToken(type); } break; } case Token.GET_REF: case Token.DEL_REF: { VisitExpression(child, 0); AddToken(type); break; } case Token.SETPROP: case Token.SETPROP_OP: { VisitExpression(child, 0); child = child.GetNext(); string property = child.GetString(); child = child.GetNext(); if (type == Token.SETPROP_OP) { AddIcode(Icode_DUP); StackChange(1); AddStringOp(Token.GETPROP, property); // Compensate for the following USE_STACK StackChange(-1); } VisitExpression(child, 0); AddStringOp(Token.SETPROP, property); StackChange(-1); break; } case Token.SETELEM: case Token.SETELEM_OP: { VisitExpression(child, 0); child = child.GetNext(); VisitExpression(child, 0); child = child.GetNext(); if (type == Token.SETELEM_OP) { AddIcode(Icode_DUP2); StackChange(2); AddToken(Token.GETELEM); StackChange(-1); // Compensate for the following USE_STACK StackChange(-1); } VisitExpression(child, 0); AddToken(Token.SETELEM); StackChange(-2); break; } case Token.SET_REF: case Token.SET_REF_OP: { VisitExpression(child, 0); child = child.GetNext(); if (type == Token.SET_REF_OP) { AddIcode(Icode_DUP); StackChange(1); AddToken(Token.GET_REF); // Compensate for the following USE_STACK StackChange(-1); } VisitExpression(child, 0); AddToken(Token.SET_REF); StackChange(-1); break; } case Token.STRICT_SETNAME: case Token.SETNAME: { string name = child.GetString(); VisitExpression(child, 0); child = child.GetNext(); VisitExpression(child, 0); AddStringOp(type, name); StackChange(-1); break; } case Token.SETCONST: { string name = child.GetString(); VisitExpression(child, 0); child = child.GetNext(); VisitExpression(child, 0); AddStringOp(Icode_SETCONST, name); StackChange(-1); break; } case Token.TYPEOFNAME: { int index = -1; // use typeofname if an activation frame exists // since the vars all exist there instead of in jregs if (itsInFunctionFlag && !itsData.itsNeedsActivation) { index = scriptOrFn.GetIndexForNameNode(node); } if (index == -1) { AddStringOp(Icode_TYPEOFNAME, node.GetString()); StackChange(1); } else { AddVarOp(Token.GETVAR, index); StackChange(1); AddToken(Token.TYPEOF); } break; } case Token.BINDNAME: case Token.NAME: case Token.STRING: { AddStringOp(type, node.GetString()); StackChange(1); break; } case Token.INC: case Token.DEC: { VisitIncDec(node, child); break; } case Token.NUMBER: { double num = node.GetDouble(); int inum = (int)num; if (inum == num) { if (inum == 0) { AddIcode(Icode_ZERO); // Check for negative zero if (1.0 / num < 0.0) { AddToken(Token.NEG); } } else { if (inum == 1) { AddIcode(Icode_ONE); } else { if ((short)inum == inum) { AddIcode(Icode_SHORTNUMBER); // write short as uin16 bit pattern AddUint16(inum & unchecked((int)(0xFFFF))); } else { AddIcode(Icode_INTNUMBER); AddInt(inum); } } } } else { int index = GetDoubleIndex(num); AddIndexOp(Token.NUMBER, index); } StackChange(1); break; } case Token.GETVAR: { if (itsData.itsNeedsActivation) { Kit.CodeBug(); } int index = scriptOrFn.GetIndexForNameNode(node); AddVarOp(Token.GETVAR, index); StackChange(1); break; } case Token.SETVAR: { if (itsData.itsNeedsActivation) { Kit.CodeBug(); } int index = scriptOrFn.GetIndexForNameNode(child); child = child.GetNext(); VisitExpression(child, 0); AddVarOp(Token.SETVAR, index); break; } case Token.SETCONSTVAR: { if (itsData.itsNeedsActivation) { Kit.CodeBug(); } int index = scriptOrFn.GetIndexForNameNode(child); child = child.GetNext(); VisitExpression(child, 0); AddVarOp(Token.SETCONSTVAR, index); break; } case Token.NULL: case Token.THIS: case Token.THISFN: case Token.FALSE: case Token.TRUE: { AddToken(type); StackChange(1); break; } case Token.ENUM_NEXT: case Token.ENUM_ID: { AddIndexOp(type, GetLocalBlockRef(node)); StackChange(1); break; } case Token.REGEXP: { int index = node.GetExistingIntProp(Node.REGEXP_PROP); AddIndexOp(Token.REGEXP, index); StackChange(1); break; } case Token.ARRAYLIT: case Token.OBJECTLIT: { VisitLiteral(node, child); break; } case Token.ARRAYCOMP: { VisitArrayComprehension(node, child, child.GetNext()); break; } case Token.REF_SPECIAL: { VisitExpression(child, 0); AddStringOp(type, (string)node.GetProp(Node.NAME_PROP)); break; } case Token.REF_MEMBER: case Token.REF_NS_MEMBER: case Token.REF_NAME: case Token.REF_NS_NAME: { int memberTypeFlags = node.GetIntProp(Node.MEMBER_TYPE_PROP, 0); // generate possible target, possible namespace and member int childCount = 0; do { VisitExpression(child, 0); ++childCount; child = child.GetNext(); } while (child != null); AddIndexOp(type, memberTypeFlags); StackChange(1 - childCount); break; } case Token.DOTQUERY: { int queryPC; UpdateLineNumber(node); VisitExpression(child, 0); AddIcode(Icode_ENTERDQ); StackChange(-1); queryPC = iCodeTop; VisitExpression(child.GetNext(), 0); AddBackwardGoto(Icode_LEAVEDQ, queryPC); break; } case Token.DEFAULTNAMESPACE: case Token.ESCXMLATTR: case Token.ESCXMLTEXT: { VisitExpression(child, 0); AddToken(type); break; } case Token.YIELD: { if (child != null) { VisitExpression(child, 0); } else { AddIcode(Icode_UNDEF); StackChange(1); } AddToken(Token.YIELD); AddUint16(node.GetLineno() & unchecked((int)(0xFFFF))); break; } case Token.WITHEXPR: { Node enterWith = node.GetFirstChild(); Node with = enterWith.GetNext(); VisitExpression(enterWith.GetFirstChild(), 0); AddToken(Token.ENTERWITH); StackChange(-1); VisitExpression(with.GetFirstChild(), 0); AddToken(Token.LEAVEWITH); break; } default: { throw BadTree(node); } } if (savedStackDepth + 1 != stackDepth) { Kit.CodeBug(); } }
private Node CreateBinary(int nodeType, Node left, Node right) { switch (nodeType) { case Token.ADD: { // numerical addition and string concatenation if (left.type == Token.STRING) { string s2; if (right.type == Token.STRING) { s2 = right.GetString(); } else { if (right.type == Token.NUMBER) { s2 = ScriptRuntime.NumberToString(right.GetDouble(), 10); } else { break; } } string s1 = left.GetString(); left.SetString(System.String.Concat(s1, s2)); return left; } else { if (left.type == Token.NUMBER) { if (right.type == Token.NUMBER) { left.SetDouble(left.GetDouble() + right.GetDouble()); return left; } else { if (right.type == Token.STRING) { string s1; string s2; s1 = ScriptRuntime.NumberToString(left.GetDouble(), 10); s2 = right.GetString(); right.SetString(System.String.Concat(s1, s2)); return right; } } } } // can't do anything if we don't know both types - since // 0 + object is supposed to call toString on the object and do // string concantenation rather than addition break; } case Token.SUB: { // numerical subtraction if (left.type == Token.NUMBER) { double ld = left.GetDouble(); if (right.type == Token.NUMBER) { //both numbers left.SetDouble(ld - right.GetDouble()); return left; } else { if (ld == 0.0) { // first 0: 0-x -> -x return new Node(Token.NEG, right); } } } else { if (right.type == Token.NUMBER) { if (right.GetDouble() == 0.0) { //second 0: x - 0 -> +x // can not make simply x because x - 0 must be number return new Node(Token.POS, left); } } } break; } case Token.MUL: { // numerical multiplication if (left.type == Token.NUMBER) { double ld = left.GetDouble(); if (right.type == Token.NUMBER) { //both numbers left.SetDouble(ld * right.GetDouble()); return left; } else { if (ld == 1.0) { // first 1: 1 * x -> +x return new Node(Token.POS, right); } } } else { if (right.type == Token.NUMBER) { if (right.GetDouble() == 1.0) { //second 1: x * 1 -> +x // can not make simply x because x - 0 must be number return new Node(Token.POS, left); } } } // can't do x*0: Infinity * 0 gives NaN, not 0 break; } case Token.DIV: { // number division if (right.type == Token.NUMBER) { double rd = right.GetDouble(); if (left.type == Token.NUMBER) { // both constants -- just divide, trust Java to handle x/0 left.SetDouble(left.GetDouble() / rd); return left; } else { if (rd == 1.0) { // second 1: x/1 -> +x // not simply x to force number convertion return new Node(Token.POS, left); } } } break; } case Token.AND: { // Since x && y gives x, not false, when Boolean(x) is false, // and y, not Boolean(y), when Boolean(x) is true, x && y // can only be simplified if x is defined. See bug 309957. int leftStatus = IsAlwaysDefinedBoolean(left); if (leftStatus == ALWAYS_FALSE_BOOLEAN) { // if the first one is false, just return it return left; } else { if (leftStatus == ALWAYS_TRUE_BOOLEAN) { // if first is true, set to second return right; } } break; } case Token.OR: { // Since x || y gives x, not true, when Boolean(x) is true, // and y, not Boolean(y), when Boolean(x) is false, x || y // can only be simplified if x is defined. See bug 309957. int leftStatus = IsAlwaysDefinedBoolean(left); if (leftStatus == ALWAYS_TRUE_BOOLEAN) { // if the first one is true, just return it return left; } else { if (leftStatus == ALWAYS_FALSE_BOOLEAN) { // if first is false, set to second return right; } } break; } } return new Node(nodeType, left, right); }
// Check if Node always mean true or false in boolean context private static int IsAlwaysDefinedBoolean(Node node) { switch (node.GetType()) { case Token.FALSE: case Token.NULL: { return ALWAYS_FALSE_BOOLEAN; } case Token.TRUE: { return ALWAYS_TRUE_BOOLEAN; } case Token.NUMBER: { double num = node.GetDouble(); if (num == num && num != 0.0) { return ALWAYS_TRUE_BOOLEAN; } else { return ALWAYS_FALSE_BOOLEAN; } break; } } return 0; }
private Node CreateUnary(int nodeType, Node child) { int childType = child.GetType(); switch (nodeType) { case Token.DELPROP: { Node n; if (childType == Token.NAME) { // Transform Delete(Name "a") // to Delete(Bind("a"), String("a")) child.SetType(Token.BINDNAME); Node left = child; Node right = Node.NewString(child.GetString()); n = new Node(nodeType, left, right); } else { if (childType == Token.GETPROP || childType == Token.GETELEM) { Node left = child.GetFirstChild(); Node right = child.GetLastChild(); child.RemoveChild(left); child.RemoveChild(right); n = new Node(nodeType, left, right); } else { if (childType == Token.GET_REF) { Node @ref = child.GetFirstChild(); child.RemoveChild(@ref); n = new Node(Token.DEL_REF, @ref); } else { // Always evaluate delete operand, see ES5 11.4.1 & bug #726121 n = new Node(nodeType, new Node(Token.TRUE), child); } } } return n; } case Token.TYPEOF: { if (childType == Token.NAME) { child.SetType(Token.TYPEOFNAME); return child; } break; } case Token.BITNOT: { if (childType == Token.NUMBER) { int value = ScriptRuntime.ToInt32(child.GetDouble()); child.SetDouble(~value); return child; } break; } case Token.NEG: { if (childType == Token.NUMBER) { child.SetDouble(-child.GetDouble()); return child; } break; } case Token.NOT: { int status = IsAlwaysDefinedBoolean(child); if (status != 0) { int type; if (status == ALWAYS_TRUE_BOOLEAN) { type = Token.FALSE; } else { type = Token.TRUE; } if (childType == Token.TRUE || childType == Token.FALSE) { child.SetType(type); return child; } return new Node(type); } break; } } return new Node(nodeType, child); }