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());
    }
  }
private void interceptInvokeVirtualMethodWithoutInvokeDynamic(MutableClass mutableClass,
      ListIterator<AbstractInsnNode> instructions, MethodInsnNode targetMethod) {
    boolean isStatic = targetMethod.getOpcode() == Opcodes.INVOKESTATIC;

    instructions.remove(); 

    Type[] argumentTypes = Type.getArgumentTypes(targetMethod.desc);

    instructions.add(new LdcInsnNode(argumentTypes.length));
    instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object"));

    
    for (int i = argumentTypes.length - 1; i >= 0; i--) {
      Type type = argumentTypes[i];
      int argWidth = type.getSize();

      if (argWidth == 1) {                               
        instructions.add(new InsnNode(Opcodes.DUP_X1));  
        instructions.add(new InsnNode(Opcodes.SWAP));    
        instructions.add(new LdcInsnNode(i));            
        instructions.add(new InsnNode(Opcodes.SWAP));    
        box(type, instructions);                         
        instructions.add(new InsnNode(Opcodes.AASTORE)); 
      } else if (argWidth == 2) {                        
        instructions.add(new InsnNode(Opcodes.DUP_X2));  
        instructions.add(new InsnNode(Opcodes.DUP_X2));  
        instructions.add(new InsnNode(Opcodes.POP));     
        box(type, instructions);                         
        instructions.add(new LdcInsnNode(i));            
        instructions.add(new InsnNode(Opcodes.SWAP));    
        instructions.add(new InsnNode(Opcodes.AASTORE)); 
      }
    }

    if (isStatic) { 
      instructions.add(new InsnNode(Opcodes.ACONST_NULL)); 
      instructions.add(new InsnNode(Opcodes.SWAP));        
    }

    
    instructions.add(new LdcInsnNode(targetMethod.owner + "/" + targetMethod.name + targetMethod.desc)); 
    
    instructions.add(new InsnNode(Opcodes.DUP_X2));       
    instructions.add(new InsnNode(Opcodes.POP));          

    instructions.add(new LdcInsnNode(mutableClass.classType)); 
    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:
        
      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());
    }
  }
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 ( ) ) ; } }
Intercepts the method without using the invokedynamic bytecode instruction.
Should be called through interceptInvokeVirtualMethod, not directly.
Intercepts the method without using the invokedynamic bytecode instruction . Should be called through interceptInvokeVirtualMethod not directly .