/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.compiler.assembler; import java.io.PrintWriter; import java.util.Arrays; import java.util.HashMap; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.CodeSizeEvaluator; import org.objectweb.asm.util.TraceMethodVisitor; import com.github.anba.es6draft.compiler.assembler.Code.MethodCode; /** * */ public class InstructionAssembler { private static final boolean VERIFY_STACK = true; private static final boolean EVALUATE_SIZE = false; private static final boolean TRACE = false; private static final int MAX_STRING_SIZE = 16384; private static final class Methods { // class: StringBuilder static final MethodName StringBuilder_append_String = MethodName.findVirtual(Types.StringBuilder, "append", Type.methodType(Types.StringBuilder, Types.String)); static final MethodName StringBuilder_init_int = MethodName.findConstructor(Types.StringBuilder, Type.methodType(Type.VOID_TYPE, Type.INT_TYPE)); static final MethodName StringBuilder_toString = MethodName.findVirtual(Types.StringBuilder, "toString", Type.methodType(Types.String)); static final MethodName Boolean_valueOf = MethodName.findStatic(Types.Boolean, "valueOf", Type.methodType(Types.Boolean, Type.BOOLEAN_TYPE)); static final MethodName Boolean_booleanValue = MethodName.findVirtual(Types.Boolean, "booleanValue", Type.methodType(Type.BOOLEAN_TYPE)); static final MethodName Character_valueOf = MethodName.findStatic(Types.Character, "valueOf", Type.methodType(Types.Character, Type.CHAR_TYPE)); static final MethodName Character_charValue = MethodName.findVirtual(Types.Character, "charValue", Type.methodType(Type.CHAR_TYPE)); static final MethodName Byte_valueOf = MethodName.findStatic(Types.Byte, "valueOf", Type.methodType(Types.Byte, Type.BYTE_TYPE)); static final MethodName Byte_byteValue = MethodName.findVirtual(Types.Byte, "byteValue", Type.methodType(Type.BYTE_TYPE)); static final MethodName Short_valueOf = MethodName.findStatic(Types.Short, "valueOf", Type.methodType(Types.Short, Type.SHORT_TYPE)); static final MethodName Short_shortValue = MethodName.findVirtual(Types.Short, "shortValue", Type.methodType(Type.SHORT_TYPE)); static final MethodName Integer_valueOf = MethodName.findStatic(Types.Integer, "valueOf", Type.methodType(Types.Integer, Type.INT_TYPE)); static final MethodName Integer_intValue = MethodName.findVirtual(Types.Integer, "intValue", Type.methodType(Type.INT_TYPE)); static final MethodName Long_valueOf = MethodName.findStatic(Types.Long, "valueOf", Type.methodType(Types.Long, Type.LONG_TYPE)); static final MethodName Long_longValue = MethodName.findVirtual(Types.Long, "longValue", Type.methodType(Type.LONG_TYPE)); static final MethodName Float_valueOf = MethodName.findStatic(Types.Float, "valueOf", Type.methodType(Types.Float, Type.FLOAT_TYPE)); static final MethodName Float_floatValue = MethodName.findVirtual(Types.Float, "floatValue", Type.methodType(Type.FLOAT_TYPE)); static final MethodName Double_valueOf = MethodName.findStatic(Types.Double, "valueOf", Type.methodType(Types.Double, Type.DOUBLE_TYPE)); static final MethodName Double_doubleValue = MethodName.findVirtual(Types.Double, "doubleValue", Type.methodType(Type.DOUBLE_TYPE)); } private static final class $CodeSizeEvaluator extends CodeSizeEvaluator { private final MethodCode method; $CodeSizeEvaluator(MethodCode method, MethodVisitor mv) { super(Opcodes.ASM5, mv); this.method = method; } @Override public void visitEnd() { super.visitEnd(); System.out.printf("%s: [%d, %d]\n", method.methodName, getMinSize(), getMaxSize()); } } private static final class $TraceMethodVisitor extends MethodVisitor { private final MethodCode method; $TraceMethodVisitor(MethodCode method, MethodVisitor mv) { super(Opcodes.ASM5, new TraceMethodVisitor(mv, new SimpleTypeTextifier())); this.method = method; } @Override public void visitEnd() { super.visitEnd(); trace(); } public void trace() { PrintWriter printWriter = new PrintWriter(System.out); printWriter.format("%nClass=%s, Method=%s [%s]%n", method.classCode.className, method.methodName, method.methodDescriptor); ((TraceMethodVisitor) mv).p.print(printWriter); printWriter.format("%n"); printWriter.flush(); } } private final MethodCode method; private final MethodVisitor methodVisitor; private final ConstantPool constantPool; private final Stack stack; private final Variables variables = new Variables(); private final HashMap<Class<?>, Type> typeCache = new HashMap<>(); private int lastLineNumber = -1; private Type getType(Class<?> c) { Type type = typeCache.get(c); if (type == null) { typeCache.put(c, type = Type.of(c)); } return type; } /** * Creates a new instruction assembler * * @param method * the method object */ public InstructionAssembler(MethodCode method) { this.method = method; this.methodVisitor = decorate(method.methodVisitor); this.constantPool = method.classCode.constantPool; this.stack = createStack(variables); } /** * Returns the method object. * * @return the method object */ public final MethodCode getMethod() { return method; } /** * Returns the underlying {@link MethodVisitor}. * * @return the method visitor */ public final MethodVisitor getMethodVisitor() { return methodVisitor; } /** * Returns the last emitted line number. * * @return the last line number */ protected final int getLastLineNumber() { return lastLineNumber; } protected Stack createStack(Variables variables) { if (VERIFY_STACK) { return new Stack(variables); } return new EmptyStack(variables); } protected MethodVisitor decorate(MethodVisitor mv) { if (EVALUATE_SIZE) { mv = new $CodeSizeEvaluator(method, mv); } if (TRACE) { mv = new $TraceMethodVisitor(method, mv); } return mv; } /** * Print current bytecode. */ public void trace() { if (methodVisitor instanceof $TraceMethodVisitor) { (($TraceMethodVisitor) methodVisitor).trace(); } } /* */ /** * Starts assembling the bytecode instructions. * <p> * Sub-classes need to call {@code super.begin()} at the beginning of this method. */ public void begin() { methodVisitor.visitCode(); enterVariableScope(); initializeParameters(); } /** * Stops assembling the bytecode instructions. * <p> * Sub-classes need to call {@code super.end()} at the end of this method. */ public void end() { exitVariableScope(); variables.close(); methodVisitor.visitMaxs(0, 0); methodVisitor.visitEnd(); } /* parameters and locals */ protected final void restoreVariables(VariablesSnapshot snapshot) { this.variables.restore(snapshot); } public final VariablesSnapshot getVariablesSnapshot() { return variables.snapshot(); } public final VariablesSnapshot getVariablesSnapshot(int startSlot) { return variables.snapshot(startSlot); } private boolean isInstanceMethod() { return (method.access & Opcodes.ACC_STATIC) == 0; } private void initializeParameters() { int slot = 0; if (isInstanceMethod()) { variables.reserveSlot("<this>", Types.Object, slot); slot += Types.Object.getSize(); } for (Type argument : method.methodDescriptor.parameterList()) { variables.reserveSlot(argument, slot); slot += argument.getSize(); } } private int parameterSlot(int index) { MethodTypeDescriptor desc = method.methodDescriptor; int slot = isInstanceMethod() ? 1 : 0; for (int i = 0; i < index; ++i) { slot += desc.parameterType(i).getSize(); } return slot; } private void checkParameterType(int index, Type type) { if (!method.methodDescriptor.parameterType(index).equals(type)) { throw new IllegalArgumentException(); } } protected void setParameterName(String name, int index, Type type) { checkParameterType(index, type); MethodTypeDescriptor desc = method.methodDescriptor; variables.addVariable(name, desc.parameterType(index), parameterSlot(index)); } public boolean hasParameter(int index, Class<?> clazz) { MethodTypeDescriptor desc = method.methodDescriptor; return index < desc.parameterCount() && desc.parameterType(index).equals(getType(clazz)); } public <T> Variable<T> getParameter(int index, Class<T> clazz) { checkParameterType(index, getType(clazz)); MethodTypeDescriptor desc = method.methodDescriptor; return new Variable<>("(parameter)", desc.parameterType(index), parameterSlot(index)); } public <T> void loadParameter(int index, Class<T> clazz) { load(getParameter(index, clazz)); } public void loadParameter(int index, Type type) { checkParameterType(index, type); load(index, type); } public void enterVariableScope() { VariableScope scope = variables.enter(); methodVisitor.visitLabel(scope.start); } public void exitVariableScope() { VariableScope scope = variables.exit(); methodVisitor.visitLabel(scope.end); for (Variable<?> variable : scope) { localVariable(variable, scope.start, scope.end); } } private void localVariable(Variable<?> variable, Label start, Label end) { if (variable.hasSlot()) { methodVisitor.visitLocalVariable(variable.getName(), variable.getType().descriptor(), null, start, end, variable.getSlot()); } } /** * Creates a new named variable. * * @param <T> * the variable class type * @param name * the variable name * @param clazz * the variable class * @return the new variable object */ public <T> Variable<T> newVariable(String name, Class<T> clazz) { assert name != null; return variables.newVariable(name, getType(clazz)); } /** * Creates a new unnamed variable. * * @param <T> * the variable class type * @param clazz * the variable class * @return the new variable object */ public <T> Variable<T> newScratchVariable(Class<T> clazz) { return variables.newVariable(null, getType(clazz)); } public void freeVariable(Variable<?> variable) { variables.freeVariable(variable); } public <T> MutableValue<T> arrayElement(Value<T[]> array, int index, Class<T> clazz) { return new ArrayElement<>(array, index, clazz); } private static final class ArrayElement<V> implements MutableValue<V> { private final Value<V[]> array; private final int index; private final Class<V> clazz; ArrayElement(Value<V[]> array, int index, Class<V> clazz) { this.array = array; this.index = index; this.clazz = clazz; } @Override public void load(InstructionAssembler assembler) { assembler.aload(array, index, assembler.getType(clazz)); } @Override public void store(InstructionAssembler assembler) { Variable<V> value = assembler.newScratchVariable(clazz); assembler.store(value); assembler.astore(array, index, value); assembler.freeVariable(value); } @Override public void store(InstructionAssembler assembler, Value<? extends V> value) { assembler.astore(array, index, value); } } /* annotations */ public void annotation(Type type, boolean visible) { methodVisitor.visitAnnotation(type.descriptor(), visible); } /* stack operations */ public final boolean hasStack() { return !(stack instanceof EmptyStack); } public final int getStackSize() { return stack.getStackPointer(); } public final Type[] getStack() { return stack.getStack(); } protected final void restoreStack(Type[] stack) { this.stack.setStack(stack); } protected final boolean isEqualStack(Jump label, Type[] stack) { return Arrays.equals(label.stack(), stack); } /** * Emits a <code>goto</code> instruction without discarding the current stack state. * * @param target * the target instruction to jump to */ public final void nonDestructiveGoTo(Jump target) { Type[] st = stack.getStackDirect(); int sp = stack.getStackPointer(); goTo(target); stack.setStack(st, sp); } /* */ public void mark(TryCatchLabel label) { methodVisitor.visitLabel(label.label()); // Stack object does not need to be updated. } public void mark(Jump jump) { methodVisitor.visitLabel(jump.label()); stack.mark(jump); } public void lineInfo(int line) { if (lastLineNumber == line) { // omit duplicate line info entries return; } lastLineNumber = line; Label label = new Label(); methodVisitor.visitLabel(label); methodVisitor.visitLineNumber(line, label); } /* try-catch-finally instructions */ /** * Defines a try-catch block for the error {@code type}. * * @param start * the start label of the try-catch block * @param end * the end label of the try-catch block * @param handler * the catch handler label * @param type * the exception type */ public void tryCatch(TryCatchLabel start, TryCatchLabel end, TryCatchLabel handler, Type type) { methodVisitor.visitTryCatchBlock(start.label(), end.label(), handler.label(), type.internalName()); } /** * Defines a try-finally block. * * @param start * the start label of the try-finally block * @param end * the end label of the try-finally block * @param handler * the finally handler label */ public void tryFinally(TryCatchLabel start, TryCatchLabel end, TryCatchLabel handler) { methodVisitor.visitTryCatchBlock(start.label(), end.label(), handler.label(), null); } /** * Marks the start of a catch-handler. * * @param handler * the catch handler label * @param exception * the exception type * @see #tryCatch(TryCatchLabel, TryCatchLabel, TryCatchLabel, Type) */ public void catchHandler(TryCatchLabel handler, Type exception) { methodVisitor.visitLabel(handler.label()); stack.catchHandler(exception); } /** * Marks the start of a finally-handler. * * @param handler * the finally handler label * @see #tryFinally(TryCatchLabel, TryCatchLabel, TryCatchLabel) */ public void finallyHandler(TryCatchLabel handler) { methodVisitor.visitLabel(handler.label()); stack.catchHandler(Types.Throwable); } /* constant value instructions */ public void anull() { methodVisitor.visitInsn(Opcodes.ACONST_NULL); stack.anull(); } public <T> Value<T> anullValue() { return asm -> asm.anull(); } public void aconst(String cst) { if (cst == null) { anull(); } else if (cst.length() <= MAX_STRING_SIZE) { constantPool.aconst(this, cst); } else { // string literal too large, split to avoid bytecode error anew(Types.StringBuilder); dup(); iconst(cst.length()); invoke(Methods.StringBuilder_init_int); for (int i = 0, len = cst.length(); i < len; i += MAX_STRING_SIZE) { String chunk = cst.substring(i, Math.min(i + MAX_STRING_SIZE, len)); constantPool.aconst(this, chunk); invoke(Methods.StringBuilder_append_String); } invoke(Methods.StringBuilder_toString); } } /** * ∅ → value * * @param b * the constant boolean to load on the stack */ public void iconst(boolean b) { methodVisitor.visitInsn(b ? Opcodes.ICONST_1 : Opcodes.ICONST_0); stack.iconst(); } public void iconst(int value) { if (-1 <= value && value <= 5) { methodVisitor.visitInsn(Opcodes.ICONST_0 + value); stack.iconst(); } else if (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE) { bipush(value); } else if (Short.MIN_VALUE <= value && value <= Short.MAX_VALUE) { sipush(value); } else { constantPool.iconst(this, Integer.valueOf(value)); } } public void lconst(long value) { if (value == 0) { methodVisitor.visitInsn(Opcodes.LCONST_0); stack.lconst(); } else if (value == 1) { methodVisitor.visitInsn(Opcodes.LCONST_1); stack.lconst(); } else { constantPool.lconst(this, Long.valueOf(value)); } } public void fconst(float value) { if (value == 0 && Float.floatToRawIntBits(value) == 0) { methodVisitor.visitInsn(Opcodes.FCONST_0); stack.fconst(); } else if (value == 1) { methodVisitor.visitInsn(Opcodes.FCONST_1); stack.fconst(); } else if (value == 2) { methodVisitor.visitInsn(Opcodes.FCONST_2); stack.fconst(); } else { constantPool.fconst(this, Float.valueOf(value)); } } public void dconst(double value) { if (value == 0 && Double.doubleToRawLongBits(value) == 0) { methodVisitor.visitInsn(Opcodes.DCONST_0); stack.dconst(); } else if (value == 1) { methodVisitor.visitInsn(Opcodes.DCONST_1); stack.dconst(); } else { constantPool.dconst(this, Double.valueOf(value)); } } public void ldc(Object cst) { methodVisitor.visitLdcInsn(cst); stack.ldc(cst); } public void bipush(int value) { methodVisitor.visitIntInsn(Opcodes.BIPUSH, value); stack.bipush(); } public void sipush(int value) { methodVisitor.visitIntInsn(Opcodes.SIPUSH, value); stack.sipush(); } /** * ∅ → handle * * @param method * the method name descriptor */ public void handle(MethodName method) { hconst(method.toHandle()); } public void hconst(Handle handle) { methodVisitor.visitLdcInsn(handle.handle()); stack.hconst(); } public void tconst(Type type) { methodVisitor.visitLdcInsn(type.type()); stack.tconst(type); } public void tconst(MethodTypeDescriptor type) { methodVisitor.visitLdcInsn(type.type()); stack.tconst(type); } /* local load instructions */ /** * ∅ → this */ public final void loadThis() { assert isInstanceMethod(); load(0, Types.Object); } public final void load(Value<?> value) { value.load(this); } public final void load(Variable<?> variable) { assert variable.isAlive() : "variable out of scope"; load(variable.getSlot(), variable.getType()); } private void load(int var, Type type) { assert var >= 0 && variables.isActive(var) : "variable is not initialized"; switch (type.getOpcode(Opcodes.ILOAD)) { case Opcodes.ILOAD: iload(var); return; case Opcodes.LLOAD: lload(var); return; case Opcodes.FLOAD: fload(var); return; case Opcodes.DLOAD: dload(var); return; case Opcodes.ALOAD: aload(var); return; default: throw new IllegalArgumentException(); } } private void iload(int var) { methodVisitor.visitVarInsn(Opcodes.ILOAD, var); stack.iload(var); } private void lload(int var) { methodVisitor.visitVarInsn(Opcodes.LLOAD, var); stack.lload(var); } private void fload(int var) { methodVisitor.visitVarInsn(Opcodes.FLOAD, var); stack.fload(var); } private void dload(int var) { methodVisitor.visitVarInsn(Opcodes.DLOAD, var); stack.dload(var); } private void aload(int var) { methodVisitor.visitVarInsn(Opcodes.ALOAD, var); stack.aload(var); } /* array load instructions */ /** * ∅ → value. * * @param <T> * the array component type * @param array * the array * @param index * the array index * @param type * the array element type */ public final <T> void aload(Value<T[]> array, int index, Type type) { assert index >= 0; load(array); iconst(index); aload(type); } /** * array → value. * * @param index * the array index * @param type * the array element type */ public final void aload(int index, Type type) { assert index >= 0; iconst(index); aload(type); } public final void aload(Type type) { switch (type.getOpcode(Opcodes.IALOAD)) { case Opcodes.IALOAD: iaload(); return; case Opcodes.LALOAD: laload(); return; case Opcodes.FALOAD: faload(); return; case Opcodes.DALOAD: daload(); return; case Opcodes.AALOAD: aaload(); return; case Opcodes.BALOAD: baload(); return; case Opcodes.CALOAD: caload(); return; case Opcodes.SALOAD: saload(); return; default: throw new IllegalArgumentException(); } } public void iaload() { methodVisitor.visitInsn(Opcodes.IALOAD); stack.iaload(); } public void laload() { methodVisitor.visitInsn(Opcodes.LALOAD); stack.laload(); } public void faload() { methodVisitor.visitInsn(Opcodes.FALOAD); stack.faload(); } public void daload() { methodVisitor.visitInsn(Opcodes.DALOAD); stack.daload(); } public void aaload() { methodVisitor.visitInsn(Opcodes.AALOAD); stack.aaload(); } public void baload() { methodVisitor.visitInsn(Opcodes.BALOAD); stack.baload(); } public void caload() { methodVisitor.visitInsn(Opcodes.CALOAD); stack.caload(); } public void saload() { methodVisitor.visitInsn(Opcodes.SALOAD); stack.saload(); } /* local store instructions */ public final void store(MutableValue<?> value) { value.store(this); } public final <V> void store(MutableValue<V> value, Value<? extends V> v) { value.store(this, v); } public final void store(Variable<?> variable) { assert variable.isAlive() : "variable out of scope"; store(variable.getSlot(), variable.getType()); } private void store(int var, Type type) { assert var >= 0; variables.activate(var); switch (type.getOpcode(Opcodes.ISTORE)) { case Opcodes.ISTORE: istore(var); return; case Opcodes.LSTORE: lstore(var); return; case Opcodes.FSTORE: fstore(var); return; case Opcodes.DSTORE: dstore(var); return; case Opcodes.ASTORE: astore(var); return; default: throw new IllegalArgumentException(); } } private void istore(int var) { methodVisitor.visitVarInsn(Opcodes.ISTORE, var); stack.istore(var); } private void lstore(int var) { methodVisitor.visitVarInsn(Opcodes.LSTORE, var); stack.lstore(var); } private void fstore(int var) { methodVisitor.visitVarInsn(Opcodes.FSTORE, var); stack.fstore(var); } private void dstore(int var) { methodVisitor.visitVarInsn(Opcodes.DSTORE, var); stack.dstore(var); } private void astore(int var) { methodVisitor.visitVarInsn(Opcodes.ASTORE, var); stack.astore(var); } /* array store instructions */ /** * array → array * * @param index * the array index * @param element * the string element to store */ public final void astore(int index, String element) { dup(); iconst(index); aconst(element); astore(Types.String); } /** * array → array * * @param index * the array index * @param element * the element to store */ public final void astore(int index, Value<?> element) { dup(); iconst(index); load(element); astore(Types.Object); } /** * ∅ → ∅ * * @param array * the array * @param index * the array index * @param element * the int element to store */ public final void astore(Value<int[]> array, int index, int element) { load(array); iconst(index); iconst(element); astore(Type.INT_TYPE); } /** * ∅ → ∅ * * @param <T> * the array component type * @param array * the array * @param index * the array index * @param element * the element to store */ public final <T> void astore(Value<T[]> array, int index, Value<? extends T> element) { load(array); iconst(index); load(element); astore(Types.Object); } public final void astore(Type type) { switch (type.getOpcode(Opcodes.IASTORE)) { case Opcodes.IASTORE: iastore(); return; case Opcodes.LASTORE: lastore(); return; case Opcodes.FASTORE: fastore(); return; case Opcodes.DASTORE: dastore(); return; case Opcodes.AASTORE: aastore(); return; case Opcodes.BASTORE: bastore(); return; case Opcodes.CASTORE: castore(); return; case Opcodes.SASTORE: sastore(); return; default: throw new IllegalArgumentException(); } } public void iastore() { methodVisitor.visitInsn(Opcodes.IASTORE); stack.iastore(); } public void lastore() { methodVisitor.visitInsn(Opcodes.LASTORE); stack.lastore(); } public void fastore() { methodVisitor.visitInsn(Opcodes.FASTORE); stack.fastore(); } public void dastore() { methodVisitor.visitInsn(Opcodes.DASTORE); stack.dastore(); } public void aastore() { methodVisitor.visitInsn(Opcodes.AASTORE); stack.aastore(); } public void bastore() { methodVisitor.visitInsn(Opcodes.BASTORE); stack.bastore(); } public void castore() { methodVisitor.visitInsn(Opcodes.CASTORE); stack.castore(); } public void sastore() { methodVisitor.visitInsn(Opcodes.SASTORE); stack.sastore(); } /* stack instructions */ /** * value → [] * * @param type * the topmost stack value */ public void pop(Type type) { switch (type.getSize()) { case 0: return; case 1: pop(); return; case 2: pop2(); return; default: throw new AssertionError(); } } public void pop() { methodVisitor.visitInsn(Opcodes.POP); stack.pop(); } public void pop2() { methodVisitor.visitInsn(Opcodes.POP2); stack.pop2(); } /** * value → value, value * * @param type * the topmost stack value */ public void dup(Type type) { switch (type.getSize()) { case 1: dup(); return; case 2: dup2(); return; default: throw new IllegalArgumentException(); } } public void dup() { methodVisitor.visitInsn(Opcodes.DUP); stack.dup(); } /** * lvalue, rvalue → rvalue, lvalue, rvalue * * @param ltype * the second topmost stack value * @param rtype * the topmost stack value */ public void dupX(Type ltype, Type rtype) { int lsize = ltype.getSize(), rsize = rtype.getSize(); if (lsize == 1 && rsize == 1) { dupX1(); } else if (lsize == 1 && rsize == 2) { dup2X1(); } else if (lsize == 2 && rsize == 1) { dupX2(); } else if (lsize == 2 && rsize == 2) { dup2X2(); } else { throw new IllegalArgumentException(); } } public void dupX1() { methodVisitor.visitInsn(Opcodes.DUP_X1); stack.dupX1(); } public void dupX2() { methodVisitor.visitInsn(Opcodes.DUP_X2); stack.dupX2(); } public void dup2() { methodVisitor.visitInsn(Opcodes.DUP2); stack.dup2(); } public void dup2X1() { methodVisitor.visitInsn(Opcodes.DUP2_X1); stack.dup2X1(); } public void dup2X2() { methodVisitor.visitInsn(Opcodes.DUP2_X2); stack.dup2X2(); } /** * lvalue, rvalue → rvalue, lvalue * * @param ltype * the second topmost stack value * @param rtype * the topmost stack value */ public void swap(Type ltype, Type rtype) { int lsize = ltype.getSize(), rsize = rtype.getSize(); if (lsize == 1 && rsize == 1) { swap(); } else if (lsize == 1 && rsize == 2) { swap1_2(); } else if (lsize == 2 && rsize == 1) { swap2_1(); } else if (lsize == 2 && rsize == 2) { swap2(); } else { throw new IllegalArgumentException(); } } public void swap() { methodVisitor.visitInsn(Opcodes.SWAP); stack.swap(); } /** * {value3}, {value2, value1} → {value2, value1}, {value3} */ public final void swap1_2() { dup2X1(); pop2(); } /** * {value3, value2}, {value1} → {value1}, {value3, value2} */ public final void swap2_1() { dupX2(); pop(); } /** * {value4, value3}, {value2, value1} → {value2, value1}, {value4, value3} */ public final void swap2() { dup2X2(); pop2(); } /* math instructions */ public final void add(Type type) { switch (type.getOpcode(Opcodes.IADD)) { case Opcodes.IADD: iadd(); return; case Opcodes.LADD: ladd(); return; case Opcodes.FADD: fadd(); return; case Opcodes.DADD: dadd(); return; default: throw new IllegalArgumentException(); } } public void iadd() { methodVisitor.visitInsn(Opcodes.IADD); stack.iadd(); } public void ladd() { methodVisitor.visitInsn(Opcodes.LADD); stack.ladd(); } public void fadd() { methodVisitor.visitInsn(Opcodes.FADD); stack.fadd(); } public void dadd() { methodVisitor.visitInsn(Opcodes.DADD); stack.dadd(); } public final void sub(Type type) { switch (type.getOpcode(Opcodes.ISUB)) { case Opcodes.ISUB: isub(); return; case Opcodes.LSUB: lsub(); return; case Opcodes.FSUB: fsub(); return; case Opcodes.DSUB: dsub(); return; default: throw new IllegalArgumentException(); } } public void isub() { methodVisitor.visitInsn(Opcodes.ISUB); stack.isub(); } public void lsub() { methodVisitor.visitInsn(Opcodes.LSUB); stack.lsub(); } public void fsub() { methodVisitor.visitInsn(Opcodes.FSUB); stack.fsub(); } public void dsub() { methodVisitor.visitInsn(Opcodes.DSUB); stack.dsub(); } public final void mul(Type type) { switch (type.getOpcode(Opcodes.IMUL)) { case Opcodes.IMUL: imul(); return; case Opcodes.LMUL: lmul(); return; case Opcodes.FMUL: fmul(); return; case Opcodes.DMUL: dmul(); return; default: throw new IllegalArgumentException(); } } public void imul() { methodVisitor.visitInsn(Opcodes.IMUL); stack.imul(); } public void lmul() { methodVisitor.visitInsn(Opcodes.LMUL); stack.lmul(); } public void fmul() { methodVisitor.visitInsn(Opcodes.FMUL); stack.fmul(); } public void dmul() { methodVisitor.visitInsn(Opcodes.DMUL); stack.dmul(); } public final void div(Type type) { switch (type.getOpcode(Opcodes.IDIV)) { case Opcodes.IDIV: idiv(); return; case Opcodes.LDIV: ldiv(); return; case Opcodes.FDIV: fdiv(); return; case Opcodes.DDIV: ddiv(); return; default: throw new IllegalArgumentException(); } } public void idiv() { methodVisitor.visitInsn(Opcodes.IDIV); stack.idiv(); } public void ldiv() { methodVisitor.visitInsn(Opcodes.LDIV); stack.ldiv(); } public void fdiv() { methodVisitor.visitInsn(Opcodes.FDIV); stack.fdiv(); } public void ddiv() { methodVisitor.visitInsn(Opcodes.DDIV); stack.ddiv(); } public final void rem(Type type) { switch (type.getOpcode(Opcodes.IREM)) { case Opcodes.IREM: irem(); return; case Opcodes.LREM: lrem(); return; case Opcodes.FREM: frem(); return; case Opcodes.DREM: drem(); return; default: throw new IllegalArgumentException(); } } public void irem() { methodVisitor.visitInsn(Opcodes.IREM); stack.irem(); } public void lrem() { methodVisitor.visitInsn(Opcodes.LREM); stack.lrem(); } public void frem() { methodVisitor.visitInsn(Opcodes.FREM); stack.frem(); } public void drem() { methodVisitor.visitInsn(Opcodes.DREM); stack.drem(); } public final void neg(Type type) { switch (type.getOpcode(Opcodes.INEG)) { case Opcodes.INEG: ineg(); return; case Opcodes.LNEG: lneg(); return; case Opcodes.FNEG: fneg(); return; case Opcodes.DNEG: dneg(); return; default: throw new IllegalArgumentException(); } } public void ineg() { methodVisitor.visitInsn(Opcodes.INEG); stack.ineg(); } public void lneg() { methodVisitor.visitInsn(Opcodes.LNEG); stack.lneg(); } public void fneg() { methodVisitor.visitInsn(Opcodes.FNEG); stack.fneg(); } public void dneg() { methodVisitor.visitInsn(Opcodes.DNEG); stack.dneg(); } public final void shl(Type type) { switch (type.getOpcode(Opcodes.ISHL)) { case Opcodes.ISHL: ishl(); return; case Opcodes.LSHL: lshl(); return; default: throw new IllegalArgumentException(); } } public void ishl() { methodVisitor.visitInsn(Opcodes.ISHL); stack.ishl(); } public void lshl() { methodVisitor.visitInsn(Opcodes.LSHL); stack.lshl(); } public final void shr(Type type) { switch (type.getOpcode(Opcodes.ISHR)) { case Opcodes.ISHR: ishr(); return; case Opcodes.LSHR: lshr(); return; default: throw new IllegalArgumentException(); } } public void ishr() { methodVisitor.visitInsn(Opcodes.ISHR); stack.ishr(); } public void lshr() { methodVisitor.visitInsn(Opcodes.LSHR); stack.lshr(); } public final void ushr(Type type) { switch (type.getOpcode(Opcodes.IUSHR)) { case Opcodes.IUSHR: iushr(); return; case Opcodes.LUSHR: lushr(); return; default: throw new IllegalArgumentException(); } } public void iushr() { methodVisitor.visitInsn(Opcodes.IUSHR); stack.iushr(); } public void lushr() { methodVisitor.visitInsn(Opcodes.LUSHR); stack.lushr(); } public final void and(Type type) { switch (type.getOpcode(Opcodes.IAND)) { case Opcodes.IAND: iand(); return; case Opcodes.LAND: land(); return; default: throw new IllegalArgumentException(); } } public void iand() { methodVisitor.visitInsn(Opcodes.IAND); stack.iand(); } public void land() { methodVisitor.visitInsn(Opcodes.LAND); stack.land(); } public final void or(Type type) { switch (type.getOpcode(Opcodes.IOR)) { case Opcodes.IOR: ior(); return; case Opcodes.LOR: lor(); return; default: throw new IllegalArgumentException(); } } public void ior() { methodVisitor.visitInsn(Opcodes.IOR); stack.ior(); } public void lor() { methodVisitor.visitInsn(Opcodes.LOR); stack.lor(); } public final void xor(Type type) { switch (type.getOpcode(Opcodes.IXOR)) { case Opcodes.IXOR: ixor(); return; case Opcodes.LXOR: lxor(); return; default: throw new IllegalArgumentException(); } } public void ixor() { methodVisitor.visitInsn(Opcodes.IXOR); stack.ixor(); } public void lxor() { methodVisitor.visitInsn(Opcodes.LXOR); stack.lxor(); } /* local increment instructions */ public void iinc(Variable<Integer> variable, int increment) { assert variable.isAlive() : "variable out of scope"; iinc(variable.getSlot(), increment); } private void iinc(int var, int increment) { assert var >= 0 && Short.MIN_VALUE <= increment && increment <= Short.MAX_VALUE; methodVisitor.visitIincInsn(var, increment); // no stack change } /* primitive type cast instructions */ public void cast(Type from, Type to) { switch (from.getSort()) { case Type.Sort.BYTE: case Type.Sort.CHAR: case Type.Sort.SHORT: case Type.Sort.INT: switch (to.getSort()) { case Type.Sort.INT: // nop return; case Type.Sort.LONG: i2l(); return; case Type.Sort.FLOAT: i2f(); return; case Type.Sort.DOUBLE: i2d(); return; case Type.Sort.BYTE: i2b(); return; case Type.Sort.CHAR: i2c(); return; case Type.Sort.SHORT: i2s(); return; default: throw new IllegalArgumentException(); } case Type.Sort.LONG: switch (to.getSort()) { case Type.Sort.INT: l2i(); return; case Type.Sort.LONG: // nop return; case Type.Sort.FLOAT: l2f(); return; case Type.Sort.DOUBLE: l2d(); return; case Type.Sort.BYTE: l2i(); i2b(); return; case Type.Sort.CHAR: l2i(); i2c(); return; case Type.Sort.SHORT: l2i(); i2s(); return; default: throw new IllegalArgumentException(); } case Type.Sort.FLOAT: switch (to.getSort()) { case Type.Sort.INT: f2i(); return; case Type.Sort.LONG: f2l(); return; case Type.Sort.FLOAT: // nop return; case Type.Sort.DOUBLE: f2d(); return; case Type.Sort.BYTE: f2i(); i2b(); return; case Type.Sort.CHAR: f2i(); i2c(); return; case Type.Sort.SHORT: f2i(); i2s(); return; default: throw new IllegalArgumentException(); } case Type.Sort.DOUBLE: switch (to.getSort()) { case Type.Sort.INT: d2i(); return; case Type.Sort.LONG: d2l(); return; case Type.Sort.FLOAT: d2f(); return; case Type.Sort.DOUBLE: // nop return; case Type.Sort.BYTE: d2i(); i2b(); return; case Type.Sort.CHAR: d2i(); i2c(); return; case Type.Sort.SHORT: d2i(); i2s(); return; default: throw new IllegalArgumentException(); } default: throw new IllegalArgumentException(); } } public void i2l() { methodVisitor.visitInsn(Opcodes.I2L); stack.i2l(); } public void i2f() { methodVisitor.visitInsn(Opcodes.I2F); stack.i2f(); } public void i2d() { methodVisitor.visitInsn(Opcodes.I2D); stack.i2d(); } public void l2i() { methodVisitor.visitInsn(Opcodes.L2I); stack.l2i(); } public void l2f() { methodVisitor.visitInsn(Opcodes.L2F); stack.l2f(); } public void l2d() { methodVisitor.visitInsn(Opcodes.L2D); stack.l2d(); } public void f2i() { methodVisitor.visitInsn(Opcodes.F2I); stack.f2i(); } public void f2l() { methodVisitor.visitInsn(Opcodes.F2L); stack.f2l(); } public void f2d() { methodVisitor.visitInsn(Opcodes.F2D); stack.f2d(); } public void d2i() { methodVisitor.visitInsn(Opcodes.D2I); stack.d2i(); } public void d2l() { methodVisitor.visitInsn(Opcodes.D2L); stack.d2l(); } public void d2f() { methodVisitor.visitInsn(Opcodes.D2F); stack.d2f(); } public void i2b() { methodVisitor.visitInsn(Opcodes.I2B); stack.i2b(); } public void i2c() { methodVisitor.visitInsn(Opcodes.I2C); stack.i2c(); } public void i2s() { methodVisitor.visitInsn(Opcodes.I2S); stack.i2s(); } /** * value → not(value) */ public final void not() { iconst(1); ixor(); } /** * value → bitnot(value) */ public final void bitnot() { iconst(-1); ixor(); } /* compare instructions */ public void lcmp() { methodVisitor.visitInsn(Opcodes.LCMP); stack.lcmp(); } public void fcmpl() { methodVisitor.visitInsn(Opcodes.FCMPL); stack.fcmpl(); } public void fcmpg() { methodVisitor.visitInsn(Opcodes.FCMPG); stack.fcmpg(); } public void dcmpl() { methodVisitor.visitInsn(Opcodes.DCMPL); stack.dcmpl(); } public void dcmpg() { methodVisitor.visitInsn(Opcodes.DCMPG); stack.dcmpg(); } /* jump instructions */ public void ifeq(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFEQ, jump.target()); stack.ifeq(jump); } public void ifne(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFNE, jump.target()); stack.ifne(jump); } public void iflt(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFLT, jump.target()); stack.iflt(jump); } public void ifge(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFGE, jump.target()); stack.ifge(jump); } public void ifgt(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFGT, jump.target()); stack.ifgt(jump); } public void ifle(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFLE, jump.target()); stack.ifle(jump); } public void ificmpeq(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ICMPEQ, jump.target()); stack.ificmpeq(jump); } public void ificmpne(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ICMPNE, jump.target()); stack.ificmpne(jump); } public void ificmplt(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ICMPLT, jump.target()); stack.ificmplt(jump); } public void ificmpge(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ICMPGE, jump.target()); stack.ificmpge(jump); } public void ificmpgt(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ICMPGT, jump.target()); stack.ificmpgt(jump); } public void ificmple(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ICMPLE, jump.target()); stack.ificmple(jump); } public void ifacmpeq(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ACMPEQ, jump.target()); stack.ifacmpeq(jump); } public void ifacmpne(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IF_ACMPNE, jump.target()); stack.ifacmpne(jump); } public void goTo(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.GOTO, jump.target()); stack.goTo(jump); } public void ifnull(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFNULL, jump.target()); stack.ifnull(jump); } public void ifnonnull(Jump jump) { methodVisitor.visitJumpInsn(Opcodes.IFNONNULL, jump.target()); stack.ifnonnull(jump); } /* sub-routine instructions (not supported) */ public void jsr(Jump jump) { throw new UnsupportedOperationException(); } public void ret(int var) { throw new UnsupportedOperationException(); } /* switch instructions */ private static Label[] toLabels(Jump... jumps) { Label[] labels = new Label[jumps.length]; for (int i = 0, length = jumps.length; i < length; ++i) { labels[i] = jumps[i].target(); } return labels; } public void tableswitch(int min, int max, Jump dflt, Jump[] jumps) { methodVisitor.visitTableSwitchInsn(min, max, dflt.target(), toLabels(jumps)); stack.tableswitch(min, max, dflt, jumps); } public void lookupswitch(Jump dflt, int[] keys, Jump[] jumps) { methodVisitor.visitLookupSwitchInsn(dflt.target(), keys, toLabels(jumps)); stack.lookupswitch(dflt, keys, jumps); } /* return instructions */ /** * value → ∅ */ public void _return() { Type returnType = method.methodDescriptor.returnType(); switch (returnType.getOpcode(Opcodes.IRETURN)) { case Opcodes.IRETURN: ireturn(); return; case Opcodes.LRETURN: lreturn(); return; case Opcodes.FRETURN: freturn(); return; case Opcodes.DRETURN: dreturn(); return; case Opcodes.ARETURN: areturn(); return; case Opcodes.RETURN: voidreturn(); return; default: throw new IllegalArgumentException(); } } public void ireturn() { methodVisitor.visitInsn(Opcodes.IRETURN); stack.ireturn(); } public void lreturn() { methodVisitor.visitInsn(Opcodes.LRETURN); stack.lreturn(); } public void freturn() { methodVisitor.visitInsn(Opcodes.FRETURN); stack.freturn(); } public void dreturn() { methodVisitor.visitInsn(Opcodes.DRETURN); stack.dreturn(); } public void areturn() { methodVisitor.visitInsn(Opcodes.ARETURN); stack.areturn(); } public void voidreturn() { methodVisitor.visitInsn(Opcodes.RETURN); stack.voidreturn(); } /* field instructions */ /** * ∅ → value * * @param field * the field descriptor */ public void get(FieldName field) { switch (field.allocation) { case Instance: getfield(field.owner, field.name, field.descriptor); break; case Static: getstatic(field.owner, field.name, field.descriptor); break; default: throw new AssertionError(); } } /** * value → ∅ * * @param field * the field descriptor */ public void put(FieldName field) { switch (field.allocation) { case Instance: putfield(field.owner, field.name, field.descriptor); break; case Static: putstatic(field.owner, field.name, field.descriptor); break; default: throw new AssertionError(); } } public void getstatic(Type owner, String name, Type desc) { methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, owner.internalName(), name, desc.descriptor()); stack.getstatic(desc); } public void putstatic(Type owner, String name, Type desc) { methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, owner.internalName(), name, desc.descriptor()); stack.putstatic(desc); } public void getfield(Type owner, String name, Type desc) { methodVisitor.visitFieldInsn(Opcodes.GETFIELD, owner.internalName(), name, desc.descriptor()); stack.getfield(owner, desc); } public void putfield(Type owner, String name, Type desc) { methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, owner.internalName(), name, desc.descriptor()); stack.putfield(owner, desc); } /* invoke instructions */ /** * parameters → value * * @param method * the method for the invocation */ public void invoke(MethodName method) { switch (method.invoke) { case Interface: invokeinterface(method.owner, method.name, method.descriptor); break; case Special: invokespecial(method.owner, method.name, method.descriptor, false); break; case SpecialInterface: invokespecial(method.owner, method.name, method.descriptor, true); break; case Static: invokestatic(method.owner, method.name, method.descriptor, false); break; case StaticInterface: invokestatic(method.owner, method.name, method.descriptor, true); break; case Virtual: invokevirtual(method.owner, method.name, method.descriptor, false); break; case VirtualInterface: invokevirtual(method.owner, method.name, method.descriptor, true); break; default: throw new AssertionError(); } } public void invokevirtual(Type owner, String name, MethodTypeDescriptor desc, boolean itf) { methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner.internalName(), name, desc.descriptor(), itf); stack.invokevirtual(desc); } public void invokespecial(Type owner, String name, MethodTypeDescriptor desc, boolean itf) { methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, owner.internalName(), name, desc.descriptor(), itf); stack.invokespecial(desc); } public void invokestatic(Type owner, String name, MethodTypeDescriptor desc, boolean itf) { methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, owner.internalName(), name, desc.descriptor(), itf); stack.invokestatic(desc); } public void invokeinterface(Type owner, String name, MethodTypeDescriptor desc) { methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner.internalName(), name, desc.descriptor(), true); stack.invokeinterface(desc); } /* invokedynamic instructions */ private static final Object[] EMPTY_BSM_ARGS = new Object[] {}; public final void invokedynamic(String name, MethodTypeDescriptor desc, Handle bsm) { invokedynamic(name, desc, bsm, EMPTY_BSM_ARGS); } public void invokedynamic(String name, MethodTypeDescriptor desc, Handle bsm, Object... bsmArgs) { methodVisitor.visitInvokeDynamicInsn(name, desc.descriptor(), bsm.handle(), bsmArgs); stack.invokedynamic(desc); } /* other instructions */ /** * ∅ → object * * @param type * the type descriptor * @param init * the init-method call descriptor */ public final void anew(Type type, MethodName init) { anew(type); dup(); invoke(init); } /** * ∅ → object * * @param type * the type descriptor * @param init * the init-method call descriptor * @param arguments * the constructor call arguments */ public final void anew(Type type, MethodName init, Value<?>... arguments) { anew(type); dup(); for (Value<?> argument : arguments) { load(argument); } invoke(init); } public void anew(Type type) { methodVisitor.visitTypeInsn(Opcodes.NEW, type.internalName()); stack.anew(type); } private static final int arrayType(Type type) { switch (type.getSort()) { case Type.Sort.BOOLEAN: return Opcodes.T_BOOLEAN; case Type.Sort.CHAR: return Opcodes.T_CHAR; case Type.Sort.BYTE: return Opcodes.T_BYTE; case Type.Sort.SHORT: return Opcodes.T_SHORT; case Type.Sort.INT: return Opcodes.T_INT; case Type.Sort.FLOAT: return Opcodes.T_FLOAT; case Type.Sort.LONG: return Opcodes.T_LONG; case Type.Sort.DOUBLE: return Opcodes.T_DOUBLE; case Type.Sort.OBJECT: case Type.Sort.ARRAY: case Type.Sort.METHOD: case Type.Sort.VOID: default: throw new IllegalArgumentException(); } } /** * ∅ → array * * @param length * the array length * @param type * the array component type */ public final void newarray(int length, Type type) { assert length >= 0; iconst(length); newarray(type); } public void newarray(Type type) { methodVisitor.visitIntInsn(Opcodes.NEWARRAY, arrayType(type)); stack.newarray(type); } /** * ∅ → array * * @param length * the array length * @param type * the array component type */ public final void anewarray(int length, Type type) { assert length >= 0; iconst(length); anewarray(type); } /** * ∅ → array * * @param type * the array component type * @param elements * the array elements */ public final void anewarray(Type type, Value<?>... elements) { anewarray(elements.length, type); int index = 0; for (Value<?> element : elements) { dup(); iconst(index++); load(element); astore(type); } } public void anewarray(Type type) { methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, type.internalName()); stack.newarray(type); } public void arraylength() { methodVisitor.visitInsn(Opcodes.ARRAYLENGTH); stack.arraylength(); } public void athrow() { methodVisitor.visitInsn(Opcodes.ATHROW); stack.athrow(); } public void checkcast(Type type) { methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, type.internalName()); stack.checkcast(type); } public void instanceOf(Type type) { methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, type.internalName()); stack.instanceOf(type); } public void monitorenter() { methodVisitor.visitInsn(Opcodes.MONITORENTER); stack.monitorenter(); } public void monitorexit() { methodVisitor.visitInsn(Opcodes.MONITOREXIT); stack.monitorexit(); } public void multianewarray(Type type, int dims) { // TODO: descriptor() call correct? or internalName()? methodVisitor.visitMultiANewArrayInsn(type.descriptor(), dims); stack.multianewarray(type, dims); } public void nop() { methodVisitor.visitInsn(Opcodes.NOP); } /* boxed wrapper helper */ private static final MethodName[] boxMethods; private static final MethodName[] unboxMethods; static { MethodName[] bm = new MethodName[12]; bm[Type.Sort.BOOLEAN] = Methods.Boolean_valueOf; bm[Type.Sort.CHAR] = Methods.Character_valueOf; bm[Type.Sort.BYTE] = Methods.Byte_valueOf; bm[Type.Sort.SHORT] = Methods.Short_valueOf; bm[Type.Sort.INT] = Methods.Integer_valueOf; bm[Type.Sort.FLOAT] = Methods.Float_valueOf; bm[Type.Sort.LONG] = Methods.Long_valueOf; bm[Type.Sort.DOUBLE] = Methods.Double_valueOf; boxMethods = bm; MethodName[] um = new MethodName[12]; um[Type.Sort.BOOLEAN] = Methods.Boolean_booleanValue; um[Type.Sort.CHAR] = Methods.Character_charValue; um[Type.Sort.BYTE] = Methods.Byte_byteValue; um[Type.Sort.SHORT] = Methods.Short_shortValue; um[Type.Sort.INT] = Methods.Integer_intValue; um[Type.Sort.FLOAT] = Methods.Float_floatValue; um[Type.Sort.LONG] = Methods.Long_longValue; um[Type.Sort.DOUBLE] = Methods.Double_doubleValue; unboxMethods = um; } /** * value → boxed * * @param type * the type kind */ public final void toBoxed(Type type) { MethodName call = boxMethods[type.getSort()]; if (call != null) { invoke(call); } } /** * boxed → value * * @param type * the type kind */ public final void toUnboxed(Type type) { MethodName call = unboxMethods[type.getSort()]; if (call != null) { invoke(call); } } /** * Returns the wrapper for {@code type}, or {@code type} if it does not represent a primitive. * * @param type * the type kind * @return the type's wrapper */ public static final Type wrapper(Type type) { return type.asWrapper(); } }