0001: /***** BEGIN LICENSE BLOCK *****
0002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
0003: *
0004: * The contents of this file are subject to the Common Public
0005: * License Version 1.0 (the "License"); you may not use this file
0006: * except in compliance with the License. You may obtain a copy of
0007: * the License at http://www.eclipse.org/legal/cpl-v10.html
0008: *
0009: * Software distributed under the License is distributed on an "AS
0010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
0011: * implied. See the License for the specific language governing
0012: * rights and limitations under the License.
0013: *
0014: * Copyright (C) 2006 Charles O Nutter <headius@headius.com>
0015: *
0016: * Alternatively, the contents of this file may be used under the terms of
0017: * either of the GNU General Public License Version 2 or later (the "GPL"),
0018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
0019: * in which case the provisions of the GPL or the LGPL are applicable instead
0020: * of those above. If you wish to allow use of your version of this file only
0021: * under the terms of either the GPL or the LGPL, and not to allow others to
0022: * use your version of this file under the terms of the CPL, indicate your
0023: * decision by deleting the provisions above and replace them with the notice
0024: * and other provisions required by the GPL or the LGPL. If you do not delete
0025: * the provisions above, a recipient may use your version of this file under
0026: * the terms of any one of the CPL, the GPL or the LGPL.
0027: ***** END LICENSE BLOCK *****/package org.jruby.compiler.impl;
0028:
0029: import java.io.File;
0030: import java.io.FileOutputStream;
0031: import java.io.IOException;
0032: import java.io.PrintStream;
0033: import java.math.BigInteger;
0034: import java.util.HashMap;
0035: import java.util.Map;
0036: import java.util.Stack;
0037:
0038: import jregex.Pattern;
0039:
0040: import org.jruby.Ruby;
0041: import org.jruby.RubyArray;
0042: import org.jruby.RubyBignum;
0043: import org.jruby.RubyBoolean;
0044: import org.jruby.RubyClass;
0045: import org.jruby.RubyFixnum;
0046: import org.jruby.RubyFloat;
0047: import org.jruby.RubyHash;
0048: import org.jruby.RubyModule;
0049: import org.jruby.RubyRange;
0050: import org.jruby.RubyRegexp;
0051: import org.jruby.RubyString;
0052: import org.jruby.RubySymbol;
0053: import org.jruby.ast.Node;
0054: import org.jruby.ast.executable.Script;
0055: import org.jruby.compiler.ArrayCallback;
0056: import org.jruby.compiler.BranchCallback;
0057: import org.jruby.compiler.ClosureCallback;
0058: import org.jruby.compiler.Compiler;
0059: import org.jruby.compiler.NotCompilableException;
0060: import org.jruby.evaluator.EvaluationState;
0061: import org.jruby.exceptions.JumpException;
0062: import org.jruby.internal.runtime.GlobalVariables;
0063: import org.jruby.javasupport.util.CompilerHelpers;
0064: import org.jruby.lexer.yacc.ISourcePosition;
0065: import org.jruby.parser.ReOptions;
0066: import org.jruby.parser.StaticScope;
0067: import org.jruby.runtime.Arity;
0068: import org.jruby.runtime.Block;
0069: import org.jruby.runtime.CallType;
0070: import org.jruby.runtime.CallbackFactory;
0071: import org.jruby.runtime.CompiledBlock;
0072: import org.jruby.runtime.CompiledBlockCallback;
0073: import org.jruby.runtime.DynamicScope;
0074: import org.jruby.runtime.MethodIndex;
0075: import org.jruby.runtime.ThreadContext;
0076: import org.jruby.runtime.Visibility;
0077: import org.jruby.runtime.builtin.IRubyObject;
0078: import org.jruby.util.ByteList;
0079: import org.jruby.util.CodegenUtils;
0080: import org.jruby.util.JRubyClassLoader;
0081: import org.objectweb.asm.ClassVisitor;
0082: import org.objectweb.asm.ClassWriter;
0083: import org.objectweb.asm.Label;
0084: import org.objectweb.asm.Opcodes;
0085:
0086: /**
0087: *
0088: * @author headius
0089: */
0090: public class StandardASMCompiler implements Compiler, Opcodes {
0091: private static final CodegenUtils cg = CodegenUtils.cg;
0092: private static final String THREADCONTEXT = cg
0093: .p(ThreadContext.class);
0094: private static final String RUBY = cg.p(Ruby.class);
0095: private static final String IRUBYOBJECT = cg.p(IRubyObject.class);
0096:
0097: private static final String METHOD_SIGNATURE = cg.sig(
0098: IRubyObject.class,
0099: new Class[] { ThreadContext.class, IRubyObject.class,
0100: IRubyObject[].class, Block.class });
0101: private static final String CLOSURE_SIGNATURE = cg.sig(
0102: IRubyObject.class, new Class[] { ThreadContext.class,
0103: IRubyObject.class, IRubyObject[].class });
0104:
0105: private static final int THREADCONTEXT_INDEX = 0;
0106: private static final int SELF_INDEX = 1;
0107: private static final int ARGS_INDEX = 2;
0108: private static final int CLOSURE_INDEX = 3;
0109: private static final int SCOPE_INDEX = 4;
0110: private static final int RUNTIME_INDEX = 5;
0111: private static final int VISIBILITY_INDEX = 6;
0112: private static final int LOCAL_VARS_INDEX = 7;
0113:
0114: private Stack SkinnyMethodAdapters = new Stack();
0115: private Stack arities = new Stack();
0116: private Stack scopeStarts = new Stack();
0117:
0118: private String classname;
0119: private String sourcename;
0120:
0121: //Map classWriters = new HashMacg.p();
0122: private ClassWriter classWriter;
0123: ClassWriter currentMultiStub = null;
0124: int methodIndex = -1;
0125: int multiStubCount = -1;
0126: int innerIndex = -1;
0127:
0128: int lastLine = -1;
0129:
0130: /**
0131: * Used to make determinations about non-local returns and similar flow control
0132: */
0133: boolean isCompilingClosure;
0134:
0135: /** Creates a new instance of StandardCompilerContext */
0136: public StandardASMCompiler(String classname, String sourcename) {
0137: this .classname = classname;
0138: this .sourcename = sourcename;
0139: }
0140:
0141: public StandardASMCompiler(Node node) {
0142: // determine new class name based on filename of incoming node
0143: // must generate unique classnames for evals, since they could be generated many times in a given run
0144: classname = "EVAL" + hashCode();
0145: sourcename = "EVAL" + hashCode();
0146: }
0147:
0148: public Class loadClass(JRubyClassLoader classLoader)
0149: throws ClassNotFoundException {
0150: classLoader.defineClass(cg.c(classname), classWriter
0151: .toByteArray());
0152:
0153: return classLoader.loadClass(cg.c(classname));
0154: }
0155:
0156: public void writeClass(File destination) throws IOException {
0157: writeClass(classname, destination, classWriter);
0158: }
0159:
0160: private void writeClass(String classname, File destination,
0161: ClassWriter writer) throws IOException {
0162: String fullname = classname + ".class";
0163: String filename = null;
0164: String path = null;
0165: if (fullname.lastIndexOf("/") == -1) {
0166: filename = fullname;
0167: path = "";
0168: } else {
0169: filename = fullname
0170: .substring(fullname.lastIndexOf("/") + 1);
0171: path = fullname.substring(0, fullname.lastIndexOf("/"));
0172: }
0173: // create dir if necessary
0174: File pathfile = new File(destination, path);
0175: pathfile.mkdirs();
0176:
0177: FileOutputStream out = new FileOutputStream(new File(pathfile,
0178: filename));
0179:
0180: out.write(writer.toByteArray());
0181: }
0182:
0183: public String getClassname() {
0184: return classname;
0185: }
0186:
0187: public String getSourcename() {
0188: return sourcename;
0189: }
0190:
0191: public ClassVisitor getClassVisitor() {
0192: return classWriter;
0193: }
0194:
0195: public SkinnyMethodAdapter getMethodAdapter() {
0196: return (SkinnyMethodAdapter) SkinnyMethodAdapters.peek();
0197: }
0198:
0199: public SkinnyMethodAdapter popMethodAdapter() {
0200: return (SkinnyMethodAdapter) SkinnyMethodAdapters.pop();
0201: }
0202:
0203: public void pushMethodAdapter(SkinnyMethodAdapter mv) {
0204: SkinnyMethodAdapters.push(mv);
0205: }
0206:
0207: public int getArity() {
0208: return ((Integer) arities.peek()).intValue();
0209: }
0210:
0211: public void pushArity(Arity arity) {
0212: arities.push(arity);
0213: }
0214:
0215: public Arity popArity() {
0216: return (Arity) arities.pop();
0217: }
0218:
0219: public void pushScopeStart(Label start) {
0220: scopeStarts.push(start);
0221: }
0222:
0223: public Label popScopeStart() {
0224: return (Label) scopeStarts.pop();
0225: }
0226:
0227: public void startScript() {
0228: classWriter = new ClassWriter(true);
0229:
0230: // Create the class with the appropriate class name and source file
0231: classWriter.visit(V1_4, ACC_PUBLIC + ACC_SUPER, classname,
0232: null, cg.p(Object.class), new String[] { cg
0233: .p(Script.class) });
0234: classWriter.visitSource(sourcename, null);
0235:
0236: createClassInit();
0237: createConstructor();
0238: }
0239:
0240: public void endScript() {
0241: // add Script#run impl, used for running this script with a specified threadcontext and self
0242: // root method of a script is always in __load__ method
0243: String methodName = "__file__";
0244: SkinnyMethodAdapter mv = new SkinnyMethodAdapter(
0245: getClassVisitor().visitMethod(ACC_PUBLIC, "run",
0246: METHOD_SIGNATURE, null, null));
0247: mv.start();
0248:
0249: // invoke __file__ with threadcontext, self, args (null), and block (null)
0250: // These are all +1 because run is an instance method where others are static
0251: mv.aload(THREADCONTEXT_INDEX + 1);
0252: mv.aload(SELF_INDEX + 1);
0253: mv.aload(ARGS_INDEX + 1);
0254: mv.aload(CLOSURE_INDEX + 1);
0255:
0256: mv.invokestatic(classname, methodName, METHOD_SIGNATURE);
0257: mv.areturn();
0258: mv.end();
0259:
0260: // add main impl, used for detached or command-line execution of this script with a new runtime
0261: // root method of a script is always in stub0, method0
0262: mv = new SkinnyMethodAdapter(getClassVisitor().visitMethod(
0263: ACC_PUBLIC | ACC_STATIC, "main",
0264: cg.sig(Void.TYPE, cg.params(String[].class)), null,
0265: null));
0266: mv.start();
0267:
0268: // new instance to invoke run against
0269: mv.newobj(classname);
0270: mv.dup();
0271: mv.invokespecial(classname, "<init>", cg.sig(Void.TYPE));
0272:
0273: // invoke run with threadcontext and topself
0274: mv.invokestatic(cg.p(Ruby.class), "getDefaultInstance", cg
0275: .sig(Ruby.class));
0276: mv.dup();
0277:
0278: mv.invokevirtual(RUBY, "getCurrentContext", cg
0279: .sig(ThreadContext.class));
0280: mv.swap();
0281: mv.invokevirtual(RUBY, "getTopSelf", cg.sig(IRubyObject.class));
0282: mv.getstatic(cg.p(IRubyObject.class), "NULL_ARRAY", cg
0283: .ci(IRubyObject[].class));
0284: mv.getstatic(cg.p(Block.class), "NULL_BLOCK", cg
0285: .ci(Block.class));
0286:
0287: mv.invokevirtual(classname, "run", METHOD_SIGNATURE);
0288: mv.voidreturn();
0289: mv.end();
0290: }
0291:
0292: private void createConstructor() {
0293: ClassVisitor cv = getClassVisitor();
0294:
0295: SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cv
0296: .visitMethod(ACC_PUBLIC, "<init>", cg.sig(Void.TYPE),
0297: null, null));
0298: mv.start();
0299: mv.aload(0);
0300: mv.invokespecial(cg.p(Object.class), "<init>", cg
0301: .sig(Void.TYPE));
0302: mv.voidreturn();
0303: mv.end();
0304: }
0305:
0306: private void createClassInit() {
0307: ClassVisitor cv = getClassVisitor();
0308:
0309: cv.visitField(ACC_STATIC | ACC_PRIVATE | ACC_FINAL,
0310: "$isClassLoaded", cg.ci(Boolean.TYPE), null,
0311: Boolean.FALSE);
0312: cv.visitField(ACC_STATIC | ACC_PRIVATE | ACC_FINAL, "$class",
0313: cg.ci(Class.class), null, null);
0314:
0315: SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cv
0316: .visitMethod(ACC_PUBLIC, "<clinit>", cg.sig(Void.TYPE),
0317: null, null));
0318: mv.start();
0319:
0320: // This is a little hacky...since clinit recurses, set a boolean so we don't continue trying to load class
0321: mv.getstatic(classname, "$isClassLoaded", cg.ci(Boolean.TYPE));
0322: Label doNotLoadClass = new Label();
0323: mv.ifne(doNotLoadClass);
0324:
0325: mv.ldc(Boolean.TRUE);
0326: mv.putstatic(classname, "$isClassLoaded", cg.ci(Boolean.TYPE));
0327: mv.ldc(cg.c(classname));
0328: mv.invokestatic(cg.p(Class.class), "forName", cg.sig(
0329: Class.class, cg.params(String.class)));
0330: mv.putstatic(classname, "$class", cg.ci(Class.class));
0331:
0332: mv.label(doNotLoadClass);
0333: mv.voidreturn();
0334: mv.end();
0335: }
0336:
0337: public Object beginMethod(String friendlyName, ClosureCallback args) {
0338: SkinnyMethodAdapter newMethod = new SkinnyMethodAdapter(
0339: getClassVisitor().visitMethod(ACC_PUBLIC | ACC_STATIC,
0340: friendlyName, METHOD_SIGNATURE, null, null));
0341: pushMethodAdapter(newMethod);
0342:
0343: newMethod.start();
0344:
0345: // set up a local IRuby variable
0346: newMethod.aload(THREADCONTEXT_INDEX);
0347: invokeThreadContext("getRuntime", cg.sig(Ruby.class));
0348: newMethod.astore(RUNTIME_INDEX);
0349:
0350: // set visibility
0351: newMethod.getstatic(cg.p(Visibility.class), "PRIVATE", cg
0352: .ci(Visibility.class));
0353: newMethod.astore(VISIBILITY_INDEX);
0354:
0355: // store the local vars in a local variable
0356: loadThreadContext();
0357: invokeThreadContext("getCurrentScope", cg
0358: .sig(DynamicScope.class));
0359: newMethod.dup();
0360: newMethod.astore(LOCAL_VARS_INDEX);
0361: newMethod.invokevirtual(cg.p(DynamicScope.class), "getValues",
0362: cg.sig(IRubyObject[].class));
0363: newMethod.astore(SCOPE_INDEX);
0364:
0365: if (args != null) {
0366: args.compile(this );
0367: } else {
0368: pushArity(null);
0369: }
0370:
0371: // visit a label to start scoping for local vars in this method
0372: Label start = new Label();
0373: newMethod.label(start);
0374: pushScopeStart(start);
0375:
0376: return newMethod;
0377: }
0378:
0379: public void endMethod(Object token) {
0380: assert token instanceof SkinnyMethodAdapter;
0381:
0382: SkinnyMethodAdapter mv = (SkinnyMethodAdapter) token;
0383: // return last value from execution
0384: mv.areturn();
0385:
0386: // end of variable scope
0387: Label end = new Label();
0388: mv.label(end);
0389:
0390: // local variable for lvars array
0391: mv.visitLocalVariable("lvars", cg.ci(IRubyObject[].class),
0392: null, popScopeStart(), end, LOCAL_VARS_INDEX);
0393:
0394: mv.end();
0395:
0396: popMethodAdapter();
0397: popArity();
0398: }
0399:
0400: public void lineNumber(ISourcePosition position) {
0401: if (lastLine == (lastLine = position.getEndLine()))
0402: return; // did not change lines for this node, don't bother relabeling
0403:
0404: Label l = new Label();
0405: SkinnyMethodAdapter mv = getMethodAdapter();
0406: mv.label(l);
0407: // line numbers are zero-based; add one for them to make sense in an editor
0408: mv.visitLineNumber(position.getStartLine() + 1, l);
0409: }
0410:
0411: public void invokeAttrAssign(String name) {
0412: SkinnyMethodAdapter mv = getMethodAdapter();
0413:
0414: // start with [recv, args]
0415:
0416: // get args[length - 1] and stuff it under the receiver
0417: // dup args * 2
0418: mv.dup(); // [recv, args, args]
0419: mv.dup(); // [recv, args, args, args]
0420: mv.arraylength(); // [recv, args, args, len]
0421: mv.iconst_1(); // [recv, args, args, len, 1]
0422: mv.isub(); // [recv, args, args, len-1]
0423: // load from array
0424: mv.arrayload(); // [recv, args, val]
0425: mv.dup_x2(); // [val, recv, args, val]
0426: mv.pop(); // [val, recv, args]
0427:
0428: invokeDynamic(name, true, true, CallType.NORMAL, null, true); // [val, result]
0429:
0430: // pop result, use args[length - 1] captured above
0431: mv.pop(); // [val]
0432: }
0433:
0434: public void invokeDynamic(String name, boolean hasReceiver,
0435: boolean hasArgs, CallType callType,
0436: ClosureCallback closureArg, boolean attrAssign) {
0437: SkinnyMethodAdapter mv = getMethodAdapter();
0438: String callSig = cg.sig(IRubyObject.class, cg.params(
0439: IRubyObject.class, IRubyObject[].class,
0440: ThreadContext.class, String.class, IRubyObject.class,
0441: CallType.class, Block.class));
0442: String callSigIndexed = cg.sig(IRubyObject.class, cg.params(
0443: IRubyObject.class, IRubyObject[].class,
0444: ThreadContext.class, Byte.TYPE, String.class,
0445: IRubyObject.class, CallType.class, Block.class));
0446:
0447: int index = MethodIndex.getIndex(name);
0448:
0449: if (hasArgs) {
0450: if (hasReceiver) {
0451: // Call with args
0452: // receiver already present
0453: } else {
0454: // FCall
0455: // no receiver present, use self
0456: loadSelf();
0457: // put self under args
0458: mv.swap();
0459: }
0460: } else {
0461: if (hasReceiver) {
0462: // receiver already present
0463: // empty args list
0464: mv.getstatic(cg.p(IRubyObject.class), "NULL_ARRAY", cg
0465: .ci(IRubyObject[].class));
0466:
0467: } else {
0468: // VCall
0469: // no receiver present, use self
0470: loadSelf();
0471:
0472: // empty args list
0473: mv.getstatic(cg.p(IRubyObject.class), "NULL_ARRAY", cg
0474: .ci(IRubyObject[].class));
0475: }
0476: }
0477:
0478: loadThreadContext();
0479:
0480: if (index != 0) {
0481: // load method index
0482: mv.ldc(new Integer(index));
0483: }
0484:
0485: mv.ldc(name);
0486:
0487: // load self for visibility checks
0488: loadSelf();
0489:
0490: mv.getstatic(cg.p(CallType.class), callType.toString(), cg
0491: .ci(CallType.class));
0492:
0493: if (closureArg == null) {
0494: mv.getstatic(cg.p(Block.class), "NULL_BLOCK", cg
0495: .ci(Block.class));
0496: } else {
0497: closureArg.compile(this );
0498: }
0499:
0500: Label tryBegin = new Label();
0501: Label tryEnd = new Label();
0502: Label tryCatch = new Label();
0503: if (closureArg != null) {
0504: // wrap with try/catch for block flow-control exceptions
0505: // FIXME: for flow-control from containing blocks, but it's not working right;
0506: // stack is not as expected for invoke calls below...
0507: //mv.trycatch(tryBegin, tryEnd, tryCatch, cg.p(JumpException.class));
0508:
0509: mv.label(tryBegin);
0510: }
0511:
0512: if (attrAssign) {
0513: if (index != 0) {
0514: invokeUtilityMethod("doAttrAssignIndexed",
0515: callSigIndexed);
0516: } else {
0517: invokeUtilityMethod("doAttrAssign", callSig);
0518: }
0519: } else {
0520: if (index != 0) {
0521: invokeUtilityMethod("doInvokeDynamicIndexed",
0522: callSigIndexed);
0523: } else {
0524: invokeUtilityMethod("doInvokeDynamic", callSig);
0525: }
0526: }
0527:
0528: if (closureArg != null) {
0529: mv.label(tryEnd);
0530:
0531: // no physical break, terminate loop and skip catch block
0532: Label normalEnd = new Label();
0533: mv.go_to(normalEnd);
0534:
0535: mv.label(tryCatch);
0536: {
0537: loadClosure();
0538: invokeUtilityMethod("handleJumpException", cg.sig(
0539: IRubyObject.class, cg.params(
0540: JumpException.class, Block.class)));
0541: }
0542:
0543: mv.label(normalEnd);
0544: }
0545: }
0546:
0547: public void yield(boolean hasArgs) {
0548: loadClosure();
0549:
0550: SkinnyMethodAdapter method = getMethodAdapter();
0551:
0552: if (hasArgs) {
0553: method.swap();
0554:
0555: loadThreadContext();
0556: method.swap();
0557:
0558: // args now here
0559: } else {
0560: loadThreadContext();
0561:
0562: // empty args
0563: method.aconst_null();
0564: }
0565:
0566: method.aconst_null();
0567: method.aconst_null();
0568: method.ldc(Boolean.FALSE);
0569:
0570: method.invokevirtual(cg.p(Block.class), "yield", cg.sig(
0571: IRubyObject.class, cg.params(ThreadContext.class,
0572: IRubyObject.class, IRubyObject.class,
0573: RubyModule.class, Boolean.TYPE)));
0574: }
0575:
0576: private void invokeIRubyObject(String methodName, String signature) {
0577: getMethodAdapter().invokeinterface(IRUBYOBJECT, methodName,
0578: signature);
0579: }
0580:
0581: public void loadThreadContext() {
0582: getMethodAdapter().aload(THREADCONTEXT_INDEX);
0583: }
0584:
0585: public void loadClosure() {
0586: loadThreadContext();
0587: invokeThreadContext("getFrameBlock", cg.sig(Block.class));
0588: }
0589:
0590: public void loadSelf() {
0591: getMethodAdapter().aload(SELF_INDEX);
0592: }
0593:
0594: public void loadRuntime() {
0595: getMethodAdapter().aload(RUNTIME_INDEX);
0596: }
0597:
0598: public void loadVisibility() {
0599: getMethodAdapter().aload(VISIBILITY_INDEX);
0600: }
0601:
0602: public void loadNil() {
0603: loadRuntime();
0604: invokeIRuby("getNil", cg.sig(IRubyObject.class));
0605: }
0606:
0607: public void loadSymbol(String symbol) {
0608: loadRuntime();
0609:
0610: SkinnyMethodAdapter mv = getMethodAdapter();
0611:
0612: mv.ldc(symbol);
0613:
0614: invokeIRuby("newSymbol", cg.sig(RubySymbol.class, cg
0615: .params(String.class)));
0616: }
0617:
0618: public void loadObject() {
0619: loadRuntime();
0620:
0621: invokeIRuby("getObject", cg.sig(RubyClass.class, cg.params()));
0622: }
0623:
0624: public void consumeCurrentValue() {
0625: getMethodAdapter().pop();
0626: }
0627:
0628: public void duplicateCurrentValue() {
0629: getMethodAdapter().dup();
0630: }
0631:
0632: public void swapValues() {
0633: getMethodAdapter().swap();
0634: }
0635:
0636: public void retrieveSelf() {
0637: loadSelf();
0638: }
0639:
0640: public void retrieveSelfClass() {
0641: loadSelf();
0642: invokeIRubyObject("getMetaClass", cg.sig(RubyClass.class));
0643: }
0644:
0645: public void assignLocalVariable(int index) {
0646: SkinnyMethodAdapter mv = getMethodAdapter();
0647: mv.dup();
0648:
0649: mv.aload(SCOPE_INDEX);
0650: mv.swap();
0651: mv.ldc(new Integer(index));
0652: mv.swap();
0653: mv.arraystore();
0654: }
0655:
0656: public void assignLastLine() {
0657: SkinnyMethodAdapter mv = getMethodAdapter();
0658: mv.dup();
0659:
0660: loadThreadContext();
0661: invokeThreadContext("getCurrentScope", cg
0662: .sig(DynamicScope.class));
0663: mv.swap();
0664: mv.invokevirtual(cg.p(DynamicScope.class), "setLastLine", cg
0665: .sig(Void.TYPE, cg.params(IRubyObject.class)));
0666: }
0667:
0668: public void assignLocalVariableBlockArg(int argIndex, int varIndex) {
0669: SkinnyMethodAdapter mv = getMethodAdapter();
0670:
0671: // this is copying values, but it would be more efficient to just use the args in-place
0672: mv.aload(LOCAL_VARS_INDEX);
0673: mv.ldc(new Integer(varIndex));
0674: mv.aload(ARGS_INDEX);
0675: mv.ldc(new Integer(argIndex));
0676: mv.arrayload();
0677: mv.iconst_0();
0678: mv.invokevirtual(cg.p(DynamicScope.class), "setValue", cg.sig(
0679: Void.TYPE, cg.params(Integer.TYPE, IRubyObject.class,
0680: Integer.TYPE)));
0681: }
0682:
0683: public void retrieveLocalVariable(int index) {
0684: SkinnyMethodAdapter mv = getMethodAdapter();
0685:
0686: mv.aload(SCOPE_INDEX);
0687: mv.ldc(new Integer(index));
0688: mv.arrayload();
0689: nullToNil();
0690: }
0691:
0692: public void retrieveLastLine() {
0693: SkinnyMethodAdapter mv = getMethodAdapter();
0694:
0695: loadThreadContext();
0696: invokeThreadContext("getCurrentScope", cg
0697: .sig(DynamicScope.class));
0698: mv.invokevirtual(cg.p(DynamicScope.class), "getLastLine", cg
0699: .sig(IRubyObject.class));
0700: nullToNil();
0701: }
0702:
0703: public void retrieveBackRef() {
0704: SkinnyMethodAdapter mv = getMethodAdapter();
0705:
0706: loadThreadContext();
0707: invokeThreadContext("getCurrentScope", cg
0708: .sig(DynamicScope.class));
0709: mv.invokevirtual(cg.p(DynamicScope.class), "getBackRef", cg
0710: .sig(IRubyObject.class));
0711: nullToNil();
0712: }
0713:
0714: public void assignLocalVariable(int index, int depth) {
0715: if (depth == 0) {
0716: assignLocalVariable(index);
0717: return;
0718: }
0719:
0720: SkinnyMethodAdapter mv = getMethodAdapter();
0721: mv.dup();
0722:
0723: mv.aload(LOCAL_VARS_INDEX);
0724: mv.swap();
0725: mv.ldc(new Integer(index));
0726: mv.swap();
0727: mv.ldc(new Integer(depth));
0728: mv.invokevirtual(cg.p(DynamicScope.class), "setValue", cg.sig(
0729: Void.TYPE, cg.params(Integer.TYPE, IRubyObject.class,
0730: Integer.TYPE)));
0731: }
0732:
0733: public void assignLocalVariableBlockArg(int argIndex, int varIndex,
0734: int depth) {
0735: if (depth == 0) {
0736: assignLocalVariableBlockArg(argIndex, varIndex);
0737: return;
0738: }
0739:
0740: SkinnyMethodAdapter mv = getMethodAdapter();
0741:
0742: mv.aload(LOCAL_VARS_INDEX);
0743: mv.ldc(new Integer(varIndex));
0744: mv.aload(ARGS_INDEX);
0745: mv.ldc(new Integer(argIndex));
0746: mv.arrayload();
0747: mv.ldc(new Integer(depth));
0748: mv.invokevirtual(cg.p(DynamicScope.class), "setValue", cg.sig(
0749: Void.TYPE, cg.params(Integer.TYPE, IRubyObject.class,
0750: Integer.TYPE)));
0751: }
0752:
0753: public void retrieveLocalVariable(int index, int depth) {
0754: if (depth == 0) {
0755: retrieveLocalVariable(index);
0756: return;
0757: }
0758:
0759: SkinnyMethodAdapter mv = getMethodAdapter();
0760:
0761: mv.aload(LOCAL_VARS_INDEX);
0762: mv.ldc(new Integer(index));
0763: mv.ldc(new Integer(depth));
0764: mv.invokevirtual(cg.p(DynamicScope.class), "getValue", cg.sig(
0765: IRubyObject.class, cg
0766: .params(Integer.TYPE, Integer.TYPE)));
0767: nullToNil();
0768: }
0769:
0770: public void assignConstantInCurrent(String name) {
0771: SkinnyMethodAdapter mv = getMethodAdapter();
0772:
0773: loadThreadContext();
0774: mv.ldc(name);
0775: mv.dup2_x1();
0776: mv.pop2();
0777: invokeThreadContext("setConstantInCurrent", cg.sig(
0778: IRubyObject.class, cg.params(String.class,
0779: IRubyObject.class)));
0780: }
0781:
0782: public void assignConstantInModule(String name) {
0783: SkinnyMethodAdapter mv = getMethodAdapter();
0784:
0785: loadThreadContext();
0786: mv.ldc(name);
0787: mv.swap2();
0788: invokeThreadContext("setConstantInCurrent", cg.sig(
0789: IRubyObject.class, cg.params(String.class,
0790: RubyModule.class, IRubyObject.class)));
0791: }
0792:
0793: public void assignConstantInObject(String name) {
0794: SkinnyMethodAdapter mv = getMethodAdapter();
0795:
0796: // load Object under value
0797: loadRuntime();
0798: invokeIRuby("getObject", cg.sig(RubyClass.class, cg.params()));
0799: mv.swap();
0800:
0801: assignConstantInModule(name);
0802: }
0803:
0804: public void retrieveConstant(String name) {
0805: SkinnyMethodAdapter mv = getMethodAdapter();
0806:
0807: loadThreadContext();
0808: mv.ldc(name);
0809: invokeThreadContext("getConstant", cg.sig(IRubyObject.class, cg
0810: .params(String.class)));
0811: }
0812:
0813: public void retrieveConstantFromModule(String name) {
0814: SkinnyMethodAdapter mv = getMethodAdapter();
0815: mv.visitTypeInsn(CHECKCAST, cg.p(RubyModule.class));
0816: mv.ldc(name);
0817: mv.invokevirtual(cg.p(RubyModule.class), "getConstantFrom", cg
0818: .sig(IRubyObject.class, cg.params(String.class)));
0819: }
0820:
0821: public void retrieveClassVariable(String name) {
0822: loadThreadContext();
0823: loadRuntime();
0824: loadSelf();
0825: getMethodAdapter().ldc(name);
0826:
0827: invokeUtilityMethod("fetchClassVariable", cg.sig(
0828: IRubyObject.class, cg.params(ThreadContext.class,
0829: Ruby.class, IRubyObject.class, String.class)));
0830: }
0831:
0832: public void assignClassVariable(String name) {
0833: SkinnyMethodAdapter method = getMethodAdapter();
0834:
0835: loadThreadContext();
0836: method.swap();
0837: loadRuntime();
0838: method.swap();
0839: loadSelf();
0840: method.swap();
0841: getMethodAdapter().ldc(name);
0842: method.swap();
0843:
0844: invokeUtilityMethod("setClassVariable", cg.sig(
0845: IRubyObject.class, cg.params(ThreadContext.class,
0846: Ruby.class, IRubyObject.class, String.class,
0847: IRubyObject.class)));
0848: }
0849:
0850: private void loadScope(int depth) {
0851: SkinnyMethodAdapter mv = getMethodAdapter();
0852: // get the appropriate array out of the scopes
0853: mv.aload(SCOPE_INDEX);
0854: mv.ldc(new Integer(depth - 1));
0855: mv.arrayload();
0856: }
0857:
0858: public void createNewFloat(double value) {
0859: SkinnyMethodAdapter mv = getMethodAdapter();
0860:
0861: loadRuntime();
0862: mv.ldc(new Double(value));
0863:
0864: invokeIRuby("newFloat", cg.sig(RubyFloat.class, cg
0865: .params(Double.TYPE)));
0866: }
0867:
0868: public void createNewFixnum(long value) {
0869: SkinnyMethodAdapter mv = getMethodAdapter();
0870:
0871: loadRuntime();
0872: mv.ldc(new Long(value));
0873:
0874: invokeIRuby("newFixnum", cg.sig(RubyFixnum.class, cg
0875: .params(Long.TYPE)));
0876: }
0877:
0878: public void createNewBignum(BigInteger value) {
0879: SkinnyMethodAdapter mv = getMethodAdapter();
0880:
0881: loadRuntime();
0882: mv.ldc(value.toString());
0883:
0884: mv.invokestatic(cg.p(RubyBignum.class), "newBignum", cg.sig(
0885: RubyBignum.class, cg.params(Ruby.class, String.class)));
0886: }
0887:
0888: public void createNewString(ArrayCallback callback, int count) {
0889: SkinnyMethodAdapter mv = getMethodAdapter();
0890: loadRuntime();
0891: invokeIRuby("newString", cg.sig(RubyString.class, cg.params()));
0892: for (int i = 0; i < count; i++) {
0893: callback.nextValue(this , null, i);
0894: mv.invokevirtual(cg.p(RubyString.class), "append", cg.sig(
0895: RubyString.class, cg.params(IRubyObject.class)));
0896: }
0897: }
0898:
0899: public void createNewString(ByteList value) {
0900: SkinnyMethodAdapter mv = getMethodAdapter();
0901:
0902: // FIXME: this is sub-optimal, storing string value in a java.lang.String again
0903: loadRuntime();
0904: mv.ldc(value.toString());
0905:
0906: invokeIRuby("newString", cg.sig(RubyString.class, cg
0907: .params(String.class)));
0908: }
0909:
0910: public void createNewSymbol(String name) {
0911: loadRuntime();
0912: getMethodAdapter().ldc(name);
0913: invokeIRuby("newSymbol", cg.sig(RubySymbol.class, cg
0914: .params(String.class)));
0915: }
0916:
0917: public void createNewArray() {
0918: SkinnyMethodAdapter mv = getMethodAdapter();
0919:
0920: loadRuntime();
0921: // put under object array already present
0922: mv.swap();
0923:
0924: invokeIRuby("newArrayNoCopy", cg.sig(RubyArray.class, cg
0925: .params(IRubyObject[].class)));
0926: }
0927:
0928: public void createEmptyArray() {
0929: SkinnyMethodAdapter mv = getMethodAdapter();
0930:
0931: loadRuntime();
0932:
0933: invokeIRuby("newArray", cg.sig(RubyArray.class, cg.params()));
0934: }
0935:
0936: public void createObjectArray(Object[] sourceArray,
0937: ArrayCallback callback) {
0938: buildObjectArray(IRUBYOBJECT, sourceArray, callback);
0939: }
0940:
0941: public void createObjectArray(int elementCount) {
0942: SkinnyMethodAdapter method = getMethodAdapter();
0943:
0944: // create the array
0945: method.ldc(new Integer(elementCount));
0946: method.anewarray(cg.p(IRubyObject.class));
0947:
0948: // for each element, swap with array and insert
0949: for (int i = 0; i < elementCount; i++) {
0950: method.dup_x1();
0951: method.dup_x1();
0952: method.pop();
0953:
0954: method.ldc(new Integer(elementCount - 1 - i));
0955: method.swap();
0956:
0957: method.arraystore();
0958: }
0959: }
0960:
0961: private void buildObjectArray(String type, Object[] sourceArray,
0962: ArrayCallback callback) {
0963: SkinnyMethodAdapter mv = getMethodAdapter();
0964:
0965: mv.ldc(new Integer(sourceArray.length));
0966: mv.anewarray(type);
0967:
0968: for (int i = 0; i < sourceArray.length; i++) {
0969: mv.dup();
0970: mv.ldc(new Integer(i));
0971:
0972: callback.nextValue(this , sourceArray, i);
0973:
0974: mv.arraystore();
0975: }
0976: }
0977:
0978: public void createEmptyHash() {
0979: SkinnyMethodAdapter mv = getMethodAdapter();
0980:
0981: loadRuntime();
0982:
0983: mv.invokestatic(cg.p(RubyHash.class), "newHash", cg.sig(
0984: RubyHash.class, cg.params(Ruby.class)));
0985: }
0986:
0987: public void createNewHash(Object elements, ArrayCallback callback,
0988: int keyCount) {
0989: SkinnyMethodAdapter mv = getMethodAdapter();
0990:
0991: loadRuntime();
0992:
0993: // create a new hashmap
0994: mv.newobj(cg.p(HashMap.class));
0995: mv.dup();
0996: mv.invokespecial(cg.p(HashMap.class), "<init>", cg
0997: .sig(Void.TYPE));
0998:
0999: for (int i = 0; i < keyCount; i++) {
1000: mv.dup();
1001: callback.nextValue(this , elements, i);
1002: mv.invokevirtual(cg.p(HashMap.class), "put", cg
1003: .sig(Object.class, cg.params(Object.class,
1004: Object.class)));
1005: mv.pop();
1006: }
1007:
1008: loadNil();
1009: mv.invokestatic(cg.p(RubyHash.class), "newHash", cg.sig(
1010: RubyHash.class, cg.params(Ruby.class, Map.class,
1011: IRubyObject.class)));
1012: }
1013:
1014: public void createNewRange(boolean isExclusive) {
1015: SkinnyMethodAdapter mv = getMethodAdapter();
1016:
1017: loadRuntime();
1018:
1019: mv.dup_x2();
1020: mv.pop();
1021:
1022: mv.ldc(new Boolean(isExclusive));
1023:
1024: mv.invokestatic(cg.p(RubyRange.class), "newRange", cg.sig(
1025: RubyRange.class, cg.params(Ruby.class,
1026: IRubyObject.class, IRubyObject.class,
1027: Boolean.TYPE)));
1028: }
1029:
1030: /**
1031: * Invoke IRubyObject.isTrue
1032: */
1033: private void isTrue() {
1034: invokeIRubyObject("isTrue", cg.sig(Boolean.TYPE));
1035: }
1036:
1037: public void performBooleanBranch(BranchCallback trueBranch,
1038: BranchCallback falseBranch) {
1039: Label afterJmp = new Label();
1040: Label falseJmp = new Label();
1041:
1042: SkinnyMethodAdapter mv = getMethodAdapter();
1043:
1044: // call isTrue on the result
1045: isTrue();
1046:
1047: mv.ifeq(falseJmp); // EQ == 0 (i.e. false)
1048: trueBranch.branch(this );
1049: mv.go_to(afterJmp);
1050:
1051: // FIXME: optimize for cases where we have no false branch
1052: mv.label(falseJmp);
1053: falseBranch.branch(this );
1054:
1055: mv.label(afterJmp);
1056: }
1057:
1058: public void performLogicalAnd(BranchCallback longBranch) {
1059: Label afterJmp = new Label();
1060: Label falseJmp = new Label();
1061:
1062: SkinnyMethodAdapter mv = getMethodAdapter();
1063:
1064: // dup it since we need to return appropriately if it's false
1065: mv.dup();
1066:
1067: // call isTrue on the result
1068: isTrue();
1069:
1070: mv.ifeq(falseJmp); // EQ == 0 (i.e. false)
1071: // pop the extra result and replace with the send part of the AND
1072: mv.pop();
1073: longBranch.branch(this );
1074: mv.label(falseJmp);
1075: }
1076:
1077: public void performLogicalOr(BranchCallback longBranch) {
1078: Label afterJmp = new Label();
1079: Label falseJmp = new Label();
1080:
1081: SkinnyMethodAdapter mv = getMethodAdapter();
1082:
1083: // dup it since we need to return appropriately if it's false
1084: mv.dup();
1085:
1086: // call isTrue on the result
1087: isTrue();
1088:
1089: mv.ifne(falseJmp); // EQ == 0 (i.e. false)
1090: // pop the extra result and replace with the send part of the AND
1091: mv.pop();
1092: longBranch.branch(this );
1093: mv.label(falseJmp);
1094: }
1095:
1096: public void performBooleanLoop(BranchCallback condition,
1097: BranchCallback body, boolean checkFirst) {
1098: // FIXME: handle next/continue, break, etc
1099: SkinnyMethodAdapter mv = getMethodAdapter();
1100:
1101: Label tryBegin = new Label();
1102: Label tryEnd = new Label();
1103: Label tryCatch = new Label();
1104:
1105: mv.trycatch(tryBegin, tryEnd, tryCatch, cg
1106: .p(JumpException.class));
1107:
1108: mv.label(tryBegin);
1109: {
1110: Label endJmp = new Label();
1111: if (checkFirst) {
1112: // calculate condition
1113: condition.branch(this );
1114: // call isTrue on the result
1115: isTrue();
1116:
1117: mv.ifeq(endJmp); // EQ == 0 (i.e. false)
1118: }
1119:
1120: Label topJmp = new Label();
1121:
1122: mv.label(topJmp);
1123:
1124: body.branch(this );
1125:
1126: // clear result after each loop
1127: mv.pop();
1128:
1129: // calculate condition
1130: condition.branch(this );
1131: // call isTrue on the result
1132: isTrue();
1133:
1134: mv.ifne(topJmp); // NE == nonzero (i.e. true)
1135:
1136: if (checkFirst) {
1137: mv.label(endJmp);
1138: }
1139: }
1140:
1141: mv.label(tryEnd);
1142:
1143: // no physical break, terminate loop and skip catch block
1144: Label normalBreak = new Label();
1145: mv.go_to(normalBreak);
1146:
1147: mv.label(tryCatch);
1148: {
1149: mv.dup();
1150: mv.invokevirtual(cg.p(JumpException.class), "getJumpType",
1151: cg.sig(JumpException.JumpType.class));
1152: mv.invokevirtual(cg.p(JumpException.JumpType.class),
1153: "getTypeId", cg.sig(Integer.TYPE));
1154:
1155: Label tryDefault = new Label();
1156: Label breakLabel = new Label();
1157:
1158: mv.lookupswitch(tryDefault,
1159: new int[] { JumpException.JumpType.BREAK },
1160: new Label[] { breakLabel });
1161:
1162: // default is to just re-throw unhandled exception
1163: mv.label(tryDefault);
1164: mv.athrow();
1165:
1166: // break just terminates the loop normally, unless it's a block break...
1167: mv.label(breakLabel);
1168:
1169: // JRUBY-530 behavior
1170: mv.dup();
1171: mv.invokevirtual(cg.p(JumpException.class), "getTarget", cg
1172: .sig(Object.class));
1173: loadClosure();
1174: Label notBlockBreak = new Label();
1175: mv.if_acmpne(notBlockBreak);
1176: mv.dup();
1177: mv.aconst_null();
1178: mv.invokevirtual(cg.p(JumpException.class), "setTarget", cg
1179: .sig(Void.TYPE, cg.params(Object.class)));
1180: mv.athrow();
1181:
1182: mv.label(notBlockBreak);
1183: // target is not == closure, normal loop exit, pop remaining exception object
1184: mv.pop();
1185: }
1186:
1187: mv.label(normalBreak);
1188: loadNil();
1189: }
1190:
1191: public void performReturn() {
1192: if (isCompilingClosure) {
1193: throw new NotCompilableException(
1194: "Can't compile non-local return");
1195: }
1196:
1197: // otherwise, just do a local return
1198: SkinnyMethodAdapter mv = getMethodAdapter();
1199: mv.areturn();
1200: }
1201:
1202: public void createNewClosure(StaticScope scope, int arity,
1203: ClosureCallback body, ClosureCallback args) {
1204: // FIXME: This isn't quite done yet; waiting to have full support for passing closures so we can test it
1205: ClassVisitor cv = getClassVisitor();
1206: SkinnyMethodAdapter method;
1207:
1208: String closureMethodName = "closure" + ++innerIndex;
1209: String closureFieldName = "_" + closureMethodName;
1210:
1211: // declare the field
1212: cv.visitField(ACC_PRIVATE | ACC_STATIC, closureFieldName, cg
1213: .ci(CompiledBlockCallback.class), null, null);
1214:
1215: ////////////////////////////
1216: // closure implementation
1217: method = new SkinnyMethodAdapter(cv.visitMethod(ACC_PUBLIC
1218: | ACC_STATIC, closureMethodName, CLOSURE_SIGNATURE,
1219: null, null));
1220:
1221: // FIXME: I don't like this pre/post state management.
1222: boolean previousIsCompilingClosure = isCompilingClosure;
1223: isCompilingClosure = true;
1224:
1225: pushMethodAdapter(method);
1226:
1227: method.start();
1228:
1229: // store the local vars in a local variable
1230: loadThreadContext();
1231: invokeThreadContext("getCurrentScope", cg
1232: .sig(DynamicScope.class));
1233: method.dup();
1234: method.astore(LOCAL_VARS_INDEX);
1235: method.invokevirtual(cg.p(DynamicScope.class), "getValues", cg
1236: .sig(IRubyObject[].class));
1237: method.astore(SCOPE_INDEX);
1238:
1239: // set up a local IRuby variable
1240: method.aload(THREADCONTEXT_INDEX);
1241: invokeThreadContext("getRuntime", cg.sig(Ruby.class));
1242: method.astore(RUNTIME_INDEX);
1243:
1244: // set up block arguments
1245: args.compile(this );
1246:
1247: // start of scoping for closure's vars
1248: Label start = new Label();
1249: method.label(start);
1250:
1251: // visit the body of the closure
1252: body.compile(this );
1253:
1254: method.areturn();
1255:
1256: // end of scoping for closure's vars
1257: Label end = new Label();
1258: method.label(end);
1259: method.end();
1260:
1261: popMethodAdapter();
1262:
1263: // FIXME: I don't like this pre/post state management.
1264: isCompilingClosure = previousIsCompilingClosure;
1265:
1266: method = getMethodAdapter();
1267:
1268: // Done with closure compilation
1269: /////////////////////////////////////////////////////////////////////////////
1270:
1271: // Now, store a compiled block object somewhere we can access it in the future
1272:
1273: // in current method, load the field to see if we've created a BlockCallback yet
1274: method.getstatic(classname, closureFieldName, cg
1275: .ci(CompiledBlockCallback.class));
1276: Label alreadyCreated = new Label();
1277: method.ifnonnull(alreadyCreated);
1278:
1279: // no callback, construct it
1280: getCallbackFactory();
1281:
1282: method.ldc(closureMethodName);
1283: method.invokevirtual(cg.p(CallbackFactory.class),
1284: "getBlockCallback", cg.sig(CompiledBlockCallback.class,
1285: cg.params(String.class)));
1286: method.putstatic(classname, closureFieldName, cg
1287: .ci(CompiledBlockCallback.class));
1288:
1289: method.label(alreadyCreated);
1290:
1291: // Construct the block for passing to the target method
1292: loadThreadContext();
1293: loadSelf();
1294: method.ldc(new Integer(arity));
1295:
1296: buildStaticScopeNames(method, scope);
1297:
1298: method.getstatic(classname, closureFieldName, cg
1299: .ci(CompiledBlockCallback.class));
1300:
1301: invokeUtilityMethod("createBlock", cg.sig(CompiledBlock.class,
1302: cg.params(ThreadContext.class, IRubyObject.class,
1303: Integer.TYPE, String[].class,
1304: CompiledBlockCallback.class)));
1305: }
1306:
1307: private void buildStaticScopeNames(SkinnyMethodAdapter method,
1308: StaticScope scope) {
1309: // construct static scope list of names
1310: method.ldc(new Integer(scope.getNumberOfVariables()));
1311: method.anewarray(cg.p(String.class));
1312: for (int i = 0; i < scope.getNumberOfVariables(); i++) {
1313: method.dup();
1314: method.ldc(new Integer(i));
1315: method.ldc(scope.getVariables()[i]);
1316: method.arraystore();
1317: }
1318: }
1319:
1320: /**
1321: * This is for utility methods used by the compiler, to reduce the amount of code generation
1322: * necessary. All of these live in CompilerHelpers.
1323: */
1324: private void invokeUtilityMethod(String methodName, String signature) {
1325: getMethodAdapter().invokestatic(cg.p(CompilerHelpers.class),
1326: methodName, signature);
1327: }
1328:
1329: private void invokeThreadContext(String methodName, String signature) {
1330: SkinnyMethodAdapter mv = getMethodAdapter();
1331: mv.invokevirtual(THREADCONTEXT, methodName, signature);
1332: }
1333:
1334: private void invokeIRuby(String methodName, String signature) {
1335: SkinnyMethodAdapter mv = getMethodAdapter();
1336: mv.invokevirtual(RUBY, methodName, signature);
1337: }
1338:
1339: private void getCallbackFactory() {
1340: SkinnyMethodAdapter mv = getMethodAdapter();
1341: loadRuntime();
1342: getCompiledClass();
1343: mv.dup();
1344: mv.invokevirtual(cg.p(Class.class), "getClassLoader", cg
1345: .sig(ClassLoader.class));
1346: mv.invokestatic(cg.p(CallbackFactory.class), "createFactory",
1347: cg.sig(CallbackFactory.class, cg.params(Ruby.class,
1348: Class.class, ClassLoader.class)));
1349: }
1350:
1351: private void getCompiledClass() {
1352: SkinnyMethodAdapter mv = getMethodAdapter();
1353: mv.getstatic(classname, "$class", cg.ci(Class.class));
1354: }
1355:
1356: private void getRubyClass() {
1357: loadSelf();
1358: // FIXME: This doesn't seem *quite* right. If actually within a class...end, is self.getMetaClass the correct class? should be self, no?
1359: invokeIRubyObject("getMetaClass", cg.sig(RubyClass.class));
1360: }
1361:
1362: private void println() {
1363: SkinnyMethodAdapter mv = getMethodAdapter();
1364:
1365: mv.dup();
1366: mv.getstatic(cg.p(System.class), "out", cg
1367: .ci(PrintStream.class));
1368: mv.swap();
1369:
1370: mv.invokevirtual(cg.p(PrintStream.class), "println", cg.sig(
1371: Void.TYPE, cg.params(Object.class)));
1372: }
1373:
1374: public void defineAlias(String newName, String oldName) {
1375: getRubyClass();
1376: getMethodAdapter().ldc(newName);
1377: getMethodAdapter().ldc(oldName);
1378: getMethodAdapter().invokevirtual(
1379: cg.p(RubyModule.class),
1380: "defineAlias",
1381: cg
1382: .sig(Void.TYPE, cg.params(String.class,
1383: String.class)));
1384: loadNil();
1385: // TODO: should call method_added, and possibly push nil.
1386: }
1387:
1388: public void defineNewMethod(String name, StaticScope scope,
1389: ClosureCallback body, ClosureCallback args) {
1390: if (isCompilingClosure) {
1391: throw new NotCompilableException(
1392: "Can't compile def within closure yet");
1393: }
1394:
1395: // TODO: build arg list based on number of args, optionals, etc
1396: ++methodIndex;
1397: String methodName = cg.cleanJavaIdentifier(name) + "__"
1398: + methodIndex;
1399:
1400: beginMethod(methodName, args);
1401:
1402: SkinnyMethodAdapter mv = getMethodAdapter();
1403:
1404: // callbacks to fill in method body
1405: body.compile(this );
1406:
1407: endMethod(mv);
1408:
1409: // return to previous method
1410: mv = getMethodAdapter();
1411:
1412: // prepare to call "def" utility method to handle def logic
1413: loadThreadContext();
1414:
1415: loadVisibility();
1416:
1417: loadSelf();
1418:
1419: // load the class we're creating, for binding purposes
1420: getCompiledClass();
1421:
1422: mv.ldc(name);
1423:
1424: mv.ldc(methodName);
1425:
1426: buildStaticScopeNames(mv, scope);
1427:
1428: mv.ldc(new Integer(0));
1429:
1430: invokeUtilityMethod("def", cg.sig(IRubyObject.class, cg.params(
1431: ThreadContext.class, Visibility.class,
1432: IRubyObject.class, Class.class, String.class,
1433: String.class, String[].class, Integer.TYPE)));
1434: }
1435:
1436: public void processRequiredArgs(Arity arity, int totalArgs) {
1437: SkinnyMethodAdapter newMethod = getMethodAdapter();
1438:
1439: // check arity
1440: newMethod.ldc(new Integer(arity.getValue()));
1441: newMethod.invokestatic(cg.p(Arity.class), "createArity", cg
1442: .sig(Arity.class, cg.params(Integer.TYPE)));
1443: loadRuntime();
1444: newMethod.aload(ARGS_INDEX);
1445: newMethod.invokevirtual(cg.p(Arity.class), "checkArity", cg
1446: .sig(Void.TYPE, cg.params(Ruby.class,
1447: IRubyObject[].class)));
1448:
1449: // optional has different checks for size
1450: if (!arity.isFixed()) {
1451: loadRuntime();
1452: newMethod.aload(ARGS_INDEX);
1453: newMethod.arraylength();
1454: newMethod.ldc(new Integer(totalArgs));
1455: invokeUtilityMethod("raiseArgumentError", cg.sig(Void.TYPE,
1456: cg.params(Ruby.class, Integer.TYPE, Integer.TYPE)));
1457: }
1458:
1459: // arraycopy all arguments into local vars array
1460: Label noArgs = new Label();
1461: newMethod.aload(ARGS_INDEX);
1462: newMethod.ifnull(noArgs);
1463: newMethod.aload(ARGS_INDEX);
1464: newMethod.arraylength();
1465: newMethod.ifeq(noArgs);
1466: newMethod.aload(LOCAL_VARS_INDEX);
1467: newMethod.aload(ARGS_INDEX);
1468: newMethod.dup();
1469: newMethod.arraylength();
1470: newMethod.invokevirtual(cg.p(DynamicScope.class),
1471: "setArgValues", cg.sig(Void.TYPE, cg.params(
1472: IRubyObject[].class, Integer.TYPE)));
1473: newMethod.label(noArgs);
1474:
1475: // push down the argument count of this method
1476: pushArity(arity);
1477: }
1478:
1479: public void assignOptionalArgs(Object object,
1480: int expectedArgsCount, int size, ArrayCallback optEval) {
1481: SkinnyMethodAdapter newMethod = getMethodAdapter();
1482:
1483: // NOTE: By the time we're here, arity should have already been checked. We proceed without boundschecking.
1484:
1485: // opt args are handled with a switch; the key is how many args we have coming in, and the cases are
1486: // each opt arg index. The cases fall-through, so remaining opt args are handled.
1487: newMethod.aload(ARGS_INDEX);
1488: newMethod.arraylength();
1489:
1490: Label defaultLabel = new Label();
1491: Label[] labels = new Label[size];
1492:
1493: for (int i = 0; i < size; i++) {
1494: labels[i] = new Label();
1495: }
1496:
1497: newMethod.tableswitch(expectedArgsCount, expectedArgsCount
1498: + size - 1, defaultLabel, labels);
1499:
1500: for (int i = 0; i < size; i++) {
1501: newMethod.label(labels[i]);
1502: optEval.nextValue(this , object, i);
1503: newMethod.pop();
1504: }
1505:
1506: newMethod.label(defaultLabel);
1507: }
1508:
1509: public void loadFalse() {
1510: loadRuntime();
1511: invokeIRuby("getFalse", cg.sig(RubyBoolean.class));
1512: }
1513:
1514: public void loadTrue() {
1515: loadRuntime();
1516: invokeIRuby("getTrue", cg.sig(RubyBoolean.class));
1517: }
1518:
1519: public void retrieveInstanceVariable(String name) {
1520: loadSelf();
1521:
1522: SkinnyMethodAdapter mv = getMethodAdapter();
1523:
1524: mv.ldc(name);
1525: invokeIRubyObject("getInstanceVariable", cg.sig(
1526: IRubyObject.class, cg.params(String.class)));
1527:
1528: // check if it's null; if so, load nil
1529: mv.dup();
1530: Label notNull = new Label();
1531: mv.ifnonnull(notNull);
1532:
1533: // pop the dup'ed null
1534: mv.pop();
1535: // replace it with nil
1536: loadNil();
1537:
1538: mv.label(notNull);
1539: }
1540:
1541: public void assignInstanceVariable(String name) {
1542: SkinnyMethodAdapter mv = getMethodAdapter();
1543:
1544: loadSelf();
1545: mv.swap();
1546:
1547: mv.ldc(name);
1548: mv.swap();
1549:
1550: invokeIRubyObject("setInstanceVariable", cg.sig(
1551: IRubyObject.class, cg.params(String.class,
1552: IRubyObject.class)));
1553: }
1554:
1555: public void assignInstanceVariableBlockArg(int argIndex, String name) {
1556: SkinnyMethodAdapter mv = getMethodAdapter();
1557:
1558: loadSelf();
1559: mv.ldc(name);
1560:
1561: mv.aload(ARGS_INDEX);
1562: mv.ldc(new Integer(argIndex));
1563: mv.arrayload();
1564:
1565: invokeIRubyObject("setInstanceVariable", cg.sig(
1566: IRubyObject.class, cg.params(String.class,
1567: IRubyObject.class)));
1568: }
1569:
1570: public void retrieveGlobalVariable(String name) {
1571: loadRuntime();
1572:
1573: SkinnyMethodAdapter mv = getMethodAdapter();
1574:
1575: invokeIRuby("getGlobalVariables", cg.sig(GlobalVariables.class));
1576: mv.ldc(name);
1577: mv.invokevirtual(cg.p(GlobalVariables.class), "get", cg.sig(
1578: IRubyObject.class, cg.params(String.class)));
1579: }
1580:
1581: public void assignGlobalVariable(String name) {
1582: loadRuntime();
1583:
1584: SkinnyMethodAdapter mv = getMethodAdapter();
1585:
1586: invokeIRuby("getGlobalVariables", cg.sig(GlobalVariables.class));
1587: mv.swap();
1588: mv.ldc(name);
1589: mv.swap();
1590: mv.invokevirtual(cg.p(GlobalVariables.class), "set", cg.sig(
1591: IRubyObject.class, cg.params(String.class,
1592: IRubyObject.class)));
1593: }
1594:
1595: public void assignGlobalVariableBlockArg(int argIndex, String name) {
1596: loadRuntime();
1597:
1598: SkinnyMethodAdapter mv = getMethodAdapter();
1599:
1600: invokeIRuby("getGlobalVariables", cg.sig(GlobalVariables.class));
1601: mv.ldc(name);
1602:
1603: mv.aload(ARGS_INDEX);
1604: mv.ldc(new Integer(argIndex));
1605: mv.arrayload();
1606:
1607: mv.invokevirtual(cg.p(GlobalVariables.class), "set", cg.sig(
1608: IRubyObject.class, cg.params(String.class,
1609: IRubyObject.class)));
1610: }
1611:
1612: public void negateCurrentValue() {
1613: SkinnyMethodAdapter mv = getMethodAdapter();
1614:
1615: isTrue();
1616: Label isTrue = new Label();
1617: Label end = new Label();
1618: mv.ifne(isTrue);
1619: loadTrue();
1620: mv.go_to(end);
1621: mv.label(isTrue);
1622: loadFalse();
1623: mv.label(end);
1624: }
1625:
1626: public void splatCurrentValue() {
1627: SkinnyMethodAdapter method = getMethodAdapter();
1628:
1629: method
1630: .invokestatic(cg.p(EvaluationState.class),
1631: "splatValue", cg.sig(IRubyObject.class, cg
1632: .params(IRubyObject.class)));
1633: }
1634:
1635: public void singlifySplattedValue() {
1636: SkinnyMethodAdapter method = getMethodAdapter();
1637: method
1638: .invokestatic(cg.p(EvaluationState.class),
1639: "aValueSplat", cg.sig(IRubyObject.class, cg
1640: .params(IRubyObject.class)));
1641: }
1642:
1643: public void ensureRubyArray() {
1644: invokeUtilityMethod("ensureRubyArray", cg.sig(RubyArray.class,
1645: cg.params(IRubyObject.class)));
1646: }
1647:
1648: public void forEachInValueArray(int start, int count,
1649: Object source, ArrayCallback callback) {
1650: SkinnyMethodAdapter method = getMethodAdapter();
1651:
1652: Label noMoreArrayElements = new Label();
1653: for (; start < count; start++) {
1654: // confirm we're not past the end of the array
1655: method.dup(); // dup the original array object
1656: method.invokevirtual(cg.p(RubyArray.class), "getLength", cg
1657: .sig(Integer.TYPE, cg.params()));
1658: method.ldc(new Integer(start));
1659: method.ifle(noMoreArrayElements); // if length <= start, end loop
1660:
1661: // extract item from array
1662: method.dup(); // dup the original array object
1663: method.ldc(new Integer(start)); // index for the item
1664: method.invokevirtual(cg.p(RubyArray.class), "entry", cg
1665: .sig(IRubyObject.class, cg.params(Long.TYPE))); // extract item
1666: callback.nextValue(this , source, start);
1667: }
1668: method.label(noMoreArrayElements);
1669: }
1670:
1671: public void loadInteger(int value) {
1672: throw new UnsupportedOperationException("Not supported yet.");
1673: }
1674:
1675: public void performGEBranch(BranchCallback trueBranch,
1676: BranchCallback falseBranch) {
1677: throw new UnsupportedOperationException("Not supported yet.");
1678: }
1679:
1680: public void performGTBranch(BranchCallback trueBranch,
1681: BranchCallback falseBranch) {
1682: throw new UnsupportedOperationException("Not supported yet.");
1683: }
1684:
1685: public void performLEBranch(BranchCallback trueBranch,
1686: BranchCallback falseBranch) {
1687: throw new UnsupportedOperationException("Not supported yet.");
1688: }
1689:
1690: public void performLTBranch(BranchCallback trueBranch,
1691: BranchCallback falseBranch) {
1692: throw new UnsupportedOperationException("Not supported yet.");
1693: }
1694:
1695: public void loadRubyArraySize() {
1696: throw new UnsupportedOperationException("Not supported yet.");
1697: }
1698:
1699: public void issueBreakEvent() {
1700: SkinnyMethodAdapter mv = getMethodAdapter();
1701:
1702: mv.newobj(cg.p(JumpException.class));
1703: mv.dup();
1704: mv.getstatic(cg.p(JumpException.JumpType.class), "BreakJump",
1705: cg.ci(JumpException.JumpType.class));
1706: mv.invokespecial(cg.p(JumpException.class), "<init>", cg.sig(
1707: Void.TYPE, cg.params(JumpException.JumpType.class)));
1708:
1709: // set result into jump exception
1710: mv.dup_x1();
1711: mv.swap();
1712: mv.invokevirtual(cg.p(JumpException.class), "setValue", cg.sig(
1713: Void.TYPE, cg.params(Object.class)));
1714:
1715: mv.athrow();
1716: }
1717:
1718: public void asString() {
1719: SkinnyMethodAdapter mv = getMethodAdapter();
1720: mv.invokeinterface(cg.p(IRubyObject.class), "asString", cg.sig(
1721: RubyString.class, cg.params()));
1722: }
1723:
1724: public void nthRef(int match) {
1725: SkinnyMethodAdapter mv = getMethodAdapter();
1726:
1727: mv.ldc(new Integer(match));
1728: loadThreadContext();
1729: invokeThreadContext("getBackref", cg.sig(IRubyObject.class, cg
1730: .params()));
1731: mv.invokestatic(cg.p(RubyRegexp.class), "nth_match", cg.sig(
1732: IRubyObject.class, cg.params(Integer.TYPE,
1733: IRubyObject.class)));
1734: }
1735:
1736: public void match() {
1737: SkinnyMethodAdapter mv = getMethodAdapter();
1738: mv.invokevirtual(cg.p(RubyRegexp.class), "match2", cg.sig(
1739: IRubyObject.class, cg.params()));
1740: }
1741:
1742: public void match2() {
1743: SkinnyMethodAdapter mv = getMethodAdapter();
1744: mv.invokevirtual(cg.p(RubyRegexp.class), "match", cg.sig(
1745: IRubyObject.class, cg.params(IRubyObject.class)));
1746: }
1747:
1748: public void match3() {
1749: SkinnyMethodAdapter mv = getMethodAdapter();
1750:
1751: mv.dup();
1752: mv.visitTypeInsn(INSTANCEOF, cg.p(RubyString.class));
1753:
1754: Label l0 = new Label();
1755: mv.visitJumpInsn(IFEQ, l0);
1756:
1757: mv.invokevirtual(cg.p(RubyRegexp.class), "match", cg.sig(
1758: IRubyObject.class, cg.params(IRubyObject.class)));
1759:
1760: Label l1 = new Label();
1761: mv.visitJumpInsn(GOTO, l1);
1762: mv.visitLabel(l0);
1763:
1764: mv.swap();
1765: loadThreadContext();
1766: mv.swap();
1767: mv.ldc("=~");
1768: mv.swap();
1769:
1770: mv.invokeinterface(cg.p(IRubyObject.class), "callMethod", cg
1771: .sig(IRubyObject.class, cg.params(ThreadContext.class,
1772: String.class, IRubyObject.class)));
1773: mv.visitLabel(l1);
1774: }
1775:
1776: private int constants = 0;
1777:
1778: private String getNewConstant(String type, String name_prefix) {
1779: ClassVisitor cv = getClassVisitor();
1780:
1781: String realName;
1782: synchronized (this ) {
1783: realName = name_prefix + constants++;
1784: }
1785:
1786: // declare the field
1787: cv.visitField(ACC_PRIVATE | ACC_STATIC, realName, type, null,
1788: null).visitEnd();
1789: return realName;
1790: }
1791:
1792: public void createNewRegexp(final ByteList value,
1793: final int options, final String lang) {
1794: SkinnyMethodAdapter mv = getMethodAdapter();
1795: String name = getNewConstant(cg.ci(Pattern.class),
1796: "literal_re_");
1797: String name_flags = getNewConstant(cg.ci(Integer.TYPE),
1798: "literal_re_flags_");
1799:
1800: loadRuntime();
1801:
1802: // load string, for Regexp#source and Regexp#inspect
1803: String regexpString = null;
1804: if ((options & ReOptions.RE_UNICODE) > 0) {
1805: regexpString = value.toUtf8String();
1806: } else {
1807: regexpString = value.toString();
1808: }
1809: mv.ldc(regexpString);
1810:
1811: // in current method, load the field to see if we've created a Pattern yet
1812:
1813: mv.visitFieldInsn(GETSTATIC, classname, name, cg
1814: .ci(Pattern.class));
1815: mv.dup();
1816:
1817: Label alreadyCreated = new Label();
1818: mv.ifnonnull(alreadyCreated);
1819: mv.pop();
1820: mv.ldc(new Integer(options));
1821: invokeUtilityMethod("regexpLiteralFlags", cg.sig(Integer.TYPE,
1822: cg.params(Integer.TYPE)));
1823: mv.visitFieldInsn(PUTSTATIC, classname, name_flags, cg
1824: .ci(Integer.TYPE));
1825:
1826: loadRuntime();
1827: mv.ldc(regexpString);
1828: mv.ldc(new Integer(options));
1829: invokeUtilityMethod("regexpLiteral", cg.sig(Pattern.class, cg
1830: .params(Ruby.class, String.class, Integer.TYPE)));
1831: mv.dup();
1832:
1833: mv.visitFieldInsn(PUTSTATIC, classname, name, cg
1834: .ci(Pattern.class));
1835:
1836: mv.label(alreadyCreated);
1837:
1838: mv.visitFieldInsn(GETSTATIC, classname, name_flags, cg
1839: .ci(Integer.TYPE));
1840: if (null == lang) {
1841: mv.aconst_null();
1842: } else {
1843: mv.ldc(lang);
1844: }
1845:
1846: mv.invokestatic(cg.p(RubyRegexp.class), "newRegexp", cg.sig(
1847: RubyRegexp.class, cg.params(Ruby.class, String.class,
1848: Pattern.class, Integer.TYPE, String.class)));
1849: }
1850:
1851: public void defineClass(String name, StaticScope staticScope,
1852: ClosureCallback super Callback,
1853: ClosureCallback pathCallback, ClosureCallback bodyCallback) {
1854: // TODO: build arg list based on number of args, optionals, etc
1855: ++methodIndex;
1856: String methodName = "rubyclass__"
1857: + cg.cleanJavaIdentifier(name) + "__" + methodIndex;
1858:
1859: beginMethod(methodName, null);
1860:
1861: SkinnyMethodAdapter mv = getMethodAdapter();
1862:
1863: // class def bodies default to public visibility
1864: mv.getstatic(cg.p(Visibility.class), "PUBLIC", cg
1865: .ci(Visibility.class));
1866: mv.astore(VISIBILITY_INDEX);
1867:
1868: // Here starts the logic for the class definition
1869: loadRuntime();
1870:
1871: super Callback.compile(this );
1872:
1873: invokeUtilityMethod("prepareSuperClass", cg.sig(
1874: RubyClass.class, cg.params(Ruby.class,
1875: IRubyObject.class)));
1876:
1877: loadThreadContext();
1878:
1879: pathCallback.compile(this );
1880:
1881: invokeUtilityMethod("prepareClassNamespace", cg.sig(
1882: RubyModule.class, cg.params(ThreadContext.class,
1883: IRubyObject.class)));
1884:
1885: mv.swap();
1886:
1887: mv.ldc(name);
1888:
1889: mv.swap();
1890:
1891: mv.invokevirtual(cg.p(RubyModule.class),
1892: "defineOrGetClassUnder", cg.sig(RubyClass.class, cg
1893: .params(String.class, RubyClass.class)));
1894:
1895: // set self to the class
1896: mv.dup();
1897: mv.astore(SELF_INDEX);
1898:
1899: // CLASS BODY
1900: loadThreadContext();
1901: mv.swap();
1902:
1903: // FIXME: this should be in a try/finally
1904: invokeThreadContext("preCompiledClass", cg.sig(Void.TYPE, cg
1905: .params(RubyModule.class)));
1906:
1907: bodyCallback.compile(this );
1908:
1909: loadThreadContext();
1910: invokeThreadContext("postCompiledClass", cg.sig(Void.TYPE, cg
1911: .params()));
1912:
1913: endMethod(mv);
1914:
1915: // return to previous method
1916: mv = getMethodAdapter();
1917:
1918: // prepare to call class definition method
1919: loadThreadContext();
1920: loadSelf();
1921: mv.getstatic(cg.p(IRubyObject.class), "NULL_ARRAY", cg
1922: .ci(IRubyObject[].class));
1923: mv.getstatic(cg.p(Block.class), "NULL_BLOCK", cg
1924: .ci(Block.class));
1925:
1926: mv.invokestatic(classname, methodName, METHOD_SIGNATURE);
1927: }
1928:
1929: public void defineModule(String name, StaticScope staticScope,
1930: ClosureCallback pathCallback, ClosureCallback bodyCallback) {
1931: // TODO: build arg list based on number of args, optionals, etc
1932: ++methodIndex;
1933: String methodName = "rubymodule__"
1934: + cg.cleanJavaIdentifier(name) + "__" + methodIndex;
1935:
1936: beginMethod(methodName, null);
1937:
1938: SkinnyMethodAdapter mv = getMethodAdapter();
1939:
1940: // module def bodies default to public visibility
1941: mv.getstatic(cg.p(Visibility.class), "PUBLIC", cg
1942: .ci(Visibility.class));
1943: mv.astore(VISIBILITY_INDEX);
1944:
1945: // Here starts the logic for the module definition
1946: loadThreadContext();
1947:
1948: pathCallback.compile(this );
1949:
1950: invokeUtilityMethod("prepareClassNamespace", cg.sig(
1951: RubyModule.class, cg.params(ThreadContext.class,
1952: IRubyObject.class)));
1953:
1954: mv.ldc(name);
1955:
1956: mv.invokevirtual(cg.p(RubyModule.class), "defineModuleUnder",
1957: cg.sig(RubyModule.class, cg.params(String.class)));
1958:
1959: // set self to the module
1960: mv.dup();
1961: mv.astore(SELF_INDEX);
1962:
1963: // MODULE BODY
1964: loadThreadContext();
1965: mv.swap();
1966:
1967: // FIXME: this should be in a try/finally
1968: invokeThreadContext("preCompiledClass", cg.sig(Void.TYPE, cg
1969: .params(RubyModule.class)));
1970:
1971: bodyCallback.compile(this );
1972:
1973: loadThreadContext();
1974: invokeThreadContext("postCompiledClass", cg.sig(Void.TYPE, cg
1975: .params()));
1976:
1977: endMethod(mv);
1978:
1979: // return to previous method
1980: mv = getMethodAdapter();
1981:
1982: // prepare to call class definition method
1983: loadThreadContext();
1984: loadSelf();
1985: mv.getstatic(cg.p(IRubyObject.class), "NULL_ARRAY", cg
1986: .ci(IRubyObject[].class));
1987: mv.getstatic(cg.p(Block.class), "NULL_BLOCK", cg
1988: .ci(Block.class));
1989:
1990: mv.invokestatic(classname, methodName, METHOD_SIGNATURE);
1991: }
1992:
1993: public void pollThreadEvents() {
1994: loadThreadContext();
1995: invokeThreadContext("pollThreadEvents", cg.sig(Void.TYPE));
1996: }
1997:
1998: private void nullToNil() {
1999: loadRuntime();
2000: invokeUtilityMethod("nullToNil", cg.sig(IRubyObject.class, cg
2001: .params(IRubyObject.class, Ruby.class)));
2002: }
2003:
2004: public void branchIfModule(BranchCallback moduleCallback,
2005: BranchCallback notModuleCallback) {
2006: SkinnyMethodAdapter mv = getMethodAdapter();
2007: mv.dup();
2008: mv.visitTypeInsn(INSTANCEOF, cg.p(RubyModule.class));
2009:
2010: Label falseJmp = new Label();
2011: Label afterJmp = new Label();
2012:
2013: mv.ifeq(falseJmp); // EQ == 0 (i.e. false)
2014:
2015: moduleCallback.branch(this);
2016:
2017: mv.go_to(afterJmp);
2018: mv.label(falseJmp);
2019:
2020: notModuleCallback.branch(this);
2021:
2022: mv.label(afterJmp);
2023: }
2024: }
|