edu.ubc.mirrors.holograms.HologramMethodGenerator.java Source code

Java tutorial

Introduction

Here is the source code for edu.ubc.mirrors.holograms.HologramMethodGenerator.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Robin Salkeld
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
package edu.ubc.mirrors.holograms;

import static edu.ubc.mirrors.holograms.HologramClassGenerator.arrayMirrorType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.classMirrorType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.classType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.getHologramType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.getOriginalInternalClassName;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.getPrimitiveArrayMirrorType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.getSortName;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.hologramType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.instanceHologramType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.instanceMirrorType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.objectHologramType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.throwableType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.hologramThrowableType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.objectMirrorType;
import static edu.ubc.mirrors.holograms.HologramClassGenerator.stringType;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.LocalVariablesSorter;

import edu.ubc.mirrors.ArrayMirror;
import edu.ubc.mirrors.ClassMirror;
import edu.ubc.mirrors.MethodHandle;
import edu.ubc.mirrors.ObjectArrayMirror;
import edu.ubc.mirrors.ObjectMirror;
import edu.ubc.mirrors.Reflection;

public class HologramMethodGenerator extends InstructionAdapter {

    static String activeMethod = null;

    private AnalyzerAdapter analyzer;
    private LocalVariablesSorter lvs;
    private Type methodType;
    private Type owner;
    private String name;

    private final boolean isToString;
    private final boolean isGetStackTrace;

    private int access;

    public HologramMethodGenerator(String owner, int access, String name, String desc, MethodVisitor superVisitor,
            boolean isToString, boolean isGetStackTrace) {
        super(Opcodes.ASM4, null);
        this.analyzer = new AnalyzerAdapter(owner, access, name, desc, superVisitor);
        this.mv = analyzer;
        this.name = name;
        this.owner = Type.getObjectType(owner);
        this.access = access;
        this.methodType = Type.getMethodType(desc);
        this.isToString = isToString;
        this.isGetStackTrace = isGetStackTrace;

        activeMethod = name + desc;
    }

    public void getClassMirror(Type type) {
        if (type.getSort() == Type.OBJECT && type.getInternalName().startsWith("hologram")) {
            getstatic(type.getInternalName(), "classMirror", classMirrorType.getDescriptor());
        } else {
            getstatic(owner.getInternalName(), "classMirror", classMirrorType.getDescriptor());
            aconst(type.getDescriptor());
            new MethodHandle() {
                protected void methodCall() throws Throwable {
                    ObjectHologram.getClassMirrorForType(null, null);
                }
            }.invoke(this);
        }
    }

    @Override
    public void visitEnd() {
        super.visitEnd();

        activeMethod = null;
    }

    public void setLocalVariablesSorter(LocalVariablesSorter lvs) {
        this.lvs = lvs;
    }

