1 package org.robolectric.util.reflector;
2 
3 import static org.objectweb.asm.Opcodes.ACC_FINAL;
4 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
5 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
6 import static org.objectweb.asm.Opcodes.ACC_STATIC;
7 import static org.objectweb.asm.Opcodes.ACC_SUPER;
8 import static org.objectweb.asm.Opcodes.V1_5;
9 
10 import java.lang.annotation.Annotation;
11 import java.lang.reflect.AccessibleObject;
12 import java.lang.reflect.Field;
13 import java.lang.reflect.InvocationTargetException;
14 import java.lang.reflect.Method;
15 import java.lang.reflect.Modifier;
16 import java.util.HashSet;
17 import java.util.Set;
18 import javax.annotation.Nullable;
19 import org.objectweb.asm.ClassWriter;
20 import org.objectweb.asm.Label;
21 import org.objectweb.asm.MethodVisitor;
22 import org.objectweb.asm.Opcodes;
23 import org.objectweb.asm.Type;
24 import org.objectweb.asm.commons.GeneratorAdapter;
25 
26 @SuppressWarnings("NewApi")
27 class ReflectorClassWriter extends ClassWriter {
28 
29   private static final Type OBJECT_TYPE = Type.getType(Object.class);
30   private static final Type CLASS_TYPE = Type.getType(Class.class);
31   private static final Type FIELD_TYPE = Type.getType(Field.class);
32   private static final Type METHOD_TYPE = Type.getType(Method.class);
33   private static final Type CONSTRUCTOR_TYPE = Type.getType(java.lang.reflect.Constructor.class);
34 
35   private static final Type STRING_TYPE = Type.getType(String.class);
36   private static final Type STRINGBUILDER_TYPE = Type.getType(StringBuilder.class);
37 
38   private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
39   private static final Type ASSERTION_ERROR_TYPE = Type.getType(AssertionError.class);
40 
41   private static final Type INVOCATION_TARGET_EXCEPTION_TYPE =
42       Type.getType(InvocationTargetException.class);
43 
44   private static final Type REFLECTIVE_OPERATION_EXCEPTION_TYPE =
45       Type.getType(ReflectiveOperationException.class);
46 
47   private static final org.objectweb.asm.commons.Method CLASS$GET_DECLARED_FIELD =
48       findMethod(Class.class, "getDeclaredField", new Class<?>[] {String.class});
49   private static final org.objectweb.asm.commons.Method CLASS$GET_DECLARED_METHOD =
50       findMethod(Class.class, "getDeclaredMethod", new Class<?>[] {String.class, Class[].class});
51   private static final org.objectweb.asm.commons.Method CLASS$GET_DECLARED_CONSTRUCTOR =
52       findMethod(Class.class, "getDeclaredConstructor", new Class<?>[] {Class[].class});
53   private static final org.objectweb.asm.commons.Method ACCESSIBLE_OBJECT$SET_ACCESSIBLE =
54       findMethod(AccessibleObject.class, "setAccessible", new Class<?>[] {boolean.class});
55   private static final org.objectweb.asm.commons.Method FIELD$GET =
56       findMethod(Field.class, "get", new Class<?>[] {Object.class});
57   private static final org.objectweb.asm.commons.Method FIELD$SET =
58       findMethod(Field.class, "set", new Class<?>[] {Object.class, Object.class});
59   private static final org.objectweb.asm.commons.Method METHOD$INVOKE =
60       findMethod(Method.class, "invoke", new Class<?>[] {Object.class, Object[].class});
61   private static final org.objectweb.asm.commons.Method CONSTRUCTOR$NEWINSTANCE =
62       findMethod(
63           java.lang.reflect.Constructor.class, "newInstance", new Class<?>[] {Object[].class});
64   private static final org.objectweb.asm.commons.Method THROWABLE$GET_CAUSE =
65       findMethod(Throwable.class, "getCause", new Class<?>[] {});
66   private static final org.objectweb.asm.commons.Method OBJECT_INIT =
67       new org.objectweb.asm.commons.Method("<init>", Type.VOID_TYPE, new Type[0]);
68   private static final org.objectweb.asm.commons.Method STRINGBUILDER$APPEND =
69       findMethod(StringBuilder.class, "append", new Class<?>[] {String.class});
70   private static final org.objectweb.asm.commons.Method STRINGBUILDER$TO_STRING =
71       findMethod(StringBuilder.class, "toString", new Class<?>[] {});
72   private static final org.objectweb.asm.commons.Method CLASS$GET_CLASS_LOADER =
73       findMethod(Class.class, "getClassLoader", new Class<?>[] {});
74   private static final org.objectweb.asm.commons.Method STRING$VALUE_OF =
75       findMethod(String.class, "valueOf", new Class<?>[] {Object.class});
76   private static final org.objectweb.asm.commons.Method ASSERTION_ERROR_INIT =
77       new org.objectweb.asm.commons.Method(
78           "<init>", Type.VOID_TYPE, new Type[] {STRING_TYPE, THROWABLE_TYPE});
79   private static final String TARGET_FIELD = "__target__";
80 
findMethod( Class<?> clazz, String methodName, Class<?>[] paramTypes)81   private static org.objectweb.asm.commons.Method findMethod(
82       Class<?> clazz, String methodName, Class<?>[] paramTypes) {
83     try {
84       return asmMethod(clazz.getMethod(methodName, paramTypes));
85     } catch (NoSuchMethodException e) {
86       throw new AssertionError(e);
87     }
88   }
89 
90   private final Class<?> iClass;
91   private final Type iType;
92   private final Type reflectorType;
93   private final Type targetType;
94   private final boolean directModifier;
95 
96   private int nextMethodNumber = 0;
97   private final Set<String> fieldRefs = new HashSet<>();
98 
ReflectorClassWriter(Class<?> iClass, Class<?> targetClass, String reflectorName)99   ReflectorClassWriter(Class<?> iClass, Class<?> targetClass, String reflectorName) {
100     super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
101 
102     this.iClass = iClass;
103     iType = Type.getType(iClass);
104     reflectorType = asType(reflectorName);
105     targetType = Type.getType(targetClass);
106 
107     ForType forType = iClass.getAnnotation(ForType.class);
108     directModifier = forType != null && forType.direct();
109   }
110 
write()111   void write() {
112     int accessModifiers = iClass.getModifiers() & Modifier.PUBLIC;
113     visit(
114         V1_5,
115         accessModifiers | ACC_SUPER | ACC_FINAL,
116         reflectorType.getInternalName(),
117         null,
118         OBJECT_TYPE.getInternalName(),
119         new String[] {iType.getInternalName()});
120 
121     writeTargetField();
122 
123     writeConstructor();
124 
125     for (Method method : iClass.getMethods()) {
126       if (method.isDefault()) continue;
127 
128       Accessor accessor = method.getAnnotation(Accessor.class);
129       Constructor constructor = method.getAnnotation(Constructor.class);
130       if (accessor != null) {
131         new AccessorMethodWriter(method, accessor).write();
132       } else if (constructor != null) {
133         new ConstructorMethodWriter(method).write();
134       } else {
135         new ReflectorMethodWriter(method).write();
136       }
137     }
138 
139     visitEnd();
140   }
141 
writeTargetField()142   private void writeTargetField() {
143     visitField(ACC_PRIVATE, TARGET_FIELD, targetType.getDescriptor(), null, null);
144   }
145 
writeConstructor()146   private void writeConstructor() {
147     org.objectweb.asm.commons.Method initMethod =
148         new org.objectweb.asm.commons.Method("<init>", Type.VOID_TYPE, new Type[] {targetType});
149 
150     GeneratorAdapter init = new GeneratorAdapter(ACC_PUBLIC, initMethod, null, null, this);
151     init.loadThis();
152     init.invokeConstructor(OBJECT_TYPE, OBJECT_INIT);
153 
154     init.loadThis();
155     init.loadArg(0);
156     init.putField(reflectorType, TARGET_FIELD, targetType);
157 
158     init.returnValue();
159     init.endMethod();
160   }
161 
162   /** Generates bytecode for a setter or getter method. */
163   private class AccessorMethodWriter extends BaseAdapter {
164 
165     private final String targetFieldName;
166     private final String fieldRefName;
167     private final boolean isSetter;
168 
AccessorMethodWriter(Method method, Accessor accessor)169     private AccessorMethodWriter(Method method, Accessor accessor) {
170       super(method);
171 
172       targetFieldName = accessor.value();
173       this.fieldRefName = "field$" + targetFieldName;
174 
175       String methodName = method.getName();
176       if (methodName.startsWith("get")) {
177         if (method.getReturnType().equals(void.class)) {
178           throw new IllegalArgumentException(method + " should have a non-void return type");
179         }
180         if (method.getParameterCount() != 0) {
181           throw new IllegalArgumentException(method + " should take no parameters");
182         }
183         isSetter = false;
184       } else if (methodName.startsWith("set")) {
185         if (!method.getReturnType().equals(void.class)) {
186           throw new IllegalArgumentException(method + " should have a void return type");
187         }
188         if (method.getParameterCount() != 1) {
189           throw new IllegalArgumentException(method + " should take a single parameter");
190         }
191         isSetter = true;
192       } else {
193         throw new IllegalArgumentException(
194             methodName + " doesn't appear to be a setter or a getter");
195       }
196     }
197 
write()198     void write() {
199       // write our field to hold target field reference (but just once)...
200       if (fieldRefs.add(targetFieldName)) {
201         visitField(ACC_PRIVATE | ACC_STATIC, fieldRefName, FIELD_TYPE.getDescriptor(), null, null);
202       }
203 
204       visitCode();
205 
206       if (isSetter) {
207         // pseudocode:
208         //   field_x.set(this, arg0);
209         loadFieldRef();
210         loadTarget();
211         loadArg(0);
212         Class<?> parameterType = iMethod.getParameterTypes()[0];
213         if (parameterType.isPrimitive()) {
214           box(Type.getType(parameterType));
215         }
216         invokeVirtual(FIELD_TYPE, FIELD$SET);
217         returnValue();
218       } else { // getter
219         // pseudocode:
220         //   return field_x.get(this);
221         loadFieldRef();
222         loadTarget();
223         invokeVirtual(FIELD_TYPE, FIELD$GET);
224 
225         castForReturn(iMethod.getReturnType());
226         returnValue();
227       }
228 
229       endMethod();
230     }
231 
loadFieldRef()232     private void loadFieldRef() {
233       // pseudocode:
234       //   if (field$x == null) {
235       //     field$x = targetClass.getDeclaredField(name);
236       //     field$x.setAccessible(true);
237       //   }
238       // -> field reference on stack
239       getStatic(reflectorType, fieldRefName, FIELD_TYPE);
240       dup();
241       Label haveMethodRef = newLabel();
242       ifNonNull(haveMethodRef);
243       pop();
244 
245       // pseudocode:
246       //   targetClass.getDeclaredField(name);
247       push(targetType);
248       push(targetFieldName);
249       invokeVirtual(CLASS_TYPE, CLASS$GET_DECLARED_FIELD);
250 
251       // pseudocode:
252       //   <field>.setAccessible(true);
253       dup();
254       push(true);
255       invokeVirtual(FIELD_TYPE, ACCESSIBLE_OBJECT$SET_ACCESSIBLE);
256 
257       // pseudocode:
258       //   field$x = method;
259       dup();
260       putStatic(reflectorType, fieldRefName, FIELD_TYPE);
261       mark(haveMethodRef);
262     }
263   }
264 
265   private class ConstructorMethodWriter extends BaseAdapter {
266 
267     private final String constructorRefName;
268     private final Type[] targetParamTypes;
269 
ConstructorMethodWriter(Method method)270     private ConstructorMethodWriter(Method method) {
271       super(method);
272       int myMethodNumber = nextMethodNumber++;
273       this.constructorRefName = "constructor" + myMethodNumber;
274       this.targetParamTypes = resolveParamTypes(iMethod);
275     }
276 
write()277     void write() {
278       // write field to hold method reference...
279       visitField(
280           ACC_PRIVATE | ACC_STATIC,
281           constructorRefName,
282           CONSTRUCTOR_TYPE.getDescriptor(),
283           null,
284           null);
285 
286       visitCode();
287 
288       // pseudocode:
289       //   try {
290       //     return constructorN.newInstance(*args);
291       //   } catch (InvocationTargetException e) {
292       //     throw e.getCause();
293       //   } catch (ReflectiveOperationException e) {
294       //     throw new AssertionError("Error invoking reflector method in ClassLoader " +
295       // Instrumentation.class.getClassLoader(), e);
296       //   }
297       Label tryStart = new Label();
298       Label tryEnd = new Label();
299       Label handleInvocationTargetException = new Label();
300       visitTryCatchBlock(
301           tryStart,
302           tryEnd,
303           handleInvocationTargetException,
304           INVOCATION_TARGET_EXCEPTION_TYPE.getInternalName());
305       Label handleReflectiveOperationException = new Label();
306       visitTryCatchBlock(
307           tryStart,
308           tryEnd,
309           handleReflectiveOperationException,
310           REFLECTIVE_OPERATION_EXCEPTION_TYPE.getInternalName());
311 
312       mark(tryStart);
313       loadOriginalConstructorRef();
314       loadArgArray();
315       invokeVirtual(CONSTRUCTOR_TYPE, CONSTRUCTOR$NEWINSTANCE);
316       mark(tryEnd);
317 
318       castForReturn(iMethod.getReturnType());
319       returnValue();
320 
321       mark(handleInvocationTargetException);
322 
323       int exceptionLocalVar = newLocal(THROWABLE_TYPE);
324       storeLocal(exceptionLocalVar);
325       loadLocal(exceptionLocalVar);
326       invokeVirtual(THROWABLE_TYPE, THROWABLE$GET_CAUSE);
327       throwException();
328       mark(handleReflectiveOperationException);
329       exceptionLocalVar = newLocal(REFLECTIVE_OPERATION_EXCEPTION_TYPE);
330       storeLocal(exceptionLocalVar);
331       newInstance(STRINGBUILDER_TYPE);
332       dup();
333       invokeConstructor(STRINGBUILDER_TYPE, OBJECT_INIT);
334       push("Error invoking reflector method in ClassLoader ");
335       invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER$APPEND);
336       push(targetType);
337       invokeVirtual(CLASS_TYPE, CLASS$GET_CLASS_LOADER);
338       invokeStatic(STRING_TYPE, STRING$VALUE_OF);
339       invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER$APPEND);
340       invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER$TO_STRING);
341       int messageLocalVar = newLocal(STRING_TYPE);
342       storeLocal(messageLocalVar);
343       newInstance(ASSERTION_ERROR_TYPE);
344       dup();
345       loadLocal(messageLocalVar);
346       loadLocal(exceptionLocalVar);
347       invokeConstructor(ASSERTION_ERROR_TYPE, ASSERTION_ERROR_INIT);
348       throwException();
349 
350       endMethod();
351     }
352 
loadOriginalConstructorRef()353     private void loadOriginalConstructorRef() {
354       // pseudocode:
355       //   if (constructorN == null) {
356       //     constructorN = targetClass.getDeclaredConstructor(paramTypes);
357       //     constructorN.setAccessible(true);
358       //   }
359       // -> constructor reference on stack
360       getStatic(reflectorType, constructorRefName, CONSTRUCTOR_TYPE);
361       dup();
362       Label haveConstructorRef = newLabel();
363       ifNonNull(haveConstructorRef);
364       pop();
365 
366       // pseudocode:
367       //   targetClass.getDeclaredConstructor(paramTypes);
368       push(targetType);
369       Type[] paramTypes = targetParamTypes;
370       push(paramTypes.length);
371       newArray(CLASS_TYPE);
372       for (int i = 0; i < paramTypes.length; i++) {
373         dup();
374         push(i);
375         push(paramTypes[i]);
376         arrayStore(CLASS_TYPE);
377       }
378       invokeVirtual(CLASS_TYPE, CLASS$GET_DECLARED_CONSTRUCTOR);
379 
380       // pseudocode:
381       //   <constructor>.setAccessible(true);
382       dup();
383       push(true);
384       invokeVirtual(CONSTRUCTOR_TYPE, ACCESSIBLE_OBJECT$SET_ACCESSIBLE);
385 
386       // pseudocode:
387       //   constructorN = constructor;
388       dup();
389       putStatic(reflectorType, constructorRefName, CONSTRUCTOR_TYPE);
390       mark(haveConstructorRef);
391     }
392   }
393 
394   private class ReflectorMethodWriter extends BaseAdapter {
395 
396     private final String methodRefName;
397     private final Type[] targetParamTypes;
398 
ReflectorMethodWriter(Method method)399     private ReflectorMethodWriter(Method method) {
400       super(method);
401       int myMethodNumber = nextMethodNumber++;
402       this.methodRefName = "method" + myMethodNumber;
403       this.targetParamTypes = resolveParamTypes(iMethod);
404     }
405 
write()406     void write() {
407       // write field to hold method reference...
408       visitField(ACC_PRIVATE | ACC_STATIC, methodRefName, METHOD_TYPE.getDescriptor(), null, null);
409 
410       visitCode();
411 
412       // pseudocode:
413       //   try {
414       //     return methodN.invoke(this, *args);
415       //   } catch (InvocationTargetException e) {
416       //     throw e.getCause();
417       //   } catch (ReflectiveOperationException e) {
418       //     throw new AssertionError("Error invoking reflector method in ClassLoader " +
419       // Instrumentation.class.getClassLoader(), e);
420       //   }
421       Label tryStart = new Label();
422       Label tryEnd = new Label();
423       Label handleInvocationTargetException = new Label();
424       visitTryCatchBlock(
425           tryStart,
426           tryEnd,
427           handleInvocationTargetException,
428           INVOCATION_TARGET_EXCEPTION_TYPE.getInternalName());
429       Label handleReflectiveOperationException = new Label();
430       visitTryCatchBlock(
431           tryStart,
432           tryEnd,
433           handleReflectiveOperationException,
434           REFLECTIVE_OPERATION_EXCEPTION_TYPE.getInternalName());
435 
436       mark(tryStart);
437       loadOriginalMethodRef();
438       loadTarget();
439       loadArgArray();
440       invokeVirtual(METHOD_TYPE, METHOD$INVOKE);
441       mark(tryEnd);
442 
443       castForReturn(iMethod.getReturnType());
444       returnValue();
445 
446       mark(handleInvocationTargetException);
447 
448       int exceptionLocalVar = newLocal(THROWABLE_TYPE);
449       storeLocal(exceptionLocalVar);
450       loadLocal(exceptionLocalVar);
451       invokeVirtual(THROWABLE_TYPE, THROWABLE$GET_CAUSE);
452       throwException();
453       mark(handleReflectiveOperationException);
454       exceptionLocalVar = newLocal(REFLECTIVE_OPERATION_EXCEPTION_TYPE);
455       storeLocal(exceptionLocalVar);
456       newInstance(STRINGBUILDER_TYPE);
457       dup();
458       invokeConstructor(STRINGBUILDER_TYPE, OBJECT_INIT);
459       push("Error invoking reflector method in ClassLoader ");
460       invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER$APPEND);
461       push(targetType);
462       invokeVirtual(CLASS_TYPE, CLASS$GET_CLASS_LOADER);
463       invokeStatic(STRING_TYPE, STRING$VALUE_OF);
464       invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER$APPEND);
465       invokeVirtual(STRINGBUILDER_TYPE, STRINGBUILDER$TO_STRING);
466       int messageLocalVar = newLocal(STRING_TYPE);
467       storeLocal(messageLocalVar);
468       newInstance(ASSERTION_ERROR_TYPE);
469       dup();
470       loadLocal(messageLocalVar);
471       loadLocal(exceptionLocalVar);
472       invokeConstructor(ASSERTION_ERROR_TYPE, ASSERTION_ERROR_INIT);
473       throwException();
474 
475       endMethod();
476     }
477 
loadOriginalMethodRef()478     private void loadOriginalMethodRef() {
479       // pseudocode:
480       //   if (methodN == null) {
481       //     methodN = targetClass.getDeclaredMethod(name, paramTypes);
482       //     methodN.setAccessible(true);
483       //   }
484       // -> method reference on stack
485       getStatic(reflectorType, methodRefName, METHOD_TYPE);
486       dup();
487       Label haveMethodRef = newLabel();
488       ifNonNull(haveMethodRef);
489       pop();
490 
491       // pseudocode:
492       //   targetClass.getDeclaredMethod(name, paramTypes);
493       push(targetType);
494       push(getMethodName());
495       Type[] paramTypes = targetParamTypes;
496       push(paramTypes.length);
497       newArray(CLASS_TYPE);
498       for (int i = 0; i < paramTypes.length; i++) {
499         dup();
500         push(i);
501         push(paramTypes[i]);
502         arrayStore(CLASS_TYPE);
503       }
504       invokeVirtual(CLASS_TYPE, CLASS$GET_DECLARED_METHOD);
505 
506       // pseudocode:
507       //   <method>.setAccessible(true);
508       dup();
509       push(true);
510       invokeVirtual(METHOD_TYPE, ACCESSIBLE_OBJECT$SET_ACCESSIBLE);
511 
512       // pseudocode:
513       //   methodN = method;
514       dup();
515       putStatic(reflectorType, methodRefName, METHOD_TYPE);
516       mark(haveMethodRef);
517     }
518   }
519 
getInternalNames(final Class<?>[] types)520   private static String[] getInternalNames(final Class<?>[] types) {
521     if (types == null) {
522       return null;
523     }
524     String[] names = new String[types.length];
525     for (int i = 0; i < names.length; ++i) {
526       names[i] = Type.getType(types[i]).getInternalName();
527     }
528     return names;
529   }
530 
asType(String reflectorName)531   private Type asType(String reflectorName) {
532     return Type.getType("L" + reflectorName.replace('.', '/') + ";");
533   }
534 
asmMethod(Method method)535   private static org.objectweb.asm.commons.Method asmMethod(Method method) {
536     return org.objectweb.asm.commons.Method.getMethod(method);
537   }
538 
539   /** Hide ugly constructor chaining. */
540   private class BaseAdapter extends GeneratorAdapter {
541     final Method iMethod;
542 
BaseAdapter(Method method)543     BaseAdapter(Method method) {
544       this(org.objectweb.asm.commons.Method.getMethod(method), method);
545     }
546 
BaseAdapter(org.objectweb.asm.commons.Method asmMethod, Method method)547     private BaseAdapter(org.objectweb.asm.commons.Method asmMethod, Method method) {
548       this(
549           method,
550           asmMethod,
551           ReflectorClassWriter.this.visitMethod(
552               Opcodes.ACC_PUBLIC,
553               asmMethod.getName(),
554               asmMethod.getDescriptor(),
555               null,
556               ReflectorClassWriter.getInternalNames(method.getExceptionTypes())));
557     }
558 
BaseAdapter( Method method, org.objectweb.asm.commons.Method asmMethod, MethodVisitor methodVisitor)559     private BaseAdapter(
560         Method method, org.objectweb.asm.commons.Method asmMethod, MethodVisitor methodVisitor) {
561       super(
562           Opcodes.ASM6,
563           methodVisitor,
564           Opcodes.ACC_PUBLIC,
565           asmMethod.getName(),
566           asmMethod.getDescriptor());
567 
568       this.iMethod = method;
569     }
570 
loadTarget()571     void loadTarget() {
572       if (isAnnotatedStatic()) {
573         loadNull();
574       } else {
575         loadThis();
576         getField(reflectorType, TARGET_FIELD, targetType);
577       }
578     }
579 
castForReturn(Class<?> returnType)580     void castForReturn(Class<?> returnType) {
581       if (returnType.isPrimitive()) {
582         unbox(Type.getType(returnType));
583       } else {
584         checkCast(Type.getType(returnType));
585       }
586     }
587 
getMethodName()588     String getMethodName() {
589       String methodName = iMethod.getName();
590       if (iMethod.isAnnotationPresent(Direct.class) || directModifier) {
591         methodName =
592             "$$robo$$"
593                 + targetType.getClassName().replace('.', '_').replace('$', '_')
594                 + "$"
595                 + methodName;
596       }
597       return methodName;
598     }
599 
isAnnotatedStatic()600     boolean isAnnotatedStatic() {
601       return iMethod.isAnnotationPresent(Static.class);
602     }
603 
loadNull()604     void loadNull() {
605       visitInsn(Opcodes.ACONST_NULL);
606     }
607 
resolveParamTypes(Method iMethod)608     protected Type[] resolveParamTypes(Method iMethod) {
609       Class<?>[] iParamTypes = iMethod.getParameterTypes();
610       Annotation[][] paramAnnotations = iMethod.getParameterAnnotations();
611 
612       Type[] targetParamTypes = new Type[iParamTypes.length];
613       for (int i = 0; i < iParamTypes.length; i++) {
614         Class<?> paramType = findWithType(paramAnnotations[i]);
615         if (paramType == null) {
616           paramType = iParamTypes[i];
617         }
618         targetParamTypes[i] = Type.getType(paramType);
619       }
620       return targetParamTypes;
621     }
622 
623     @Nullable
findWithType(Annotation[] paramAnnotation)624     private Class<?> findWithType(Annotation[] paramAnnotation) {
625       for (Annotation annotation : paramAnnotation) {
626         if (annotation instanceof WithType) {
627           String withTypeName = ((WithType) annotation).value();
628           try {
629             return Class.forName(withTypeName, true, iClass.getClassLoader());
630           } catch (ClassNotFoundException e1) {
631             // it's okay, ignore
632           }
633         }
634       }
635       return null;
636     }
637   }
638 }
639