| /* |
| * 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.apache.zest.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.apache.zest.api.entity.Lifecycle; |
| import org.apache.zest.api.mixin.Initializable; |
| import org.apache.zest.api.util.Classes; |
| import org.apache.zest.api.util.Methods; |
| import org.apache.zest.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.apache.zest.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; |
| } |
| } |