org.qi4j.runtime.composite.TransientClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.qi4j.runtime.composite.TransientClassLoader.java

Source

/*
 * Copyright (c) 2010, Rickard berg. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.qi4j.runtime.composite;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.qi4j.api.entity.Lifecycle;
import org.qi4j.api.mixin.Initializable;
import org.qi4j.api.util.Classes;
import org.qi4j.api.util.Methods;
import org.qi4j.functional.Iterables;

import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ICONST_2;
import static org.objectweb.asm.Opcodes.ICONST_3;
import static org.objectweb.asm.Opcodes.ICONST_4;
import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Type.getInternalName;
import static org.qi4j.api.util.Classes.interfacesOf;

/**
 * Generate subclasses of classes used for transients. All methods delegate to CompositeInvoker.
 */
public class TransientClassLoader extends ClassLoader {
    private static int jdkVersion = Opcodes.V1_5;
    public static final String GENERATED_POSTFIX = "_Proxy";

    static {
        String jdkString = System.getProperty("java.specification.version");

        if (jdkString.equals("1.6")) {
            jdkVersion = Opcodes.V1_6;
        } else if (jdkString.equals("1.7")) {
            jdkVersion = Opcodes.V1_7;
        }
    }

    public TransientClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        if (name.endsWith(GENERATED_POSTFIX)) {
            Class baseClass = null;
            String baseName = name.substring(0, name.length() - 6);
            try {
                baseClass = loadClass(baseName);
            } catch (ClassNotFoundException e) {
                // Try replacing the last _ with $
                while (true) {
                    int idx = baseName.lastIndexOf("_");
                    if (idx != -1) {
                        baseName = baseName.substring(0, idx) + "$" + baseName.substring(idx + 1);
                        try {
                            baseClass = loadClass(baseName);
                            break;
                        } catch (ClassNotFoundException e1) {
                            // Try again
                        }
                    } else {
                        throw e;
                    }
                }
            }

            byte[] b = generateClass(name, baseClass);
            return defineClass(name, b, 0, b.length, baseClass.getProtectionDomain());
        }

