我成功实现了字节码方法内联优化,生成的代码对我来说似乎还可以。然而,验证失败并显示以下消息:
java.lang.VerifyError: Expecting a stackmap frame at branch target 47
Exception Details:
Location:
code/sxu/asm/example/Caller.test(II)V @44: goto
Reason:
Expected stackmap frame at this location.
而对应的字节码是:
public void test(int, int);
flags: ACC_PUBLIC
Code:
stack=4, 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: aload 5
16: getfield #40 // Field code/sxu/asm/example/Callee._a:Ljava/lang/String;
19: invokevirtual #46 // Method java/lang/String.length:()I
22: aload 5
24: getfield #49 // Field code/sxu/asm/example/Callee._b:Ljava/lang/String;
27: invokevirtual #46 // Method java/lang/String.length:()I
30: iadd
31: istore 6
33: iload 6
35: iload 4
37: iload_3
38: iadd
39: iadd
40: istore 6
42: iload 6
44: goto 47
47: isub
48: istore 7
50: getstatic #59 // Field java/lang/System.out:Ljava/io/PrintStream;
53: iload 7
55: invokevirtual #65 // Method java/io/PrintStream.println:(I)V
58: getstatic #59 // Field java/lang/System.out:Ljava/io/PrintStream;
61: ldc #67 // String 1..........
63: invokevirtual #70 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: return
LocalVariableTable:
Start Length Slot Name Signature
14 33 0 this Lcode/sxu/asm/example/Callee;
14 33 1 t I
14 33 2 p I
33 14 7 tmp I
0 67 0 this Lcode/sxu/asm/example/Caller;
0 67 1 a I
0 67 2 b I
50 17 7 r I
LineNumberTable:
line 16: 0
line 13: 14
line 14: 33
line 15: 42
line 18: 50
line 19: 58
line 20: 66
}
第 9-11 行:存储堆叠参数。第 14-44 行:Callee 的内联字节码,最后一个ireturn
被替换为GOTO
.
Java7中验证失败的两种可能解决方案:
添加
-XX:-UseSplitVerifier
用于 VM 参数。该选项有效,但在 Java8 中已弃用。在第 44 行之前添加堆栈映射表(GOTO 指令之前的那个),它列出了作为跳转目标的位置的类型(来自Stackmap frame description)。
对我来说,选项 2 更可取,但我在框架的构造上有问题。我的代码是:
if (opcode == Opcodes.RETURN || opcode == Opcodes.IRETURN) {
FrameNode stackMap = new FrameNode(Opcodes.NEW, -1, null, -1, null);
stackMap.accept(mv); //Visit The framenode before GOTO
super.visitJumpInsn(Opcodes.GOTO, end);
} else {
super.visitInsn(opcode);
}
我认为两者都Opcodes.NEW/SMAE
应该在这里工作。但是无法计算剩下的四个参数,因为访问者没有访问过目标代码,也不知道 nStack、nlocals..
那么任何人都可以在这里构建 FrameNode 或示例来处理这种情况提供建议吗?谢谢。
FramNode FrameNode ASM 文档的描述:
公共 FrameNode(int 类型,int nLocal,Object[] local,int nStack,Object[] 堆栈)
构造一个新的 FrameNode。
参数:
type - the type of this frame. Must be Opcodes.F_NEW for expanded frames, or Opcodes.F_FULL, Opcodes.F_APPEND, Opcodes.F_CHOP, Opcodes.F_SAME or Opcodes.F_APPEND, Opcodes.F_SAME1 for compressed frames.
nLocal - number of local variables of this stack map frame.
local - the types of the local variables of this stack map frame. Elements of this list can be Integer, String or LabelNode objects (for primitive, reference and uninitialized types respectively - see MethodVisitor).
nStack - number of operand stack elements of this stack map frame.
stack - the types of the operand stack elements of this stack map frame. Elements of this list can be Integer, String or LabelNode objects (for primitive, reference and uninitialized types respectively - see MethodVisitor).