内联 tryCatchBlock 导致当前帧的堆栈大小与堆栈映射异常不匹配
Inlining tryCatchBlock results in Current frame's stack size doesn't match stackmap exception
我正在使用 ASM 将包含 try-catch 块的 Callee::calcualte(int,int)int
的主体内联到 Caller::test
方法。生成的字节码似乎没问题,但由于异常而导致验证失败:
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})
生成代码中标签14到标签52的字节码指令来自Callee::calculate的body,标签9到标签12的3条指令弹出两个int参数和接收者(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 ]
}
谁能给点建议?我已经被这个问题困扰了三天。这里的stackmap处理应该有问题,但我不知道如何调整这个错误。
为了您的方便,我还post Caller 和 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..........");
}
}
更新
原始Callee::calculate的字节码:
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 */
我的代码也被推送到Github Repository,classMainInliner
可以直接在class路径添加ASM lib后运行。
项目中的主要程序是MethodCallInliner::visitMethodInsn(..),其中新建了一个InliningAdapter
,用来访问Callee::calculate()
体。
=========================================
LocalVariableTable更新:
根据@Holger 的解释和一些选项:
- 避免声明任何正式变量,让 ASM 推断一切。
要禁用声明正式变量,visitLocalVariable
被覆盖但在 InliningAdapter
和 MethodCallInliner
中都是空实现,LocalVariableTable
在生成的代码中消失但验证仍在失败并出现相同的错误。我也试过
ClassReader.accept(, ClassReader.EXPAND_FRAME|ClassReader.SKIP_DEBUG)
但是结果和空覆盖一样visitLocalVariable
- 合并 Callee 的局部变量table 到生成的 table.
完整生成的字节码是:
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 ]
}
原来的 LocalVariableTables 是:
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
合并似乎没问题(我认为如果这些名称在不同的区域,则不必避免变量名称冲突,例如,两个 this
符号在不同的区域)。但在@50::iload 处验证仍然失败,并显示相同的消息。
在调用calculate
之前,栈上有一个int
值,调用后会用到。正常完成的方法调用只会消耗适当的参数值,而不会影响所有其他操作数堆栈值,而不管调用的方法内部发生了什么。如果方法不是 void
,return 值将被压入堆栈。
当您内联方法的代码时,情况会发生变化。然后,除了使用参数和压入一个 return 值之外,代码可能会对操作数堆栈产生影响。在您的情况下,有一个异常处理程序将恢复正常执行。但是,正如 中所讨论的,异常处理程序从一个仅包含一个值的操作数堆栈开始,即遇到的异常。在遇到异常之前压入堆栈的所有值都将被刷新。将方法的代码内联到调用者之后,这也会影响调用者的操作数堆栈。
所以在你的例子中,两个代码路径在内联代码的末尾附近合并,一个用于正常完成的情况,其中堆栈上的 int
值将被保留,异常的路径处理程序,其中值已被删除。这种不匹配导致 VerifyError
.
没有简单的解决办法。您不能强制异常处理程序保留该值,因此您必须重写代码以不依赖于保留推送的值,这使您最初通过插入指令进行内联的想法无效。你必须知道,甚至相反的情况也是可能的:当一个方法遇到 return 指令时,有多少额外的未使用值悬在堆栈上并不重要,因为堆栈帧将在 [= 时被销毁24=]ing 给呼叫者。因此,天真地内联代码可能会在堆栈上留下额外的值,这会在您对方法有分支或方法有多个 return 指令时导致错误。
我正在使用 ASM 将包含 try-catch 块的 Callee::calcualte(int,int)int
的主体内联到 Caller::test
方法。生成的字节码似乎没问题,但由于异常而导致验证失败:
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})
生成代码中标签14到标签52的字节码指令来自Callee::calculate的body,标签9到标签12的3条指令弹出两个int参数和接收者(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 ]
}
谁能给点建议?我已经被这个问题困扰了三天。这里的stackmap处理应该有问题,但我不知道如何调整这个错误。
为了您的方便,我还post Caller 和 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..........");
}
}
更新
原始Callee::calculate的字节码:
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 */
我的代码也被推送到Github Repository,classMainInliner
可以直接在class路径添加ASM lib后运行。
项目中的主要程序是MethodCallInliner::visitMethodInsn(..),其中新建了一个InliningAdapter
,用来访问Callee::calculate()
体。
=========================================
LocalVariableTable更新:
根据@Holger 的解释和一些选项:
- 避免声明任何正式变量,让 ASM 推断一切。
要禁用声明正式变量,visitLocalVariable
被覆盖但在 InliningAdapter
和 MethodCallInliner
中都是空实现,LocalVariableTable
在生成的代码中消失但验证仍在失败并出现相同的错误。我也试过
ClassReader.accept(, ClassReader.EXPAND_FRAME|ClassReader.SKIP_DEBUG)
但是结果和空覆盖一样visitLocalVariable
- 合并 Callee 的局部变量table 到生成的 table.
完整生成的字节码是:
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 ]
}
原来的 LocalVariableTables 是:
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
合并似乎没问题(我认为如果这些名称在不同的区域,则不必避免变量名称冲突,例如,两个 this
符号在不同的区域)。但在@50::iload 处验证仍然失败,并显示相同的消息。
在调用calculate
之前,栈上有一个int
值,调用后会用到。正常完成的方法调用只会消耗适当的参数值,而不会影响所有其他操作数堆栈值,而不管调用的方法内部发生了什么。如果方法不是 void
,return 值将被压入堆栈。
当您内联方法的代码时,情况会发生变化。然后,除了使用参数和压入一个 return 值之外,代码可能会对操作数堆栈产生影响。在您的情况下,有一个异常处理程序将恢复正常执行。但是,正如
所以在你的例子中,两个代码路径在内联代码的末尾附近合并,一个用于正常完成的情况,其中堆栈上的 int
值将被保留,异常的路径处理程序,其中值已被删除。这种不匹配导致 VerifyError
.
没有简单的解决办法。您不能强制异常处理程序保留该值,因此您必须重写代码以不依赖于保留推送的值,这使您最初通过插入指令进行内联的想法无效。你必须知道,甚至相反的情况也是可能的:当一个方法遇到 return 指令时,有多少额外的未使用值悬在堆栈上并不重要,因为堆栈帧将在 [= 时被销毁24=]ing 给呼叫者。因此,天真地内联代码可能会在堆栈上留下额外的值,这会在您对方法有分支或方法有多个 return 指令时导致错误。