Repository: zest-java Updated Branches: refs/heads/master a2d2100be -> a789141d5
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/runtime/src/main/java/org/qi4j/runtime/composite/FragmentClassLoader.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/composite/FragmentClassLoader.java b/core/runtime/src/main/java/org/qi4j/runtime/composite/FragmentClassLoader.java new file mode 100644 index 0000000..4cf4439 --- /dev/null +++ b/core/runtime/src/main/java/org/qi4j/runtime/composite/FragmentClassLoader.java @@ -0,0 +1,852 @@ +/* + * 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.ClassWriter; +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 mixins/modifiers that implement all interfaces not in the class itself + * and which delegates those calls to a given composite invoker. + */ +@SuppressWarnings( "raw" ) +public class FragmentClassLoader + extends ClassLoader +{ + private static final int JDK_VERSION; + public static final String GENERATED_POSTFIX = "_Stub"; + + static + { + String jdkString = System.getProperty( "java.specification.version" ); + switch( jdkString ) + { + case "1.8": + JDK_VERSION = Opcodes.V1_8; + break; + case "1.7": + default: + JDK_VERSION = Opcodes.V1_7; + break; + } + } + + public FragmentClassLoader( ClassLoader parent ) + { + super( parent ); + } + + @Override + protected Class findClass( String name ) + throws ClassNotFoundException + { + if( name.endsWith( GENERATED_POSTFIX ) ) + { + Class baseClass; + String baseName = name.substring( 0, name.length() - 5 ); + 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; + } + } + } +// To Allow JDK classes to be composed. + if( name.startsWith( "java." )) + name = "qi4j." + name; + + byte[] b = generateClass( name, baseClass ); + return defineClass( name, b, 0, b.length, baseClass.getProtectionDomain() ); + } + + // Try the classloader of this classloader -> get classes in Zest 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 ); + + // Class definition start + cw.visit( JDK_VERSION, ACC_PUBLIC + ACC_SUPER, classSlash, null, baseClassSlash, null ); + + // Composite reference + { + cw.visitField( ACC_PUBLIC, "_instance", "Lorg/qi4j/api/composite/CompositeInvoker;", null, null ).visitEnd(); + } + + // Static Method references + boolean hasProxyMethods = false; + { + int idx = 1; + for( Method method : baseClass.getMethods() ) + { + if( isOverridden(method, baseClass) ) + { + cw.visitField( ACC_PRIVATE + ACC_STATIC, "m" + idx++, "Ljava/lang/reflect/Method;", null, + null ).visitEnd(); + hasProxyMethods = true; + } + } + } + + // 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(); + MethodVisitor cmv = cw.visitMethod( ACC_PUBLIC, "<init>", desc, null, null ); + cmv.visitCode(); + cmv.visitVarInsn(ALOAD, 0); + + int idx = 1; + for( Class aClass : constructor.getParameterTypes() ) + { + final int opcode; + if (aClass.equals(Integer.TYPE)) { + opcode = ILOAD; + } else if (aClass.equals(Long.TYPE)) { + opcode = LLOAD; + } else if (aClass.equals(Float.TYPE)) { + opcode = FLOAD; + } else if (aClass.equals(Double.TYPE)) { + opcode = DLOAD; + } else { + opcode = ALOAD; + } + cmv.visitVarInsn(opcode, idx++); + } + + cmv.visitMethodInsn(INVOKESPECIAL, baseClassSlash, "<init>", desc, false); + cmv.visitInsn(RETURN); + cmv.visitMaxs(idx, idx); + cmv.visitEnd(); + } + } + + + // Overloaded and unimplemented methods + if( hasProxyMethods ) + { + Method[] methods = baseClass.getMethods(); + int idx = 0; + List<Label> exceptionLabels = new ArrayList<>(); + for( Method method : methods ) + { + if( isOverridden(method, baseClass) ) + { + idx++; + String methodName = method.getName(); + String desc = org.objectweb.asm.commons.Method.getMethod( method ).getDescriptor(); + + String[] exceptions = null; + { + MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, methodName, desc, null, null ); + 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;", true ); + + // 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 + MethodVisitor mv; + 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, false ); + + // Return value + if( !method.getReturnType().equals( Void.TYPE ) ) + { + returnResult( mv, method.getReturnType() ); + } + else + { + mv.visitInsn( RETURN ); + } + + mv.visitMaxs( 1, 1 ); + mv.visitEnd(); + } + } + } + + // Class initializer + { + MethodVisitor mv; + 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( isOverridden(method, baseClass) ) + { + method.setAccessible( true ); + Class methodClass; + if( Modifier.isAbstract( method.getModifiers() ) ) + { + methodClass = method.getDeclaringClass(); + } + else + { + try + { + methodClass = getInterfaceMethodDeclaration( method, + baseClass ); // Overridden method lookup + } + catch( NoSuchMethodException e ) + { + throw new ClassNotFoundException( name, e ); + } + } + + 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;", false ); + 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", false ); + 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 isOverridden(Method method, Class baseClass) + { + if( Modifier.isAbstract( method.getModifiers() ) ) + { + return true; // Implement all abstract methods + } + + if( Modifier.isFinal( method.getModifiers() ) ) + { + return false; // Cannot override final methods + } + + if( isInterfaceMethod( method, baseClass ) ) + { + // if() used for clarity. + //noinspection RedundantIfStatement + if( isInternalQi4jMethod( method, baseClass ) ) + { + return false; // Skip methods in Zest-internal interfaces + } + else + { + return true; + } + } + else + { + return false; + } + } + + 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;", false ); + } + else if( aClass.equals( Long.TYPE ) ) + { + mv.visitVarInsn( LLOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false ); + idx++; // Extra jump + } + else if( aClass.equals( Short.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false ); + } + else if( aClass.equals( Byte.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false ); + } + else if( aClass.equals( Double.TYPE ) ) + { + mv.visitVarInsn( DLOAD, idx ); + idx++; // Extra jump + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false ); + } + else if( aClass.equals( Float.TYPE ) ) + { + mv.visitVarInsn( FLOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false ); + } + else if( aClass.equals( Boolean.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false ); + } + else if( aClass.equals( Character.TYPE ) ) + { + mv.visitVarInsn( ILOAD, idx ); + mv.visitMethodInsn( INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false ); + } + 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", false ); + 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", false ); + 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", false ); + 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", false ); + 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", false ); + 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", false ); + 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", false ); + 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", false ); + 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; + } +}
