用ASM构建FrameNode api
Construct FrameNode with ASM api
我成功实现了字节码方法内联优化,生成的代码对我来说似乎还不错。然而,验证失败并显示消息:
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 指令之前的那个)之前添加堆栈映射 table,它列出了跳转目标位置的类型(来自 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的建议或示例来处理这种情况?谢谢
FramNodeFrameNode ASM Document的描述:
public FrameNode(int type,
int nLocal,
Object[] local,
int nStack,
Object[] stack)
Constructs a new FrameNode.
Parameters:
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).
只要你的字节码是正确的,你可以让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
初始化您的 class 编写器,并避免 class 加载问题覆盖 getCommonSuperClass
。
假设无论您使用何种途径生成字节码(访问者、ClassNode
等),最终您都会使用 ClassWriter
生成 class 字节。
如果你真的想手动做,先试试这个,然后用asmifier看看如何用asm写框架代码。
我成功实现了字节码方法内联优化,生成的代码对我来说似乎还不错。然而,验证失败并显示消息:
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 指令之前的那个)之前添加堆栈映射 table,它列出了跳转目标位置的类型(来自 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的建议或示例来处理这种情况?谢谢
FramNodeFrameNode ASM Document的描述:
public FrameNode(int type, int nLocal, Object[] local, int nStack, Object[] stack)
Constructs a new FrameNode.
Parameters:
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).
只要你的字节码是正确的,你可以让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
初始化您的 class 编写器,并避免 class 加载问题覆盖 getCommonSuperClass
。
假设无论您使用何种途径生成字节码(访问者、ClassNode
等),最终您都会使用 ClassWriter
生成 class 字节。
如果你真的想手动做,先试试这个,然后用asmifier看看如何用asm写框架代码。