    public void setAnalyzer(AnalyzerAdapter analyzer) {
        this.analyzer = analyzer;
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
        // Various special cases here to adjust for the fact that Object and Throwable
        // are still in the hierarchy and have some methods with incompatible types.

        Type stringHologramType = getHologramType(String.class);
        if (name.equals("toString") && desc.equals(Type.getMethodDescriptor(stringHologramType))) {
            desc = Type.getMethodDescriptor(Type.getType(String.class));
            if (owner.equals(Type.getInternalName(Hologram.class))) {
                owner = OBJECT_TYPE.getInternalName();
                // Handle calling Object.toString() with an invokespecial opcode, 
                // which doesn't work any more since we've changed the superclass.
                if (opcode == Opcodes.INVOKESPECIAL) {
                    opcode = Opcodes.INVOKESTATIC;
                    owner = objectHologramType.getInternalName();
                    name = "hologramToString";
                    desc = Type.getMethodDescriptor(stringType, hologramType);
                }
            }

            super.visitMethodInsn(opcode, owner, name, desc);

            getClassMirror(this.owner);
            invokestatic(objectHologramType.getInternalName(), "makeStringHologram",
                    Type.getMethodDescriptor(hologramType, stringType, classMirrorType));
            checkcast(stringHologramType);
            return;
        }

        if (name.equals("getClass") && desc.equals(Type.getMethodDescriptor(getHologramType(Class.class)))) {
            invokeinterface(Type.getInternalName(Hologram.class), "getMirror",
                    Type.getMethodDescriptor(objectMirrorType));
            invokeinterface(objectMirrorType.getInternalName(), "getClassMirror",
                    Type.getMethodDescriptor(Type.getType(ClassMirror.class)));

            invokestatic(objectHologramType.getInternalName(), "make",
                    Type.getMethodDescriptor(hologramType, objectMirrorType));
            checkcast(getHologramType(Class.class));
            return;
        }

        if (owner.equals(Type.getInternalName(Hologram.class))) {
            if (name.equals("<init>") && this.owner.equals(getHologramType(Throwable.class))) {
                owner = Type.getInternalName(Throwable.class);
            } else if (name.equals("<init>") || name.equals("toString")) {
                owner = objectHologramType.getInternalName();
            } else {
                owner = OBJECT_TYPE.getInternalName();
            }
        }

        if (name.equals("clone")) {
            if (desc.equals(Type.getMethodDescriptor(hologramType))) {
                desc = Type.getMethodDescriptor(OBJECT_TYPE);

                // Object.clone() on an array type is legal since array types
                // are treated specially by the JVM, but the mirror interfaces 
                // are not granted the same leeway.
                Object t = stackType(0);
                if (t instanceof String) {
                    String internalName = (String) t;
                    Type type = Type.getObjectType(internalName);
                    if (HologramClassGenerator.getOriginalType(type).getSort() == Type.ARRAY) {
                        owner = internalName;
                    }
                }
            }
            if (owner.equals(Type.getType(ObjectArrayMirror.class).getInternalName())
                    || (owner.startsWith("hologramarray") && !owner.startsWith("hologramarrayimpl"))) {
                String originalName = getOriginalInternalClassName(owner);
                owner = getHologramType(Type.getObjectType(originalName), true).getInternalName();
                checkcast(Type.getObjectType(owner));
            }
        }

        //        if (owner.equals(getHologramType(Throwable.class).getInternalName())) {
        //            if (name.equals("<init>") && desc.equals(Type.getMethodDescriptor(Type.VOID_TYPE, getHologramType(String.class)))) {
        //                desc = Type.getMethodDescriptor(Type.VOID_TYPE, objectHologramType);
        //            }
        //        }

        if (owner.equals(hologramThrowableType.getInternalName()) && name.equals("fillInStackTrace")
                && desc.equals(Type.getMethodDescriptor(hologramThrowableType))) {
            desc = Type.getMethodDescriptor(throwableType);
            owner = throwableType.getInternalName();
        }

        if (name.equals("equals")
                && desc.equals(Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Hologram.class)))) {
            desc = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, OBJECT_TYPE);
        }

        if (name.equals("<init>") && !owner.equals(Type.getInternalName(Throwable.class))) {
            int argsSize = Type.getArgumentsAndReturnSizes(desc) >> 2;
            desc = HologramClassGenerator.addMirrorParam(desc);

            Object targetType = stackType(argsSize - 1);
            if (targetType.equals(Opcodes.UNINITIALIZED_THIS)) {
                // If the target is an uninitialized this (i.e. we're calling super(...)
                // or this(...)), pass along the extra mirror argument
                load((methodType.getArgumentsAndReturnSizes() >> 2) - 1, instanceMirrorType);
            } else if (targetType instanceof Label) {
                // If the target is just uninitialized (i.e. we're calling <init> after
                // a new), create the mirror
                getClassMirror(Type.getObjectType(owner));
                invokeinterface(classMirrorType.getInternalName(), "newRawInstance",
                        Type.getMethodDescriptor(instanceMirrorType));
            } else {
                // Shouldn't happen
                throw new RuntimeException("Calling <init> on already initialized type: " + targetType);
            }
        }

        super.visitMethodInsn(opcode, owner, name, desc);

        if (owner.equals(Type.getInternalName(Throwable.class)) && name.equals("getStackTraceElement")) {
            Type steType = Type.getType(StackTraceElement.class);
            invokestatic(Type.getInternalName(ObjectHologram.class), "cleanStackTraceElement",
                    Type.getMethodDescriptor(steType, steType));
        }
    }

    @Override
    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
        boolean isSet = (opcode == Opcodes.PUTFIELD || opcode == Opcodes.PUTSTATIC);
        boolean isStatic = (opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC);

        Type fieldType = Type.getType(desc);
        int setValueLocal = -1;

        if (isStatic) {
            // For a static field the instance is null
            if (isSet) {
                setValueLocal = lvs.newLocal(fieldType);
                store(setValueLocal, fieldType);
            }
            aconst(null);
            if (isSet) {
                load(setValueLocal, fieldType);
            }
        } else {
            // If this is an "uninitialized this", the mirror is the nth argument instead
            // of the mirror field on ObjectHologram.
            Object stackType = stackType(isSet ? 1 : 0);
            if (stackType == Opcodes.UNINITIALIZED_THIS) {
                // Pop the original argument
                if (isSet) {
                    setValueLocal = lvs.newLocal(fieldType);
                    store(setValueLocal, fieldType);
                }

                pop();
                load((methodType.getArgumentsAndReturnSizes() >> 2) - 1, instanceMirrorType);

                MethodHandle.OBJECT_HOLOGRAM_MAKE.invoke(this);

                if (isSet) {
                    load(setValueLocal, fieldType);
                }
            }
        }

        getClassMirror(Type.getObjectType(owner));
        aconst(name);

        Type fieldTypeForMirrorCall = fieldType;
        int fieldSort = fieldType.getSort();
        String suffix = "";
        if (fieldSort == Type.ARRAY || fieldSort == Type.OBJECT) {
            fieldTypeForMirrorCall = hologramType;
        } else {
            suffix = HologramClassGenerator.getSortName(fieldSort);
        }

        // Call the appropriate getter/setter method on the mirror
        String methodDesc;
        if (isSet) {
            methodDesc = Type.getMethodDescriptor(Type.VOID_TYPE, hologramType, fieldTypeForMirrorCall,
                    classMirrorType, stringType);
        } else {
            methodDesc = Type.getMethodDescriptor(fieldTypeForMirrorCall, hologramType, classMirrorType,
                    stringType);
        }
        invokestatic(instanceHologramType.getInternalName(), (isSet ? "set" : "get") + suffix + "Field",
                methodDesc);

        if (!isSet && fieldTypeForMirrorCall.equals(hologramType)) {
            checkcast(fieldType);
        }
    }

    private Object stackType(int indexFromTop) {
        return analyzer.stack.get(analyzer.stack.size() - 1 - indexFromTop);
    }

    @Override
    public void visitMultiANewArrayInsn(String desc, int dims) {
        Type hologramArrayType = Type.getType(desc);
        Type originalElementType = Type
                .getObjectType(getOriginalInternalClassName(hologramArrayType.getInternalName())).getElementType();

        Type intArrayType = Type.getObjectType("[I");

        // Push the dimension values into an array
        int dimsArrayVar = lvs.newLocal(intArrayType);
        aconst(dims);
        newarray(Type.INT_TYPE);
        store(dimsArrayVar, intArrayType);

        for (int d = dims - 1; d >= 0; d--) {
            load(dimsArrayVar, intArrayType);
            swap();
            aconst(d);
            swap();
            astore(Type.INT_TYPE);
        }

        anew(hologramArrayType);
        dup();

        getClassMirror(originalElementType);
        load(dimsArrayVar, intArrayType);
        invokeinterface(classMirrorType.getInternalName(), "newArray",
                Type.getMethodDescriptor(Type.getType(ArrayMirror.class), intArrayType));

        invokespecial(hologramArrayType.getInternalName(), "<init>",
                Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(ObjectArrayMirror.class)));
    }

    @Override
    public void visitInsn(int opcode) {
        Type arrayElementType = null;
        boolean isArrayLoad = (Opcodes.IALOAD <= opcode && opcode < Opcodes.IALOAD + 8);
        boolean isArrayStore = (Opcodes.IASTORE <= opcode && opcode < Opcodes.IASTORE + 8);
        if (isArrayLoad || isArrayStore) {
            switch (opcode) {
            case Opcodes.IALOAD:
                arrayElementType = Type.INT_TYPE;
                break;
            case Opcodes.LALOAD:
                arrayElementType = Type.LONG_TYPE;
                break;
            case Opcodes.FALOAD:
                arrayElementType = Type.FLOAT_TYPE;
                break;
            case Opcodes.DALOAD:
                arrayElementType = Type.DOUBLE_TYPE;
                break;
            case Opcodes.AALOAD:
                arrayElementType = hologramType;
                break;
            case Opcodes.BALOAD:
                arrayElementType = Type.BYTE_TYPE;
                break;
            case Opcodes.CALOAD:
                arrayElementType = Type.CHAR_TYPE;
                break;
            case Opcodes.SALOAD:
                arrayElementType = Type.SHORT_TYPE;
                break;
            case Opcodes.IASTORE:
                arrayElementType = Type.INT_TYPE;
                break;
            case Opcodes.LASTORE:
                arrayElementType = Type.LONG_TYPE;
                break;
            case Opcodes.FASTORE:
                arrayElementType = Type.FLOAT_TYPE;
                break;
            case Opcodes.DASTORE:
                arrayElementType = Type.DOUBLE_TYPE;
                break;
            case Opcodes.AASTORE:
                arrayElementType = hologramType;
                break;
            case Opcodes.BASTORE:
                arrayElementType = Type.BYTE_TYPE;
                break;
            case Opcodes.CASTORE:
                arrayElementType = Type.CHAR_TYPE;
                break;
            case Opcodes.SASTORE:
                arrayElementType = Type.SHORT_TYPE;
                break;
            }

            Type mirrorType = HologramClassGenerator.objectArrayMirrorType;
            if (arrayElementType.getSort() != Type.OBJECT && arrayElementType.getSort() != Type.ARRAY) {
                mirrorType = getPrimitiveArrayMirrorType(arrayElementType);
            }

            // Use the analyzer to figure out the expected array element type
            Type arrayElementTypeForMirrorCall = arrayElementType;
            Type hologramArrayType = Type
                    .getObjectType((String) stackType(isArrayStore ? 1 + arrayElementType.getSize() : 1));
            if (hologramArrayType == null) {
                hologramArrayType = Type.getType(ObjectArrayHologram.class);
            }
            if (arrayElementType.equals(hologramType)) {
                Type originalType = Type
                        .getObjectType(getOriginalInternalClassName(hologramArrayType.getInternalName()));
                arrayElementType = getHologramType(
                        Reflection.makeArrayType(originalType.getDimensions() - 1, originalType.getElementType()));
                hologramArrayType = Type.getType(ObjectArrayHologram.class);
            }

            // Call the appropriate getter/setter method on the hologram
            String methodDesc;
            if (isArrayStore) {
                methodDesc = Type.getMethodDescriptor(Type.VOID_TYPE, mirrorType, Type.INT_TYPE,
                        arrayElementTypeForMirrorCall);
            } else {
                methodDesc = Type.getMethodDescriptor(arrayElementTypeForMirrorCall, mirrorType, Type.INT_TYPE);
            }
            invokestatic(hologramArrayType.getInternalName(), (isArrayStore ? "setHologram" : "getHologram"),
                    methodDesc);
            if (!isArrayStore && arrayElementTypeForMirrorCall.equals(hologramType)) {
                checkcast(arrayElementType);
            }

            return;
        }

        if (opcode == Opcodes.ARRAYLENGTH) {
            invokeinterface(arrayMirrorType.getInternalName(), "length", Type.getMethodDescriptor(Type.INT_TYPE));
            return;
        }

        if (opcode == Opcodes.ARETURN) {
            if (isToString) {
                invokestatic(objectHologramType.getInternalName(), "getRealStringForHologram",
                        Type.getMethodDescriptor(Type.getType(String.class), Type.getType(ObjectHologram.class)));
            } else if (isGetStackTrace) {
                invokestatic(objectHologramType.getInternalName(), "getRealStackTraceForHologram",
                        Type.getMethodDescriptor(Type.getType(StackTraceElement[].class),
                                Type.getType(Hologram.class)));
            }
        }

        if (opcode == Opcodes.RETURN && owner.equals(hologramThrowableType) && name.equals("<init>")) {
            load(0, owner);
            new MethodHandle() {
                protected void methodCall() throws Throwable {
                    ObjectHologram.register(null);
                    ;
                }
            }.invoke(this);
        }

        super.visitInsn(opcode);
    }

    @Override
    public void visitTypeInsn(int opcode, String type) {
        if (opcode == Opcodes.ANEWARRAY) {
            String originalTypeName = getOriginalInternalClassName(type);
            Type arrayType = Reflection.makeArrayType(1, Type.getObjectType(originalTypeName));

            getClassMirror(Type.getObjectType(type));
            swap();
            invokeinterface(classMirrorType.getInternalName(), "newArray",
                    Type.getMethodDescriptor(Type.getType(ArrayMirror.class), Type.INT_TYPE));

            // Instantiate the hologram class
            Type hologramArrayType = getHologramType(arrayType, true);
            anew(hologramArrayType);
            dupX1();
            swap();

            invokespecial(hologramArrayType.getInternalName(), "<init>",
                    Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(ObjectArrayMirror.class)));
            return;
        }

        if (opcode == Opcodes.NEW && type.equals(Type.getInternalName(Hologram.class))) {
            type = objectHologramType.getInternalName();
        }

        super.visitTypeInsn(opcode, type);
    }

    @Override
    public void visitIntInsn(int opcode, int operand) {
        if (opcode == Opcodes.NEWARRAY) {
            // Wrap with an ArrayMirror
            Type elementType;
            switch (operand) {
            case Opcodes.T_BOOLEAN:
                elementType = Type.BOOLEAN_TYPE;
                break;
            case Opcodes.T_BYTE:
                elementType = Type.BYTE_TYPE;
                break;
            case Opcodes.T_CHAR:
                elementType = Type.CHAR_TYPE;
                break;
            case Opcodes.T_SHORT:
                elementType = Type.SHORT_TYPE;
                break;
            case Opcodes.T_INT:
                elementType = Type.INT_TYPE;
                break;
            case Opcodes.T_LONG:
                elementType = Type.LONG_TYPE;
                break;
            case Opcodes.T_FLOAT:
                elementType = Type.FLOAT_TYPE;
                break;
            case Opcodes.T_DOUBLE:
                elementType = Type.DOUBLE_TYPE;
                break;
            default:
                throw new IllegalArgumentException("Unknown type number: " + operand);
            }

            getClassMirror(elementType);
            swap();
            invokeinterface(classMirrorType.getInternalName(), "newArray",
                    Type.getMethodDescriptor(Type.getType(ArrayMirror.class), Type.INT_TYPE));

            String arrayHologramType = "edu/ubc/mirrors/holograms/" + getSortName(elementType.getSort())
                    + "ArrayHologram";

            invokestatic(objectHologramType.getInternalName(), "make",
                    Type.getMethodDescriptor(hologramType, objectMirrorType));
            checkcast(Type.getObjectType(arrayHologramType));

            return;
        }

        super.visitIntInsn(opcode, operand);
    }

    @Override
    public void visitLdcInsn(Object cst) {
        super.visitLdcInsn(cst);

        if (cst instanceof String) {
            Type hologramStringType = getHologramType(stringType);
            getClassMirror(this.owner);
            invokestatic(objectHologramType.getInternalName(), "makeStringHologram",
                    Type.getMethodDescriptor(hologramType, stringType, classMirrorType));
            checkcast(hologramStringType);
        } else if (cst instanceof Type) {
            Type hologramClassType = getHologramType(classType);
            getClassMirror(this.owner);
            invokestatic(objectHologramType.getInternalName(), "makeClassHologram",
                    Type.getMethodDescriptor(hologramType, classType, classMirrorType));
            checkcast(hologramClassType);
        }
    }

    @Override
    public void visitMaxs(int maxStack, int maxLocals) {
        // TODO calculate this more precisely (or get ASM to do it for me)
        super.visitMaxs(maxStack + 20, maxLocals + 20);
    }

    @Override
    public void visitLineNumber(int line, Label start) {
        super.visitLineNumber(line, start);

        //        getstatic(Type.getInternalName(System.class), "out", Type.getDescriptor(PrintStream.class));
        //        aconst(owner + "#" + name + ":" + line);
        //        invokevirtual(Type.getInternalName(PrintStream.class), "println", Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE));
    }

    public static void initializeStaticFields(Type owner, InstructionAdapter mv) {
        // Initialize the static field that holds the ClassMirror for this holographic Class
        mv.aconst(owner);
        new MethodHandle() {
            protected void methodCall() throws Throwable {
                ObjectHologram.getClassMirrorForHolographicClass(null);
            }
        }.invoke(mv);
        mv.dup();
        mv.putstatic(owner.getInternalName(), "classMirror", classMirrorType.getDescriptor());
    }

    @Override
    public void visitCode() {
        super.visitCode();

        if (name.equals("<clinit>")) {
            initializeStaticFields(owner, this);

            // Skip the static initializer for classes that were already defined - 
            // these will have already been executed when
            // loading the original class and state will be proxied by ClassMirrors instead.
            Label afterEarlyReturn = new Label();
            new MethodHandle() {
                protected void methodCall() throws Throwable {
                    ((ClassMirror) null).initialized();
                }
            }.invoke(this);
            ifeq(afterEarlyReturn);
            areturn(Type.VOID_TYPE);
            mark(afterEarlyReturn);
            visitFrame(Opcodes.F_NEW, 0, new Object[0], 0, new Object[0]);
        }

        if (name.equals("<init>")) {
            lvs.newLocal(instanceMirrorType);

            if (owner.equals(getHologramType(Throwable.class))) {
                load(0, owner);
                load((methodType.getArgumentsAndReturnSizes() >> 2) - 1, instanceMirrorType);
                putfield(owner.getInternalName(), "mirror", Type.getDescriptor(ObjectMirror.class));
            }
        }
    }

    private void boxIfNeeded(Type type) {
        Class<?> boxingType = Reflection.getBoxingType(type);
        if (boxingType != null) {
            invokestatic(Type.getInternalName(boxingType), "valueOf",
                    Type.getMethodType(Type.getType(boxingType), type).getDescriptor());
        }
    }

    public void generateNativeThunk() {
        visitCode();

        getClassMirror(owner);
        aconst(name);
        aconst(HologramClassGenerator.getOriginalType(methodType).getDescriptor());

        Type[] parameterTypes = methodType.getArgumentTypes();
        int var = 0;
        if ((Opcodes.ACC_STATIC & access) == 0) {
            load(var, owner);
            MethodHandle.OBJECT_HOLOGRAM_GET_MIRROR.invoke(this);
            var++;
        } else {
            aconst(null);
        }
        aconst(parameterTypes.length);
        newarray(OBJECT_TYPE);

        for (int param = 0; param < parameterTypes.length; param++) {
            Type paramType = parameterTypes[param];
            dup();
            aconst(param);
            load(var, paramType);
            boxIfNeeded(paramType);
            if (HologramClassGenerator.isRefType(paramType)) {
                MethodHandle.OBJECT_HOLOGRAM_GET_MIRROR.invoke(this);
            }
            astore(OBJECT_TYPE);
            var += paramType.getSize();
        }

        MethodHandle.OBJECT_HOLOGRAM_INVOKE_METHOD_HANDLER.invoke(this);

        Type returnType = methodType.getReturnType();
        if (returnType.getSort() != Type.VOID) {
            Class<?> boxingType = Reflection.getBoxingType(returnType);
            if (boxingType != null) {
                checkcast(Type.getType(boxingType));
                invokevirtual(Type.getInternalName(boxingType), returnType.getClassName() + "Value",
                        Type.getMethodType(returnType).getDescriptor());
            } else {
                MethodHandle.OBJECT_HOLOGRAM_MAKE.invoke(this);
                checkcast(returnType);
            }
        }
        areturn(returnType);
        visitMaxs(Math.max(2, var + 1), Math.max(2, var + 1));
        visitEnd();
    }
}