Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 */
like image 745
Manos Ntoulias Avatar asked Mar 11 '19 13:03

Manos Ntoulias


2 Answers

invokespecial is used to implement either of three things

  1. A constructor invocation
  2. Invoking a private method
  3. Doing a super. … call

While 1. does not apply here (as the target method’s name is not <init>), either of the other cases requires the receiver type to be of the current class or a subclass of it. So even when the method’s declaring class is Type, the actual receiver’s type is expected to be assignable to the current class, Type$ClassType.

The closest equivalent to what you’ve created with your change, is a super invocation, though in Java source code, calling a method via super enforces the receiver reference to be the same as this, which is intrinsically assignable to the current class.

On the bytecode level, the rules are less restrictive, still, a method invocation allowing to bypass method declarations in your current class or its subclasses is not allowed to be invoked on a type reference which could point to an instance of an entirely unrelated subclass hierarchy, i.e. a Type not being a Type$ClassType.

The relevant JVMS rule has been cited in apangin’s answer already.

like image 151
Holger Avatar answered Nov 18 '22 07:11

Holger


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.

like image 25
apangin Avatar answered Nov 18 '22 09:11

apangin