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 */
invokespecial
is used to implement either of three things
private
methodsuper. …
callWhile 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With