private void interceptInvokeVirtualMethodWithoutInvokeDynamic()

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());
    }
  }