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); }
// 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 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); }
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)); }