private void VisitSetConstVar(Node node, Node child, bool needValue) { if (!hasVarsInRegs) { Kit.CodeBug(); } int varIndex = fnCurrent.GetVarIndex(node); GenerateExpression(child.GetNext(), node); bool isNumber = (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1); short reg = varRegisters[varIndex]; int beyond = cfw.AcquireLabel(); int noAssign = cfw.AcquireLabel(); if (isNumber) { cfw.AddILoad(reg + 2); cfw.Add(ByteCode.IFNE, noAssign); short stack = cfw.GetStackTop(); cfw.AddPush(1); cfw.AddIStore(reg + 2); cfw.AddDStore(reg); if (needValue) { cfw.AddDLoad(reg); cfw.MarkLabel(noAssign, stack); } else { cfw.Add(ByteCode.GOTO, beyond); cfw.MarkLabel(noAssign, stack); cfw.Add(ByteCode.POP2); } } else { cfw.AddILoad(reg + 1); cfw.Add(ByteCode.IFNE, noAssign); short stack = cfw.GetStackTop(); cfw.AddPush(1); cfw.AddIStore(reg + 1); cfw.AddAStore(reg); if (needValue) { cfw.AddALoad(reg); cfw.MarkLabel(noAssign, stack); } else { cfw.Add(ByteCode.GOTO, beyond); cfw.MarkLabel(noAssign, stack); cfw.Add(ByteCode.POP); } } cfw.MarkLabel(beyond); }
private void VisitSetElem(int type, Node node, Node child) { GenerateExpression(child, node); child = child.GetNext(); if (type == Token.SETELEM_OP) { cfw.Add(ByteCode.DUP); } GenerateExpression(child, node); child = child.GetNext(); bool indexIsNumber = (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1); if (type == Token.SETELEM_OP) { if (indexIsNumber) { // stack: ... object object number // -> ... object number object number cfw.Add(ByteCode.DUP2_X1); cfw.AddALoad(contextLocal); AddOptRuntimeInvoke("getObjectIndex", "(Ljava/lang/Object;D" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } else { // stack: ... object object indexObject // -> ... object indexObject object indexObject cfw.Add(ByteCode.DUP_X1); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("getObjectElem", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } } GenerateExpression(child, node); cfw.AddALoad(contextLocal); if (indexIsNumber) { AddScriptRuntimeInvoke("setObjectIndex", "(Ljava/lang/Object;" + "D" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } else { AddScriptRuntimeInvoke("setObjectElem", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Ljava/lang/Object;"); } }
private void VisitGetVar(Node node) { if (!hasVarsInRegs) { Kit.CodeBug(); } int varIndex = fnCurrent.GetVarIndex(node); short reg = varRegisters[varIndex]; if (VarIsDirectCallParameter(varIndex)) { // Remember that here the isNumber flag means that we // want to use the incoming parameter in a Number // context, so test the object type and convert the // value as necessary. if (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { DcpLoadAsNumber(reg); } else { DcpLoadAsObject(reg); } } else { if (fnCurrent.IsNumberVar(varIndex)) { cfw.AddDLoad(reg); } else { cfw.AddALoad(reg); } } }
private void VisitSetVar(Node node, Node child, bool needValue) { if (!hasVarsInRegs) { Kit.CodeBug(); } int varIndex = fnCurrent.GetVarIndex(node); GenerateExpression(child.GetNext(), node); bool isNumber = (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1); short reg = varRegisters[varIndex]; bool[] constDeclarations = fnCurrent.fnode.GetParamAndVarConst(); if (constDeclarations[varIndex]) { if (!needValue) { if (isNumber) { cfw.Add(ByteCode.POP2); } else { cfw.Add(ByteCode.POP); } } } else { if (VarIsDirectCallParameter(varIndex)) { if (isNumber) { if (needValue) { cfw.Add(ByteCode.DUP2); } cfw.AddALoad(reg); cfw.Add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); int isNumberLabel = cfw.AcquireLabel(); int beyond = cfw.AcquireLabel(); cfw.Add(ByteCode.IF_ACMPEQ, isNumberLabel); short stack = cfw.GetStackTop(); AddDoubleWrap(); cfw.AddAStore(reg); cfw.Add(ByteCode.GOTO, beyond); cfw.MarkLabel(isNumberLabel, stack); cfw.AddDStore(reg + 1); cfw.MarkLabel(beyond); } else { if (needValue) { cfw.Add(ByteCode.DUP); } cfw.AddAStore(reg); } } else { bool isNumberVar = fnCurrent.IsNumberVar(varIndex); if (isNumber) { if (isNumberVar) { cfw.AddDStore(reg); if (needValue) { cfw.AddDLoad(reg); } } else { if (needValue) { cfw.Add(ByteCode.DUP2); } // Cannot save number in variable since !isNumberVar, // so convert to object AddDoubleWrap(); cfw.AddAStore(reg); } } else { if (isNumberVar) { Kit.CodeBug(); } cfw.AddAStore(reg); if (needValue) { cfw.AddALoad(reg); } } } } }
private void VisitBitOp(Node node, int type, Node child) { int childNumberFlag = node.GetIntProp(Node.ISNUMBER_PROP, -1); GenerateExpression(child, node); // special-case URSH; work with the target arg as a long, so // that we can return a 32-bit unsigned value, and call // toUint32 instead of toInt32. if (type == Token.URSH) { AddScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J"); GenerateExpression(child.GetNext(), node); AddScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I"); // Looks like we need to explicitly mask the shift to 5 bits - // LUSHR takes 6 bits. cfw.AddPush(31); cfw.Add(ByteCode.IAND); cfw.Add(ByteCode.LUSHR); cfw.Add(ByteCode.L2D); AddDoubleWrap(); return; } if (childNumberFlag == -1) { AddScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I"); GenerateExpression(child.GetNext(), node); AddScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I"); } else { AddScriptRuntimeInvoke("toInt32", "(D)I"); GenerateExpression(child.GetNext(), node); AddScriptRuntimeInvoke("toInt32", "(D)I"); } switch (type) { case Token.BITOR: { cfw.Add(ByteCode.IOR); break; } case Token.BITXOR: { cfw.Add(ByteCode.IXOR); break; } case Token.BITAND: { cfw.Add(ByteCode.IAND); break; } case Token.RSH: { cfw.Add(ByteCode.ISHR); break; } case Token.LSH: { cfw.Add(ByteCode.ISHL); break; } default: { throw Codegen.BadTree(); } } cfw.Add(ByteCode.I2D); if (childNumberFlag == -1) { AddDoubleWrap(); } }
private void VisitIfJumpRelOp(Node node, Node child, int trueGOTO, int falseGOTO) { if (trueGOTO == -1 || falseGOTO == -1) { throw Codegen.BadTree(); } int type = node.GetType(); Node rChild = child.GetNext(); if (type == Token.INSTANCEOF || type == Token.IN) { GenerateExpression(child, node); GenerateExpression(rChild, node); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke((type == Token.INSTANCEOF) ? "instanceOf" : "in", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Z"); cfw.Add(ByteCode.IFNE, trueGOTO); cfw.Add(ByteCode.GOTO, falseGOTO); return; } int childNumberFlag = node.GetIntProp(Node.ISNUMBER_PROP, -1); int left_dcp_register = NodeIsDirectCallParameter(child); int right_dcp_register = NodeIsDirectCallParameter(rChild); if (childNumberFlag != -1) { // Force numeric context on both parameters and optimize // direct call case as Optimizer currently does not handle it if (childNumberFlag != Node.RIGHT) { // Left already has number content GenerateExpression(child, node); } else { if (left_dcp_register != -1) { DcpLoadAsNumber(left_dcp_register); } else { GenerateExpression(child, node); AddObjectToDouble(); } } if (childNumberFlag != Node.LEFT) { // Right already has number content GenerateExpression(rChild, node); } else { if (right_dcp_register != -1) { DcpLoadAsNumber(right_dcp_register); } else { GenerateExpression(rChild, node); AddObjectToDouble(); } } GenSimpleCompare(type, trueGOTO, falseGOTO); } else { if (left_dcp_register != -1 && right_dcp_register != -1) { // Generate code to dynamically check for number content // if both operands are dcp short stack = cfw.GetStackTop(); int leftIsNotNumber = cfw.AcquireLabel(); cfw.AddALoad(left_dcp_register); cfw.Add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); cfw.Add(ByteCode.IF_ACMPNE, leftIsNotNumber); cfw.AddDLoad(left_dcp_register + 1); DcpLoadAsNumber(right_dcp_register); GenSimpleCompare(type, trueGOTO, falseGOTO); if (stack != cfw.GetStackTop()) { throw Codegen.BadTree(); } cfw.MarkLabel(leftIsNotNumber); int rightIsNotNumber = cfw.AcquireLabel(); cfw.AddALoad(right_dcp_register); cfw.Add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); cfw.Add(ByteCode.IF_ACMPNE, rightIsNotNumber); cfw.AddALoad(left_dcp_register); AddObjectToDouble(); cfw.AddDLoad(right_dcp_register + 1); GenSimpleCompare(type, trueGOTO, falseGOTO); if (stack != cfw.GetStackTop()) { throw Codegen.BadTree(); } cfw.MarkLabel(rightIsNotNumber); // Load both register as objects to call generic cmp_* cfw.AddALoad(left_dcp_register); cfw.AddALoad(right_dcp_register); } else { GenerateExpression(child, node); GenerateExpression(rChild, node); } if (type == Token.GE || type == Token.GT) { cfw.Add(ByteCode.SWAP); } string routine = ((type == Token.LT) || (type == Token.GT)) ? "cmp_LT" : "cmp_LE"; AddScriptRuntimeInvoke(routine, "(Ljava/lang/Object;" + "Ljava/lang/Object;" + ")Z"); cfw.Add(ByteCode.IFNE, trueGOTO); cfw.Add(ByteCode.GOTO, falseGOTO); } }
private void VisitIncDec(Node node) { int incrDecrMask = node.GetExistingIntProp(Node.INCRDECR_PROP); Node child = node.GetFirstChild(); switch (child.GetType()) { case Token.GETVAR: { if (!hasVarsInRegs) { Kit.CodeBug(); } bool post = ((incrDecrMask & Node.POST_FLAG) != 0); int varIndex = fnCurrent.GetVarIndex(child); short reg = varRegisters[varIndex]; if (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { int offset = VarIsDirectCallParameter(varIndex) ? 1 : 0; cfw.AddDLoad(reg + offset); if (post) { cfw.Add(ByteCode.DUP2); } cfw.AddPush(1.0); if ((incrDecrMask & Node.DECR_FLAG) == 0) { cfw.Add(ByteCode.DADD); } else { cfw.Add(ByteCode.DSUB); } if (!post) { cfw.Add(ByteCode.DUP2); } cfw.AddDStore(reg + offset); } else { if (VarIsDirectCallParameter(varIndex)) { DcpLoadAsObject(reg); } else { cfw.AddALoad(reg); } if (post) { cfw.Add(ByteCode.DUP); } AddObjectToDouble(); cfw.AddPush(1.0); if ((incrDecrMask & Node.DECR_FLAG) == 0) { cfw.Add(ByteCode.DADD); } else { cfw.Add(ByteCode.DSUB); } AddDoubleWrap(); if (!post) { cfw.Add(ByteCode.DUP); } cfw.AddAStore(reg); break; } break; } case Token.NAME: { cfw.AddALoad(variableObjectLocal); cfw.AddPush(child.GetString()); // push name cfw.AddALoad(contextLocal); cfw.AddPush(incrDecrMask); AddScriptRuntimeInvoke("nameIncrDecr", "(Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/String;" + "Lorg/mozilla/javascript/Context;" + "I)Ljava/lang/Object;"); break; } case Token.GETPROPNOWARN: { throw Kit.CodeBug(); } case Token.GETPROP: { Node getPropChild = child.GetFirstChild(); GenerateExpression(getPropChild, node); GenerateExpression(getPropChild.GetNext(), node); cfw.AddALoad(contextLocal); cfw.AddPush(incrDecrMask); AddScriptRuntimeInvoke("propIncrDecr", "(Ljava/lang/Object;" + "Ljava/lang/String;" + "Lorg/mozilla/javascript/Context;" + "I)Ljava/lang/Object;"); break; } case Token.GETELEM: { Node elemChild = child.GetFirstChild(); GenerateExpression(elemChild, node); GenerateExpression(elemChild.GetNext(), node); cfw.AddALoad(contextLocal); cfw.AddPush(incrDecrMask); if (elemChild.GetNext().GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { AddOptRuntimeInvoke("elemIncrDecr", "(Ljava/lang/Object;" + "D" + "Lorg/mozilla/javascript/Context;" + "I" + ")Ljava/lang/Object;"); } else { AddScriptRuntimeInvoke("elemIncrDecr", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "I" + ")Ljava/lang/Object;"); } break; } case Token.GET_REF: { Node refChild = child.GetFirstChild(); GenerateExpression(refChild, node); cfw.AddALoad(contextLocal); cfw.AddPush(incrDecrMask); AddScriptRuntimeInvoke("refIncrDecr", "(Lorg/mozilla/javascript/Ref;" + "Lorg/mozilla/javascript/Context;" + "I)Ljava/lang/Object;"); break; } default: { Codegen.BadTree(); break; } } }
private void VisitArithmetic(Node node, int opCode, Node child, Node parent) { int childNumberFlag = node.GetIntProp(Node.ISNUMBER_PROP, -1); if (childNumberFlag != -1) { GenerateExpression(child, node); GenerateExpression(child.GetNext(), node); cfw.Add(opCode); } else { bool childOfArithmetic = IsArithmeticNode(parent); GenerateExpression(child, node); if (!IsArithmeticNode(child)) { AddObjectToDouble(); } GenerateExpression(child.GetNext(), node); if (!IsArithmeticNode(child.GetNext())) { AddObjectToDouble(); } cfw.Add(opCode); if (!childOfArithmetic) { AddDoubleWrap(); } } }
private void GenerateCallArgArray(Node node, Node argChild, bool directCall) { int argCount = 0; for (Node child = argChild; child != null; child = child.GetNext()) { ++argCount; } // load array object to set arguments if (argCount == 1 && itsOneArgArray >= 0) { cfw.AddALoad(itsOneArgArray); } else { AddNewObjectArray(argCount); } // Copy arguments into it for (int i = 0; i != argCount; ++i) { // If we are compiling a generator an argument could be the result // of a yield. In that case we will have an immediate on the stack // which we need to avoid if (!isGenerator) { cfw.Add(ByteCode.DUP); cfw.AddPush(i); } if (!directCall) { GenerateExpression(argChild, node); } else { // If this has also been a directCall sequence, the Number // flag will have remained set for any parameter so that // the values could be copied directly into the outgoing // args. Here we want to force it to be treated as not in // a Number context, so we set the flag off. int dcp_register = NodeIsDirectCallParameter(argChild); if (dcp_register >= 0) { DcpLoadAsObject(dcp_register); } else { GenerateExpression(argChild, node); int childNumberFlag = argChild.GetIntProp(Node.ISNUMBER_PROP, -1); if (childNumberFlag == Node.BOTH) { AddDoubleWrap(); } } } // When compiling generators, any argument to a method may be a // yield expression. Hence we compile the argument first and then // load the argument index and assign the value to the args array. if (isGenerator) { short tempLocal = GetNewWordLocal(); cfw.AddAStore(tempLocal); cfw.Add(ByteCode.CHECKCAST, "[Ljava/lang/Object;"); cfw.Add(ByteCode.DUP); cfw.AddPush(i); cfw.AddALoad(tempLocal); ReleaseWordLocal(tempLocal); } cfw.Add(ByteCode.AASTORE); argChild = argChild.GetNext(); } }
private void GenerateFunctionAndThisObj(Node node, Node parent) { // Place on stack (function object, function this) pair int type = node.GetType(); switch (node.GetType()) { case Token.GETPROPNOWARN: { throw Kit.CodeBug(); } case Token.GETPROP: case Token.GETELEM: { Node target = node.GetFirstChild(); GenerateExpression(target, node); Node id = target.GetNext(); if (type == Token.GETPROP) { string property = id.GetString(); cfw.AddPush(property); cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("getPropFunctionAndThis", "(Ljava/lang/Object;" + "Ljava/lang/String;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Callable;"); } else { GenerateExpression(id, node); // id if (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { AddDoubleWrap(); } cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("getElemFunctionAndThis", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/Callable;"); } break; } case Token.NAME: { string name = node.GetString(); cfw.AddPush(name); cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("getNameFunctionAndThis", "(Ljava/lang/String;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Callable;"); break; } default: { // including GETVAR GenerateExpression(node, parent); cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("getValueFunctionAndThis", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/Callable;"); break; } } // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis cfw.AddALoad(contextLocal); AddScriptRuntimeInvoke("lastStoredScriptable", "(Lorg/mozilla/javascript/Context;" + ")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 GenerateStatement(Node node) { UpdateLineNumber(node); int type = node.GetType(); Node child = node.GetFirstChild(); switch (type) { case Token.LOOP: case Token.LABEL: case Token.WITH: case Token.SCRIPT: case Token.BLOCK: case Token.EMPTY: { // no-ops. if (compilerEnv.IsGenerateObserverCount()) { // Need to add instruction count even for no-ops to catch // cases like while (1) {} AddInstructionCount(1); } while (child != null) { GenerateStatement(child); child = child.GetNext(); } break; } case Token.LOCAL_BLOCK: { bool prevLocal = inLocalBlock; inLocalBlock = true; int local = GetNewWordLocal(); if (isGenerator) { cfw.Add(ByteCode.ACONST_NULL); cfw.AddAStore(local); } node.PutIntProp(Node.LOCAL_PROP, local); while (child != null) { GenerateStatement(child); child = child.GetNext(); } ReleaseWordLocal((short)local); node.RemoveProp(Node.LOCAL_PROP); inLocalBlock = prevLocal; break; } case Token.FUNCTION: { int fnIndex = node.GetExistingIntProp(Node.FUNCTION_PROP); OptFunctionNode ofn = OptFunctionNode.Get(scriptOrFn, fnIndex); int t = ofn.fnode.GetFunctionType(); if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { VisitFunction(ofn, t); } else { if (t != FunctionNode.FUNCTION_STATEMENT) { throw Codegen.BadTree(); } } break; } case Token.TRY: { VisitTryCatchFinally((Jump)node, child); break; } case Token.CATCH_SCOPE: { // nothing stays on the stack on entry into a catch scope cfw.SetStackTop((short)0); int local = GetLocalBlockRegister(node); int scopeIndex = node.GetExistingIntProp(Node.CATCH_SCOPE_PROP); string name = child.GetString(); // name of exception child = child.GetNext(); GenerateExpression(child, node); // load expression object if (scopeIndex == 0) { cfw.Add(ByteCode.ACONST_NULL); } else { // Load previous catch scope object cfw.AddALoad(local); } cfw.AddPush(name); cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("newCatchScope", "(Ljava/lang/Throwable;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/String;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.AddAStore(local); break; } case Token.THROW: { GenerateExpression(child, node); if (compilerEnv.IsGenerateObserverCount()) { AddInstructionCount(); } GenerateThrowJavaScriptException(); break; } case Token.RETHROW: { if (compilerEnv.IsGenerateObserverCount()) { AddInstructionCount(); } cfw.AddALoad(GetLocalBlockRegister(node)); cfw.Add(ByteCode.ATHROW); break; } case Token.RETURN_RESULT: case Token.RETURN: { if (!isGenerator) { if (child != null) { GenerateExpression(child, node); } else { if (type == Token.RETURN) { Codegen.PushUndefined(cfw); } else { if (popvLocal < 0) { throw Codegen.BadTree(); } cfw.AddALoad(popvLocal); } } } if (compilerEnv.IsGenerateObserverCount()) { AddInstructionCount(); } if (epilogueLabel == -1) { if (!hasVarsInRegs) { throw Codegen.BadTree(); } epilogueLabel = cfw.AcquireLabel(); } cfw.Add(ByteCode.GOTO, epilogueLabel); break; } case Token.SWITCH: { if (compilerEnv.IsGenerateObserverCount()) { AddInstructionCount(); } VisitSwitch((Jump)node, child); break; } case Token.ENTERWITH: { GenerateExpression(child, node); cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("enterWith", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.AddAStore(variableObjectLocal); IncReferenceWordLocal(variableObjectLocal); break; } case Token.LEAVEWITH: { cfw.AddALoad(variableObjectLocal); AddScriptRuntimeInvoke("leaveWith", "(Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.AddAStore(variableObjectLocal); DecReferenceWordLocal(variableObjectLocal); break; } case Token.ENUM_INIT_KEYS: case Token.ENUM_INIT_VALUES: case Token.ENUM_INIT_ARRAY: { GenerateExpression(child, node); cfw.AddALoad(contextLocal); int enumType = type == Token.ENUM_INIT_KEYS ? ScriptRuntime.ENUMERATE_KEYS : type == Token.ENUM_INIT_VALUES ? ScriptRuntime.ENUMERATE_VALUES : ScriptRuntime.ENUMERATE_ARRAY; cfw.AddPush(enumType); AddScriptRuntimeInvoke("enumInit", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "I" + ")Ljava/lang/Object;"); cfw.AddAStore(GetLocalBlockRegister(node)); break; } case Token.EXPR_VOID: { if (child.GetType() == Token.SETVAR) { VisitSetVar(child, child.GetFirstChild(), false); } else { if (child.GetType() == Token.SETCONSTVAR) { VisitSetConstVar(child, child.GetFirstChild(), false); } else { if (child.GetType() == Token.YIELD) { GenerateYieldPoint(child, false); } else { GenerateExpression(child, node); if (node.GetIntProp(Node.ISNUMBER_PROP, -1) != -1) { cfw.Add(ByteCode.POP2); } else { cfw.Add(ByteCode.POP); } } } } break; } case Token.EXPR_RESULT: { GenerateExpression(child, node); if (popvLocal < 0) { popvLocal = GetNewWordLocal(); } cfw.AddAStore(popvLocal); break; } case Token.TARGET: { if (compilerEnv.IsGenerateObserverCount()) { AddInstructionCount(); } int label = GetTargetLabel(node); cfw.MarkLabel(label); if (compilerEnv.IsGenerateObserverCount()) { SaveCurrentCodeOffset(); } break; } case Token.JSR: case Token.GOTO: case Token.IFEQ: case Token.IFNE: { if (compilerEnv.IsGenerateObserverCount()) { AddInstructionCount(); } VisitGoto((Jump)node, type, child); break; } case Token.FINALLY: { // This is the non-exception case for a finally block. In // other words, since we inline finally blocks wherever // jsr was previously used, and jsr is only used when the // function is not a generator, we don't need to generate // this case if the function isn't a generator. if (!isGenerator) { break; } if (compilerEnv.IsGenerateObserverCount()) { SaveCurrentCodeOffset(); } // there is exactly one value on the stack when enterring // finally blocks: the return address (or its int encoding) cfw.SetStackTop((short)1); // Save return address in a new local int finallyRegister = GetNewWordLocal(); int finallyStart = cfw.AcquireLabel(); int finallyEnd = cfw.AcquireLabel(); cfw.MarkLabel(finallyStart); GenerateIntegerWrap(); cfw.AddAStore(finallyRegister); while (child != null) { GenerateStatement(child); child = child.GetNext(); } cfw.AddALoad(finallyRegister); cfw.Add(ByteCode.CHECKCAST, "java/lang/Integer"); GenerateIntegerUnwrap(); BodyCodegen.FinallyReturnPoint ret = finallys.Get(node); ret.tableLabel = cfw.AcquireLabel(); cfw.Add(ByteCode.GOTO, ret.tableLabel); ReleaseWordLocal((short)finallyRegister); cfw.MarkLabel(finallyEnd); break; } case Token.DEBUGGER: { break; } default: { throw Codegen.BadTree(); } } }
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 void VisitStatement(Node node, int initialStackDepth) { int type = node.GetType(); Node child = node.GetFirstChild(); switch (type) { case Token.FUNCTION: { int fnIndex = node.GetExistingIntProp(Node.FUNCTION_PROP); int fnType = scriptOrFn.GetFunctionNode(fnIndex).GetFunctionType(); // Only function expressions or function expression // statements need closure code creating new function // object on stack as function statements are initialized // at script/function start. // In addition, function expressions can not be present here // at statement level, they must only be present as expressions. if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { AddIndexOp(Icode_CLOSURE_STMT, fnIndex); } else { if (fnType != FunctionNode.FUNCTION_STATEMENT) { throw Kit.CodeBug(); } } // For function statements or function expression statements // in scripts, we need to ensure that the result of the script // is the function if it is the last statement in the script. // For example, eval("function () {}") should return a // function, not undefined. if (!itsInFunctionFlag) { AddIndexOp(Icode_CLOSURE_EXPR, fnIndex); StackChange(1); AddIcode(Icode_POP_RESULT); StackChange(-1); } break; } case Token.LABEL: case Token.LOOP: case Token.BLOCK: case Token.EMPTY: case Token.WITH: { UpdateLineNumber(node); goto case Token.SCRIPT; } case Token.SCRIPT: { // fall through while (child != null) { VisitStatement(child, initialStackDepth); child = child.GetNext(); } break; } case Token.ENTERWITH: { VisitExpression(child, 0); AddToken(Token.ENTERWITH); StackChange(-1); break; } case Token.LEAVEWITH: { AddToken(Token.LEAVEWITH); break; } case Token.LOCAL_BLOCK: { int local = AllocLocal(); node.PutIntProp(Node.LOCAL_PROP, local); UpdateLineNumber(node); while (child != null) { VisitStatement(child, initialStackDepth); child = child.GetNext(); } AddIndexOp(Icode_LOCAL_CLEAR, local); ReleaseLocal(local); break; } case Token.DEBUGGER: { AddIcode(Icode_DEBUGGER); break; } case Token.SWITCH: { UpdateLineNumber(node); // See comments in IRFactory.createSwitch() for description // of SWITCH node VisitExpression(child, 0); for (Jump caseNode = (Jump)child.GetNext(); caseNode != null; caseNode = (Jump)caseNode.GetNext()) { if (caseNode.GetType() != Token.CASE) { throw BadTree(caseNode); } Node test = caseNode.GetFirstChild(); AddIcode(Icode_DUP); StackChange(1); VisitExpression(test, 0); AddToken(Token.SHEQ); StackChange(-1); // If true, Icode_IFEQ_POP will jump and remove case // value from stack AddGoto(caseNode.target, Icode_IFEQ_POP); StackChange(-1); } AddIcode(Icode_POP); StackChange(-1); break; } case Token.TARGET: { MarkTargetLabel(node); break; } case Token.IFEQ: case Token.IFNE: { Node target = ((Jump)node).target; VisitExpression(child, 0); AddGoto(target, type); StackChange(-1); break; } case Token.GOTO: { Node target = ((Jump)node).target; AddGoto(target, type); break; } case Token.JSR: { Node target = ((Jump)node).target; AddGoto(target, Icode_GOSUB); break; } case Token.FINALLY: { // Account for incomming GOTOSUB address StackChange(1); int finallyRegister = GetLocalBlockRef(node); AddIndexOp(Icode_STARTSUB, finallyRegister); StackChange(-1); while (child != null) { VisitStatement(child, initialStackDepth); child = child.GetNext(); } AddIndexOp(Icode_RETSUB, finallyRegister); break; } case Token.EXPR_VOID: case Token.EXPR_RESULT: { UpdateLineNumber(node); VisitExpression(child, 0); AddIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT); StackChange(-1); break; } case Token.TRY: { Jump tryNode = (Jump)node; int exceptionObjectLocal = GetLocalBlockRef(tryNode); int scopeLocal = AllocLocal(); AddIndexOp(Icode_SCOPE_SAVE, scopeLocal); int tryStart = iCodeTop; bool savedFlag = itsInTryFlag; itsInTryFlag = true; while (child != null) { VisitStatement(child, initialStackDepth); child = child.GetNext(); } itsInTryFlag = savedFlag; Node catchTarget = tryNode.target; if (catchTarget != null) { int catchStartPC = labelTable[GetTargetLabel(catchTarget)]; AddExceptionHandler(tryStart, catchStartPC, catchStartPC, false, exceptionObjectLocal, scopeLocal); } Node finallyTarget = tryNode.GetFinally(); if (finallyTarget != null) { int finallyStartPC = labelTable[GetTargetLabel(finallyTarget)]; AddExceptionHandler(tryStart, finallyStartPC, finallyStartPC, true, exceptionObjectLocal, scopeLocal); } AddIndexOp(Icode_LOCAL_CLEAR, scopeLocal); ReleaseLocal(scopeLocal); break; } case Token.CATCH_SCOPE: { int localIndex = GetLocalBlockRef(node); int scopeIndex = node.GetExistingIntProp(Node.CATCH_SCOPE_PROP); string name = child.GetString(); child = child.GetNext(); VisitExpression(child, 0); // load expression object AddStringPrefix(name); AddIndexPrefix(localIndex); AddToken(Token.CATCH_SCOPE); AddUint8(scopeIndex != 0 ? 1 : 0); StackChange(-1); break; } case Token.THROW: { UpdateLineNumber(node); VisitExpression(child, 0); AddToken(Token.THROW); AddUint16(lineNumber & unchecked((int)(0xFFFF))); StackChange(-1); break; } case Token.RETHROW: { UpdateLineNumber(node); AddIndexOp(Token.RETHROW, GetLocalBlockRef(node)); break; } case Token.RETURN: { UpdateLineNumber(node); if (node.GetIntProp(Node.GENERATOR_END_PROP, 0) != 0) { // We're in a generator, so change RETURN to GENERATOR_END AddIcode(Icode_GENERATOR_END); AddUint16(lineNumber & unchecked((int)(0xFFFF))); } else { if (child != null) { VisitExpression(child, ECF_TAIL); AddToken(Token.RETURN); StackChange(-1); } else { AddIcode(Icode_RETUNDEF); } } break; } case Token.RETURN_RESULT: { UpdateLineNumber(node); AddToken(Token.RETURN_RESULT); break; } case Token.ENUM_INIT_KEYS: case Token.ENUM_INIT_VALUES: case Token.ENUM_INIT_ARRAY: { VisitExpression(child, 0); AddIndexOp(type, GetLocalBlockRef(node)); StackChange(-1); break; } case Icode_GENERATOR: { break; } default: { throw BadTree(node); } } if (stackDepth != initialStackDepth) { throw Kit.CodeBug(); } }
public int GetVarIndex(Node n) { int index = n.GetIntProp(Node.VARIABLE_PROP, -1); if (index == -1) { Node node; int type = n.GetType(); if (type == Token.GETVAR) { node = n; } else { if (type == Token.SETVAR || type == Token.SETCONSTVAR) { node = n.GetFirstChild(); } else { throw Kit.CodeBug(); } } index = fnode.GetIndexForNameNode(node); if (index < 0) { throw Kit.CodeBug(); } n.PutIntProp(Node.VARIABLE_PROP, index); } return index; }