in sandbox/src/main/java/org/robolectric/internal/bytecode/OldClassInstrumentor.java [190:283]
private void interceptInvokeVirtualMethodWithoutInvokeDynamic(MutableClass mutableClass,
ListIterator<AbstractInsnNode> instructions, MethodInsnNode targetMethod) {
boolean isStatic = targetMethod.getOpcode() == Opcodes.INVOKESTATIC;
instructions.remove(); // remove the method invocation
Type[] argumentTypes = Type.getArgumentTypes(targetMethod.desc);
instructions.add(new LdcInsnNode(argumentTypes.length));
instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object"));
// first, move any arguments into an Object[] in reverse order
for (int i = argumentTypes.length - 1; i >= 0; i--) {
Type type = argumentTypes[i];
int argWidth = type.getSize();
if (argWidth == 1) { // A B C []
instructions.add(new InsnNode(Opcodes.DUP_X1)); // A B [] C []
instructions.add(new InsnNode(Opcodes.SWAP)); // A B [] [] C
instructions.add(new LdcInsnNode(i)); // A B [] [] C 2
instructions.add(new InsnNode(Opcodes.SWAP)); // A B [] [] 2 C
box(type, instructions); // A B [] [] 2 (C)
instructions.add(new InsnNode(Opcodes.AASTORE)); // A B [(C)]
} else if (argWidth == 2) { // A B _C_ []
instructions.add(new InsnNode(Opcodes.DUP_X2)); // A B [] _C_ []
instructions.add(new InsnNode(Opcodes.DUP_X2)); // A B [] [] _C_ []
instructions.add(new InsnNode(Opcodes.POP)); // A B [] [] _C_
box(type, instructions); // A B [] [] (C)
instructions.add(new LdcInsnNode(i)); // A B [] [] (C) 2
instructions.add(new InsnNode(Opcodes.SWAP)); // A B [] [] 2 (C)
instructions.add(new InsnNode(Opcodes.AASTORE)); // A B [(C)]
}
}
if (isStatic) { // []
instructions.add(new InsnNode(Opcodes.ACONST_NULL)); // [] null
instructions.add(new InsnNode(Opcodes.SWAP)); // null []
}
// instance []
instructions.add(new LdcInsnNode(targetMethod.owner + "/" + targetMethod.name + targetMethod.desc)); // target method signature
// instance [] signature
instructions.add(new InsnNode(Opcodes.DUP_X2)); // signature instance [] signature
instructions.add(new InsnNode(Opcodes.POP)); // signature instance []
instructions.add(new LdcInsnNode(mutableClass.classType)); // signature instance [] class
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getType(RobolectricInternals.class).getInternalName(), "intercept",
"(Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;",
false));
final Type returnType = Type.getReturnType(targetMethod.desc);
switch (returnType.getSort()) {
case ARRAY:
/* falls through */
case OBJECT:
String remappedType = mutableClass.config.mappedTypeName(returnType.getInternalName());
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, remappedType));
break;
case VOID:
instructions.add(new InsnNode(Opcodes.POP));
break;
case Type.LONG:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Long.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Long.class), "longValue", Type.getMethodDescriptor(Type.LONG_TYPE), false));
break;
case Type.FLOAT:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Float.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Float.class), "floatValue", Type.getMethodDescriptor(Type.FLOAT_TYPE), false));
break;
case Type.DOUBLE:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Double.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Double.class), "doubleValue", Type.getMethodDescriptor(Type.DOUBLE_TYPE), false));
break;
case Type.BOOLEAN:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Boolean.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Boolean.class), "booleanValue", Type.getMethodDescriptor(Type.BOOLEAN_TYPE), false));
break;
case Type.INT:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Integer.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Integer.class), "intValue", Type.getMethodDescriptor(Type.INT_TYPE), false));
break;
case Type.SHORT:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Short.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Short.class), "shortValue", Type.getMethodDescriptor(Type.SHORT_TYPE), false));
break;
case Type.BYTE:
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Byte.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Byte.class), "byteValue", Type.getMethodDescriptor(Type.BYTE_TYPE), false));
break;
default:
throw new RuntimeException("Not implemented: " + getClass().getName() + " cannot intercept methods with return type " + returnType.getClassName());
}
}