        // Try the classloader of this classloader -> get classes in Qi4j such as CompositeInvoker
        return getClass().getClassLoader().loadClass(name);
    }

    public static byte[] generateClass(String name, Class baseClass) throws ClassNotFoundException {
        String classSlash = name.replace('.', '/');
        String baseClassSlash = getInternalName(baseClass);

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        // Class definition start
        cw.visit(jdkVersion, ACC_PUBLIC + ACC_SUPER, classSlash, null, baseClassSlash, null);

        // Composite reference
        {
            fv = cw.visitField(ACC_PUBLIC, "_instance", "Lorg/qi4j/api/composite/CompositeInvoker;", null, null);
            fv.visitEnd();
        }

        // Static Method references
        {
            int idx = 1;
            for (Method method : baseClass.getMethods()) {
                if (isOverloaded(method, baseClass)) {
                    fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "m" + idx++, "Ljava/lang/reflect/Method;", null,
                            null);
                    fv.visitEnd();
                }
            }
        }

        // Constructors
        for (Constructor constructor : baseClass.getDeclaredConstructors()) {
            if (Modifier.isPublic(constructor.getModifiers()) || Modifier.isProtected(constructor.getModifiers())) {
                String desc = org.objectweb.asm.commons.Method.getMethod(constructor).getDescriptor();
                mv = cw.visitMethod(ACC_PUBLIC, "<init>", desc, null, null);
                mv.visitCode();
                mv.visitVarInsn(ALOAD, 0);

                int idx = 1;
                for (Class aClass : constructor.getParameterTypes()) {
                    // TODO Handle other types than objects (?)
                    mv.visitVarInsn(ALOAD, idx++);
                }

                mv.visitMethodInsn(INVOKESPECIAL, baseClassSlash, "<init>", desc);
                mv.visitInsn(RETURN);
                mv.visitMaxs(idx, idx);
                mv.visitEnd();
            }
        }

        // Overloaded and unimplemented methods
        Method[] methods = baseClass.getMethods();
        int idx = 0;
        List<Label> exceptionLabels = new ArrayList<Label>();
        for (Method method : methods) {
            if (isOverloaded(method, baseClass)) {
                idx++;
                String methodName = method.getName();
                String desc = org.objectweb.asm.commons.Method.getMethod(method).getDescriptor();

                String[] exceptions = null;
                {
                    mv = cw.visitMethod(ACC_PUBLIC, methodName, desc, null, exceptions);
                    if (isInternalQi4jMethod(method, baseClass)) {
                        // generate a NoOp method...
                        mv.visitInsn(RETURN);
                    } else {
                        Label endLabel = null; // Use this if return type is void
                        if (method.getExceptionTypes().length > 0) {
                            exceptions = new String[method.getExceptionTypes().length];
                            for (int i = 0; i < method.getExceptionTypes().length; i++) {
                                Class<?> aClass = method.getExceptionTypes()[i];
                                exceptions[i] = getInternalName(aClass);
                            }
                        }
                        mv.visitCode();
                        Label l0 = new Label();
                        Label l1 = new Label();

                        exceptionLabels.clear();
                        for (Class<?> declaredException : method.getExceptionTypes()) {
                            Label ld = new Label();
                            mv.visitTryCatchBlock(l0, l1, ld, getInternalName(declaredException));
                            exceptionLabels.add(ld); // Reuse this further down for the catch
                        }

                        Label lruntime = new Label();
                        mv.visitTryCatchBlock(l0, l1, lruntime, "java/lang/RuntimeException");
                        Label lerror = new Label();
                        mv.visitTryCatchBlock(l0, l1, lerror, "java/lang/Throwable");

                        mv.visitLabel(l0);
                        mv.visitVarInsn(ALOAD, 0);
                        mv.visitFieldInsn(GETFIELD, classSlash, "_instance",
                                "Lorg/qi4j/api/composite/CompositeInvoker;");
                        mv.visitFieldInsn(GETSTATIC, classSlash, "m" + idx, "Ljava/lang/reflect/Method;");

                        int paramCount = method.getParameterTypes().length;
                        int stackIdx = 0;
                        if (paramCount == 0) {
                            // Send in null as parameter
                            mv.visitInsn(ACONST_NULL);
                        } else {
                            insn(mv, paramCount);
                            mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
                            int pidx = 0;
                            for (Class<?> aClass : method.getParameterTypes()) {
                                mv.visitInsn(DUP);
                                insn(mv, pidx++);
                                stackIdx = wrapParameter(mv, aClass, stackIdx + 1);
                                mv.visitInsn(AASTORE);
                            }
                        }

                        // Call method
                        mv.visitMethodInsn(INVOKEINTERFACE, "org/qi4j/api/composite/CompositeInvoker",
                                "invokeComposite",
                                "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");

                        // Return value
                        if (!method.getReturnType().equals(Void.TYPE)) {
                            unwrapResult(mv, method.getReturnType(), l1);
                        } else {
                            mv.visitInsn(POP);
                            mv.visitLabel(l1);
                            endLabel = new Label();
                            mv.visitJumpInsn(GOTO, endLabel);
                        }

                        // Increase stack to beyond method args
                        stackIdx++;

                        // Declared exceptions
                        int exceptionIdx = 0;
                        for (Class<?> aClass : method.getExceptionTypes()) {
                            mv.visitLabel(exceptionLabels.get(exceptionIdx++));
                            mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { getInternalName(aClass) });
                            mv.visitVarInsn(ASTORE, stackIdx);
                            mv.visitVarInsn(ALOAD, stackIdx);
                            mv.visitInsn(ATHROW);
                        }

                        // RuntimeException and Error catch-all
                        mv.visitLabel(lruntime);
                        mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/RuntimeException" });
                        mv.visitVarInsn(ASTORE, stackIdx);
                        mv.visitVarInsn(ALOAD, stackIdx);
                        mv.visitInsn(ATHROW);

                        mv.visitLabel(lerror);
                        mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });
                        mv.visitVarInsn(ASTORE, stackIdx);
                        mv.visitVarInsn(ALOAD, stackIdx);
                        mv.visitTypeInsn(CHECKCAST, "java/lang/Error");
                        mv.visitInsn(ATHROW);

                        // Return type = void
                        if (endLabel != null) {
                            mv.visitLabel(endLabel);
                            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
                            mv.visitInsn(RETURN);
                        }

                        mv.visitMaxs(0, 0);
                        mv.visitEnd();
                    }
                }

                if (!Modifier.isAbstract(method.getModifiers())) {
                    // Add method with _ as prefix
                    mv = cw.visitMethod(ACC_PUBLIC, "_" + method.getName(), desc, null, exceptions);
                    mv.visitCode();
                    mv.visitVarInsn(ALOAD, 0);

                    // Parameters
                    int stackIdx = 1;
                    for (Class<?> aClass : method.getParameterTypes()) {
                        stackIdx = loadParameter(mv, aClass, stackIdx) + 1;
                    }

                    // Call method
                    mv.visitMethodInsn(INVOKESPECIAL, baseClassSlash, method.getName(), desc);

                    // Return value
                    if (!method.getReturnType().equals(Void.TYPE)) {
                        returnResult(mv, method.getReturnType());
                    } else {
                        mv.visitInsn(RETURN);
                    }

                    mv.visitMaxs(1, 1);
                    mv.visitEnd();
                }
            }
        }

        // Class initializer
        {
            mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            Label l1 = new Label();
            Label l2 = new Label();
            mv.visitTryCatchBlock(l0, l1, l2, "java/lang/NoSuchMethodException");
            mv.visitLabel(l0);

            // Lookup methods and store in static variables
            int midx = 0;
            for (Method method : methods) {
                if (isOverloaded(method, baseClass)) {
                    method.setAccessible(true);
                    Class methodClass;
                    methodClass = method.getDeclaringClass();

                    midx++;

                    mv.visitLdcInsn(Type.getType(methodClass));
                    mv.visitLdcInsn(method.getName());
                    insn(mv, method.getParameterTypes().length);
                    mv.visitTypeInsn(ANEWARRAY, "java/lang/Class");

                    int pidx = 0;
                    for (Class<?> aClass : method.getParameterTypes()) {
                        mv.visitInsn(DUP);
                        insn(mv, pidx++);
                        type(mv, aClass);
                        mv.visitInsn(AASTORE);
                    }

                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod",
                            "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
                    mv.visitFieldInsn(PUTSTATIC, classSlash, "m" + midx, "Ljava/lang/reflect/Method;");
                }
            }

            mv.visitLabel(l1);
            Label l3 = new Label();
            mv.visitJumpInsn(GOTO, l3);
            mv.visitLabel(l2);
            mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/NoSuchMethodException" });
            mv.visitVarInsn(ASTORE, 0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/NoSuchMethodException", "printStackTrace", "()V");
            mv.visitLabel(l3);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            mv.visitInsn(RETURN);
            mv.visitMaxs(6, 1);
            mv.visitEnd();
        }

        cw.visitEnd();

        return cw.toByteArray();
    }

    private static boolean isOverloaded(Method method, Class baseClass) {
        if (Modifier.isFinal(method.getModifiers())) {
            return false; // Cannot override final methods
        } else {
            return true;
        }
    }

    private static boolean isInternalQi4jMethod(Method method, Class baseClass) {
        return isDeclaredIn(method, Initializable.class, baseClass)
                || isDeclaredIn(method, Lifecycle.class, baseClass);
    }

    private static boolean isDeclaredIn(Method method, Class clazz, Class baseClass) {
        if (!clazz.isAssignableFrom(baseClass)) {
            return false;
        }

        try {
            clazz.getMethod(method.getName(), method.getParameterTypes());
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private static Class getInterfaceMethodDeclaration(Method method, Class clazz) throws NoSuchMethodException {
        Iterable<Class<?>> interfaces = Iterables.map(Classes.RAW_CLASS, interfacesOf(clazz));
        for (Class<?> anInterface : interfaces) {
            try {
                anInterface.getMethod(method.getName(), method.getParameterTypes());
                return anInterface;
            } catch (NoSuchMethodException e) {
                // Try next
            }
        }

        throw new NoSuchMethodException(method.getName());
    }

    private static boolean isInterfaceMethod(Method method, Class baseClass) {
        for (Class aClass : Iterables.filter(Methods.HAS_METHODS,
                Iterables.map(Classes.RAW_CLASS, interfacesOf(baseClass)))) {
            try {
                Method m = aClass.getMethod(method.getName(), method.getParameterTypes());
                m.setAccessible(true);
                return true;
            } catch (NoSuchMethodException e) {
                // Ignore
            }
        }
        return false;
    }

    private static void type(MethodVisitor mv, Class<?> aClass) {
        if (aClass.equals(Integer.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Long.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Short.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Byte.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Double.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Float.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Boolean.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
        } else if (aClass.equals(Character.TYPE)) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;");
        } else {
            mv.visitLdcInsn(Type.getType(aClass));
        }
    }

    private static int wrapParameter(MethodVisitor mv, Class<?> aClass, int idx) {
        if (aClass.equals(Integer.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
        } else if (aClass.equals(Long.TYPE)) {
            mv.visitVarInsn(LLOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
            idx++; // Extra jump
        } else if (aClass.equals(Short.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
        } else if (aClass.equals(Byte.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
        } else if (aClass.equals(Double.TYPE)) {
            mv.visitVarInsn(DLOAD, idx);
            idx++; // Extra jump
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
        } else if (aClass.equals(Float.TYPE)) {
            mv.visitVarInsn(FLOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
        } else if (aClass.equals(Boolean.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
        } else if (aClass.equals(Character.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
        } else {
            mv.visitVarInsn(ALOAD, idx);
        }

        return idx;
    }

    private static void unwrapResult(MethodVisitor mv, Class<?> aClass, Label label) {
        if (aClass.equals(Integer.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
            mv.visitLabel(label);
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Long.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
            mv.visitLabel(label);
            mv.visitInsn(LRETURN);
        } else if (aClass.equals(Short.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
            mv.visitLabel(label);
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Byte.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
            mv.visitLabel(label);
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Double.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
            mv.visitLabel(label);
            mv.visitInsn(DRETURN);
        } else if (aClass.equals(Float.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
            mv.visitLabel(label);
            mv.visitInsn(FRETURN);
        } else if (aClass.equals(Boolean.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
            mv.visitLabel(label);
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Character.TYPE)) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
            mv.visitLabel(label);
            mv.visitInsn(IRETURN);
        } else {
            mv.visitTypeInsn(CHECKCAST, getInternalName(aClass));
            mv.visitLabel(label);
            mv.visitInsn(ARETURN);
        }
    }

    private static int loadParameter(MethodVisitor mv, Class<?> aClass, int idx) {
        if (aClass.equals(Integer.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
        } else if (aClass.equals(Long.TYPE)) {
            mv.visitVarInsn(LLOAD, idx);
            idx++; // Extra jump
        } else if (aClass.equals(Short.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
        } else if (aClass.equals(Byte.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
        } else if (aClass.equals(Double.TYPE)) {
            mv.visitVarInsn(DLOAD, idx);
            idx++; // Extra jump
        } else if (aClass.equals(Float.TYPE)) {
            mv.visitVarInsn(FLOAD, idx);
        } else if (aClass.equals(Boolean.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
        } else if (aClass.equals(Character.TYPE)) {
            mv.visitVarInsn(ILOAD, idx);
        } else {
            mv.visitVarInsn(ALOAD, idx);
        }

        return idx;
    }

    private static void returnResult(MethodVisitor mv, Class<?> aClass) {
        if (aClass.equals(Integer.TYPE)) {
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Long.TYPE)) {
            mv.visitInsn(LRETURN);
        } else if (aClass.equals(Short.TYPE)) {
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Byte.TYPE)) {
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Double.TYPE)) {
            mv.visitInsn(DRETURN);
        } else if (aClass.equals(Float.TYPE)) {
            mv.visitInsn(FRETURN);
        } else if (aClass.equals(Boolean.TYPE)) {
            mv.visitInsn(IRETURN);
        } else if (aClass.equals(Character.TYPE)) {
            mv.visitInsn(IRETURN);
        } else {
            mv.visitTypeInsn(CHECKCAST, getInternalName(aClass));
            mv.visitInsn(ARETURN);
        }
    }

    private static void insn(MethodVisitor mv, int length) {
        switch (length) {
        case 0:
            mv.visitInsn(ICONST_0);
            return;
        case 1:
            mv.visitInsn(ICONST_1);
            return;
        case 2:
            mv.visitInsn(ICONST_2);
            return;
        case 3:
            mv.visitInsn(ICONST_3);
            return;
        case 4:
            mv.visitInsn(ICONST_4);
            return;
        case 5:
            mv.visitInsn(ICONST_5);
            return;
        default:
            mv.visitIntInsn(BIPUSH, length);
        }
    }

    public static boolean isGenerated(Class clazz) {
        return clazz.getName().endsWith(GENERATED_POSTFIX);
    }

    public static boolean isGenerated(Object object) {
        return object.getClass().getName().endsWith(GENERATED_POSTFIX);
    }

    public Class loadFragmentClass(Class fragmentClass) throws ClassNotFoundException {
        return loadClass(fragmentClass.getName().replace('$', '_') + GENERATED_POSTFIX);
    }

    public static Class getSourceClass(Class fragmentClass) {
        return fragmentClass.getName().endsWith(GENERATED_POSTFIX) ? fragmentClass.getSuperclass() : fragmentClass;
    }
}