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