我正在使用 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);
最佳答案
我在回答这个问题时假设您说:
// 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
,然后才能访问局部变量。
参见 method visitor 的 ASM4 java 文档
visitTryCatchBlock must be called before the labels passed as arguments have been visited, and the visitLocalVariable and visitLineNumber methods must be called after the labels passed as arguments have been visited.
不遵循上述内容可能会导致堆栈映射生成不正确。因此,将 context.push
移动到 mv.visitLabel(l5)
之后的底部应该会生成具有正确堆栈映射的代码。
关于java - 方法中的局部变量类型错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11272363/