internal static void RunFlowAnalyzes(OptFunctionNode fn, Node[] statementNodes) { int paramCount = fn.fnode.GetParamCount(); int varCount = fn.fnode.GetParamAndVarCount(); int[] varTypes = new int[varCount]; // If the variable is a parameter, it could have any type. for (int i = 0; i != paramCount; ++i) { varTypes[i] = Rhino.Optimizer.Optimizer.AnyType; } // If the variable is from a "var" statement, its typeEvent will be set // when we see the setVar node. for (int i_1 = paramCount; i_1 != varCount; ++i_1) { varTypes[i_1] = Rhino.Optimizer.Optimizer.NoType; } Block[] theBlocks = BuildBlocks(statementNodes); ReachingDefDataFlow(fn, statementNodes, theBlocks, varTypes); TypeFlow(fn, statementNodes, theBlocks, varTypes); for (int i_2 = paramCount; i_2 != varCount; i_2++) { if (varTypes[i_2] == Rhino.Optimizer.Optimizer.NumberType) { fn.SetIsNumberVar(i_2); } } }
private void OptimizeFunction(OptFunctionNode theFunction) { if (theFunction.fnode.RequiresActivation()) { return; } inDirectCallFunction = theFunction.IsTargetOfDirectCall(); this.theFunction = theFunction; ObjArray statementsArray = new ObjArray(); BuildStatementList_r(theFunction.fnode, statementsArray); Node[] theStatementNodes = new Node[statementsArray.Size()]; statementsArray.ToArray(theStatementNodes); Block.RunFlowAnalyzes(theFunction, theStatementNodes); if (!theFunction.fnode.RequiresActivation()) { parameterUsedInNumberContext = false; foreach (Node theStatementNode in theStatementNodes) { RewriteForNumberVariables(theStatementNode, NumberType); } theFunction.SetParameterNumberContext(parameterUsedInNumberContext); } }
private void GenerateFunctionInit(ClassFileWriter cfw, OptFunctionNode ofn) { int CONTEXT_ARG = 1; int SCOPE_ARG = 2; cfw.StartMethod(GetFunctionInitMethodName(ofn), FUNCTION_INIT_SIGNATURE, (short)(ClassFileWriter.ACC_PRIVATE | ClassFileWriter.ACC_FINAL)); // Call NativeFunction.initScriptFunction cfw.AddLoadThis(); cfw.AddALoad(CONTEXT_ARG); cfw.AddALoad(SCOPE_ARG); cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, "org/mozilla/javascript/NativeFunction", "initScriptFunction", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")V"); // precompile all regexp literals if (ofn.fnode.GetRegexpCount() != 0) { cfw.AddALoad(CONTEXT_ARG); cfw.AddInvoke(ByteCode.INVOKESTATIC, mainClassName, REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE); } cfw.Add(ByteCode.RETURN); // 3 = (scriptThis/functionRef) + scope + context cfw.StopMethod((short)3); }
private void VisitOptimizedCall(Node node, OptFunctionNode target, int type, Node child) { Node firstArgChild = child.GetNext(); string className = codegen.mainClassName; short thisObjLocal = 0; if (type == Token.NEW) { GenerateExpression(child, node); } else { GenerateFunctionAndThisObj(child, node); thisObjLocal = GetNewWordLocal(); cfw.AddAStore(thisObjLocal); } // stack: ... functionObj int beyond = cfw.AcquireLabel(); int regularCall = cfw.AcquireLabel(); cfw.Add(ByteCode.DUP); cfw.Add(ByteCode.INSTANCEOF, className); cfw.Add(ByteCode.IFEQ, regularCall); cfw.Add(ByteCode.CHECKCAST, className); cfw.Add(ByteCode.DUP); cfw.Add(ByteCode.GETFIELD, className, Codegen.ID_FIELD_NAME, "I"); cfw.AddPush(codegen.GetIndex(target.fnode)); cfw.Add(ByteCode.IF_ICMPNE, regularCall); // stack: ... directFunct cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); // stack: ... directFunc cx scope if (type == Token.NEW) { cfw.Add(ByteCode.ACONST_NULL); } else { cfw.AddALoad(thisObjLocal); } // stack: ... directFunc cx scope thisObj Node argChild = firstArgChild; while (argChild != null) { int dcp_register = NodeIsDirectCallParameter(argChild); if (dcp_register >= 0) { cfw.AddALoad(dcp_register); cfw.AddDLoad(dcp_register + 1); } else { if (argChild.GetIntProp(Node.ISNUMBER_PROP, -1) == Node.BOTH) { cfw.Add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); GenerateExpression(argChild, node); } else { GenerateExpression(argChild, node); cfw.AddPush(0.0); } } argChild = argChild.GetNext(); } cfw.Add(ByteCode.GETSTATIC, "org/mozilla/javascript/ScriptRuntime", "emptyArgs", "[Ljava/lang/Object;"); cfw.AddInvoke(ByteCode.INVOKESTATIC, codegen.mainClassName, (type == Token.NEW) ? codegen.GetDirectCtorName(target.fnode) : codegen.GetBodyMethodName(target.fnode), codegen.GetBodyMethodSignature(target.fnode)); cfw.Add(ByteCode.GOTO, beyond); cfw.MarkLabel(regularCall); // stack: ... functionObj cfw.AddALoad(contextLocal); cfw.AddALoad(variableObjectLocal); // stack: ... functionObj cx scope if (type != Token.NEW) { cfw.AddALoad(thisObjLocal); ReleaseWordLocal(thisObjLocal); } // stack: ... functionObj cx scope thisObj // XXX: this will generate code for the child array the second time, // so expression code generation better not to alter tree structure... GenerateCallArgArray(node, firstArgChild, true); if (type == Token.NEW) { AddScriptRuntimeInvoke("newObject", "(Ljava/lang/Object;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + ")Lorg/mozilla/javascript/Scriptable;"); } else { cfw.AddInvoke(ByteCode.INVOKEINTERFACE, "org/mozilla/javascript/Callable", "call", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + ")Ljava/lang/Object;"); } cfw.MarkLabel(beyond); }
private void VisitFunction(OptFunctionNode ofn, int functionType) { int fnIndex = codegen.GetIndex(ofn.fnode); cfw.Add(ByteCode.NEW, codegen.mainClassName); // Call function constructor cfw.Add(ByteCode.DUP); cfw.AddALoad(variableObjectLocal); cfw.AddALoad(contextLocal); // load 'cx' cfw.AddPush(fnIndex); cfw.AddInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName, "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE); if (functionType == FunctionNode.FUNCTION_EXPRESSION) { // Leave closure object on stack and do not pass it to // initFunction which suppose to connect statements to scope return; } cfw.AddPush(functionType); cfw.AddALoad(variableObjectLocal); cfw.AddALoad(contextLocal); // load 'cx' AddOptRuntimeInvoke("initFunction", "(Lorg/mozilla/javascript/NativeFunction;" + "I" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Context;" + ")V"); }
private void EmitDirectConstructor(ClassFileWriter cfw, OptFunctionNode ofn) { cfw.StartMethod(GetDirectCtorName(ofn.fnode), GetBodyMethodSignature(ofn.fnode), (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE)); int argCount = ofn.fnode.GetParamCount(); int firstLocal = (4 + argCount * 3) + 1; cfw.AddALoad(0); // this cfw.AddALoad(1); // cx cfw.AddALoad(2); // scope cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, "org/mozilla/javascript/BaseFunction", "createObject", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.AddAStore(firstLocal); cfw.AddALoad(0); cfw.AddALoad(1); cfw.AddALoad(2); cfw.AddALoad(firstLocal); for (int i = 0; i < argCount; i++) { cfw.AddALoad(4 + (i * 3)); cfw.AddDLoad(5 + (i * 3)); } cfw.AddALoad(4 + argCount * 3); cfw.AddInvoke(ByteCode.INVOKESTATIC, mainClassName, GetBodyMethodName(ofn.fnode), GetBodyMethodSignature(ofn.fnode)); int exitLabel = cfw.AcquireLabel(); cfw.Add(ByteCode.DUP); // make a copy of direct call result cfw.Add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Scriptable"); cfw.Add(ByteCode.IFEQ, exitLabel); // cast direct call result cfw.Add(ByteCode.CHECKCAST, "org/mozilla/javascript/Scriptable"); cfw.Add(ByteCode.ARETURN); cfw.MarkLabel(exitLabel); cfw.AddALoad(firstLocal); cfw.Add(ByteCode.ARETURN); cfw.StopMethod((short)(firstLocal + 1)); }
internal virtual string GetFunctionInitMethodName(OptFunctionNode ofn) { return "_i" + GetIndex(ofn.fnode); }
private void PrintLiveOnEntrySet(OptFunctionNode fn) { }
private static bool FindDefPoints(OptFunctionNode fn, Node n, int[] varTypes) { bool result = false; Node first = n.GetFirstChild(); for (Node next = first; next != null; next = next.GetNext()) { result |= FindDefPoints(fn, next, varTypes); } switch (n.GetType()) { case Token.DEC: case Token.INC: { if (first.GetType() == Token.GETVAR) { // theVar is a Number now int i = fn.GetVarIndex(first); result |= AssignType(varTypes, i, Rhino.Optimizer.Optimizer.NumberType); } break; } case Token.SETVAR: { Node rValue = first.GetNext(); int theType = FindExpressionType(fn, rValue, varTypes); int i = fn.GetVarIndex(n); result |= AssignType(varTypes, i, theType); break; } } return result; }
private bool DoTypeFlow(OptFunctionNode fn, Node[] statementNodes, int[] varTypes) { bool changed = false; for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) { Node n = statementNodes[i]; if (n != null) { changed |= FindDefPoints(fn, n, varTypes); } } return changed; }
private static int FindExpressionType(OptFunctionNode fn, Node n, int[] varTypes) { switch (n.GetType()) { case Token.NUMBER: { return Rhino.Optimizer.Optimizer.NumberType; } case Token.CALL: case Token.NEW: case Token.REF_CALL: { return Rhino.Optimizer.Optimizer.AnyType; } case Token.GETELEM: case Token.GETPROP: case Token.NAME: case Token.THIS: { return Rhino.Optimizer.Optimizer.AnyType; } case Token.GETVAR: { return varTypes[fn.GetVarIndex(n)]; } case Token.INC: case Token.DEC: case Token.MUL: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.BITNOT: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.POS: case Token.NEG: { return Rhino.Optimizer.Optimizer.NumberType; } case Token.VOID: { // NYI: undefined type return Rhino.Optimizer.Optimizer.AnyType; } case Token.FALSE: case Token.TRUE: case Token.EQ: case Token.NE: case Token.LT: case Token.LE: case Token.GT: case Token.GE: case Token.SHEQ: case Token.SHNE: case Token.NOT: case Token.INSTANCEOF: case Token.IN: case Token.DEL_REF: case Token.DELPROP: { // NYI: boolean type return Rhino.Optimizer.Optimizer.AnyType; } case Token.STRING: case Token.TYPEOF: case Token.TYPEOFNAME: { // NYI: string type return Rhino.Optimizer.Optimizer.AnyType; } case Token.NULL: case Token.REGEXP: case Token.ARRAYCOMP: case Token.ARRAYLIT: case Token.OBJECTLIT: { return Rhino.Optimizer.Optimizer.AnyType; } case Token.ADD: { // XXX: actually, we know it's not // number, but no type yet for that // if the lhs & rhs are known to be numbers, we can be sure that's // the result, otherwise it could be a string. Node child = n.GetFirstChild(); int lType = FindExpressionType(fn, child, varTypes); int rType = FindExpressionType(fn, child.GetNext(), varTypes); return lType | rType; } case Token.HOOK: { // we're not distinguishing strings yet Node ifTrue = n.GetFirstChild().GetNext(); Node ifFalse = ifTrue.GetNext(); int ifTrueType = FindExpressionType(fn, ifTrue, varTypes); int ifFalseType = FindExpressionType(fn, ifFalse, varTypes); return ifTrueType | ifFalseType; } case Token.COMMA: case Token.SETVAR: case Token.SETNAME: case Token.SETPROP: case Token.SETELEM: { return FindExpressionType(fn, n.GetLastChild(), varTypes); } case Token.AND: case Token.OR: { Node child = n.GetFirstChild(); int lType = FindExpressionType(fn, child, varTypes); int rType = FindExpressionType(fn, child.GetNext(), varTypes); return lType | rType; } } return Rhino.Optimizer.Optimizer.AnyType; }
private void InitLiveOnEntrySets(OptFunctionNode fn, Node[] statementNodes) { int listLength = fn.GetVarCount(); itsUseBeforeDefSet = new BitArray(listLength); itsNotDefSet = new BitArray(listLength); itsLiveOnEntrySet = new BitArray(listLength); itsLiveOnExitSet = new BitArray(listLength); for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) { Node n = statementNodes[i]; LookForVariableAccess(fn, n); } itsNotDefSet.Flip(0, listLength); }
private void LookForVariableAccess(OptFunctionNode fn, Node n) { switch (n.GetType()) { case Token.TYPEOFNAME: { // TYPEOFNAME may be used with undefined names, which is why // this is handled separately from GETVAR above. int varIndex = fn.fnode.GetIndexForNameNode(n); if (varIndex > -1 && !itsNotDefSet.Get(varIndex)) { itsUseBeforeDefSet.Set(varIndex); } break; } case Token.DEC: case Token.INC: { Node child = n.GetFirstChild(); if (child.GetType() == Token.GETVAR) { int varIndex = fn.GetVarIndex(child); if (!itsNotDefSet.Get(varIndex)) { itsUseBeforeDefSet.Set(varIndex); } itsNotDefSet.Set(varIndex); } else { LookForVariableAccess(fn, child); } break; } case Token.SETVAR: { Node lhs = n.GetFirstChild(); Node rhs = lhs.GetNext(); LookForVariableAccess(fn, rhs); itsNotDefSet.Set(fn.GetVarIndex(n)); break; } case Token.GETVAR: { int varIndex = fn.GetVarIndex(n); if (!itsNotDefSet.Get(varIndex)) { itsUseBeforeDefSet.Set(varIndex); } break; } default: { Node child_1 = n.GetFirstChild(); while (child_1 != null) { LookForVariableAccess(fn, child_1); child_1 = child_1.GetNext(); } break; } } }
private static void TypeFlow(OptFunctionNode fn, Node[] statementNodes, Block[] theBlocks, int[] varTypes) { bool[] visit = new bool[theBlocks.Length]; bool[] doneOnce = new bool[theBlocks.Length]; int vIndex = 0; bool needRescan = false; visit[vIndex] = true; while (true) { if (visit[vIndex] || !doneOnce[vIndex]) { doneOnce[vIndex] = true; visit[vIndex] = false; if (theBlocks[vIndex].DoTypeFlow(fn, statementNodes, varTypes)) { Block[] succ = theBlocks[vIndex].itsSuccessors; if (succ != null) { for (int i = 0; i < succ.Length; i++) { int index = succ[i].itsBlockID; visit[index] = true; needRescan |= (index < vIndex); } } } } if (vIndex == (theBlocks.Length - 1)) { if (needRescan) { vIndex = 0; needRescan = false; } else { break; } } else { vIndex++; } } }
private static void ReachingDefDataFlow(OptFunctionNode fn, Node[] statementNodes, Block[] theBlocks, int[] varTypes) { for (int i = 0; i < theBlocks.Length; i++) { theBlocks[i].InitLiveOnEntrySets(fn, statementNodes); } bool[] visit = new bool[theBlocks.Length]; bool[] doneOnce = new bool[theBlocks.Length]; int vIndex = theBlocks.Length - 1; bool needRescan = false; visit[vIndex] = true; while (true) { if (visit[vIndex] || !doneOnce[vIndex]) { doneOnce[vIndex] = true; visit[vIndex] = false; if (theBlocks[vIndex].DoReachedUseDataFlow()) { Block[] pred = theBlocks[vIndex].itsPredecessors; if (pred != null) { for (int i_1 = 0; i_1 < pred.Length; i_1++) { int index = pred[i_1].itsBlockID; visit[index] = true; needRescan |= (index > vIndex); } } } } if (vIndex == 0) { if (needRescan) { vIndex = theBlocks.Length - 1; needRescan = false; } else { break; } } else { vIndex--; } } theBlocks[0].MarkAnyTypeVariables(varTypes); }