Bad local variable type in method

bytecode, java, java-bytecode-asm

Solution

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

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

This with some troubleshooting fixed the problem. I'm pretty sure it was related to not properly differentiating between the scope of bound variables, and needing to declare local vars with visitLocalVariable. At least that's one of the things I fixed between it not working and when it started working.

Problem

I'm using ASM 4 to generate some classes on the fly. Everything went quite well until I got to generating code to do exception handling. The generated bytecode is at the bottom. Here is the error I'm getting: ``` 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 on 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 modify it to make it general. Here's the code that's generating the try/catch part: ``` 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); ```

Original source