I am using ASM to inline body of Callee::calcualte(int,int)int
, which contains a try-catch block, to the Caller::test
method. The generated bytecode seems OK but verification fails due to Exception:
Exception in thread "main" java.lang.VerifyError: Instruction type does not match stack map
Exception Details:
Location:
CallerI.test(II)V @50: iload
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @50
flags: { }
locals: { 'CallerI', integer, integer, integer, integer, 'code/sxu/asm/example/Callee', integer, 'java/lang/ReflectiveOperationException' }
stack: { }
Stackmap Frame:
bci: @50
flags: { }
locals: { 'CallerI', integer, integer, integer, integer, 'code/sxu/asm/example/Callee', integer, 'java/lang/Object' }
stack: { integer }
Bytecode:
0000000: 1b1c 602a b400 0e1b 1c3e 3604 3a05 0336
0000010: 06b8 002e 1230 1232 b200 3812 30b8 003e
0000020: b600 443a 07a7 000d 3a07 b200 4a12 4cb6
0000030: 0052 1506 a700 0364 3605 b200 5515 05b6
0000040: 0058 b200 5512 5ab6 0052 b1
Exception Handler Table:
bci [17, 37] => handler: 40
bci [17, 37] => handler: 40
Stackmap Table:
full_frame(@40,{Object[#2],Integer,Integer,Integer,Integer,Object[#21],Integer},{Object[#101]})
full_frame(@50,{Object[#2],Integer,Integer,Integer,Integer,Object[#21],Integer,Object[#4]},{Integer})
full_frame(@55,{Object[#2],Integer,Integer,Integer,Integer,Object[#21],Integer,Object[#4]},{Integer,Integer})
The bytecode instructions from label 14 to label 52 in the generated code are from body of Callee::calculate, and three instructions from label 9 to 12 pops up two int arguments and the receiver (Callee).
//The generated bytecode method.
public void test(int, int);
flags: ACC_PUBLIC
Code:
stack=6, locals=8, args_size=3
0: iload_1
1: iload_2
2: iadd
3: aload_0
4: getfield #14 // Field _callee:Lcode/sxu/asm/example/Callee;
7: iload_1
8: iload_2
9: istore_3
10: istore 4
12: astore 5
14: iconst_0
15: istore 6
17: invokestatic #46 // Method java/lang/invoke/MethodHandles.publicLookup:()Ljava/lang/invoke/MethodHandles$Lookup;
20: ldc #48 // class java/lang/String
22: ldc #50 // String say
24: getstatic #56 // Field java/lang/Void.TYPE:Ljava/lang/Class;
27: ldc #48 // class java/lang/String
29: invokestatic #62 // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
32: invokevirtual #68 // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
35: astore 7
37: goto 50
40: astore 7
42: getstatic #74 // Field java/lang/System.err:Ljava/io/PrintStream;
45: ldc #76 // String I find exception in the catch
47: invokevirtual #82 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
50: iload 6
52: goto 55
55: isub
56: istore 5
58: getstatic #85 // Field java/lang/System.out:Ljava/io/PrintStream;
61: iload 5
63: invokevirtual #88 // Method java/io/PrintStream.println:(I)V
66: getstatic #85 // Field java/lang/System.out:Ljava/io/PrintStream;
69: ldc #90 // String 1..........
71: invokevirtual #82 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
74: return
Exception table:
from to target type
17 37 40 Class java/lang/NoSuchMethodException
17 37 40 Class java/lang/IllegalAccessException
LocalVariableTable:
Start Length Slot Name Signature
14 41 0 this Lcode/sxu/asm/example/Callee;
14 41 1 t I
14 41 2 p I
17 38 3 tmp I
42 8 4 e Ljava/lang/ReflectiveOperationException;
0 75 0 this LCallerI;
0 75 1 a I
0 75 2 b I
58 17 5 r I
LineNumberTable:
line 16: 0
line 18: 14
line 26: 17
line 27: 37
line 29: 42
line 31: 50
line 18: 58
line 19: 66
line 20: 74
StackMapTable: number_of_entries = 3
frame_type = 255 /* full_frame */
offset_delta = 40
locals = [ class CallerI, int, int, int, int, class code/sxu/asm/example/Callee, int ]
stack = [ class java/lang/ReflectiveOperationException ]
frame_type = 255 /* full_frame */
offset_delta = 9
locals = [ class CallerI, int, int, int, int, class code/sxu/asm/example/Callee, int, class java/lang/Object ]
stack = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 4
locals = [ class CallerI, int, int, int, int, class code/sxu/asm/example/Callee, int, class java/lang/Object ]
stack = [ int, int ]
}
Can anyone provide advice for it? I have been stub by this problem for three days. There should be something wrong with the stackmap processing here but i have no idea how to adjust this error.
For your convenice, i also post the origina methods of Caller and Callee:
public class Callee {
public final String _a;
public final String _b;
public Callee(String a, String b){
_a = a;
_b = b;
}
....
public int calculate(int t, int p){
int tmp=0;
try {
MethodHandle handle = MethodHandles.publicLookup().findVirtual(String.class, "say", MethodType.methodType(void.class, String.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
// TODO Auto-generated catch block
System.err.println("I find exception in the catch");
}
return tmp;
}
}
public class Caller {
final Callee _callee;
public Caller(Callee callee){
_callee = callee;
}
...
public void test(int a, int b){
int r = a+b-_callee.calculate(a, b);
System.out.println(r);
System.out.println("1..........");
}
}
UPDATE
The original Callee::calculate's bytecode:
public int calculate(int, int);
flags: ACC_PUBLIC
Code:
stack=5, locals=5, args_size=3
0: iconst_0
1: istore_3
2: invokestatic #26 // Method java/lang/invoke/MethodHandles.publicLookup:()Ljava/lang/invoke/MethodHandles$Lookup;
5: ldc #32 // class java/lang/String
7: ldc #34 // String say
9: getstatic #36 // Field java/lang/Void.TYPE:Ljava/lang/Class;
12: ldc #32 // class java/lang/String
14: invokestatic #42 // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
17: invokevirtual #48 // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
20: astore 4
22: goto 35
25: astore 4
27: getstatic #54 // Field java/lang/System.err:Ljava/io/PrintStream;
30: ldc #60 // String I find exception in the catch
32: invokevirtual #62 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: iload_3
36: ireturn
Exception table:
from to target type
2 22 25 Class java/lang/NoSuchMethodException
2 22 25 Class java/lang/IllegalAccessException
LineNumberTable:
line 18: 0
line 26: 2
line 27: 22
line 29: 27
line 31: 35
LocalVariableTable:
Start Length Slot Name Signature
0 37 0 this Lcode/sxu/asm/example/Callee;
0 37 1 t I
0 37 2 p I
2 35 3 tmp I
27 8 4 e Ljava/lang/ReflectiveOperationException;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 25
locals = [ class code/sxu/asm/example/Callee, int, int, int ]
stack = [ class java/lang/ReflectiveOperationException ]
frame_type = 9 /* same */
My code is also pushed to Github Repository, and the class MainInliner
can be run directly after adding ASM lib to the classpath.
The main procedure in the project is MethodCallInliner::visitMethodInsn(..), in which a new InliningAdapter
is created and used to visit Callee::calculate()
body.
=========================================
Update for LocalVariableTable:
According to @Holger's explanation and some options:
- Avoid declaring any formal variable and let ASM infer everything.
To disable declare formal variable, the visitLocalVariable
are override but empty implementation in both InliningAdapter
and MethodCallInliner
, the LocalVariableTable
disappeared in the generated code but verification still fails with the same error. I also tried
ClassReader.accept(, ClassReader.EXPAND_FRAME|ClassReader.SKIP_DEBUG)
But the result is the same as empty override visitLocalVariable
- Merge Callee's localvariabletable to the generated table.
The full generated bytecodes are:
public void test(int, int);
flags: ACC_PUBLIC
Code:
stack=6, locals=8, args_size=3
0: iload_1
1: iload_2
2: iadd
3: aload_0
4: getfield #14 // Field _callee:Lcode/sxu/asm/example/Callee;
7: iload_1
8: iload_2
9: istore_3
10: istore 4
12: astore 5
14: iconst_0
15: istore 6
17: invokestatic #46 // Method java/lang/invoke/MethodHandles.publicLookup:()Ljava/lang/invoke/MethodHandles$Lookup;
20: ldc #48 // class java/lang/String
22: ldc #50 // String say
24: getstatic #56 // Field java/lang/Void.TYPE:Ljava/lang/Class;
27: ldc #48 // class java/lang/String
29: invokestatic #62 // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
32: invokevirtual #68 // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
35: astore 7
37: goto 50
40: astore 7
42: getstatic #74 // Field java/lang/System.err:Ljava/io/PrintStream;
45: ldc #76 // String I find exception in the catch
47: invokevirtual #82 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
50: iload 6
52: goto 55
55: isub
56: istore_3
57: getstatic #85 // Field java/lang/System.out:Ljava/io/PrintStream;
60: iload_3
61: invokevirtual #88 // Method java/io/PrintStream.println:(I)V
64: getstatic #85 // Field java/lang/System.out:Ljava/io/PrintStream;
67: ldc #90 // String 1..........
69: invokevirtual #82 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
72: return
Exception table:
from to target type
17 37 40 Class java/lang/NoSuchMethodException
17 37 40 Class java/lang/IllegalAccessException
LocalVariableTable:
Start Length Slot Name Signature
14 41 5 this Lcode/sxu/asm/example/Callee;
14 41 4 t I
14 41 3 p I
17 38 6 tmp I
42 8 7 e Ljava/lang/ReflectiveOperationException;
0 73 0 this LCallerI;
0 73 1 a I
0 73 2 b I
57 16 3 r I
LineNumberTable:
line 20: 0
..
line 24: 72
StackMapTable: number_of_entries = 3
frame_type = 255 /* full_frame */
offset_delta = 40
locals = [ class CallerI, int, int, int, int, class code/sxu/asm/example/Callee, int ]
stack = [ class java/lang/ReflectiveOperationException ]
frame_type = 255 /* full_frame */
offset_delta = 9
locals = [ class CallerI, int, int, int, int, class code/sxu/asm/example/Callee, int, class java/lang/Object ]
stack = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 4
locals = [ class CallerI, int, int, int, int, class code/sxu/asm/example/Callee, int, class java/lang/Object ]
stack = [ int, int ]
}
and the original LocalVariableTables are:
Callee: LocalVariableTable:
Start Length Slot Name Signature
0 37 0 this Lcode/sxu/asm/example/Callee;
0 37 1 t I
0 37 2 p I
2 35 3 tmp I
27 8 4 e Ljava/lang/ReflectiveOperationException;
Caller: LocalVariableTable:
Start Length Slot Name Signature
0 30 0 this Lcode/sxu/asm/example/Caller;
0 30 1 a I
0 30 2 b I
14 16 3 r I
The merge seems OK(I think it is unnecessary avoid variable name conflict if these names are in different region, e.g., two this
symbol in different regions). But the verification still fails at @50::iload with the same message.