我正在使用ASM 4来动态生成一些类。一切都很顺利,直到我生成代码来进行异常处理。生成的字节码位于底部。这是我得到的错误:
java.lang.VerifyError: Instruction type does not match stack map in method some.eval.ToEvaluate$0.apply()Ljava/lang/Object; at offset 44
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2404)
at java.lang.Class.getConstructor0(Class.java:2714)
at java.lang.Class.newInstance0(Class.java:343)
at java.lang.Class.newInstance(Class.java:325)
...
这是字节码:
// Compiled from com/pkg/some/Source.java (version 1.7 : 51.0, super bit)
public class some.eval.ToEvaluate$0 extends com.pkg.lang.Lambda0 {
// Method descriptor #7 ()V
// Stack: 1, Locals: 1
public ToEvaluate$0();
0 aload_0 [this]
1 invokespecial com.pkg.lang.Lambda0() [9]
4 return
Line numbers:
[pc: 0, line: 1]
[pc: 0, line: 2]
[pc: 4, line: 3]
Local variable table:
[pc: 0, pc: 5] local: this index: 0 type: new some.eval.ToEvaluate(){}
// Method descriptor #13 ()Ljava/lang/Object;
// Stack: 5, Locals: 3
public java.lang.Object apply();
0 getstatic com.pkg.some.Primitives.equal : com.pkg.lang.Lambda [19]
3 checkcast com.pkg.lang.Lambda2 [21]
6 getstatic com.pkg.some.Primitives.divide : com.pkg.lang.Lambda [26]
9 checkcast com.pkg.lang.Lambda2 [21]
12 ldc2_w <Long 1> [27]
15 invokestatic java.lang.Long.valueOf(long) : java.lang.Long [34]
18 ldc2_w <Long 0> [35]
21 invokestatic java.lang.Long.valueOf(long) : java.lang.Long [34]
24 invokevirtual com.pkg.lang.Lambda2.apply(java.lang.Object, java.lang.Object) : java.lang.Object [39]
27 astore_1 [v1]
28 goto 44
31 astore_2 [e]
32 new some.lambda.ToRun$1 [41]
35 dup
36 invokespecial some.lambda.ToRun$1() [42]
39 aload_2 [e]
40 invokevirtual com.pkg.lang.Lambda1.apply(java.lang.Object) : java.lang.Object [47]
43 astore_1
44 ldc2_w <Long -1> [48]
47 invokestatic java.lang.Long.valueOf(long) : java.lang.Long [34]
50 invokevirtual com.pkg.lang.Lambda2.apply(java.lang.Object, java.lang.Object) : java.lang.Object [39]
53 areturn
Exception Table:
[pc: 6, pc: 28] -> 31 when : java.lang.Throwable
Line numbers:
[pc: 6, line: 50]
[pc: 12, line: 21]
[pc: 18, line: 21]
[pc: 31, line: 51]
[pc: 32, line: 52]
[pc: 44, line: 54]
[pc: 44, line: 21]
Local variable table:
[pc: 0, pc: 54] local: this index: 0 type: new some.eval.ToEvaluate(){}
[pc: 28, pc: 31] local: v1 index: 1 type: java.lang.Object
[pc: 32, pc: 44] local: e index: 2 type: java.lang.Throwable
[pc: 44, pc: 44] local: v2 index: 1 type: java.lang.Object
Stack map table: number of frames 2
[pc: 31, same_locals_1_stack_item, stack: {java.lang.Throwable}]
[pc: 44, full, stack: {com.pkg.lang.Lambda2}, locals: {some.eval.ToEvaluate$0, java.lang.Object}]
}
我使用ASMifier开始:
public static Object trycatch(Object test, Lambda1 handler) {
Object v;
try {
v = test;
} catch (Throwable e) {
v = handler.apply(e);
}
return v;
}
然后我不得不修改它以使它变得通用。这是生成try / catch部分的代码:
int varOffset = context.getVarOffset();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
Label l3 = new Label();
Label l4 = new Label();
Label l5 = new Label();
// mv.visitLocalVariable("v", "Ljava/lang/Object;", null, l1, l2, 2); // 2 == varOffset + 0
context.push(1, new VarInfo(varOffset, "v1", l1, l2, false, "java/lang/Object"));
// mv.visitLocalVariable("v", "Ljava/lang/Object;", null, l3, l5, 2); // 2 == varOffset + 0
context.push(1, new VarInfo(varOffset, "v2", l3, l5, false, "java/lang/Object"));
// mv.visitLocalVariable("e", "Ljava/lang/Throwable;", null, l4, l3, 3); // 3 == varOffset+1
context.push(1, new VarInfo(varOffset + 1, "e", l4, l3, false, "java/lang/Throwable"));
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Throwable");
mv.visitLabel(l0);
mv.visitLineNumber(50, l0);
args[0].visit(context, mv); // mv.visitVarInsn(ALOAD, 0); // execute block
mv.visitVarInsn(ASTORE, varOffset); // store v, the result
mv.visitLabel(l1);
mv.visitJumpInsn(GOTO, l3);
mv.visitLabel(l2);
mv.visitLineNumber(51, l2);
// mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });
mv.visitVarInsn(ASTORE, varOffset + 1); // e
mv.visitLabel(l4);
mv.visitLineNumber(52, l4);
args[1].visit(context, mv); // mv.visitVarInsn(ALOAD, 1); // catch block
mv.visitVarInsn(ALOAD, varOffset + 1); // e
mv.visitMethodInsn(INVOKEVIRTUAL, "com/pkg/lang/Lambda1", "apply", "(Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitVarInsn(ASTORE, varOffset); // store v, the result
mv.visitLabel(l3);
mv.visitLineNumber(54, l3);
// mv.visitFrame(F_APPEND, 1, new Object[] { "java/lang/Object" }, 0, null);
mv.visitVarInsn(ALOAD, varOffset); // load v, the result
// mv.visitInsn(ARETURN);
mv.visitLabel(l5);
// mv.visitLocalVariable("test", "Ljava/lang/Object;", null, l0, l5, 0);
// mv.visitLocalVariable("handler", "Lcom/pkg/lang/Lambda1;", null, l0, l5, 1);
答案 0 :(得分:4)
我正在回答这个问题时假设你说:
// mv.visitLocalVariable("v", "Ljava/lang/Object;", null, l1, l2, 2); // 2 == varOffset + 0
context.push(1, new VarInfo(varOffset, "v1", l1, l2, false, "java/lang/Object"));
您的意思是context.push
创建了mv.visitLocalVariable
。
我认为必须先访问标签l1
和l2
,然后才能访问本地变量。
在访问之前,必须在访问参数时调用之前调用之前调用的标签,并且在访问参数后传递的标签之后,必须调用visitLocalVariable和visitLineNumber方法。
不遵循上述规定可能会导致堆栈图生成错误。因此,在context.push
之后将mv.visitLabel(l5)
移动到底部应该使用正确的堆栈映射生成代码。
答案 1 :(得分:3)
我在ASM列表上询问过,有人提供了这个提示:
“尝试使用带有checkDataFlow选项的CheckClassAdapter来获取更多详细信息。”
这有一些故障排除修复了问题。我很确定这与未正确区分绑定变量的范围以及需要使用visitLocalVariable声明本地变量有关。至少这是我在它不工作和开始工作之间修复的事情之一。