我成功实现了字节码方法内联优化,生成的代码对我来说似乎没问题。但是,验证失败并显示消息:
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中验证失败的两种可能解决方案:
为VM参数添加-XX:-UseSplitVerifier
。该选项有效但在Java8中已弃用。
在第44行(GOTO指令前面的那个)之前添加stackmap表,它列出了作为跳转目标的位置的类型(来自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 Document的说明:
public FrameNode(int type, int nLocal, 对象[]本地, int nStack, 对象[]堆栈)
构造一个新的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).
答案 0 :(得分:2)
如果你的字节码是正确的,你可以让asm为你创建帧节点。
我通常这样做:
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES)
{
@Override
protected String getCommonSuperClass(final String type1, final String type2)
{
// the default asm merge uses Class.forName(), this prevents that.
return "java/lang/Object";
}
};
cn.accept(cw);
解释:使用COMPUTE_FRAMES
初始化您的班级作家,并避免类加载问题覆盖getCommonSuperClass
。
假设您使用任何路径生成字节码(访问者,ClassNode
等),最终您使用ClassWriter
生成类字节。
如果您确实想手动执行此操作,请先尝试此操作,然后使用asmifier查看如何使用asm编写框架代码。