private void GenerateFunctionConstructor(ClassFileWriter cfw) { int SCOPE_ARG = 1; int CONTEXT_ARG = 2; int ID_ARG = 3; cfw.StartMethod("<init>", FUNCTION_CONSTRUCTOR_SIGNATURE, ClassFileWriter.ACC_PUBLIC); cfw.AddALoad(0); cfw.AddInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, "<init>", "()V"); cfw.AddLoadThis(); cfw.AddILoad(ID_ARG); cfw.Add(ByteCode.PUTFIELD, cfw.GetClassName(), ID_FIELD_NAME, "I"); cfw.AddLoadThis(); cfw.AddALoad(CONTEXT_ARG); cfw.AddALoad(SCOPE_ARG); int start = (scriptOrFnNodes[0].GetType() == Token.SCRIPT) ? 1 : 0; int end = scriptOrFnNodes.Length; if (start == end) { throw BadTree(); } bool generateSwitch = (2 <= end - start); int switchStart = 0; int switchStackTop = 0; if (generateSwitch) { cfw.AddILoad(ID_ARG); // do switch from (start + 1, end - 1) mapping start to // the default case switchStart = cfw.AddTableSwitch(start + 1, end - 1); } for (int i = start; i != end; ++i) { if (generateSwitch) { if (i == start) { cfw.MarkTableSwitchDefault(switchStart); switchStackTop = cfw.GetStackTop(); } else { cfw.MarkTableSwitchCase(switchStart, i - 1 - start, switchStackTop); } } OptFunctionNode ofn = OptFunctionNode.Get(scriptOrFnNodes[i]); cfw.AddInvoke(ByteCode.INVOKESPECIAL, mainClassName, GetFunctionInitMethodName(ofn), FUNCTION_INIT_SIGNATURE); cfw.Add(ByteCode.RETURN); } // 4 = this + scope + context + id cfw.StopMethod((short)4); }
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 GenerateExecute(ClassFileWriter cfw) { cfw.StartMethod("exec", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;", (short)(ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL)); int CONTEXT_ARG = 1; int SCOPE_ARG = 2; cfw.AddLoadThis(); cfw.AddALoad(CONTEXT_ARG); cfw.AddALoad(SCOPE_ARG); cfw.Add(ByteCode.DUP); cfw.Add(ByteCode.ACONST_NULL); cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, cfw.GetClassName(), "call", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + ")Ljava/lang/Object;"); cfw.Add(ByteCode.ARETURN); // 3 = this + context + scope cfw.StopMethod((short)3); }
// 5: this, cx, scope, js this, args[] private void GenerateMain(ClassFileWriter cfw) { cfw.StartMethod("main", "([Ljava/lang/String;)V", (short)(ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_STATIC)); // load new ScriptImpl() cfw.Add(ByteCode.NEW, cfw.GetClassName()); cfw.Add(ByteCode.DUP); cfw.AddInvoke(ByteCode.INVOKESPECIAL, cfw.GetClassName(), "<init>", "()V"); // load 'args' cfw.Add(ByteCode.ALOAD_0); // Call mainMethodClass.main(Script script, String[] args) cfw.AddInvoke(ByteCode.INVOKESTATIC, mainMethodClass, "main", "(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V"); cfw.Add(ByteCode.RETURN); // 1 = String[] args 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); }
// How dispatch to generators works: // Two methods are generated corresponding to a user-written generator. // One of these creates a generator object (NativeGenerator), which is // returned to the user. The other method contains all of the body code // of the generator. // When a user calls a generator, the call() method dispatches control to // to the method that creates the NativeGenerator object. Subsequently when // the user invokes .next(), .send() or any such method on the generator // object, the resumeGenerator() below dispatches the call to the // method corresponding to the generator body. As a matter of convention // the generator body is given the name of the generator activation function // appended by "_gen". private void GenerateResumeGenerator(ClassFileWriter cfw) { bool hasGenerators = false; for (int i = 0; i < scriptOrFnNodes.Length; i++) { if (IsGenerator(scriptOrFnNodes[i])) { hasGenerators = true; } } // if there are no generators defined, we don't implement a // resumeGenerator(). The base class provides a default implementation. if (!hasGenerators) { return; } cfw.StartMethod("resumeGenerator", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "ILjava/lang/Object;" + "Ljava/lang/Object;)Ljava/lang/Object;", (short)(ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL)); // load arguments for dispatch to the corresponding *_gen method cfw.AddALoad(0); cfw.AddALoad(1); cfw.AddALoad(2); cfw.AddALoad(4); cfw.AddALoad(5); cfw.AddILoad(3); cfw.AddLoadThis(); cfw.Add(ByteCode.GETFIELD, cfw.GetClassName(), ID_FIELD_NAME, "I"); int startSwitch = cfw.AddTableSwitch(0, scriptOrFnNodes.Length - 1); cfw.MarkTableSwitchDefault(startSwitch); int endlabel = cfw.AcquireLabel(); for (int i_1 = 0; i_1 < scriptOrFnNodes.Length; i_1++) { ScriptNode n = scriptOrFnNodes[i_1]; cfw.MarkTableSwitchCase(startSwitch, i_1, (short)6); if (IsGenerator(n)) { string type = "(" + mainClassSignature + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/Object;" + "Ljava/lang/Object;I)Ljava/lang/Object;"; cfw.AddInvoke(ByteCode.INVOKESTATIC, mainClassName, GetBodyMethodName(n) + "_gen", type); cfw.Add(ByteCode.ARETURN); } else { cfw.Add(ByteCode.GOTO, endlabel); } } cfw.MarkLabel(endlabel); PushUndefined(cfw); cfw.Add(ByteCode.ARETURN); // this method uses as many locals as there are arguments (hence 6) cfw.StopMethod((short)6); }