private int RewriteForNumberVariables(Node n, int desired) { switch (n.GetType()) { case Token.EXPR_VOID: { Node child = n.GetFirstChild(); int type = RewriteForNumberVariables(child, NumberType); if (type == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } return NoType; } case Token.NUMBER: { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } case Token.GETVAR: { int varIndex = theFunction.GetVarIndex(n); if (inDirectCallFunction && theFunction.IsParameter(varIndex) && desired == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } else { if (theFunction.IsNumberVar(varIndex)) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } } return NoType; } case Token.INC: case Token.DEC: { Node child = n.GetFirstChild(); int type = RewriteForNumberVariables(child, NumberType); if (child.GetType() == Token.GETVAR) { if (type == NumberType && !ConvertParameter(child)) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); MarkDCPNumberContext(child); return NumberType; } return NoType; } else { if (child.GetType() == Token.GETELEM || child.GetType() == Token.GETPROP) { return type; } } return NoType; } case Token.SETVAR: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int rType = RewriteForNumberVariables(rChild, NumberType); int varIndex = theFunction.GetVarIndex(n); if (inDirectCallFunction && theFunction.IsParameter(varIndex)) { if (rType == NumberType) { if (!ConvertParameter(rChild)) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } MarkDCPNumberContext(rChild); return NoType; } else { return rType; } } else { if (theFunction.IsNumberVar(varIndex)) { if (rType != NumberType) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_DOUBLE, rChild)); } n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); MarkDCPNumberContext(rChild); return NumberType; } else { if (rType == NumberType) { if (!ConvertParameter(rChild)) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_OBJECT, rChild)); } } return NoType; } } goto case Token.LE; } case Token.LE: case Token.LT: case Token.GE: case Token.GT: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int lType = RewriteForNumberVariables(lChild, NumberType); int rType = RewriteForNumberVariables(rChild, NumberType); MarkDCPNumberContext(lChild); MarkDCPNumberContext(rChild); if (ConvertParameter(lChild)) { if (ConvertParameter(rChild)) { return NoType; } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } else { if (ConvertParameter(rChild)) { if (lType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (lType == NumberType) { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } else { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } } // we actually build a boolean value return NoType; } case Token.ADD: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int lType = RewriteForNumberVariables(lChild, NumberType); int rType = RewriteForNumberVariables(rChild, NumberType); if (ConvertParameter(lChild)) { if (ConvertParameter(rChild)) { return NoType; } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } else { if (ConvertParameter(rChild)) { if (lType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (lType == NumberType) { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } else { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } } return NoType; } case Token.BITXOR: case Token.BITOR: case Token.BITAND: case Token.RSH: case Token.LSH: case Token.SUB: case Token.MUL: case Token.DIV: case Token.MOD: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int lType = RewriteForNumberVariables(lChild, NumberType); int rType = RewriteForNumberVariables(rChild, NumberType); MarkDCPNumberContext(lChild); MarkDCPNumberContext(rChild); if (lType == NumberType) { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } else { if (!ConvertParameter(rChild)) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_DOUBLE, rChild)); n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } return NumberType; } } else { if (rType == NumberType) { if (!ConvertParameter(lChild)) { n.RemoveChild(lChild); n.AddChildToFront(new Node(Token.TO_DOUBLE, lChild)); n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } return NumberType; } else { if (!ConvertParameter(lChild)) { n.RemoveChild(lChild); n.AddChildToFront(new Node(Token.TO_DOUBLE, lChild)); } if (!ConvertParameter(rChild)) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_DOUBLE, rChild)); } n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } } goto case Token.SETELEM; } case Token.SETELEM: case Token.SETELEM_OP: { Node arrayBase = n.GetFirstChild(); Node arrayIndex = arrayBase.GetNext(); Node rValue = arrayIndex.GetNext(); int baseType = RewriteForNumberVariables(arrayBase, NumberType); if (baseType == NumberType) { if (!ConvertParameter(arrayBase)) { n.RemoveChild(arrayBase); n.AddChildToFront(new Node(Token.TO_OBJECT, arrayBase)); } } int indexType = RewriteForNumberVariables(arrayIndex, NumberType); if (indexType == NumberType) { if (!ConvertParameter(arrayIndex)) { // setting the ISNUMBER_PROP signals the codegen // to use the OptRuntime.setObjectIndex that takes // a double index n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } int rValueType = RewriteForNumberVariables(rValue, NumberType); if (rValueType == NumberType) { if (!ConvertParameter(rValue)) { n.RemoveChild(rValue); n.AddChildToBack(new Node(Token.TO_OBJECT, rValue)); } } return NoType; } case Token.GETELEM: { Node arrayBase = n.GetFirstChild(); Node arrayIndex = arrayBase.GetNext(); int baseType = RewriteForNumberVariables(arrayBase, NumberType); if (baseType == NumberType) { if (!ConvertParameter(arrayBase)) { n.RemoveChild(arrayBase); n.AddChildToFront(new Node(Token.TO_OBJECT, arrayBase)); } } int indexType = RewriteForNumberVariables(arrayIndex, NumberType); if (indexType == NumberType) { if (!ConvertParameter(arrayIndex)) { // setting the ISNUMBER_PROP signals the codegen // to use the OptRuntime.getObjectIndex that takes // a double index n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } return NoType; } case Token.CALL: { Node child = n.GetFirstChild(); // the function node // must be an object RewriteAsObjectChildren(child, child.GetFirstChild()); child = child.GetNext(); // the first arg OptFunctionNode target = (OptFunctionNode)n.GetProp(Node.DIRECTCALL_PROP); if (target != null) { while (child != null) { int type = RewriteForNumberVariables(child, NumberType); if (type == NumberType) { MarkDCPNumberContext(child); } child = child.GetNext(); } } else { RewriteAsObjectChildren(n, child); } return NoType; } default: { RewriteAsObjectChildren(n, n.GetFirstChild()); return NoType; } } }
private void VisitObjectLiteral(Node node, Node child, bool topLevel) { object[] properties = (object[])node.GetProp(Node.OBJECT_IDS_PROP); int count = properties.Length; // If code budget is tight swap out literals into separate method if (!topLevel && (count > 10 || cfw.GetCurrentCodeOffset() > 30000) && !hasVarsInRegs && !isGenerator && !inLocalBlock) { if (literals == null) { literals = new List<Node>(); } literals.Add(node); string methodName = codegen.GetBodyMethodName(scriptOrFn) + "_literal" + literals.Count; cfw.AddALoad(funObjLocal); cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); cfw.AddALoad(thisObjLocal); cfw.AddALoad(argsLocal); cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, codegen.mainClassName, methodName, "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + ")Lorg/mozilla/javascript/Scriptable;"); return; } // load array with property ids AddNewObjectArray(count); for (int i = 0; i != count; ++i) { cfw.Add(ByteCode.DUP); cfw.AddPush(i); object id = properties[i]; if (id is string) { cfw.AddPush((string)id); } else { cfw.AddPush(System.Convert.ToInt32(((int)id))); AddScriptRuntimeInvoke("wrapInt", "(I)Ljava/lang/Integer;"); } cfw.Add(ByteCode.AASTORE); } // load array with property values AddNewObjectArray(count); Node child2 = child; for (int i_1 = 0; i_1 != count; ++i_1) { cfw.Add(ByteCode.DUP); cfw.AddPush(i_1); int childType = child2.GetType(); if (childType == Token.GET || childType == Token.SET) { GenerateExpression(child2.GetFirstChild(), node); } else { GenerateExpression(child2, node); } cfw.Add(ByteCode.AASTORE); child2 = child2.GetNext(); } // check if object literal actually has any getters or setters bool hasGetterSetters = false; child2 = child; for (int i_2 = 0; i_2 != count; ++i_2) { int childType = child2.GetType(); if (childType == Token.GET || childType == Token.SET) { hasGetterSetters = true; break; } child2 = child2.GetNext(); } // create getter/setter flag array if (hasGetterSetters) { cfw.AddPush(count); cfw.Add(ByteCode.NEWARRAY, ByteCode.T_INT); child2 = child; for (int i_3 = 0; i_3 != count; ++i_3) { cfw.Add(ByteCode.DUP); cfw.AddPush(i_3); int childType = child2.GetType(); if (childType == Token.GET) { cfw.Add(ByteCode.ICONST_M1); } else { if (childType == Token.SET) { cfw.Add(ByteCode.ICONST_1); } else { cfw.Add(ByteCode.ICONST_0); } } cfw.Add(ByteCode.IASTORE); child2 = child2.GetNext(); } } else { cfw.Add(ByteCode.ACONST_NULL); } cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("newObjectLiteral", "([Ljava/lang/Object;" + "[Ljava/lang/Object;" + "[I" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); }
private int GetLocalBlockRegister(Node node) { Node localBlock = (Node)node.GetProp(Node.LOCAL_BLOCK_PROP); int localSlot = localBlock.GetExistingIntProp(Node.LOCAL_PROP); return localSlot; }
private void VisitArrayLiteral(Node node, Node child, bool topLevel) { int count = 0; for (Node cursor = child; cursor != null; cursor = cursor.GetNext()) { ++count; } // If code budget is tight swap out literals into separate method if (!topLevel && (count > 10 || cfw.GetCurrentCodeOffset() > 30000) && !hasVarsInRegs && !isGenerator && !inLocalBlock) { if (literals == null) { literals = new List<Node>(); } literals.Add(node); string methodName = codegen.GetBodyMethodName(scriptOrFn) + "_literal" + literals.Count; cfw.AddALoad(funObjLocal); cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); cfw.AddALoad(thisObjLocal); cfw.AddALoad(argsLocal); cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, codegen.mainClassName, methodName, "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + ")Lorg/mozilla/javascript/Scriptable;"); return; } // load array to store array literal objects AddNewObjectArray(count); for (int i = 0; i != count; ++i) { cfw.Add(ByteCode.DUP); cfw.AddPush(i); GenerateExpression(child, node); cfw.Add(ByteCode.AASTORE); child = child.GetNext(); } int[] skipIndexes = (int[])node.GetProp(Node.SKIP_INDEXES_PROP); if (skipIndexes == null) { cfw.Add(ByteCode.ACONST_NULL); cfw.Add(ByteCode.ICONST_0); } else { cfw.AddPush(OptRuntime.EncodeIntArray(skipIndexes)); cfw.AddPush(skipIndexes.Length); } cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); AddOptRuntimeInvoke("newArrayLiteral", "([Ljava/lang/Object;" + "Ljava/lang/String;" + "I" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); }
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 int GetLocalBlockRef(Node node) { Node localBlock = (Node)node.GetProp(Node.LOCAL_BLOCK_PROP); return localBlock.GetExistingIntProp(Node.LOCAL_PROP); }
private void VisitLiteral(Node node, Node child) { int type = node.GetType(); int count; object[] propertyIds = null; if (type == Token.ARRAYLIT) { count = 0; for (Node n = child; n != null; n = n.GetNext()) { ++count; } } else { if (type == Token.OBJECTLIT) { propertyIds = (object[])node.GetProp(Node.OBJECT_IDS_PROP); count = propertyIds.Length; } else { throw BadTree(node); } } AddIndexOp(Icode_LITERAL_NEW, count); StackChange(2); while (child != null) { int childType = child.GetType(); if (childType == Token.GET) { VisitExpression(child.GetFirstChild(), 0); AddIcode(Icode_LITERAL_GETTER); } else { if (childType == Token.SET) { VisitExpression(child.GetFirstChild(), 0); AddIcode(Icode_LITERAL_SETTER); } else { VisitExpression(child, 0); AddIcode(Icode_LITERAL_SET); } } StackChange(-1); child = child.GetNext(); } if (type == Token.ARRAYLIT) { int[] skipIndexes = (int[])node.GetProp(Node.SKIP_INDEXES_PROP); if (skipIndexes == null) { AddToken(Token.ARRAYLIT); } else { int index = literalIds.Size(); literalIds.Add(skipIndexes); AddIndexOp(Icode_SPARE_ARRAYLIT, index); } } else { int index = literalIds.Size(); literalIds.Add(propertyIds); AddIndexOp(Token.OBJECTLIT, index); } StackChange(-1); }