1 package org.robolectric.internal.bytecode; 2 3 import static org.robolectric.internal.bytecode.OldClassInstrumentor.HANDLE_EXCEPTION_METHOD; 4 import static org.robolectric.internal.bytecode.OldClassInstrumentor.ROBOLECTRIC_INTERNALS_TYPE; 5 import static org.robolectric.internal.bytecode.OldClassInstrumentor.THROWABLE_TYPE; 6 7 import org.objectweb.asm.Label; 8 import org.objectweb.asm.Opcodes; 9 import org.objectweb.asm.Type; 10 import org.objectweb.asm.tree.FieldNode; 11 import org.objectweb.asm.tree.MethodNode; 12 import org.robolectric.internal.bytecode.ClassInstrumentor.TryCatch; 13 14 public class ShadowDecorator implements ClassInstrumentor.Decorator { 15 private static final String OBJECT_DESC = Type.getDescriptor(Object.class); 16 private static final Type OBJECT_TYPE = Type.getType(Object.class); 17 private static final String GET_ROBO_DATA_SIGNATURE = "()Ljava/lang/Object;"; 18 19 @Override decorate(MutableClass mutableClass)20 public void decorate(MutableClass mutableClass) { 21 mutableClass.addInterface(Type.getInternalName(ShadowedObject.class)); 22 23 mutableClass.addField(0, new FieldNode(Opcodes.ACC_PUBLIC, 24 ShadowConstants.CLASS_HANDLER_DATA_FIELD_NAME, OBJECT_DESC, OBJECT_DESC, null)); 25 26 addRoboGetDataMethod(mutableClass); 27 } 28 29 /** 30 * For non-invokedynamic JVMs, generates this code: 31 * ```java 32 * if (__robo_data__ instanceof ThisClass) { 33 * try { 34 * return __robo_data__.$$robo$$originalMethod(params); 35 * } (Throwable t) { 36 * throw RobolectricInternals.cleanStackTrace(t); 37 * } 38 * } 39 * ``` 40 * 41 * Note that this method is only called by {@link OldClassInstrumentor}. 42 */ 43 @Override decorateMethodPreClassHandler(MutableClass mutableClass, MethodNode originalMethod, String originalMethodName, RobolectricGeneratorAdapter generator)44 public void decorateMethodPreClassHandler(MutableClass mutableClass, MethodNode originalMethod, 45 String originalMethodName, RobolectricGeneratorAdapter generator) { 46 boolean isNormalInstanceMethod = !generator.isStatic 47 && !originalMethodName.equals(ShadowConstants.CONSTRUCTOR_METHOD_NAME); 48 // maybe perform direct call... 49 if (isNormalInstanceMethod) { 50 int exceptionLocalVar = generator.newLocal(THROWABLE_TYPE); 51 Label notInstanceOfThis = new Label(); 52 53 generator.loadThis(); // this 54 generator.getField(mutableClass.classType, ShadowConstants.CLASS_HANDLER_DATA_FIELD_NAME, OBJECT_TYPE); // contents of this.__robo_data__ 55 generator.instanceOf(mutableClass.classType); // __robo_data__, is instance of same class? 56 generator.visitJumpInsn(Opcodes.IFEQ, notInstanceOfThis); // jump if no (is not instance) 57 58 TryCatch tryCatchForProxyCall = generator.tryStart(THROWABLE_TYPE); 59 generator.loadThis(); // this 60 generator.getField(mutableClass.classType, ShadowConstants.CLASS_HANDLER_DATA_FIELD_NAME, OBJECT_TYPE); // contents of this.__robo_data__ 61 generator.checkCast(mutableClass.classType); // __robo_data__ but cast to my class 62 generator.loadArgs(); // __robo_data__ instance, [args] 63 64 generator.visitMethodInsn(Opcodes.INVOKESPECIAL, mutableClass.internalClassName, originalMethod.name, originalMethod.desc, false); 65 tryCatchForProxyCall.end(); 66 67 generator.returnValue(); 68 69 // catch(Throwable) 70 tryCatchForProxyCall.handler(); 71 generator.storeLocal(exceptionLocalVar); 72 generator.loadLocal(exceptionLocalVar); 73 generator.invokeStatic(ROBOLECTRIC_INTERNALS_TYPE, HANDLE_EXCEPTION_METHOD); 74 generator.throwException(); 75 76 // callClassHandler... 77 generator.mark(notInstanceOfThis); 78 } 79 } 80 addRoboGetDataMethod(MutableClass mutableClass)81 private void addRoboGetDataMethod(MutableClass mutableClass) { 82 MethodNode initMethodNode = new MethodNode(Opcodes.ACC_PUBLIC, ShadowConstants.GET_ROBO_DATA_METHOD_NAME, GET_ROBO_DATA_SIGNATURE, null, null); 83 RobolectricGeneratorAdapter generator = new RobolectricGeneratorAdapter(initMethodNode); 84 generator.loadThis(); // this 85 generator.getField(mutableClass.classType, ShadowConstants.CLASS_HANDLER_DATA_FIELD_NAME, OBJECT_TYPE); // contents of __robo_data__ 86 generator.returnValue(); 87 generator.endMethod(); 88 mutableClass.addMethod(initMethodNode); 89 } 90 } 91