Java tutorial
/******************************************************************************* * Copyright (c) 2011-2013 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.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.LazySet; 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; /** * 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()); 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) { // [..., 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 = 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)); // 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 = 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)); // 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); } } /** * Finds the root {@link Class} in which <code>method</code> was declared. * @param method the method for which to find the root {@link Class} * @return the root {@link Class} in which <code>method</code> was declared */ private Method findRootMethod(Method method) { if (method == null) { return null; } Class<?> dc = method.getDeclaringClass(); Class<?>[] dis = dc.getInterfaces(); while ((dc = dc.getSuperclass()) != null) { try { method = dc.getDeclaredMethod(method.getName(), method.getParameterTypes()); } catch (SecurityException e) { break; } catch (NoSuchMethodException e) { break; } dis = dc.getInterfaces(); } while (dis.length > 0) { Class<?>[] newDis = new Class<?>[0]; for (Class<?> di : dis) { try { method = di.getDeclaredMethod(method.getName(), method.getParameterTypes()); newDis = di.getInterfaces(); break; // skip sibling interfaces } catch (SecurityException e) { } catch (NoSuchMethodException e) { } } dis = newDis; } return method; } /** * {@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 (Set.class.isAssignableFrom(cls)) { new_(EnumConversionSetOnSet.class); // new EnumConversionSetOnSet: [..., val, enumset] dup_x1(); // [..., enumset, val, enumset] swap(); // [..., enumset, enumset, val] invokeCons(EnumConversionSetOnSet.class, 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] } invokeVirt(LazyCollection.class, "asSet", LazySet.class); // enumlist.asSet(): [..., 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(); // Array of cType final Label ifNull = new Label(); dup(); // [..., array, array] ifnull(ifNull); // jump if array == null: [..., array] if (Object.class.isAssignableFrom(cType)) { invokeStat(Arrays.class, "asList", List.class, Object[].class); // Arrays.asList(array): [..., list] } else { invokeStat(JITCodeBlock.class, "asList", List.class, cls); // JITCodeBlock.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); } // [..., 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)); } 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)); } }