2013-12-22 81 views
0

我有一個方法,我已經在Java 7(主要版本51)類中進行了更改。使用javap,我查看了字節碼和堆棧幀映射。一切都看起來不錯:爲什麼Java 7 Bytecode Verifier會抱怨這個堆棧框架?

public int addOne(int); 
flags: ACC_PUBLIC 
Code: 
    stack=2, locals=2, args_size=2 
    0: iload_1 
    1: iconst_0 
    2: invokestatic #50     // Method isSomething:(I)Z 
    5: ifeq   12 
    8: iconst_0 
    9: goto   13 
    12: iconst_1 
    13: iadd 
    14: ireturn 

StackMapTable: number_of_entries = 2 
    frame_type = 255 /* full_frame */ 
    offset_delta = 12 
    locals = [ class test/Target, int ] 
    stack = [ int ] 
    frame_type = 255 /* full_frame */ 
    offset_delta = 0 
    locals = [ class test/Target, int ] 
    stack = [ int, int ] 

這驗證拋出此異常:

java.lang.VerifyError: Expecting a stackmap frame at branch target 12 
Exception Details: 
    Location: 
    test/Target.addOne(I)I @5: ifeq 
    Reason: 
    Expected stackmap frame at this location. 
    Bytecode: 
    0000000: 1b03 b800 3299 0007 03a7 0004 0460 ac 

是什麼力量推動我瘋狂的是,我有編譯器生成從Java源相同的代碼,它看起來是這樣的:

public int addOne(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=2, args_size=2 
     0: iload_1 
     1: iconst_0 
     2: invokestatic #16     // Method isSomething:(I)Z 
     5: ifeq   12 
     8: iconst_0 
     9: goto   13 
     12: iconst_1 
     13: iadd 
     14: ireturn 

     StackMapTable: number_of_entries = 2 
      frame_type = 76 /* same_locals_1_stack_item */ 
      stack = [ int ] 
      frame_type = 255 /* full_frame */ 
      offset_delta = 0 
      locals = [ class test/Target, int ] 
      stack = [ int, int ] 

注意,僅堆棧幀中的地圖差異是噸他合成的地圖有全部的框架 - 但這不應該導致差異。有誰知道爲什麼更靈活可能不喜歡我的綜合地圖?

+0

你是什麼用來修改字節碼? – Vulcan

+0

Javassist。我正在使用自己的代碼來計算堆棧幀。 –

+0

您的合成堆棧地圖框中的所有內容都適合我。也許這個問題是由堆棧映射幀的順序造成的?如果'same_locals_1_stack_item'框架在全幀之前被聲明*,我不認爲它會讓本地人複製,導致該框架被忽略並導致錯誤。我不熟悉框架的順序是否重要,但我確定它必須在某種程度上對'same_locals_1_stack_item'類型正常運行非常重要,所以它可能是一個延伸,但是嘗試交換堆棧的順序地圖框架。 – Vulcan

回答

1

我無法重現此問題。也許你正在創建堆棧框架,javap仍會讀取,但實際上並不有效?因爲我編輯的類具有相同的javap輸出,但它驗證很好。如果你發佈了實際的類文件,我可以看看我是否能夠找到問題,因爲我不認爲只有Javap輸出可以做什麼。

來源:

public class ArrayTest { 
    public int addOne(int x){ 
     return x + (isSomething(0) ? 0 : 1); 
    } 

    public static boolean isSomething(int z) {return true;} 
} 
方法

javap的輸出在原班

public int addOne(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=2, args_size=2 
     0: iload_1 
     1: iconst_0 
     2: invokestatic #2     // Method isSomething:(I)Z 
     5: ifeq   12 
     8: iconst_0 
     9: goto   13 
     12: iconst_1 
     13: iadd 
     14: ireturn 
     StackMapTable: number_of_entries = 2 
      frame_type = 76 /* same_locals_1_stack_item */ 
      stack = [ int ] 
      frame_type = 255 /* full_frame */ 
      offset_delta = 0 
      locals = [ class ArrayTest, int ] 
      stack = [ int, int ] 
方法

javap的輸出編輯類

public int addOne(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=2, args_size=2 
     0: iload_1 
     1: iconst_0 
     2: invokestatic #15     // Method ArrayTest.isSomething:(
)Z 
     5: ifeq   12 
     8: iconst_0 
     9: goto   13 
     12: iconst_1 
     13: iadd 
     14: ireturn 
     StackMapTable: number_of_entries = 2 
      frame_type = 255 /* full_frame */ 
      offset_delta = 12 
      locals = [ class ArrayTest2, int ] 
      stack = [ int ] 
      frame_type = 255 /* full_frame */ 
      offset_delta = 0 
      locals = [ class ArrayTest2, int ] 
      stack = [ int, int ] 

正如你所看到的,我也有同樣Javap輸出,但我的課程工作得很好。

+1

這個答案是錯誤的。首先:*第一個*框架沒有添加這個+1偏移量。其次,幀偏移量爲12和0,所以兩個幀的偏移量爲12和13.在編譯器生成的例子中,我們從第一個代碼中減去64來得到偏移量12.如果第一個偏移量爲11,那麼你會得到一個來自驗證者的「錯誤抵消」例外。 –

+1

@Charles良好的捕獲。我想這就是我發佈前未測試的結果。我會解決它。 – Antimony

+0

我接受這個答案是因爲它突出顯示了javap顯示的內容隱藏了真正的問題(見下文)。 –

1

答案是javassist很糟糕,我非常遺憾地使用它。

StackMapTable屬性是從致電CodeAttribute.getAttribute(String tag)獲得的。儘管這是您訪問它的方式,但除非它是StackMapTable類型,否則沒有API可將其添加回來。接受香草AttributeInfo作爲參數的唯一API位於MethodInfo類中。

如果方法不需要(或有)堆棧幀,您會得到一個null。如果爲新的堆棧幀映射創建AttributeInfo結構,則不應將其添加到MethodInfo(其中addAttribute API),而應將其添加到CodeAttribute所屬的位置。

這是我在做什麼:

MethodInfo mi ... 
AttributeInfo attr ... 

mi.addAttribute(attr); 

這是我需要做的:

CodeAttribute ca ... 
ca.getAttributes().add(attr); 

(當然,ca.getAttributes()返回一個類型化的List因爲我們都懷念2004年。)

我挖成允許您將StackFramMap添加到CodeAttribute的方法和周圍想通了這項工作缺乏一個通用的API。

使用頂層構造的結果是javap會使您看起來有一個正確的StackMapTable。您,但它附加到錯誤的對象,並且您不能從javap輸出中看到。

我沒有爲我的項目使用ASM,因爲我發現它對訪問者模式的迷戀使用令人討厭。我現在承認這是一個糟糕的決定。由於自2012年以來javassist還沒有更新,我想知道該項目是否已經死亡。我肯定會推出一整車修訂版。這是一團糟。

編輯哦哇。 Javassist內部代碼假定任何StackMapTable都將其歸類爲其自己的內部StackMapTable類型(因爲您還將如何添加StackMapTable屬性)。我想我可以創建我自己的StackMapTable實例,除了SFM構造函數沒有明顯的原因被包保護。它只是變得越來越壞...

相關問題