Bad local variable type in method

I use ASM 4 to generate some classes on the fly. Everything went well until I received the code for handling exceptions. The generated bytecode is at the bottom. Here is the error I get:

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) ... 

Here's the bytecode:

 // 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}] } 

I used ASMifier to start with this:

 public static Object trycatch(Object test, Lambda1 handler) { Object v; try { v = test; } catch (Throwable e) { v = handler.apply(e); } return v; } 

but then I had to change it to make it general. Here is the code that the try / catch part generates:

  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); 
+4
source share
2 answers

I asked on the ASM list, and someone kindly provided this advice:

"Try using the CheckClassAdapter with the checkDataFlow option to have more details."

This fixes the problem with some troubleshooting. I am pretty sure that this was due to the fact that it was not properly differentiated between the scope of related variables and that local vars had to be declared using visitLocalVariable. At least one of the things that I installed between them does not work and when it started to work.

+3
source

I answer the question on the assumption that when you say:

 // 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")); 

you mean that context.push creates mv.visitLocalVariable .

I believe that the labels l1 and l2 should be previewed before you can visit the local variable.

See java document asm4 visitor method

visitTryCatchBlock must be called before the tags passed as arguments are visited, and calls visitLocalVariable and visitLineNumber must be called after the tags passed as arguments have been visited.

Failure to comply with the above can lead to improper generation of the stack structure. Therefore, moving context.push down after mv.visitLabel(l5) should generate code with the correct stack map.

+4
source

All Articles