internal virtual void PushNumberAsObject(ClassFileWriter cfw, double num) { if (num == 0.0) { if (1 / num > 0) { // +0.0 cfw.Add(ByteCode.GETSTATIC, "org/mozilla/javascript/optimizer/OptRuntime", "zeroObj", "Ljava/lang/Double;"); } else { cfw.AddPush(num); AddDoubleWrap(cfw); } } else { if (num == 1.0) { cfw.Add(ByteCode.GETSTATIC, "org/mozilla/javascript/optimizer/OptRuntime", "oneObj", "Ljava/lang/Double;"); return; } else { if (num == -1.0) { cfw.Add(ByteCode.GETSTATIC, "org/mozilla/javascript/optimizer/OptRuntime", "minusOneObj", "Ljava/lang/Double;"); } else { if (num != num) { cfw.Add(ByteCode.GETSTATIC, "org/mozilla/javascript/ScriptRuntime", "NaNobj", "Ljava/lang/Double;"); } else { if (itsConstantListSize >= 2000) { // There appears to be a limit in the JVM on either the number // of static fields in a class or the size of the class // initializer. Either way, we can't have any more than 2000 // statically init'd constants. cfw.AddPush(num); AddDoubleWrap(cfw); } else { int N = itsConstantListSize; int index = 0; if (N == 0) { itsConstantList = new double[64]; } else { double[] array = itsConstantList; while (index != N && array[index] != num) { ++index; } if (N == array.Length) { array = new double[N * 2]; System.Array.Copy(itsConstantList, 0, array, 0, N); itsConstantList = array; } } if (index == N) { itsConstantList[N] = num; itsConstantListSize = N + 1; } string constantName = "_k" + index; string constantType = GetStaticConstantWrapperType(num); cfw.Add(ByteCode.GETSTATIC, mainClassName, constantName, constantType); } } } } } }
private void EmitConstantDudeInitializers(ClassFileWriter cfw) { int N = itsConstantListSize; if (N == 0) { return; } cfw.StartMethod("<clinit>", "()V", (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_FINAL)); double[] array = itsConstantList; for (int i = 0; i != N; ++i) { double num = array[i]; string constantName = "_k" + i; string constantType = GetStaticConstantWrapperType(num); cfw.AddField(constantName, constantType, (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE)); int inum = (int)num; if (inum == num) { cfw.AddPush(inum); cfw.AddInvoke(ByteCode.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); } else { cfw.AddPush(num); AddDoubleWrap(cfw); } cfw.Add(ByteCode.PUTSTATIC, mainClassName, constantName, constantType); } cfw.Add(ByteCode.RETURN); cfw.StopMethod((short)0); }
private void EmitRegExpInit(ClassFileWriter cfw) { // precompile all regexp literals int totalRegCount = 0; for (int i = 0; i != scriptOrFnNodes.Length; ++i) { totalRegCount += scriptOrFnNodes[i].GetRegexpCount(); } if (totalRegCount == 0) { return; } cfw.StartMethod(REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE, (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE)); cfw.AddField("_reInitDone", "Z", (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE | ClassFileWriter.ACC_VOLATILE)); cfw.Add(ByteCode.GETSTATIC, mainClassName, "_reInitDone", "Z"); int doInit = cfw.AcquireLabel(); cfw.Add(ByteCode.IFEQ, doInit); cfw.Add(ByteCode.RETURN); cfw.MarkLabel(doInit); // get regexp proxy and store it in local slot 1 cfw.AddALoad(0); // context cfw.AddInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "checkRegExpProxy", "(Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/RegExpProxy;"); cfw.AddAStore(1); // proxy // We could apply double-checked locking here but concurrency // shouldn't be a problem in practice for (int i_1 = 0; i_1 != scriptOrFnNodes.Length; ++i_1) { ScriptNode n = scriptOrFnNodes[i_1]; int regCount = n.GetRegexpCount(); for (int j = 0; j != regCount; ++j) { string reFieldName = GetCompiledRegexpName(n, j); string reFieldType = "Ljava/lang/Object;"; string reString = n.GetRegexpString(j); string reFlags = n.GetRegexpFlags(j); cfw.AddField(reFieldName, reFieldType, (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE)); cfw.AddALoad(1); // proxy cfw.AddALoad(0); // context cfw.AddPush(reString); if (reFlags == null) { cfw.Add(ByteCode.ACONST_NULL); } else { cfw.AddPush(reFlags); } cfw.AddInvoke(ByteCode.INVOKEINTERFACE, "org/mozilla/javascript/RegExpProxy", "compileRegExp", "(Lorg/mozilla/javascript/Context;" + "Ljava/lang/String;Ljava/lang/String;" + ")Ljava/lang/Object;"); cfw.Add(ByteCode.PUTSTATIC, mainClassName, reFieldName, reFieldType); } } cfw.AddPush(1); cfw.Add(ByteCode.PUTSTATIC, mainClassName, "_reInitDone", "Z"); cfw.Add(ByteCode.RETURN); cfw.StopMethod((short)2); }
private void GenerateNativeFunctionOverrides(ClassFileWriter cfw, string encodedSource) { // Override NativeFunction.getLanguageVersion() with // public int getLanguageVersion() { return <version-constant>; } cfw.StartMethod("getLanguageVersion", "()I", ClassFileWriter.ACC_PUBLIC); cfw.AddPush(compilerEnv.GetLanguageVersion()); cfw.Add(ByteCode.IRETURN); // 1: this and no argument or locals cfw.StopMethod((short)1); // The rest of NativeFunction overrides require specific code for each // script/function id int Do_getFunctionName = 0; int Do_getParamCount = 1; int Do_getParamAndVarCount = 2; int Do_getParamOrVarName = 3; int Do_getEncodedSource = 4; int Do_getParamOrVarConst = 5; int SWITCH_COUNT = 6; for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) { if (methodIndex == Do_getEncodedSource && encodedSource == null) { continue; } // Generate: // prologue; // switch over function id to implement function-specific action // epilogue short methodLocals; switch (methodIndex) { case Do_getFunctionName: { methodLocals = 1; // Only this cfw.StartMethod("getFunctionName", "()Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC); break; } case Do_getParamCount: { methodLocals = 1; // Only this cfw.StartMethod("getParamCount", "()I", ClassFileWriter.ACC_PUBLIC); break; } case Do_getParamAndVarCount: { methodLocals = 1; // Only this cfw.StartMethod("getParamAndVarCount", "()I", ClassFileWriter.ACC_PUBLIC); break; } case Do_getParamOrVarName: { methodLocals = 1 + 1; // this + paramOrVarIndex cfw.StartMethod("getParamOrVarName", "(I)Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC); break; } case Do_getParamOrVarConst: { methodLocals = 1 + 1 + 1; // this + paramOrVarName cfw.StartMethod("getParamOrVarConst", "(I)Z", ClassFileWriter.ACC_PUBLIC); break; } case Do_getEncodedSource: { methodLocals = 1; // Only this cfw.StartMethod("getEncodedSource", "()Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC); cfw.AddPush(encodedSource); break; } default: { throw Kit.CodeBug(); } } int count = scriptOrFnNodes.Length; int switchStart = 0; int switchStackTop = 0; if (count > 1) { // Generate switch but only if there is more then one // script/function cfw.AddLoadThis(); cfw.Add(ByteCode.GETFIELD, cfw.GetClassName(), ID_FIELD_NAME, "I"); // do switch from 1 .. count - 1 mapping 0 to the default case switchStart = cfw.AddTableSwitch(1, count - 1); } for (int i = 0; i != count; ++i) { ScriptNode n = scriptOrFnNodes[i]; if (i == 0) { if (count > 1) { cfw.MarkTableSwitchDefault(switchStart); switchStackTop = cfw.GetStackTop(); } } else { cfw.MarkTableSwitchCase(switchStart, i - 1, switchStackTop); } switch (methodIndex) { case Do_getFunctionName: { // Impelemnet method-specific switch code // Push function name if (n.GetType() == Token.SCRIPT) { cfw.AddPush(string.Empty); } else { string name = ((FunctionNode)n).GetName(); cfw.AddPush(name); } cfw.Add(ByteCode.ARETURN); break; } case Do_getParamCount: { // Push number of defined parameters cfw.AddPush(n.GetParamCount()); cfw.Add(ByteCode.IRETURN); break; } case Do_getParamAndVarCount: { // Push number of defined parameters and declared variables cfw.AddPush(n.GetParamAndVarCount()); cfw.Add(ByteCode.IRETURN); break; } case Do_getParamOrVarName: { // Push name of parameter using another switch // over paramAndVarCount int paramAndVarCount = n.GetParamAndVarCount(); if (paramAndVarCount == 0) { // The runtime should never call the method in this // case but to make bytecode verifier happy return null // as throwing execption takes more code cfw.Add(ByteCode.ACONST_NULL); cfw.Add(ByteCode.ARETURN); } else { if (paramAndVarCount == 1) { // As above do not check for valid index but always // return the name of the first param cfw.AddPush(n.GetParamOrVarName(0)); cfw.Add(ByteCode.ARETURN); } else { // Do switch over getParamOrVarName cfw.AddILoad(1); // param or var index // do switch from 1 .. paramAndVarCount - 1 mapping 0 // to the default case int paramSwitchStart = cfw.AddTableSwitch(1, paramAndVarCount - 1); for (int j = 0; j != paramAndVarCount; ++j) { if (cfw.GetStackTop() != 0) { Kit.CodeBug(); } string s = n.GetParamOrVarName(j); if (j == 0) { cfw.MarkTableSwitchDefault(paramSwitchStart); } else { cfw.MarkTableSwitchCase(paramSwitchStart, j - 1, 0); } cfw.AddPush(s); cfw.Add(ByteCode.ARETURN); } } } break; } case Do_getParamOrVarConst: { // Push name of parameter using another switch // over paramAndVarCount paramAndVarCount = n.GetParamAndVarCount(); bool[] constness = n.GetParamAndVarConst(); if (paramAndVarCount == 0) { // The runtime should never call the method in this // case but to make bytecode verifier happy return null // as throwing execption takes more code cfw.Add(ByteCode.ICONST_0); cfw.Add(ByteCode.IRETURN); } else { if (paramAndVarCount == 1) { // As above do not check for valid index but always // return the name of the first param cfw.AddPush(constness[0]); cfw.Add(ByteCode.IRETURN); } else { // Do switch over getParamOrVarName cfw.AddILoad(1); // param or var index // do switch from 1 .. paramAndVarCount - 1 mapping 0 // to the default case int paramSwitchStart = cfw.AddTableSwitch(1, paramAndVarCount - 1); for (int j = 0; j != paramAndVarCount; ++j) { if (cfw.GetStackTop() != 0) { Kit.CodeBug(); } if (j == 0) { cfw.MarkTableSwitchDefault(paramSwitchStart); } else { cfw.MarkTableSwitchCase(paramSwitchStart, j - 1, 0); } cfw.AddPush(constness[j]); cfw.Add(ByteCode.IRETURN); } } } break; } case Do_getEncodedSource: { // Push number encoded source start and end // to prepare for encodedSource.substring(start, end) cfw.AddPush(n.GetEncodedSourceStart()); cfw.AddPush(n.GetEncodedSourceEnd()); cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;"); cfw.Add(ByteCode.ARETURN); break; } default: { throw Kit.CodeBug(); } } } cfw.StopMethod(methodLocals); } }
private void GenerateScriptCtor(ClassFileWriter cfw) { cfw.StartMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC); cfw.AddLoadThis(); cfw.AddInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, "<init>", "()V"); // set id to 0 cfw.AddLoadThis(); cfw.AddPush(0); cfw.Add(ByteCode.PUTFIELD, cfw.GetClassName(), ID_FIELD_NAME, "I"); cfw.Add(ByteCode.RETURN); // 1 parameter = this cfw.StopMethod((short)1); }
private void GenerateCallMethod(ClassFileWriter cfw) { cfw.StartMethod("call", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;)Ljava/lang/Object;", (short)(ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL)); // Generate code for: // if (!ScriptRuntime.hasTopCall(cx)) { // return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); // } int nonTopCallLabel = cfw.AcquireLabel(); cfw.AddALoad(1); //cx cfw.AddInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "hasTopCall", "(Lorg/mozilla/javascript/Context;" + ")Z"); cfw.Add(ByteCode.IFNE, nonTopCallLabel); cfw.AddALoad(0); cfw.AddALoad(1); cfw.AddALoad(2); cfw.AddALoad(3); cfw.AddALoad(4); cfw.AddInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "doTopCall", "(Lorg/mozilla/javascript/Callable;" + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + ")Ljava/lang/Object;"); cfw.Add(ByteCode.ARETURN); cfw.MarkLabel(nonTopCallLabel); // Now generate switch to call the real methods cfw.AddALoad(0); cfw.AddALoad(1); cfw.AddALoad(2); cfw.AddALoad(3); cfw.AddALoad(4); int end = scriptOrFnNodes.Length; bool generateSwitch = (2 <= end); int switchStart = 0; int switchStackTop = 0; if (generateSwitch) { cfw.AddLoadThis(); cfw.Add(ByteCode.GETFIELD, cfw.GetClassName(), ID_FIELD_NAME, "I"); // do switch from (1, end - 1) mapping 0 to // the default case switchStart = cfw.AddTableSwitch(1, end - 1); } for (int i = 0; i != end; ++i) { ScriptNode n = scriptOrFnNodes[i]; if (generateSwitch) { if (i == 0) { cfw.MarkTableSwitchDefault(switchStart); switchStackTop = cfw.GetStackTop(); } else { cfw.MarkTableSwitchCase(switchStart, i - 1, switchStackTop); } } if (n.GetType() == Token.FUNCTION) { OptFunctionNode ofn = OptFunctionNode.Get(n); if (ofn.IsTargetOfDirectCall()) { int pcount = ofn.fnode.GetParamCount(); if (pcount != 0) { // loop invariant: // stack top == arguments array from addALoad4() for (int p = 0; p != pcount; ++p) { cfw.Add(ByteCode.ARRAYLENGTH); cfw.AddPush(p); int undefArg = cfw.AcquireLabel(); int beyond = cfw.AcquireLabel(); cfw.Add(ByteCode.IF_ICMPLE, undefArg); // get array[p] cfw.AddALoad(4); cfw.AddPush(p); cfw.Add(ByteCode.AALOAD); cfw.Add(ByteCode.GOTO, beyond); cfw.MarkLabel(undefArg); PushUndefined(cfw); cfw.MarkLabel(beyond); // Only one push cfw.AdjustStackTop(-1); cfw.AddPush(0.0); // restore invariant cfw.AddALoad(4); } } } } cfw.AddInvoke(ByteCode.INVOKESTATIC, mainClassName, GetBodyMethodName(n), GetBodyMethodSignature(n)); cfw.Add(ByteCode.ARETURN); } cfw.StopMethod((short)5); }