| /******************************************************************************* |
| * Copyright (c) 2011-2014 Dennis Wagelaar, Vrije Universiteit Brussel. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Dennis Wagelaar, Vrije Universiteit Brussel |
| *******************************************************************************/ |
| package org.eclipse.m2m.atl.emftvm.jit; |
| |
| import java.lang.reflect.Method; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.Enumerator; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.m2m.atl.common.ATLLogger; |
| import org.eclipse.m2m.atl.emftvm.Add; |
| import org.eclipse.m2m.atl.emftvm.Allinst; |
| import org.eclipse.m2m.atl.emftvm.AllinstIn; |
| import org.eclipse.m2m.atl.emftvm.And; |
| import org.eclipse.m2m.atl.emftvm.CodeBlock; |
| import org.eclipse.m2m.atl.emftvm.Delete; |
| import org.eclipse.m2m.atl.emftvm.Dup; |
| import org.eclipse.m2m.atl.emftvm.DupX1; |
| import org.eclipse.m2m.atl.emftvm.Enditerate; |
| import org.eclipse.m2m.atl.emftvm.ExecEnv; |
| import org.eclipse.m2m.atl.emftvm.Field; |
| import org.eclipse.m2m.atl.emftvm.Findtype; |
| import org.eclipse.m2m.atl.emftvm.FindtypeS; |
| import org.eclipse.m2m.atl.emftvm.Get; |
| import org.eclipse.m2m.atl.emftvm.GetStatic; |
| import org.eclipse.m2m.atl.emftvm.GetSuper; |
| import org.eclipse.m2m.atl.emftvm.GetTrans; |
| import org.eclipse.m2m.atl.emftvm.Getcb; |
| import org.eclipse.m2m.atl.emftvm.Getenv; |
| import org.eclipse.m2m.atl.emftvm.Getenvtype; |
| import org.eclipse.m2m.atl.emftvm.Goto; |
| import org.eclipse.m2m.atl.emftvm.If; |
| import org.eclipse.m2m.atl.emftvm.Ifn; |
| import org.eclipse.m2m.atl.emftvm.Ifte; |
| import org.eclipse.m2m.atl.emftvm.Implies; |
| import org.eclipse.m2m.atl.emftvm.Insert; |
| import org.eclipse.m2m.atl.emftvm.Instruction; |
| import org.eclipse.m2m.atl.emftvm.Invoke; |
| import org.eclipse.m2m.atl.emftvm.InvokeAllCbs; |
| import org.eclipse.m2m.atl.emftvm.InvokeCb; |
| import org.eclipse.m2m.atl.emftvm.InvokeCbS; |
| import org.eclipse.m2m.atl.emftvm.InvokeStatic; |
| import org.eclipse.m2m.atl.emftvm.InvokeSuper; |
| import org.eclipse.m2m.atl.emftvm.Isnull; |
| import org.eclipse.m2m.atl.emftvm.Iterate; |
| import org.eclipse.m2m.atl.emftvm.Load; |
| import org.eclipse.m2m.atl.emftvm.Match; |
| import org.eclipse.m2m.atl.emftvm.MatchS; |
| import org.eclipse.m2m.atl.emftvm.Model; |
| import org.eclipse.m2m.atl.emftvm.New; |
| import org.eclipse.m2m.atl.emftvm.NewS; |
| import org.eclipse.m2m.atl.emftvm.Not; |
| import org.eclipse.m2m.atl.emftvm.Operation; |
| import org.eclipse.m2m.atl.emftvm.Or; |
| import org.eclipse.m2m.atl.emftvm.Pop; |
| import org.eclipse.m2m.atl.emftvm.Push; |
| import org.eclipse.m2m.atl.emftvm.Pushf; |
| import org.eclipse.m2m.atl.emftvm.Pusht; |
| import org.eclipse.m2m.atl.emftvm.Remove; |
| import org.eclipse.m2m.atl.emftvm.Return; |
| import org.eclipse.m2m.atl.emftvm.Rule; |
| import org.eclipse.m2m.atl.emftvm.Set; |
| import org.eclipse.m2m.atl.emftvm.SetStatic; |
| import org.eclipse.m2m.atl.emftvm.Store; |
| import org.eclipse.m2m.atl.emftvm.Swap; |
| import org.eclipse.m2m.atl.emftvm.SwapX1; |
| import org.eclipse.m2m.atl.emftvm.Xor; |
| import org.eclipse.m2m.atl.emftvm.util.EMFTVMUtil; |
| import org.eclipse.m2m.atl.emftvm.util.EmftvmSwitch; |
| import org.eclipse.m2m.atl.emftvm.util.EnumConversionList; |
| import org.eclipse.m2m.atl.emftvm.util.EnumConversionListOnList; |
| import org.eclipse.m2m.atl.emftvm.util.EnumConversionSetOnSet; |
| import org.eclipse.m2m.atl.emftvm.util.EnumLiteral; |
| import org.eclipse.m2m.atl.emftvm.util.LazyCollection; |
| import org.eclipse.m2m.atl.emftvm.util.LazyList; |
| import org.eclipse.m2m.atl.emftvm.util.LazyListOnList; |
| import org.eclipse.m2m.atl.emftvm.util.NativeTypes; |
| import org.eclipse.m2m.atl.emftvm.util.StackFrame; |
| import org.eclipse.m2m.atl.emftvm.util.VMException; |
| import org.objectweb.asm.Label; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| /** |
| * Adds code to the given {@link MethodVisitor}, and returns it. |
| * @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a> |
| */ |
| public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcodes { |
| |
| /** |
| * The map of primitive types to their boxed class counterpart type. |
| */ |
| public static final Map<Class<?>, Class<?>> BOXED_CLASSES = new HashMap<Class<?>, Class<?>>(); |
| |
| static { |
| BOXED_CLASSES.put(Void.TYPE, Void.class); |
| BOXED_CLASSES.put(Boolean.TYPE, Boolean.class); |
| BOXED_CLASSES.put(Character.TYPE, Character.class); |
| BOXED_CLASSES.put(Byte.TYPE, Byte.class); |
| BOXED_CLASSES.put(Short.TYPE, Short.class); |
| BOXED_CLASSES.put(Integer.TYPE, Integer.class); |
| BOXED_CLASSES.put(Long.TYPE, Long.class); |
| BOXED_CLASSES.put(Float.TYPE, Float.class); |
| BOXED_CLASSES.put(Double.TYPE, Double.class); |
| } |
| |
| /** |
| * Returns the boxed version of <code>type</code>, or <code>type</code> if no boxing required. |
| * @param type the type to box |
| * @return the boxed version of <code>type</code>, or <code>type</code> if no boxing required |
| */ |
| public static Class<?> boxed(final Class<?> type) { |
| if (BOXED_CLASSES.containsKey(type)) { |
| return BOXED_CLASSES.get(type); |
| } |
| return type; |
| } |
| |
| /** |
| * The {@link MethodVisitor} to add code to. |
| */ |
| protected final MethodVisitor mv; |
| /** |
| * The {@link LabelSwitch} to look up generated labels. |
| */ |
| protected final LabelSwitch ls; |
| /** |
| * The JIT compiler to generate instructions for. |
| */ |
| protected final CodeBlockJIT jit; |
| /** |
| * Whether or not the current execution environment has a monitor attached. |
| */ |
| protected final boolean hasMonitor; |
| /** |
| * Set of instructions to skip while generating bytecode. |
| */ |
| protected final java.util.Set<Instruction> skipInstructions = new HashSet<Instruction>(); |
| |
| /** |
| * Creates a new {@link ByteCodeSwitch}. |
| * @param jit the JIT compiler to generate instructions for |
| * @param mv the {@link MethodVisitor} to add code to |
| * @param ls the {@link LabelSwitch} to look up generated labels |
| */ |
| public ByteCodeSwitch(final CodeBlockJIT jit, final MethodVisitor mv, final LabelSwitch ls) { |
| super(); |
| this.jit = jit; |
| this.mv = mv; |
| this.ls = ls; |
| this.hasMonitor = jit.getEnv().getMonitor() != null; |
| } |
| |
| @Override |
| public MethodVisitor caseInstruction(final Instruction object) { |
| if (ls.hasWithTarget(object)) { |
| label(ls.getFromTarget(object)); |
| } |
| return mv; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor casePush(final Push object) { |
| // Box primitive types |
| if (object.getIntValue() != null) { |
| generatePushInt(object.getIntValue()); |
| invokeStat(Integer.class, "valueOf", Integer.class, Type.INT_TYPE); |
| } else if (object.getByteValue() != null) { |
| generatePushInt(object.getByteValue()); |
| invokeStat(Byte.class, "valueOf", Byte.class, Type.BYTE_TYPE); |
| } else if (object.getCharValue() != null) { |
| generatePushInt(object.getCharValue()); |
| invokeStat(Character.class, "valueOf", Character.class, Type.CHAR_TYPE); |
| } else if (object.getShortValue() != null) { |
| generatePushInt(object.getShortValue()); |
| invokeStat(Short.class, "valueOf", Short.class, Type.SHORT_TYPE); |
| } else if (object.getLongValue() != null) { |
| mv.visitLdcInsn(object.getLongValue()); |
| invokeStat(Long.class, "valueOf", Long.class, Type.LONG_TYPE); |
| } else if (object.getDoubleValue() != null) { |
| mv.visitLdcInsn(object.getDoubleValue()); |
| invokeStat(Double.class, "valueOf", Double.class, Type.DOUBLE_TYPE); |
| } else if (object.getFloatValue() != null) { |
| mv.visitLdcInsn(object.getFloatValue()); |
| invokeStat(Float.class, "valueOf", Float.class, Type.FLOAT_TYPE); |
| } else if (object.getEnumValue() != null) { |
| new_(EnumLiteral.class); // [..., enum] |
| dup(); // [..., enum, enum] |
| ldc(object.getEnumValue()); // [..., enum, enum, name] |
| invokeCons(EnumLiteral.class, String.class); // enum.<init>(name): [..., enum] |
| } else if (object.getValue() != null) { |
| ldc(object.getValue()); |
| } else { |
| aconst_null(); // push null |
| } |
| return super.casePush(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor casePusht(final Pusht object) { |
| iconst_1(); // true |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(true) |
| return super.casePusht(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor casePushf(final Pushf object) { |
| iconst_0(); // false |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(false) |
| return super.casePushf(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor casePop(final Pop object) { |
| pop(); |
| return super.casePop(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseLoad(final Load object) { |
| aload(1); // [..., frame] |
| if (object.getCbOffset() > 0) { |
| generatePushInt(object.getCbOffset()); // [..., frame, cbOffset] |
| generatePushInt(object.getSlot()); // [..., frame, cbOffset, slot] |
| invokeVirt(StackFrame.class, "getLocal", Object.class, // frame.getLocal(cbOffset, slot) |
| Type.INT_TYPE, Type.INT_TYPE); |
| } else { |
| generatePushInt(object.getSlot()); // [..., frame, slot] |
| invokeVirt(StackFrame.class, "getLocal", Object.class, // frame.getLocal(slot) |
| Type.INT_TYPE); |
| } |
| return super.caseLoad(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseStore(final Store object) { |
| // [..., value] |
| aload(1); // [..., value, frame] |
| swap(); // [..., frame, value] |
| if (object.getCbOffset() > 0) { |
| generatePushInt(object.getCbOffset()); // [..., frame, value, cbOffset] |
| generatePushInt(object.getSlot()); // [..., frame, value, cbOffset, slot] |
| invokeVirt(StackFrame.class, "setLocal", Type.VOID_TYPE, // frame.setLocal(value, cbOffset, slot) |
| Object.class, Type.INT_TYPE, Type.INT_TYPE); |
| } else { |
| generatePushInt(object.getSlot()); // [..., frame, value, slot] |
| invokeVirt(StackFrame.class, "setLocal", Type.VOID_TYPE, // frame.setLocal(value, slot) |
| Object.class, Type.INT_TYPE); |
| } |
| return super.caseStore(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseSet(final Set object) { |
| // [..., o, v] |
| aload(0); // this: [..., o, v, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, v, cb] |
| aload(1); // frame: [..., o, v, cb, frame] |
| ldc(object.getFieldname()); // [..., o, v, cb, frame, propname] |
| invokeStat(JITCodeBlock.class, "set", Type.VOID_TYPE, // set(o, v, cb, frame, propname) |
| Object.class, Object.class, CodeBlock.class, StackFrame.class, String.class); |
| return super.caseSet(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGet(final Get object) { |
| generateSetPc(object); |
| // [..., o] |
| final Rule rule = object.getOwningBlock().getRule(); |
| final boolean hasRuleField = rule != null && rule.hasField(object.getFieldname()); |
| final boolean hasField = jit.getEnv().hasField(object.getFieldname()); |
| if (hasRuleField) { |
| aload(0); // this: [..., o, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, cb] |
| aload(1); // frame: [..., o, cb, frame] |
| ldc(object.getFieldname()); // [..., o, cb, frame, propname] |
| invokeStat(JITCodeBlock.class, "get", Object.class, // get(o, cb, frame, propname) |
| Object.class, CodeBlock.class, StackFrame.class, String.class); |
| } else if (hasField) { |
| aload(1); // frame: [..., o, frame] |
| ldc(object.getFieldname()); // [..., o, frame, propname] |
| invokeStat(JITCodeBlock.class, "get", Object.class, // get(o, frame, propname) |
| Object.class, StackFrame.class, String.class); |
| } else { |
| aload(2); // env: [..., o, env] |
| ldc(object.getFieldname()); // [..., o, env, propname] |
| invokeStat(JITCodeBlock.class, "get", Object.class, // get(o, env, propname) |
| Object.class, ExecEnv.class, String.class); |
| } |
| return super.caseGet(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGetTrans(final GetTrans object) { |
| generateSetPc(object); |
| // [..., o] |
| aload(0); // this: [..., o, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, cb] |
| aload(1); // frame: [..., o, cb, frame] |
| ldc(object.getFieldname()); // [..., o, cb, frame, propname] |
| invokeStat(JITCodeBlock.class, "getTrans", Object.class, // getTrans(o, cb, frame, propname) |
| Object.class, CodeBlock.class, StackFrame.class, String.class); |
| return super.caseGetTrans(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseSetStatic(final SetStatic object) { |
| // [..., o, v] |
| aload(0); // this: [..., o, v, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, v, cb] |
| aload(2); // env: [..., o, v, cb, env] |
| ldc(object.getFieldname()); // [..., o, v, cb, env, propname] |
| invokeStat(JITCodeBlock.class, "setStatic", Type.VOID_TYPE, // setStatic(o, v, cb, env, propname) |
| Object.class, Object.class, CodeBlock.class, ExecEnv.class, String.class); |
| return super.caseSetStatic(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGetStatic(final GetStatic object) { |
| generateSetPc(object); |
| // [..., o] |
| aload(0); // this: [..., o, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, cb] |
| aload(1); // frame: [..., o, cb, frame] |
| ldc(object.getFieldname()); // [..., o, cb, frame, propname] |
| invokeStat(JITCodeBlock.class, "getStatic", Object.class, // getStatic(o, cb, frame, propname) |
| Object.class, CodeBlock.class, StackFrame.class, String.class); |
| return super.caseGetStatic(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseFindtype(final Findtype object) { |
| // Native types can be found faster |
| if (EMFTVMUtil.NATIVE.equals(object.getModelname())) { |
| try { |
| final Class<?> type = NativeTypes.findType(object.getTypename()); |
| final Instruction nextInstr = nextInstruction(object); |
| if (nextInstr instanceof New) { |
| new_(type); |
| dup(); |
| invokeSpec(type, "<init>", Void.TYPE); |
| skipInstructions.add(nextInstr); |
| } else { |
| ldc(Type.getType(type)); // [..., type] |
| } |
| return super.caseFindtype(object); |
| } catch (ClassNotFoundException e) { |
| // fall back - will generate same exception anyway |
| } |
| } |
| aload(2); // env: [..., env] |
| ldc(object.getModelname()) ; // [..., env, modelname] |
| ldc(object.getTypename()) ; // [..., env, modelname, typename] |
| invokeIface(ExecEnv.class, "findType", Object.class, // env.findType(modelname, typename): [..., type] |
| String.class, String.class); |
| return super.caseFindtype(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseFindtypeS(final FindtypeS object) { |
| // [..., modelname, typename] |
| aload(2); // env: [..., modelname, typename, env] |
| invokeStat(JITCodeBlock.class, "findTypeS", Object.class, // findTypeS(modelname, typename, env): [..., type] |
| String.class, String.class, ExecEnv.class); |
| return super.caseFindtypeS(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseNew(final New object) { |
| if (!skipInstructions.contains(object)) { |
| // [..., type] |
| final String modelName = object.getModelname(); |
| if (modelName == null) { |
| aconst_null(); // [..., type, null] |
| } else { |
| ldc(object.getModelname()) ; // [..., type, modelname] |
| } |
| aload(2); // env: [..., type, modelname, env] |
| invokeStat(JITCodeBlock.class, "newInstance", Object.class, // newInstance(type, modelname, env): [..., object] |
| Object.class, String.class, ExecEnv.class); |
| } |
| return super.caseNew(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseNewS(final NewS object) { |
| // [..., type, modelname] |
| checkcast(String.class); // [..., type, modelname] |
| aload(2); // env: [..., type, modelname, env] |
| invokeStat(JITCodeBlock.class, "newInstance", Object.class, // newInstance(type, modelname, env): [..., object] |
| Object.class, String.class, ExecEnv.class); |
| return super.caseNewS(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseDelete(final Delete object) { |
| // [..., object] |
| checkcast(EObject.class); // [..., element] |
| aload(1); // frame: [..., element, frame] |
| invokeStat(JITCodeBlock.class, "delete", Type.VOID_TYPE, // delete(element, frame): [...] |
| EObject.class, StackFrame.class); |
| return super.caseDelete(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseDup(final Dup object) { |
| dup(); |
| return super.caseDup(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseDupX1(final DupX1 object) { |
| dup_x1(); |
| return super.caseDupX1(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseSwap(final Swap object) { |
| swap(); |
| return super.caseSwap(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseSwapX1(final SwapX1 object) { |
| // [..., a, b, c] |
| dup_x2(); // [..., c, a, b, c] |
| pop(); // [..., c, a, b] |
| swap(); // [..., c, b, a] |
| return super.caseSwapX1(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseIf(final If object) { |
| assert ls.hasWithSource(object); |
| // Unbox the Boolean object |
| checkcast(Boolean.class); |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // Boolean.booleanValue() |
| ifne(ls.getFromSource(object)); // jump if bool != 0 |
| return super.caseIf(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseIfn(final Ifn object) { |
| assert ls.hasWithSource(object); |
| // Unbox the Boolean object |
| checkcast(Boolean.class); |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // Boolean.booleanValue() |
| ifeq(ls.getFromSource(object)); // jump if bool == 0 |
| return super.caseIfn(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGoto(final Goto object) { |
| assert ls.hasWithSource(object); |
| goto_(ls.getFromSource(object)); |
| return super.caseGoto(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseIterate(final Iterate object) { |
| assert ls.hasWithSource(object); |
| // Labels |
| final Label hasNext = new Label(); |
| // Instructions |
| checkcast(Collection.class); // [..., collection] |
| invokeIface(Collection.class, "iterator", Iterator.class); // collection.iterator(): [..., iterator] |
| dup(); // [..., iterator, iterator] |
| invokeIface(Iterator.class, "hasNext", Type.BOOLEAN_TYPE); // iterator.hasNext(): [..., iterator, boolean] |
| ifne(hasNext); // jump if hasNext bool != 0: [..., iterator] |
| // has no next |
| pop(); // [...] |
| goto_(ls.getFromSource(object)); // jump over ENDITERATE |
| // has next |
| label(hasNext); |
| dup(); // [..., iterator, iterator] |
| invokeIface(Iterator.class, "next", Object.class); // iterator.next(): [..., iterator, object] |
| return super.caseIterate(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseEnditerate(final Enditerate object) { |
| assert ls.hasWithSource(object); |
| // Labels |
| final Label hasNoNext = new Label(); |
| // Instructions |
| dup(); // [..., iterator, iterator] |
| invokeIface(Iterator.class, "hasNext", Type.BOOLEAN_TYPE); // iterator.hasNext(): [..., iterator, boolean] |
| ifeq(hasNoNext); // jump if hasNext bool == 0: [..., iterator] |
| // has next |
| dup(); // [..., iterator, iterator] |
| invokeIface(Iterator.class, "next", Object.class); // iterator.next(): [..., iterator, object] |
| goto_(ls.getFromSource(object)); // jump over ITERATE |
| // has no next |
| label(hasNoNext); // [..., iterator] |
| pop(); // [...] |
| return super.caseEnditerate(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInvoke(final Invoke object) { |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: generateInvoke0(object); break; |
| case 1: generateInvoke1(object); break; |
| default: generateInvokeN(object, argcount); break; |
| } |
| return super.caseInvoke(object); |
| } |
| |
| /** |
| * Generates bytecode for an INVOKE without arguments. |
| * @param object the INVOKE instruction |
| */ |
| private void generateInvoke0(final Invoke object) { |
| // [..., self] |
| final boolean hasOp = jit.getEnv().hasOperation(object.getOpname(), object.getArgcount()); |
| // Labels |
| final Label selfStart = new Label(); |
| final Label selfEnd = new Label(); |
| // Local variable indexes |
| final int selfIdx = 4; |
| final int methodIdx = 5; |
| final int bodyIdx = 6; |
| final int subframeIdx = hasOp ? 6 : 5; |
| final int vmExceptionIdx = subframeIdx + 1; |
| final int exceptionIdx = vmExceptionIdx; |
| // Bytecode |
| label(selfStart); |
| astore(selfIdx); // self: [...] |
| if (hasOp) { // Generate Operation invocation code |
| // Labels |
| final Label ifOpNull = new Label(); |
| final Label bodyStart = new Label(); |
| // Bytecode |
| aload(2); // env: [..., env] |
| aload(selfIdx); // self: [..., env, self] |
| invokeStat(EMFTVMUtil.class, "getArgumentType", Object.class, Object.class); // EMFTVMUtil.getArgumentType(self): [..., env, context] |
| ldc(object.getOpname()); // [..., env, context, opname] |
| invokeIface(ExecEnv.class, "findOperation", Operation.class, // env.findOperation(context, opname): [..., op] |
| Object.class, String.class); |
| dup(); // [..., op, op] |
| aload(selfIdx); // self: [..., op, op, self] |
| ldc(object.getOpname()); // opname: [..., op, op, self, opname] |
| invokeStat(EMFTVMUtil.class, "findNativeMethod", Method.class, // EMFTVMUtil.findNativeMethod(op, self, opname): [..., op, method] |
| Operation.class, Object.class, String.class); |
| astore(methodIdx); // method: [..., op] |
| aload(methodIdx); // method; [..., op, method] |
| ifnonnull(ifOpNull); // jump if method != null: [..., op] |
| dup(); // [..., op, op] |
| ifnull(ifOpNull); // jump if op == null: [..., op] |
| invokeIface(Operation.class, "getBody", CodeBlock.class); // op.getBody(): [..., body] |
| label(bodyStart); |
| astore(bodyIdx); // body: [...] |
| aload(bodyIdx); // body: [..., body] |
| aload(1); // frame: [..., body, frame] |
| aload(bodyIdx); // body: [..., body, frame, body] |
| aload(selfIdx); // self: [..., body, frame, body, self] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(body, self): [..., body, newframe] |
| CodeBlock.class, Object.class); |
| invokeIface(CodeBlock.class, "execute", Object.class, StackFrame.class); // body.execute(newframe): [..., result] |
| goto_(selfEnd); // jump to end |
| label(ifOpNull); // [..., op] |
| pop(); // [...] |
| // Local variables |
| localVariable("body", CodeBlock.class, bodyStart, ifOpNull, bodyIdx); |
| } |
| // Generate native method invocation code here |
| final Method method = EMFTVMUtil.findRootMethod(object.getNativeMethod()); |
| if (method != null) { // native method recorded - try first |
| // Labels |
| final Label subframeStart = new Label(); |
| final Label tryStart = new Label(); |
| final Label tryEnd = new Label(); |
| final Label vmExceptionHandler = new Label(); |
| final Label exceptionHandler = new Label(); |
| final Label subframeNonNull = new Label(); |
| // Try-catch blocks |
| tryCatchBlock(tryStart, vmExceptionHandler, vmExceptionHandler, VMException.class); |
| tryCatchBlock(tryStart, vmExceptionHandler, exceptionHandler, Exception.class); |
| // Bytecode |
| label(subframeStart); |
| aconst_null(); // [..., null] |
| astore(subframeIdx); // subframe: [...] |
| // Check context type (no need to unbox) |
| final Class<?> dc = method.getDeclaringClass(); |
| if (!dc.equals(Object.class)) { |
| aload(selfIdx); // self: [..., self] |
| instanceof_(dc); // [..., boolean] |
| ifeq(tryEnd); // jump if context type does not match: [...] |
| } |
| // start try-block with native Java INVOKE instruction |
| label(tryStart); // [...] |
| final int opcode; |
| if (dc.isInterface()) { |
| opcode = INVOKEINTERFACE; |
| } else { |
| opcode = INVOKEVIRTUAL; |
| } |
| aload(selfIdx); // self: [..., self] |
| if (!dc.equals(Object.class)) { |
| checkcast(dc); // [..., self] |
| // Prepare self code block with subframe |
| if (CodeBlock.class.isAssignableFrom(dc)) { |
| generateSubFrame(method.toString()); // [..., self, subframe] |
| astore(subframeIdx); // subframe: [..., self] |
| dup(); // [..., self, self] |
| aload(subframeIdx); // subframe: [..., self, self, subframe] |
| invokeIface(CodeBlock.class, "setParentFrame", Type.VOID_TYPE, // self.setParentFrame(subframe): [..., self] |
| StackFrame.class); |
| } |
| } |
| mv.visitMethodInsn(opcode, // self.<method>(): [..., ?] |
| Type.getInternalName(dc), |
| method.getName(), |
| Type.getMethodDescriptor(method), |
| dc.isInterface()); |
| // Check if method returned anything |
| final Class<?> rt = method.getReturnType(); |
| // Box primitive return values |
| generateBoxing(rt, dc); // [..., result] |
| goto_(selfEnd); // jump over catch block: [..., result] |
| // catch (VMException e) |
| label(vmExceptionHandler); // [..., e] |
| astore(vmExceptionIdx); // e: [...] |
| aload(vmExceptionIdx); // e: [..., e] |
| athrow(); // throw e |
| // catch (Exception e) |
| label(exceptionHandler); // [..., e] |
| astore(exceptionIdx); // e |
| new_(VMException.class); // new VMException(): [..., vme] |
| dup(); // [..., vme, vme] |
| aload(subframeIdx); // subframe: [..., vme, vme, subframe] |
| dup(); // [..., vme, vme, subframe, subframe] |
| ifnonnull(subframeNonNull); // jump if subframe != null: [..., vme, vme, subframe] |
| pop(); // [..., vme, vme] |
| generateSubFrame(method.toString()); // [..., vme, vme, subframe] |
| label(subframeNonNull); // [..., vme, vme, subframe] |
| aload(exceptionIdx); // e: [..., vme, vme, subframe, e] |
| invokeCons(VMException.class, StackFrame.class, Throwable.class); // new VMException(subframe, e): [vme, ...] |
| athrow(); // throw vme |
| // end of try-catch |
| label(tryEnd); // [...] |
| if (!dc.equals(Object.class)) { |
| // Fall back to reflective invocation |
| ldc("JIT miss for " + method.toString()); // [..., msg] |
| invokeStat(ATLLogger.class, "info", Type.VOID_TYPE, String.class); // ATLLogger.info(msg): [...] |
| aload(1); // frame: [..., frame] |
| aload(selfIdx); // self: [..., frame, self] |
| ldc(object.getOpname()); // opname: [..., frame, self, opname] |
| if (hasOp) { |
| aload(methodIdx); // method; [..., frame, self, opname, method] |
| invokeStat(JITCodeBlock.class, "invokeNative", Object.class, // JITCodeBlock.invokeNative(frame, self, opname, method): [..., result] |
| StackFrame.class, Object.class, String.class, Method.class); |
| } else { |
| invokeStat(EMFTVMUtil.class, "invokeNative", Object.class, // EMFTVMUtil.invokeNative(frame, self, opname): [..., result] |
| StackFrame.class, Object.class, String.class); |
| } |
| } |
| // end of instructions |
| label(selfEnd); // [..., result] |
| // Local variables |
| localVariable("self", Object.class, selfStart, selfEnd, selfIdx); |
| if (hasOp) { |
| localVariable("method", Method.class, selfStart, selfEnd, methodIdx); |
| } |
| localVariable("subframe", StackFrame.class, subframeStart, selfEnd, subframeIdx); |
| localVariable("e", VMException.class, vmExceptionHandler, exceptionHandler, vmExceptionIdx); |
| localVariable("e", Exception.class, exceptionHandler, selfEnd, exceptionIdx); |
| } else { |
| aload(1); // frame: [..., frame] |
| aload(selfIdx); // self: [..., frame, self] |
| ldc(object.getOpname()); // opname: [..., frame, self, opname] |
| if (hasOp) { |
| aload(methodIdx); // method; [..., frame, self, opname, method] |
| invokeStat(JITCodeBlock.class, "invokeNative", Object.class, // JITCodeBlock.invokeNative(frame, self, opname, method): [..., result] |
| StackFrame.class, Object.class, String.class, Method.class); |
| } else { |
| invokeStat(EMFTVMUtil.class, "invokeNative", Object.class, // EMFTVMUtil.invokeNative(frame, self, opname): [..., result] |
| StackFrame.class, Object.class, String.class); |
| } |
| label(selfEnd); // [..., result] |
| // Local variables |
| localVariable("self", Object.class, selfStart, selfEnd, selfIdx); |
| if (hasOp) { |
| localVariable("method", Method.class, selfStart, selfEnd, methodIdx); |
| } |
| } |
| } |
| |
| /** |
| * Generates bytecode for an INVOKE with one argument. |
| * @param object the INVOKE instruction |
| */ |
| private void generateInvoke1(final Invoke object) { |
| // [..., self, arg] |
| final boolean hasOp = jit.getEnv().hasOperation(object.getOpname(), object.getArgcount()); |
| // Labels |
| final Label selfStart = new Label(); |
| final Label selfEnd = new Label(); |
| // Local variable indexes |
| final int selfIdx = 4; |
| final int argIdx = 5; |
| final int methodIdx = 6; |
| final int bodyIdx = 7; |
| final int subframeIdx = hasOp ? 7 : 6; |
| final int vmExceptionIdx = subframeIdx + 1; |
| final int exceptionIdx = vmExceptionIdx; |
| // Bytecode |
| label(selfStart); |
| astore(argIdx); // arg: [..., self] |
| astore(selfIdx); // self: [...] |
| if (hasOp) { // Generate Operation invocation code |
| // Labels |
| final Label ifOpNull = new Label(); |
| final Label bodyStart = new Label(); |
| // Bytecode |
| aload(2); // env: [..., env] |
| aload(selfIdx); // self: [..., env, self] |
| invokeStat(EMFTVMUtil.class, "getArgumentType", Object.class, Object.class); // EMFTVMUtil.getArgumentType(self): [..., env, context] |
| ldc(object.getOpname()); // [..., env, context, opname] |
| aload(argIdx); // arg: [..., env, context, opname, arg] |
| invokeStat(EMFTVMUtil.class, "getArgumentType", Object.class, Object.class); // EMFTVMUtil.getArgumentType(arg): [..., env, context, opname, argtype] |
| invokeIface(ExecEnv.class, "findOperation", Operation.class, // env.findOperation(context, opname, argtype): [..., op] |
| Object.class, String.class, Object.class); |
| dup(); // [..., op, op] |
| aload(selfIdx); // self: [..., op, op, self] |
| ldc(object.getOpname()); // opname: [..., op, op, self, opname] |
| aload(argIdx); // arg: [..., op, op, self, opname, arg] |
| invokeStat(EMFTVMUtil.class, "findNativeMethod", Method.class, // EMFTVMUtil.findNativeMethod(op, self, opname, arg): [..., op, method] |
| Operation.class, Object.class, String.class, Object.class); |
| astore(methodIdx); // method: [..., op] |
| aload(methodIdx); // method; [..., op, method] |
| ifnonnull(ifOpNull); // jump if method != null: [..., op] |
| dup(); // [..., op, op] |
| ifnull(ifOpNull); // jump if op == null: [..., op] |
| invokeIface(Operation.class, "getBody", CodeBlock.class); // op.getBody(): [..., body] |
| label(bodyStart); |
| astore(bodyIdx); // body: [...] |
| aload(bodyIdx); // body: [..., body] |
| aload(1); // frame: [..., body, frame] |
| aload(bodyIdx); // body: [..., body, frame, body] |
| aload(selfIdx); // self: [..., body, frame, body, self] |
| aload(argIdx); // arg: [..., body, frame, body, self, arg] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(body, self, arg): [..., body, newframe] |
| CodeBlock.class, Object.class, Object.class); |
| invokeIface(CodeBlock.class, "execute", Object.class, StackFrame.class); // body.execute(newframe): [..., result] |
| goto_(selfEnd); // jump to end |
| label(ifOpNull); // [..., op] |
| pop(); // [...] |
| // Local variables |
| localVariable("body", CodeBlock.class, bodyStart, ifOpNull, bodyIdx); |
| } |
| // Generate native method invocation code here |
| final Method method = EMFTVMUtil.findRootMethod(object.getNativeMethod()); |
| if (method != null) { // native method recorded - try first |
| // Labels |
| final Label subframeStart = new Label(); |
| final Label tryStart = new Label(); |
| final Label tryEnd = new Label(); |
| final Label vmExceptionHandler = new Label(); |
| final Label exceptionHandler = new Label(); |
| final Label subframeNonNull = new Label(); |
| // Try-catch blocks |
| tryCatchBlock(tryStart, vmExceptionHandler, vmExceptionHandler, VMException.class); |
| tryCatchBlock(tryStart, vmExceptionHandler, exceptionHandler, Exception.class); |
| // Bytecode |
| label(subframeStart); |
| aconst_null(); // [..., null] |
| astore(subframeIdx); // subframe: [...] |
| // Check context type (no need to unbox) |
| final Class<?> dc = method.getDeclaringClass(); |
| if (!dc.equals(Object.class)) { |
| aload(selfIdx); // self: [..., self] |
| instanceof_(dc); // [..., boolean] |
| ifeq(tryEnd); // jump if context type does not match: [...] |
| } |
| final Class<?> pc = method.getParameterTypes()[0]; |
| if (!pc.equals(Object.class)) { |
| aload(argIdx); // arg: [..., arg] |
| instanceof_(boxed(pc)); // [..., boolean] |
| ifeq(tryEnd); // jump if arg type does not match: [...] |
| } |
| // start try-block with native Java INVOKE instruction |
| label(tryStart); // [...] |
| final int opcode; |
| if (dc.isInterface()) { |
| opcode = INVOKEINTERFACE; |
| } else { |
| opcode = INVOKEVIRTUAL; |
| } |
| // Prepare subframe |
| if (CodeBlock.class.isAssignableFrom(dc) || CodeBlock.class.isAssignableFrom(pc)) { |
| generateSubFrame(method.toString()); // [..., subframe] |
| astore(subframeIdx); // subframe: [...] |
| } |
| aload(selfIdx); // self: [..., self] |
| if (!dc.equals(Object.class)) { |
| checkcast(dc); // [..., self] |
| // Prepare self code block with subframe |
| if (CodeBlock.class.isAssignableFrom(dc)) { |
| dup(); // [..., self, self] |
| aload(subframeIdx); // subframe: [..., self, self, subframe] |
| invokeIface(CodeBlock.class, "setParentFrame", Type.VOID_TYPE, StackFrame.class); // self.setParentFrame(subframe): [..., self] |
| } |
| } |
| aload(argIdx); // arg: [..., self, arg] |
| // Method parameter unboxing |
| if (!pc.equals(Object.class)) { |
| generateUnboxing(pc); // [..., self, arg] |
| // Prepare self code block with subframe |
| if (CodeBlock.class.isAssignableFrom(pc)) { |
| dup(); // [..., self, arg, arg] |
| aload(subframeIdx); // subframe: [..., self, arg, arg, subframe] |
| invokeIface(CodeBlock.class, "setParentFrame", Type.VOID_TYPE, StackFrame.class); // arg.setParentFrame(subframe): [..., self, arg] |
| } |
| } |
| mv.visitMethodInsn(opcode, // self.<method>(arg): [..., ?] |
| Type.getInternalName(dc), |
| method.getName(), |
| Type.getMethodDescriptor(method), |
| dc.isInterface()); |
| // Check if method returned anything |
| final Class<?> rt = method.getReturnType(); |
| // Box primitive return values |
| generateBoxing(rt, dc); // [..., result] |
| goto_(selfEnd); // jump over catch block: [..., result] |
| // catch (VMException e) |
| label(vmExceptionHandler); // [..., e] |
| astore(vmExceptionIdx); // e: [...] |
| aload(vmExceptionIdx); // e: [..., e] |
| athrow(); // throw e |
| // catch (Exception e) |
| label(exceptionHandler); // [..., e] |
| astore(exceptionIdx); // e |
| new_(VMException.class); // new VMException(): [..., vme] |
| dup(); // [..., vme, vme] |
| aload(subframeIdx); // subframe: [..., vme, vme, subframe] |
| dup(); // [..., vme, vme, subframe, subframe] |
| ifnonnull(subframeNonNull); // jump if subframe != null: [..., vme, vme, subframe] |
| pop(); // [..., vme, vme] |
| generateSubFrame(method.toString()); // [..., vme, vme, subframe] |
| label(subframeNonNull); // [..., vme, vme, subframe] |
| aload(exceptionIdx); // e: [..., vme, vme, subframe, e] |
| invokeCons(VMException.class, StackFrame.class, Throwable.class); // vme.<init>(subframe, e): [..., vme] |
| athrow(); // throw vme |
| // end of try-catch |
| label(tryEnd); // [...] |
| if (!dc.equals(Object.class) || !pc.equals(Object.class)) { |
| // Fall back to reflective invocation |
| ldc("JIT miss for " + method.toString()); // [..., msg] |
| invokeStat(ATLLogger.class, "info", Type.VOID_TYPE, String.class); // ATLLogger.info(msg): [...] |
| aload(1); // frame: [..., frame] |
| aload(selfIdx); // self: [..., frame, self] |
| ldc(object.getOpname()); // opname: [..., frame, self, opname] |
| aload(argIdx); // arg: [..., frame, self, opname, arg] |
| if (hasOp) { |
| aload(methodIdx); // method; [..., frame, self, opname, method] |
| invokeStat(JITCodeBlock.class, "invokeNative", Object.class, // JITCodeBlock.invokeNative(frame, self, opname, arg, method): [..., result] |
| StackFrame.class, Object.class, String.class, Object.class, Method.class); |
| } else { |
| invokeStat(EMFTVMUtil.class, "invokeNative", Object.class, // EMFTVMUtil.invokeNative(frame, self, opname, arg): [..., result] |
| StackFrame.class, Object.class, String.class, Object.class); |
| } |
| } |
| // end of instructions |
| label(selfEnd); // [..., result] |
| // Local variables |
| localVariable("self", Object.class, selfStart, selfEnd, selfIdx); |
| localVariable("arg", Object.class, selfStart, selfEnd, argIdx); |
| if (hasOp) { |
| localVariable("method", Method.class, selfStart, selfEnd, methodIdx); |
| } |
| localVariable("subframe", StackFrame.class, subframeStart, selfEnd, subframeIdx); |
| localVariable("e", VMException.class, vmExceptionHandler, exceptionHandler, vmExceptionIdx); |
| localVariable("e", Exception.class, exceptionHandler, selfEnd, exceptionIdx); |
| } else { |
| aload(1); // frame: [..., frame] |
| aload(selfIdx); // self: [..., frame, self] |
| ldc(object.getOpname()); // opname: [..., frame, self, opname] |
| aload(argIdx); // arg: [..., frame, self, opname, arg] |
| if (hasOp) { |
| aload(methodIdx); // method; [..., frame, self, opname, arg, method] |
| invokeStat(JITCodeBlock.class, "invokeNative", Object.class, // JITCodeBlock.invokeNative(frame, self, opname, arg, method): [..., result] |
| StackFrame.class, Object.class, String.class, Object.class, Method.class); |
| } else { |
| invokeStat(EMFTVMUtil.class, "invokeNative", Object.class, // EMFTVMUtil.invokeNative(frame, self, opname, arg): [..., result] |
| StackFrame.class, Object.class, String.class, Object.class); |
| } |
| label(selfEnd); // [..., result] |
| // Local variables |
| localVariable("self", Object.class, selfStart, selfEnd, selfIdx); |
| localVariable("arg", Object.class, selfStart, selfEnd, argIdx); |
| if (hasOp) { |
| localVariable("method", Method.class, selfStart, selfEnd, methodIdx); |
| } |
| } |
| } |
| |
| /** |
| * Generates bytecode for an INVOKE with <code>argcount</code> arguments. |
| * @param object the INVOKE instruction |
| * @param argcount the number of arguments |
| */ |
| private void generateInvokeN(final Invoke object, final int argcount) { |
| // [..., self, args] |
| final boolean hasOp = jit.getEnv().hasOperation(object.getOpname(), object.getArgcount()); |
| // Labels |
| final Label selfStart = new Label(); |
| final Label selfEnd = new Label(); |
| // Local variable indexes |
| final int selfIdx = 4; |
| final int argsIdx = 5; |
| final int methodIdx = 6; |
| final int bodyIdx = 7; |
| // Bytecode |
| generatePushInt(argcount); // [..., self, args, argcount] |
| anewarray(Object.class); // new Object[argcount]: [..., self, args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., self, args, array, arg, array] |
| swap(); // swap arg over array ref: [..., self, args, array, array, arg] |
| generatePushInt(argcount - 1 - i); // index: [..., self, args, array, array, arg, index] |
| swap(); // swap index over arg: [..., self, args, array, array, index, arg] |
| aastore(); // store: [..., self, args, array] |
| } // no more args: [..., self, array] |
| label(selfStart); |
| astore(argsIdx); // args: [..., self] |
| astore(selfIdx); // self: [...] |
| if (hasOp) { // Generate Operation invocation code |
| // Labels |
| final Label ifOpNull = new Label(); |
| final Label bodyStart = new Label(); |
| // Bytecode |
| aload(2); // env: [..., env] |
| aload(selfIdx); // self: [..., env, self] |
| invokeStat(EMFTVMUtil.class, "getArgumentType", Object.class, Object.class); // EMFTVMUtil.getArgumentType(self): [..., env, context] |
| ldc(object.getOpname()); // [..., env, context, opname] |
| aload(argsIdx); // args: [..., env, context, opname, args] |
| invokeStat(EMFTVMUtil.class, "getArgumentTypes", Object[].class, Object[].class); // EMFTVMUtil.getArgumentTypes(args): [..., env, context, opname, argtypes] |
| invokeIface(ExecEnv.class, "findOperation", Operation.class, Object.class, String.class, Object[].class); // env.findOperation(context, opname, argtypes): [..., op] |
| dup(); // [..., op, op] |
| aload(selfIdx); // self: [..., op, op, self] |
| ldc(object.getOpname()); // opname: [..., op, op, self, opname] |
| aload(argsIdx); // args: [..., op, op, self, opname, args] |
| invokeStat(EMFTVMUtil.class, "findNativeMethod", Method.class, // EMFTVMUtil.findNativeMethod(op, self, opname, args): [..., op, method] |
| Operation.class, Object.class, String.class, Object[].class); |
| astore(methodIdx); // method: [..., op] |
| aload(methodIdx); // method; [..., op, method] |
| ifnonnull(ifOpNull); // jump if method != null: [..., op] |
| dup(); // [..., op, op] |
| ifnull(ifOpNull); // jump if op == null: [..., op] |
| invokeIface(Operation.class, "getBody", CodeBlock.class); // op.getBody(): [..., body] |
| label(bodyStart); |
| astore(bodyIdx); // body: [...] |
| aload(bodyIdx); // body: [..., body] |
| aload(1); // frame: [..., body, frame] |
| aload(bodyIdx); // body: [..., body, frame, body] |
| aload(selfIdx); // self: [..., body, frame, body, self] |
| aload(argsIdx); // args: [..., body, frame, body, self, args] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(body, self, args): [..., body, newframe] |
| CodeBlock.class, Object.class, Object[].class); |
| invokeIface(CodeBlock.class, "execute", Object.class, StackFrame.class); // body.execute(newframe): [..., result] |
| goto_(selfEnd); // jump to end |
| label(ifOpNull); // [..., op] |
| pop(); // [...] |
| // Local variables |
| localVariable("body", CodeBlock.class, bodyStart, ifOpNull, bodyIdx); |
| } |
| // Generate native method invocation code here |
| aload(1); // frame: [..., frame] |
| aload(selfIdx); // self: [..., frame, self] |
| ldc(object.getOpname()); // opname: [..., frame, self, opname] |
| aload(argsIdx); // args: [..., frame, self, opname, args] |
| if (hasOp) { |
| aload(methodIdx); // method; [..., frame, self, opname, args, method] |
| invokeStat(JITCodeBlock.class, "invokeNative", Object.class, // JITCodeBlock.invokeNative(frame, self, opname, args, method): [..., result] |
| StackFrame.class, Object.class, String.class, Object[].class, Method.class); |
| } else { |
| invokeStat(EMFTVMUtil.class, "invokeNative", Object.class, // EMFTVMUtil.invokeNative(frame, self, opname, args): [..., result] |
| StackFrame.class, Object.class, String.class, Object[].class); |
| } |
| label(selfEnd); // [..., result] |
| // Local variables |
| localVariable("self", Object.class, selfStart, selfEnd, selfIdx); |
| localVariable("args", Object[].class, selfStart, selfEnd, argsIdx); |
| if (hasOp) { |
| localVariable("method", Method.class, selfStart, selfEnd, methodIdx); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInvokeSuper(final InvokeSuper object) { |
| // Pre-calc context type and perform checks |
| final Operation op = object.getOwningBlock().getOperation(); |
| if (op == null) { |
| throw new IllegalArgumentException("INVOKE_SUPER can only be used in operations"); |
| } |
| final EClassifier context = op.getEContext(); |
| if (context == null) { |
| throw new IllegalArgumentException(String.format("Operation misses context type: %s", op)); |
| } |
| if (!(context instanceof EClass) && context.getInstanceClass() == null) { |
| throw new IllegalArgumentException(String.format("Primitive EMF type without instance class %s", context)); |
| } |
| |
| // Generate bytecode |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [..., self] |
| aload(0); // [..., self, this] |
| if (!(context instanceof EClass)) { |
| getField(JITCodeBlock.class, "context", Class.class); // [..., self, context] |
| aload(1); // frame: [..., self, context, frame] |
| ldc(object.getOpname()); // [..., self, context, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeSuper", Object.class, // invokeSuper(self, context, frame, opname) |
| Object.class, Class.class, StackFrame.class, String.class); |
| } else { |
| getField(JITCodeBlock.class, "eContext", EClass.class); // [..., self, context] |
| aload(1); // frame: [..., self, context, frame] |
| ldc(object.getOpname()); // [..., self, context, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeSuper", Object.class, // invokeSuper(self, context, frame, opname) |
| Object.class, EClass.class, StackFrame.class, String.class); |
| } |
| break; |
| case 1: // [..., self, arg] |
| aload(0); // [..., self, arg, this] |
| if (!(context instanceof EClass)) { |
| getField(JITCodeBlock.class, "context", Class.class); // [..., self, context] |
| aload(1); // frame: [..., self, arg, context, frame] |
| ldc(object.getOpname()); // [..., self, arg, context, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeSuper", Object.class, // invokeSuper(self, arg, context, frame, opname) |
| Object.class, Object.class, Class.class, StackFrame.class, String.class); |
| } else { |
| getField(JITCodeBlock.class, "eContext", EClass.class); // [..., self, context] |
| aload(1); // frame: [..., self, arg, context, frame] |
| ldc(object.getOpname()); // [..., self, arg, context, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeSuper", Object.class, // invokeSuper(self, arg, context, frame, opname) |
| Object.class, Object.class, EClass.class, StackFrame.class, String.class); |
| } |
| break; |
| default: // [..., self, args] |
| generatePushInt(argcount); // [..., self, args, argcount] |
| anewarray(Object.class); // new Object[argcount]: [..., self, args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., self, args, array, arg, array] |
| swap(); // swap arg over array ref: [..., self, args, array, array, arg] |
| generatePushInt(argcount - 1 - i); // index: [..., self, args, array, array, arg, index] |
| swap(); // swap index over arg: [..., self, args, array, array, index, arg] |
| aastore(); // store: [..., self, args, array] |
| } // no more args: [..., self, array] |
| aload(0); // [..., self, array, this] |
| if (!(context instanceof EClass)) { |
| getField(JITCodeBlock.class, "context", Class.class); // [..., self, array, context] |
| aload(1); // frame: [..., self, array, context, frame] |
| ldc(object.getOpname()); // [..., self, array, context, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeSuper", Object.class, // invokeSuper(self, array, context, frame, opname) |
| Object.class, Object[].class, Class.class, StackFrame.class, String.class); |
| } else { |
| getField(JITCodeBlock.class, "eContext", EClass.class); // [..., self, array, context] |
| aload(1); // frame: [..., self, array, context, frame] |
| ldc(object.getOpname()); // [..., self, array, context, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeSuper", Object.class, // invokeSuper(self, array, context, frame, opname) |
| Object.class, Object[].class, EClass.class, StackFrame.class, String.class); |
| } |
| break; |
| } |
| return super.caseInvokeSuper(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInvokeStatic(final InvokeStatic object) { |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [..., type] |
| aload(1); // frame: [..., type, frame] |
| ldc(object.getOpname()); // [..., type, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeStatic", Object.class, // invokeStatic(type, frame, opname) |
| Object.class, StackFrame.class, String.class); |
| break; |
| case 1: // [..., type, arg] |
| aload(1); // frame: [..., type, arg, frame] |
| ldc(object.getOpname()); // [..., type, arg, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeStatic", Object.class, // invokeStatic(type, arg, frame, opname) |
| Object.class, Object.class, StackFrame.class, String.class); |
| break; |
| default: // [..., type, args] |
| generatePushInt(argcount); // [..., type, args, argcount] |
| anewarray(Object.class); // new Object[argcount]: [..., type, args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., type, args, array, arg, array] |
| swap(); // swap arg over array ref: [..., type, args, array, array, arg] |
| generatePushInt(argcount - 1 - i); // index: [..., type, args, array, array, arg, index] |
| swap(); // swap index over arg: [..., type, args, array, array, index, arg] |
| aastore(); // store: [..., type, args, array] |
| } // no more args: [..., type, array] |
| aload(1); // frame: [..., type, array, frame] |
| ldc(object.getOpname()); // [..., type, array, frame, opname] |
| invokeStat(JITCodeBlock.class, "invokeStatic", Object.class, // invokeStatic(type, array, frame, opname) |
| Object.class, Object[].class, StackFrame.class, String.class); |
| break; |
| } |
| return super.caseInvokeStatic(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseAllinst(final Allinst object) { |
| // [..., type] |
| checkcast(EClass.class); // [..., type] |
| aload(2); // env: [..., type, env] |
| invokeStat(EMFTVMUtil.class, "findAllInstances", LazyList.class, // EMFTVMUtil.findAllInstances(type, env) |
| EClass.class, ExecEnv.class); |
| return super.caseAllinst(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseAllinstIn(final AllinstIn object) { |
| // [..., type, modelName] |
| swap(); // [..., modelName, type] |
| checkcast(EClass.class); // [..., modelName, type] |
| aload(2); // env: [..., modelName, type, env] |
| invokeStat( // EMFTVMUtil.findAllInstances(modelName, type, env) |
| EMFTVMUtil.class, "findAllInstIn", LazyList.class, |
| Object.class, EClass.class, ExecEnv.class); |
| return super.caseAllinstIn(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseMatch(final Match object) { |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [...] |
| aload(1); // frame: [..., frame] |
| ldc(object.getRulename()); // [..., frame, rulename] |
| invokeStat(JITCodeBlock.class, "matchOne", Object.class, // matchOne(frame, rulename) |
| StackFrame.class, String.class); |
| break; |
| default: // [..., args] |
| generatePushInt(argcount); // [..., args, argcount] |
| anewarray(EObject.class); // new EObject[argcount]: [..., args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., args, array, arg, array] |
| swap(); // swap arg over array ref: [..., args, array, array, arg] |
| checkcast(EObject.class); // [..., args, array, array, earg] |
| generatePushInt(argcount - 1 - i); // index: [..., args, array, array, earg, index] |
| swap(); // swap index over arg: [..., args, array, array, index, earg] |
| aastore(); // store: [..., args, array] |
| } // no more args: [..., array] |
| aload(1); // frame: [..., array, frame] |
| ldc(object.getRulename()); // [..., array, frame, rulename] |
| invokeStat(JITCodeBlock.class, "matchOne", Object.class, // matchOne(array, frame, rulename) |
| Object[].class, StackFrame.class, String.class); |
| break; |
| } |
| return super.caseMatch(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseMatchS(final MatchS object) { |
| generateSetPc(object); |
| checkcast(Rule.class); // [..., rule] |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [..., rule] |
| aload(1); // frame: [..., rule, frame] |
| invokeStat(JITCodeBlock.class, "matchOne", Object.class, // matchOne(rule, frame) |
| Rule.class, StackFrame.class); |
| break; |
| default: // [..., args, rule] |
| generatePushInt(argcount); // [..., args, rule, argcount] |
| anewarray(EObject.class); // new EObject[argcount]: [..., args, rule, array] |
| swap(); // swap array ref over rule: [..., args, array, rule] |
| for (int i = 0; i < argcount; i++) { |
| dup_x2(); // copy rule below first arg: [..., args, rule, arg, array, rule] |
| pop(); // pop rule: [..., args, rule, arg, array] |
| dup_x2(); // copy array ref below rule: [..., args, array, rule, arg, array] |
| swap(); // swap arg over array ref: [..., args, array, rule, array, arg] |
| checkcast(EObject.class); // [..., args, array, rule, array, earg] |
| generatePushInt(argcount - 1 - i); // index: [..., args, array, rule, array, earg, index] |
| swap(); // swap index over arg: [..., args, array, rule, array, index, earg] |
| aastore(); // store: [..., args, array, rule] |
| } // no more args: [..., array, rule] |
| aload(1); // frame: [..., array, rule, frame] |
| invokeStat(JITCodeBlock.class, "matchOne", Object.class, // matchOne(array, rule, frame) |
| Object[].class, Rule.class, StackFrame.class); |
| break; |
| } |
| return super.caseMatchS(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseAdd(final Add object) { |
| // [..., o, v] |
| generatePushInt(-1); // [..., o, v, -1] |
| ldc(object.getFieldname()); // [..., o, v, -1, fieldName] |
| aload(0); // this: [..., o, v, -1, fieldName, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, v, -1, fieldName, cb] |
| aload(1); // frame: [..., o, v, -1, fieldName, cb, frame] |
| invokeStat(JITCodeBlock.class, "add", Type.VOID_TYPE, // add(o, v, -1, fieldName, cb, frame) |
| Object.class, Object.class, Type.INT_TYPE, String.class, CodeBlock.class, StackFrame.class); |
| return super.caseAdd(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseRemove(final Remove object) { |
| // [..., o, v] |
| ldc(object.getFieldname()); // [..., o, v, fieldName] |
| aload(0); // this: [..., o, v, fieldName, this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, v, fieldName, cb] |
| aload(1); // frame: [..., o, v, fieldName, cb, frame] |
| invokeStat(JITCodeBlock.class, "remove", Type.VOID_TYPE, // remove(o, v, fieldName, cb, frame) |
| Object.class, Object.class, String.class, CodeBlock.class, StackFrame.class); |
| return super.caseRemove(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInsert(final Insert object) { |
| // [..., o, v, index] |
| // Unbox the Integer object |
| checkcast(Integer.class); // [..., o, v, index] |
| invokeVirt(Integer.class, "intValue", Type.INT_TYPE); // Integer.intValue(): // [..., o, v, index] |
| ldc(object.getFieldname()); // [..., o, v, index, fieldName] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., o, v, index, fieldName, cb] |
| aload(1); // frame: [..., o, v, index, fieldName, cb, frame] |
| invokeStat(JITCodeBlock.class, "add", Type.VOID_TYPE, // add(o, v, index, fieldName, cb, frame) |
| Object.class, Object.class, Type.INT_TYPE, String.class, CodeBlock.class, StackFrame.class); |
| return super.caseInsert(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGetSuper(final GetSuper object) { |
| // Pre-calc context type and perform checks |
| final Field fieldCtx = object.getOwningBlock().getField(); |
| if (fieldCtx == null) { |
| throw new IllegalArgumentException("GET_SUPER can only be used in fields"); |
| } |
| final EClassifier context = fieldCtx.getEContext(); |
| if (context == null) { |
| throw new IllegalArgumentException(String.format("Field misses context type: %s", fieldCtx)); |
| } |
| if (!(context instanceof EClass) && context.getInstanceClass() == null) { |
| throw new IllegalArgumentException(String.format("Primitive EMF type without instance class %s", context)); |
| } |
| |
| // Generate bytecode |
| generateSetPc(object); |
| // [..., o] |
| aload(0); // this: [..., o, this] |
| if (!(context instanceof EClass)) { |
| getField(JITCodeBlock.class, "context", Class.class); // [..., o, context] |
| ldc(object.getFieldname()); // [..., o, context, propname] |
| aload(1); // frame: [..., o, context, propname, frame] |
| invokeStat(JITCodeBlock.class, "getSuper", Object.class, // getSuper(o, context, propname, frame) |
| Object.class, Class.class, String.class, StackFrame.class); |
| } else { |
| getField(JITCodeBlock.class, "eContext", EClass.class); // [..., o, context] |
| ldc(object.getFieldname()); // [..., o, context, propname] |
| aload(1); // frame: [..., o, context, propname, frame] |
| invokeStat(JITCodeBlock.class, "getSuper", Object.class, // getSuper(o, context, propname, frame) |
| Object.class, EClass.class, String.class, StackFrame.class); |
| } |
| return super.caseGetSuper(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGetenv(final Getenv object) { |
| aload(2); // env: [..., env] |
| return super.caseGetenv(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseReturn(final Return object) { |
| aload(1); // frame |
| areturn(); |
| return super.caseReturn(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGetcb(final Getcb object) { |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "cb", CodeBlock.class); // [..., cb] |
| invokeIface(CodeBlock.class, "getNested", EList.class); // cb.getNested(): [..., elist] |
| generatePushInt(object.getCbIndex()); // [..., elist, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // elist.get(index): [..., nestedCb] |
| return super.caseGetcb(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInvokeAllCbs(final InvokeAllCbs object) { |
| final EList<CodeBlock> nested = object.getOwningBlock().getNested(); |
| // Define labels |
| final Label argStart = new Label(); |
| final Label loopStart = new Label(); |
| final Label loopEnd = new Label(); |
| // Generate bytecode |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| // unrolled loop start |
| label(loopStart); |
| for (int i = 0; i < nested.size(); i++) { |
| dup(); // [..., nested, nested] |
| generatePushInt(i); // [..., nested, nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., nested, object] |
| checkcast(CodeBlock.class); // [..., nested, ncb] |
| astore(4); // ncb: [..., nested] |
| aload(4); // ncb: [..., nested, ncb] |
| new_(StackFrame.class); // [..., nested, ncb, newframe] |
| dup(); // [..., nested, ncb, newframe, newframe] |
| aload(1); // frame: [..., nested, ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., nested, ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., nested, ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., nested, newframe] |
| if (nested.get(i).getStackLevel() > 0) { // returns value |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., nested, result] |
| swap(); // swap result under nested: [..., result, nested] |
| } else { |
| pop(); // remove newframe: [..., nested] |
| } |
| // [..., results, nested] |
| } |
| // unrolled loop end |
| label(loopEnd); |
| pop(); // remove nested: [..., results] |
| break; |
| case 1: // [..., arg] |
| label(argStart); |
| astore(5); // arg: [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| // unrolled loop start |
| label(loopStart); |
| for (int i = 0; i < nested.size(); i++) { |
| dup(); // [..., nested, nested] |
| generatePushInt(i); // [..., nested, nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., nested, object] |
| checkcast(CodeBlock.class); // [..., nested, ncb] |
| astore(4); // ncb: [..., nested] |
| aload(4); // ncb: [..., nested, ncb] |
| aload(1); // frame: [..., nested, ncb, frame] |
| aload(4); // ncb: [..., nested, ncb, frame, ncb] |
| aload(5); // arg: [..., nested, ncb, frame, ncb, arg] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(ncb, arg): [..., nested, ncb, newframe] |
| CodeBlock.class, Object.class); |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., nested, newframe] |
| if (nested.get(i).getStackLevel() > 0) { // returns value |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., nested, result] |
| swap(); // swap result under nested: [..., result, nested] |
| } else { |
| pop(); // remove newframe: [..., nested] |
| } |
| // [..., results, nested] |
| } |
| // unrolled loop end |
| label(loopEnd); |
| pop(); // remove nested: [..., results] |
| // Create local variable table entry |
| localVariable("arg", Object.class, argStart, loopEnd, 5); |
| break; |
| default: // [..., args] |
| generatePushInt(argcount); // [..., args, argcount] |
| anewarray(Object.class); // new Object[argcount]: [..., args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., args, array, arg, array] |
| swap(); // swap arg over array ref: [..., args, array, array, arg] |
| generatePushInt(argcount - 1 - i); // index: [..., args, array, array, arg, index] |
| swap(); // swap index over arg: [..., args, array, array, index, arg] |
| aastore(); // store: [..., args, array] |
| } // no more args: [..., array] |
| label(argStart); |
| astore(5); // args: [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| // unrolled loop start |
| label(loopStart); |
| for (int i = 0; i < nested.size(); i++) { |
| dup(); // [..., nested, nested] |
| generatePushInt(i); // [..., nested, nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., nested, object] |
| checkcast(CodeBlock.class); // [..., nested, ncb] |
| astore(4); // ncb: [..., nested] |
| aload(4); // ncb: [..., nested, ncb] |
| aload(1); // frame: [..., nested, ncb, frame] |
| aload(4); // ncb: [..., nested, ncb, frame, ncb] |
| aload(5); // args: [..., nested, ncb, frame, ncb, args] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(ncb, args): [..., nested, ncb, newframe] |
| CodeBlock.class,Object[].class); |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., nested, newframe] |
| if (nested.get(i).getStackLevel() > 0) { // returns value |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., nested, result] |
| swap(); // swap result under nested: [..., result, nested] |
| } else { |
| pop(); // remove newframe: [..., nested] |
| } |
| // [..., results, nested] |
| } |
| // unrolled loop end |
| label(loopEnd); // [..., results, nested] |
| pop(); // remove nested: [..., results] |
| // Create local variable table entry |
| localVariable("args", Object[].class, argStart, loopEnd, 5); |
| break; |
| } |
| // Create local variable table entry |
| localVariable("ncb", CodeBlock.class, loopStart, loopEnd, 4); |
| return super.caseInvokeAllCbs(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInvokeCb(final InvokeCb object) { |
| // Define labels |
| final Label argStart = new Label(); |
| final Label ncbStart = new Label(); |
| final Label ncbEnd = new Label(); |
| // Generate bytecode |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| if (object.getCodeBlock().getStackLevel() > 0) { // returns value |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., result] |
| } else { |
| pop(); // remove newframe: [...] |
| } |
| label(ncbEnd); |
| break; |
| case 1: // [..., arg] |
| label(argStart); |
| astore(5); // arg: [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| aload(1); // frame: [..., ncb, frame] |
| aload(4); // ncb: [..., ncb, frame, ncb] |
| aload(5); // arg: [..., ncb, frame, ncb, arg] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(ncb, arg): [..., ncb, newframe] |
| CodeBlock.class, Object.class); |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| if (object.getCodeBlock().getStackLevel() > 0) { // returns value |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., result] |
| } else { |
| pop(); // remove newframe: [...] |
| } |
| label(ncbEnd); |
| // Create local variable table entry |
| localVariable("arg", Object.class, argStart, ncbEnd, 5); |
| break; |
| default: // [..., args] |
| generatePushInt(argcount); // [..., args, argcount] |
| anewarray(Object.class); // new Object[argcount]: [..., args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., args, array, arg, array] |
| swap(); // swap arg over array ref: [..., args, array, array, arg] |
| generatePushInt(argcount - 1 - i); // index: [..., args, array, array, arg, index] |
| swap(); // swap index over arg: [..., args, array, array, index, arg] |
| aastore(); // store: [..., args, array] |
| } // no more args: [..., array] |
| label(argStart); |
| astore(5); // args: [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| aload(1); // frame: [..., ncb, frame] |
| aload(4); // ncb: [..., ncb, frame, ncb] |
| aload(5); // args: [..., ncb, frame, ncb, args] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(ncb, args): [..., ncb, newframe] |
| CodeBlock.class, Object[].class); |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| if (object.getCodeBlock().getStackLevel() > 0) { // returns value |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., result] |
| } else { |
| pop(); // remove newframe: [...] |
| } |
| label(ncbEnd); |
| // Create local variable table entry |
| localVariable("args", Object[].class, argStart, ncbEnd, 5); |
| break; |
| } |
| // Create local variable table entry |
| localVariable("ncb", CodeBlock.class, ncbStart, ncbEnd, 4); |
| return caseInvokeCb(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseInvokeCbS(final InvokeCbS object) { |
| // Define labels |
| final Label argStart = new Label(); |
| final Label ncbStart = new Label(); |
| final Label ncbEnd = new Label(); |
| final Label stackEmpty = new Label(); |
| // Generate bytecode |
| generateSetPc(object); |
| final int argcount = object.getArgcount(); |
| switch (argcount) { |
| case 0: // [..., ncb] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| dup(); // [..., newframe, newframe] |
| invokeVirt(StackFrame.class, "stackEmpty", Type.BOOLEAN_TYPE); // newframe.stackEmpty(): [..., newframe, boolean] |
| ifne(stackEmpty); // jump if stackEmpty == true: [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., result] |
| goto_(ncbEnd); // jump over stackEmpty |
| label(stackEmpty); |
| pop(); // [...] |
| aconst_null(); // [..., null] |
| label(ncbEnd); |
| break; |
| case 1: // [..., arg, ncb] |
| checkcast(CodeBlock.class); // [..., arg, ncb] |
| label(ncbStart); |
| astore(4); // ncb: [..., arg] |
| label(argStart); |
| astore(5); // arg: [...] |
| aload(4); // ncb: [..., ncb] |
| aload(1); // frame: [..., ncb, frame] |
| aload(4); // ncb: [..., ncb, frame, ncb] |
| aload(5); // arg: [..., ncb, frame, ncb, arg] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(ncb, arg): [..., ncb, newframe] |
| CodeBlock.class, Object.class); |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| dup(); // [..., newframe, newframe] |
| invokeVirt(StackFrame.class, "stackEmpty", Type.BOOLEAN_TYPE); // newframe.stackEmpty(): [..., newframe, boolean] |
| ifne(stackEmpty); // jump if stackEmpty == true: [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., result] |
| goto_(ncbEnd); // jump over stackEmpty |
| label(stackEmpty); |
| pop(); // [...] |
| aconst_null(); // [..., null] |
| label(ncbEnd); |
| // Create local variable table entry |
| localVariable("arg", Object.class, argStart, ncbEnd, 5); |
| break; |
| default: // [..., args, ncb] |
| checkcast(CodeBlock.class); // [..., args, ncb] |
| label(ncbStart); |
| astore(4); // ncb: [..., args] |
| generatePushInt(argcount); // [..., args, argcount] |
| anewarray(Object.class); // new Object[argcount]: [..., args, array] |
| for (int i = 0; i < argcount; i++) { |
| dup_x1(); // copy array ref below value: [..., args, array, arg, array] |
| swap(); // swap arg over array ref: [..., args, array, array, arg] |
| generatePushInt(argcount - 1 - i); // index: [..., args, array, array, arg, index] |
| swap(); // swap index over arg: [..., args, array, array, index, arg] |
| aastore(); // store: [..., args, array] |
| } // no more args: [..., array] |
| label(argStart); |
| astore(5); // args: [...] |
| aload(4); // ncb: [..., ncb] |
| aload(1); // frame: [..., ncb, frame] |
| aload(4); // ncb: [..., ncb, frame, ncb] |
| aload(5); // args: [..., ncb, frame, ncb, args] |
| invokeVirt(StackFrame.class, "getSubFrame", StackFrame.class, // frame.getSubFrame(ncb, args): [..., ncb, newframe] |
| CodeBlock.class, Object[].class); |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| dup(); // [..., newframe, newframe] |
| invokeVirt(StackFrame.class, "stackEmpty", Type.BOOLEAN_TYPE); // newframe.stackEmpty(): [..., newframe, boolean] |
| ifne(stackEmpty); // jump if stackEmpty == true: [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., result] |
| goto_(ncbEnd); // jump over stackEmpty |
| label(stackEmpty); |
| pop(); // [...] |
| aconst_null(); // [..., null] |
| label(ncbEnd); |
| // Create local variable table entry |
| localVariable("args", Object[].class, argStart, ncbEnd, 5); |
| break; |
| } |
| // Create local variable table entry |
| localVariable("ncb", CodeBlock.class, ncbStart, ncbEnd, 4); |
| return caseInvokeCbS(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseNot(final Not object) { |
| // Labels |
| final Label ifFalse = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| checkcast(Boolean.class); // [..., Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., bool] |
| ifeq(ifFalse); // [...] |
| iconst_0(); // [..., false] |
| goto_(ifEnd); |
| label(ifFalse); |
| iconst_1(); // [..., true] |
| label(ifEnd); |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(bool): [..., Boolean] |
| return super.caseNot(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseAnd(final And object) { |
| // Labels |
| final Label ifFalse = new Label(); |
| final Label ncbStart = new Label(); |
| final Label ncbEnd = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| checkcast(Boolean.class); // [..., Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., bool] |
| ifeq(ifFalse); // [...] |
| generateSetPc(object); |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., Boolean] |
| label(ncbEnd); |
| goto_(ifEnd); |
| label(ifFalse); |
| iconst_0(); // [..., false] |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(bool): [..., Boolean] |
| label(ifEnd); |
| // Create local variable table entry |
| localVariable("ncb", CodeBlock.class, ncbStart, ncbEnd, 4); |
| return caseAnd(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseOr(final Or object) { |
| // Labels |
| final Label ifTrue = new Label(); |
| final Label ncbStart = new Label(); |
| final Label ncbEnd = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| checkcast(Boolean.class); // [..., Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., bool] |
| ifne(ifTrue); // [...] |
| generateSetPc(object); |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., Boolean] |
| label(ncbEnd); |
| goto_(ifEnd); |
| label(ifTrue); |
| iconst_1(); // [..., true] |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(bool): [..., Boolean] |
| label(ifEnd); |
| // Create local variable table entry |
| localVariable("ncb", CodeBlock.class, ncbStart, ncbEnd, 4); |
| return super.caseOr(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseXor(final Xor object) { |
| // Labels |
| final Label ifFirstFalse = new Label(); |
| final Label ifFalse = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| checkcast(Boolean.class); // [..., Boolean, Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., Boolean, bool] |
| ifeq(ifFirstFalse); // [..., Boolean] |
| checkcast(Boolean.class); // [..., Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., bool] |
| ifeq(ifFalse); // [...] |
| iconst_0(); // [..., false] |
| goto_(ifEnd); |
| label(ifFalse); |
| iconst_1(); // [..., true] |
| label(ifEnd); |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(bool): [..., Boolean] |
| label(ifFirstFalse); |
| return super.caseXor(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseImplies(final Implies object) { |
| // Labels |
| final Label ifFalse = new Label(); |
| final Label ncbStart = new Label(); |
| final Label ncbEnd = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| checkcast(Boolean.class); // [..., Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., bool] |
| ifeq(ifFalse); // [...] |
| generateSetPc(object); |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(ncbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., Boolean] |
| label(ncbEnd); |
| goto_(ifEnd); |
| label(ifFalse); |
| iconst_1(); // [..., true] |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(bool): [..., Boolean] |
| label(ifEnd); |
| // Create local variable table entry |
| localVariable("ncb", CodeBlock.class, ncbStart, ncbEnd, 4); |
| return super.caseImplies(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseIfte(final Ifte object) { |
| // Labels |
| final Label thenCbStart = new Label(); |
| final Label thenCbEnd = new Label(); |
| final Label ifFalse = new Label(); |
| final Label elseCbStart = new Label(); |
| final Label elseCbEnd = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| generateSetPc(object); |
| checkcast(Boolean.class); // [..., Boolean] |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // [..., bool] |
| ifeq(ifFalse); // [...] |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getThenCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(thenCbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., Boolean] |
| label(thenCbEnd); |
| goto_(ifEnd); |
| label(ifFalse); |
| aload(0); // this: [..., this] |
| getField(JITCodeBlock.class, "nested", EList.class); // [..., nested] |
| generatePushInt(object.getElseCbIndex()); // [..., nested, index] |
| invokeIface(EList.class, "get", Object.class, Type.INT_TYPE); // nested.get(index): [..., object] |
| checkcast(CodeBlock.class); // [..., ncb] |
| label(elseCbStart); |
| astore(4); // ncb: [...] |
| aload(4); // ncb: [..., ncb] |
| new_(StackFrame.class); // [..., ncb, newframe] |
| dup(); // [..., ncb, newframe, newframe] |
| aload(1); // frame: [..., ncb, newframe, newframe, frame] |
| aload(4); // ncb: [..., ncb, newframe, newframe, frame, ncb] |
| invokeCons(StackFrame.class, StackFrame.class, CodeBlock.class); // new StackFrame(parent, ncb): [..., ncb, newframe] |
| invokeIface(CodeBlock.class, "execute", StackFrame.class, StackFrame.class); // ncb.execute(newframe): [..., newframe] |
| invokeVirt(StackFrame.class, "pop", Object.class); // newframe.pop(): [..., Boolean] |
| label(elseCbEnd); |
| label(ifEnd); |
| // Create local variable table entry |
| localVariable("thenCb", CodeBlock.class, thenCbStart, thenCbEnd, 4); |
| localVariable("elseCb", CodeBlock.class, elseCbStart, elseCbEnd, 4); |
| return super.caseIfte(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseIsnull(final Isnull object) { |
| // Labels |
| final Label ifNull = new Label(); |
| final Label ifEnd = new Label(); |
| // Generate bytecode |
| ifnull(ifNull); // [...] |
| iconst_0(); // [..., false] |
| goto_(ifEnd); |
| label(ifNull); |
| iconst_1(); // [..., true] |
| label(ifEnd); |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(bool): [..., Boolean] |
| return super.caseIsnull(object); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MethodVisitor caseGetenvtype(Getenvtype object) { |
| getStatic(JITCodeBlock.class, "EXEC_ENV", EClass.class); // [..., ExecEnv] |
| return super.caseGetenvtype(object); |
| } |
| |
| /** |
| * Generates frame.setPc(pc) for <code>object</code>, iff <code>hasMonitor</code> is <code>false</code>. |
| * @param object the next instruction |
| */ |
| protected void generateSetPc(final Instruction object) { |
| if (!hasMonitor) { |
| aload(1); // frame |
| final int pc = object.getOwningBlock().getCode().indexOf(object) + 1; |
| generatePushInt(pc); // pc |
| invokeVirt(StackFrame.class, "setPc", Type.VOID_TYPE, Type.INT_TYPE); // frame.setPc(pc) |
| } |
| } |
| |
| /** |
| * Generates bytecode that creates a new sub-{@link StackFrame}. |
| * @param opname the operation name (for debugger) |
| */ |
| protected void generateSubFrame(final String opname) { |
| new_(StackFrame.class); // [..., newframe] |
| dup(); // [..., newframe, newframe] |
| aload(1); // frame: [..., newframe, newframe, frame] |
| ldc(opname); // [..., newframe, newframe, frame, opname] |
| invokeCons(StackFrame.class, StackFrame.class, String.class); // new StackFrame(frame, opname): [..., newframe] |
| } |
| |
| /** |
| * Generates boxing code for a value of type <code>cls</code>, if necessary. |
| * @param cls the value class |
| * @param selfCls the class of self |
| */ |
| protected void generateBoxing(final Class<?> cls, final Class<?> selfCls) { |
| // [..., val] |
| if (cls == Void.TYPE) { |
| mv.visitInsn(ACONST_NULL); // replacement return value: [..., null] |
| } else if (cls == Boolean.TYPE) { |
| invokeStat(Boolean.class, "valueOf", Boolean.class, Type.BOOLEAN_TYPE); // Boolean.valueOf(val): [..., Boolean] |
| } else if (cls == Character.TYPE) { |
| invokeStat(Character.class, "valueOf", Character.class, Type.CHAR_TYPE); // Character.valueOf(val): [..., Character] |
| } else if (cls == Byte.TYPE) { |
| invokeStat(Byte.class, "valueOf", Byte.class, Type.BYTE_TYPE); // Byte.valueOf(val): [..., Byte] |
| } else if (cls == Short.TYPE) { |
| invokeStat(Short.class, "valueOf", Short.class, Type.SHORT_TYPE); // Short.valueOf(val): [..., Short] |
| } else if (cls == Integer.TYPE) { |
| invokeStat(Integer.class, "valueOf", Integer.class, Type.INT_TYPE); // Integer.valueOf(val): [..., Integer] |
| } else if (cls == Long.TYPE) { |
| invokeStat(Long.class, "valueOf", Long.class, Type.LONG_TYPE); // Long.valueOf(val): [..., Long] |
| } else if (cls == Float.TYPE) { |
| invokeStat(Float.class, "valueOf", Float.class, Type.FLOAT_TYPE); // Float.valueOf(val): [..., Float] |
| } else if (cls == Double.TYPE) { |
| invokeStat(Double.class, "valueOf", Double.class, Type.DOUBLE_TYPE); // Double.valueOf(val): [..., Double] |
| } else if (Enumerator.class.isAssignableFrom(cls)) { |
| invokeVirt(Object.class, "toString", String.class); // val.toString(): [..., String] |
| new_(EnumLiteral.class); // new EnumLiteral: [..., String, enum] |
| dup_x1(); // [..., enum, String, enum] |
| swap(); // [..., enum, enum, Stringl] |
| invokeCons(EnumLiteral.class, String.class); // enum.<init>(String): [..., enum] |
| } else if (Collection.class.isAssignableFrom(cls)) { |
| if (LazyCollection.class.isAssignableFrom(cls)) { |
| // no conversion required |
| } else if (List.class.isAssignableFrom(cls)) { |
| new_(EnumConversionListOnList.class); // new EnumConversionList: [..., val, enumlist] |
| dup_x1(); // [..., enumlist, val, enumlist] |
| swap(); // [..., enumlist, enumlist, val] |
| invokeCons(EnumConversionListOnList.class, List.class); // enumlist.<init>(val): [..., enumlist] |
| if (EObject.class.isAssignableFrom(selfCls)) { |
| final Label ifModelNull = new Label(); |
| aload(2); // env: [..., enumlist, env] |
| aload(4); // self: [..., enumlist, env, self] |
| invokeIface(ExecEnv.class, "getInoutModelOf", Model.class, EObject.class); // env.getInoutModelOf(self): [..., enumlist, |
| // model] |
| ifnull(ifModelNull); // jump if model == null: [..., enumlist] |
| invokeVirt(EnumConversionList.class, "cache", EnumConversionList.class); // enumlist.cache(): [..., enumlist] |
| label(ifModelNull); // [..., enumlist] |
| } |
| } else if (java.util.Set.class.isAssignableFrom(cls)) { |
| new_(EnumConversionSetOnSet.class); // new EnumConversionSetOnSet: [..., val, enumset] |
| dup_x1(); // [..., enumset, val, enumset] |
| swap(); // [..., enumset, enumset, val] |
| invokeCons(EnumConversionSetOnSet.class, java.util.Set.class); // enumset.<init>(val): [..., enumset] |
| if (EObject.class.isAssignableFrom(selfCls)) { |
| final Label ifModelNull = new Label(); |
| aload(2); // env: [..., enumset, env] |
| aload(4); // self: [..., enumset, env, self] |
| invokeIface(ExecEnv.class, "getInoutModelOf", Model.class, EObject.class); // env.getInoutModelOf(self): [..., enumset, model] |
| ifnull(ifModelNull); // jump if model == null: [..., enumlist] |
| invokeVirt(EnumConversionSetOnSet.class, "cache", EnumConversionSetOnSet.class); // enumlist.cache(): [..., enumset] |
| label(ifModelNull); // [..., enumset] |
| } |
| } else { |
| new_(EnumConversionList.class); // new EnumConversionList: [..., val, enumlist] |
| dup_x1(); // [..., enumlist, val, enumlist] |
| swap(); // [..., enumlist, enumlist, val] |
| invokeCons(EnumConversionList.class, Collection.class); // enumlist.<init>(val): [..., enumlist] |
| if (EObject.class.isAssignableFrom(selfCls)) { |
| final Label ifModelNull = new Label(); |
| aload(2); // env: [..., enumlist, env] |
| aload(4); // self: [..., enumlist, env, self] |
| invokeIface(ExecEnv.class, "getInoutModelOf", Model.class, EObject.class); // env.getInoutModelOf(self): [..., enumlist, |
| // model] |
| ifnull(ifModelNull); // jump if model == null: [..., enumlist] |
| invokeVirt(EnumConversionList.class, "cache", EnumConversionList.class); // enumlist.cache(): [..., enumlist] |
| label(ifModelNull); // [..., enumlist] |
| } |
| } |
| } else if (cls.isArray()) { |
| final Class<?> cType = cls.getComponentType(); |
| if (Object.class.isAssignableFrom(cType)) { |
| // Array of cType |
| final Label ifNull = new Label(); |
| dup(); // [..., array, array] |
| ifnull(ifNull); // jump if array == null: [..., array] |
| invokeStat(Arrays.class, "asList", List.class, Object[].class); // Arrays.asList(array): [..., list] |
| new_(LazyListOnList.class); // new LazyListOnList: [..., list, lazylist] |
| dup_x1(); // [..., lazylist, list, lazylist] |
| swap(); // [..., lazylist, lazylist, list] |
| invokeCons(LazyListOnList.class, List.class); // lazylist.<init>(list): [..., lazylist] |
| label(ifNull); |
| } // don't wrap primitive type arrays |
| } |
| // [..., Object] |
| } |
| |
| /** |
| * Generates unboxing code for a value of type <code>cls</code>, if necessary. |
| * @param cls the value class |
| */ |
| protected void generateUnboxing(final Class<?> cls) { |
| // [..., Object] |
| if (cls == Boolean.TYPE) { |
| checkcast(Boolean.class); |
| invokeVirt(Boolean.class, "booleanValue", Type.BOOLEAN_TYPE); // Object.booleanValue(): [..., boolean] |
| } else if (cls == Character.TYPE) { |
| checkcast(Character.class); |
| invokeVirt(Character.class, "charValue", Type.CHAR_TYPE); // Object.charValue(): [..., char] |
| } else if (cls == Byte.TYPE) { |
| checkcast(Byte.class); |
| invokeVirt(Byte.class, "byteValue", Type.BYTE_TYPE); // Object.byteValue(): [..., byte] |
| } else if (cls == Short.TYPE) { |
| checkcast(Short.class); |
| invokeVirt(Short.class, "shortValue", Type.SHORT_TYPE); // Object.shortValue(): [..., short] |
| } else if (cls == Integer.TYPE) { |
| checkcast(Integer.class); |
| invokeVirt(Integer.class, "intValue", Type.INT_TYPE); // Object.intValue(): [..., int] |
| } else if (cls == Long.TYPE) { |
| checkcast(Long.class); |
| invokeVirt(Long.class, "longValue", Type.LONG_TYPE); // Object.longValue(): [..., long] |
| } else if (cls == Float.TYPE) { |
| checkcast(Float.class); |
| invokeVirt(Float.class, "floatValue", Type.FLOAT_TYPE); // Object.floatValue(): [..., float] |
| } else if (cls == Double.TYPE) { |
| checkcast(Double.class); |
| invokeVirt(Double.class, "doubleValue", Type.DOUBLE_TYPE); // Object.doubleValue(): [..., double] |
| } else if (Enumerator.class.isAssignableFrom(cls)) { |
| checkcast(EnumLiteral.class); // [..., enum] |
| invokeVirt(EnumLiteral.class, "getName", String.class); // enum.getName(): [..., name] |
| invokeStat(cls, "getByName", cls, String.class); // <cls>.getByName(name): [..., <cls>] |
| } else { |
| checkcast(cls); |
| } |
| // [..., val] |
| } |
| |
| /** |
| * Generates an optimised instruction for pushing a constant integer <code>value</code> |
| * onto the stack. |
| * @param value the constant integer value to push |
| */ |
| protected void generatePushInt(final int value) { |
| CodeBlockJIT.generatePushInt(mv, value); |
| } |
| |
| /** |
| * Inserts a label. |
| * @param label the label to insert |
| */ |
| protected void label(final Label label) { |
| mv.visitLabel(label); |
| } |
| |
| /** |
| * Generates an ALOAD instruction. |
| * @param var the index of the local variable to load |
| */ |
| protected void aload(final int var) { |
| mv.visitVarInsn(ALOAD, var); |
| } |
| |
| /** |
| * Generates an ASTORE instruction. |
| * @param var the index of the local variable to store |
| */ |
| protected void astore(final int var) { |
| mv.visitVarInsn(ASTORE, var); |
| } |
| |
| protected void new_(final Class<?> cls) { |
| mv.visitTypeInsn(NEW, Type.getInternalName(cls)); |
| } |
| |
| protected void checkcast(final Class<?> cls) { |
| mv.visitTypeInsn(CHECKCAST, Type.getInternalName(cls)); |
| } |
| |
| protected void instanceof_(final Class<?> cls) { |
| mv.visitTypeInsn(INSTANCEOF, Type.getInternalName(cls)); |
| } |
| |
| protected void anewarray(final Class<?> cls) { |
| mv.visitTypeInsn(ANEWARRAY, Type.getInternalName(cls)); |
| } |
| |
| protected void dup() { |
| mv.visitInsn(DUP); |
| } |
| |
| protected void dup_x1() { |
| mv.visitInsn(DUP_X1); |
| } |
| |
| protected void dup_x2() { |
| mv.visitInsn(DUP_X2); |
| } |
| |
| protected void pop() { |
| mv.visitInsn(POP); |
| } |
| |
| protected void swap() { |
| mv.visitInsn(SWAP); |
| } |
| |
| protected void ldc(final Object object) { |
| mv.visitLdcInsn(object); |
| } |
| |
| protected void aconst_null() { |
| mv.visitInsn(ACONST_NULL); |
| } |
| |
| protected void aastore() { |
| mv.visitInsn(AASTORE); |
| } |
| |
| protected void areturn() { |
| mv.visitInsn(ARETURN); |
| } |
| |
| protected void iconst_1() { |
| mv.visitInsn(ICONST_1); |
| } |
| |
| protected void iconst_0() { |
| mv.visitInsn(ICONST_0); |
| } |
| |
| /** |
| * Generates a method invocation instruction. |
| * @param opcode the method invocation instruction opcode (e.g. INVOKEVIRTUAL) |
| * @param owner the method owner class |
| * @param name the method name |
| * @param retType the method return type {@link Class} or {@link Type} |
| * @param argTypes the method argument type {@link Class}es or {@link Type}s |
| */ |
| protected void invoke(final int opcode, final Class<?> owner, final String name, |
| final Object retType, final Object... argTypes) { |
| final Type[] ats = new Type[argTypes.length]; |
| for (int i = 0; i < argTypes.length; i++) { |
| ats[i] = argTypes[i] instanceof Type ? (Type)argTypes[i] : Type.getType((Class<?>)argTypes[i]); |
| } |
| mv.visitMethodInsn(opcode, |
| Type.getInternalName(owner), |
| name, |
| Type.getMethodDescriptor( |
| retType instanceof Type ? (Type)retType : Type.getType((Class<?>)retType), |
| ats), |
| owner.isInterface()); |
| } |
| |
| protected void invokeIface(final Class<?> owner, final String name, |
| final Object retType, final Object... argTypes) { |
| invoke(INVOKEINTERFACE, owner, name, retType, argTypes); |
| } |
| |
| protected void invokeVirt(final Class<?> owner, final String name, |
| final Object retType, final Object... argTypes) { |
| invoke(INVOKEVIRTUAL, owner, name, retType, argTypes); |
| } |
| |
| protected void invokeStat(final Class<?> owner, final String name, |
| final Object retType, final Object... argTypes) { |
| invoke(INVOKESTATIC, owner, name, retType, argTypes); |
| } |
| |
| protected void invokeSpec(final Class<?> owner, final String name, |
| final Object retClass, final Object... argClasses) { |
| invoke(INVOKESPECIAL, owner, name, retClass, argClasses); |
| } |
| |
| protected void invokeCons(final Class<?> owner, final Object... argTypes) { |
| invoke(INVOKESPECIAL, owner, "<init>", Type.VOID_TYPE, argTypes); |
| } |
| |
| /** |
| * Generates a field access instruction. |
| * @param opcode the field access opcode (e.g. GETFIELD) |
| * @param owner the field owner class |
| * @param name the field name |
| * @param type the field {@link Class} or {@link Type} |
| */ |
| protected void field(final int opcode, final Class<?> owner, final String name, |
| final Object type) { |
| mv.visitFieldInsn(opcode, Type.getInternalName(owner), name, |
| type instanceof Type ? |
| ((Type)type).getDescriptor() : |
| Type.getDescriptor((Class<?>) type)); |
| } |
| |
| protected void getField(final Class<?> owner, final String name, final Object type) { |
| field(GETFIELD, owner, name, type); |
| } |
| |
| protected void putField(final Class<?> owner, final String name, final Object type) { |
| field(PUTFIELD, owner, name, type); |
| } |
| |
| protected void getStatic(final Class<?> owner, final String name, final Object type) { |
| field(GETSTATIC, owner, name, type); |
| } |
| |
| protected void putStatic(final Class<?> owner, final String name, final Object type) { |
| field(PUTSTATIC, owner, name, type); |
| } |
| |
| protected void ifne(final Label label) { |
| mv.visitJumpInsn(IFNE, label); |
| } |
| |
| protected void ifeq(final Label label) { |
| mv.visitJumpInsn(IFEQ, label); |
| } |
| |
| protected void goto_(final Label label) { |
| mv.visitJumpInsn(GOTO, label); |
| } |
| |
| protected void ifnull(final Label label) { |
| mv.visitJumpInsn(IFNULL, label); |
| } |
| |
| protected void ifnonnull(final Label label) { |
| mv.visitJumpInsn(IFNONNULL, label); |
| } |
| |
| protected void athrow() { |
| mv.visitInsn(ATHROW); |
| } |
| |
| /** |
| * Generates a local variable table entry. |
| * @param name the name of a local variable. |
| * @param type the type of this local variable. |
| * @param start the first instruction corresponding to the scope of this local variable (inclusive). |
| * @param end the last instruction corresponding to the scope of this local variable (exclusive). |
| * @param index the local variable's index. |
| * @throws IllegalArgumentException if one of the labels has not already been visited by this visitor (by the {@link MethodVisitor#visitLabel} method). |
| */ |
| protected void localVariable(final String name, final Class<?> type, |
| final Label start, final Label end, final int index) { |
| mv.visitLocalVariable(name, Type.getDescriptor(type), null, start, end, index); |
| } |
| |
| /** |
| * Generates a try-catch block. |
| * @param start beginning of the exception handler's scope (inclusive). |
| * @param end end of the exception handler's scope (exclusive). |
| * @param handler beginning of the exception handler's code. |
| * @param type internal name of the type of exceptions handled by the handler, or null to catch any exceptions (for "finally" blocks). |
| * @throws IllegalArgumentException if one of the labels has already been visited by this visitor (by the {@link MethodVisitor#visitLabel} method). |
| */ |
| protected void tryCatchBlock(final Label start, final Label end, |
| final Label handler, final Class<?> type) { |
| mv.visitTryCatchBlock(start, end, handler, Type.getInternalName(type)); |
| } |
| |
| /** |
| * Returns the next instruction for the given instruction, or <code>null</code>. |
| * @param instruction the instruction |
| * @return the next instruction for the given instruction, or <code>null</code> |
| */ |
| protected Instruction nextInstruction(final Instruction instruction) { |
| final List<Instruction> code = instruction.getOwningBlock().getCode(); |
| final int index = code.indexOf(instruction); |
| if (index < code.size() - 1) { |
| return code.get(index + 1); |
| } |
| return null; |
| } |
| |
| } |