public StopMethod ( short maxLocals ) : void | ||
maxLocals | short | /// the maximum number of local variable slots /// (a.k.a. Java registers) used by the method /// |
Результат | void |
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 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 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 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 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 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); }
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)); }