• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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