2012-06-30 45 views
4

我正在使用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); 

回答

3

我問ASM名單上,有人熱心地提供這樣的提示:「使用CheckClassAdapter與checkDataFlow選項有更多的細節嘗試」

這與一些疑難解答解決了問題。我敢肯定,這與無法區分綁定變量的範圍和需要使用visitLocalVariable聲明本地變量有關。至少這是我在它不工作和開始工作時所固定的東西之一。

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

我相信標籤l1l2必須首先訪問之前,您可以訪問本地變量。

method visitor

visitTryCatchBlock的ASM4 Java文檔必須調用之前作爲參數傳遞的標籤已被訪問,而visitLocalVariable和visitLineNumber方法必須後調用作爲參數傳遞標籤已被訪問過。

不遵循上面的說明可能會導致堆棧映射錯誤地生成。因此,在mv.visitLabel(l5)之後將context.push移動到底部應該使用正確的堆棧映射生成代碼。

+0

您的假設是正確的,只是它在調用visitMaxs之前不調用visitLocalVariable,直到方法結束。所以,我認爲這是滿足你描述的其餘部分。抱歉,我的問題沒有說清楚。 – mentics