javajava-8jvmbytecodeverifyerror

Invokespecial Verify Error: Type is not assignable


I've modified the line 15 of the bytecode below and changed it form invokevirtual to invokespecial (JAVA 8). Unfortunately I get a verify error ( Bad type on operand stack)

I know that the value of the operand stack must be a subclass of the class specified in the objectref but in this case #18 is Type and not Type$ClassType like the error suggest. Or to say it differently shouldn't the stackmapframe at line 15 have Type and not Type$ClassType in stack[0]? What am I missing?

edit: stackmapframes are the same before and after the change. (in case ASM COMPUTE FRAMES which I used would changed them)

Exception Details:
  Location:
    com/sun/tools/javac/code/Type$ClassType.toString()Ljava/lang/String; @15: invokespecial
  Reason:
    Type 'com/sun/tools/javac/code/Type' (current frame, stack[0]) is not assignable to 'com/sun/tools/javac/code/Type$ClassType'
  Current Frame:
    bci: @15
    flags: { }
    locals: { 'com/sun/tools/javac/code/Type$ClassType', 'java/lang/StringBuilder' }
    stack: { 'com/sun/tools/javac/code/Type', 'com/sun/tools/javac/code/TypeTag' }
  ...     
  Stackmap Table:
append_frame(@71,Object[#108])
same_frame(@85)
same_frame(@121)

Here's is the code. Type$ClassType is a direct subclass of Type and com/sun/tools/javac/code/Type$ClassType is the current class which allows us to call a superclass (like Type) with invokespecial

    public class com.sun.tools.javac.code.Type$ClassType extends com.sun.tools.javac.code.Type implements
 javax.lang.model.type.DeclaredType
    ....
    public java.lang.String toString();
        descriptor: ()Ljava/lang/String;
        flags: ACC_PUBLIC
        Code:
          stack=4, locals=2, args_size=1
             0: new           #108                // class java/lang/StringBuilder
             3: dup
             4: invokespecial #17                 // Method java/lang/StringBuilder."<init>":()V
             7: astore_1
             8: aload_0
             9: invokevirtual #13                 // Method com/sun/tools/javac/code/Type$ClassType.getEnclosingType:()Lcom/sun/tools/javac/code/Type;
            12: getstatic     #10                 // Field com/sun/tools/javac/code/TypeTag.CLASS:Lcom/sun/tools/javac/code/TypeTag;
            15: invokespecial #18                 // Method com/sun/tools/javac/code/Type.hasTag:(Lcom/sun/tools/javac/code/TypeTag;)Z
            18: ifeq          71
            .....
            StackMapTable: number_of_entries = 3
              frame_type = 252 /* append */
                offset_delta = 71
                locals = [ class java/lang/StringBuilder ]
              frame_type = 13 /* same */
              frame_type = 35 /* same */

Solution

  • You attempt to execute invokespecial on an instance of Type (returned by invokevirtual @9), while the verifier expects the reference of current class, i.e. Type$ClassType.

    See JVMS §4.10.1.9:

    One can validly replace types matching the current class and the argument types given in Descriptor on the incoming operand stack with the return type given in Descriptor, yielding the outgoing type state.