2014-02-12 96 views
2

我使用Hibernate運行時類工具。然而 奇怪的錯誤發生的情況:奇怪的java.lang.VerifyError

Caused by: java.lang.VerifyError: Bad type on operand stack in method pkg.model.ValueList$1.<init>(Lpkg/model/ValueList;)V at offset 2 
    at pkg.model.ValueList.<init>(ValueList.java:24) 

我已經創建了自己的類加載器適用的轉換和 用它來的自舉整個應用程序。 這裏是適用的轉換方法:

private byte[] transform(byte[] original) { 
     for (ClassFileTransformer transformer : transformers) { 
      try { 
       byte[] transformed = transformer.transform(this, 
         className, null, null, original); 
       if (transformed != null) { 
        original = transformed; 
        classReader = new ClassReader(transformed); 
        try { 
         CheckClassAdapter.verify(classReader, true, 
          new PrintWriter(System.out)); 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
       } 
      } catch (IllegalClassFormatException e) { 
       throw new RuntimeException(e); 
      } 
     } 
     return original; 
    } 

我這個類裝載器運行應用程序並得到下面的輸出:

<init>(Lpkg/model/ValueList;)V 
00000 ValueList$1 ValueList : :  ALOAD 0 
00001 ValueList$1 ValueList : ValueList$1 :  ALOAD 1 
00002 ValueList$1 ValueList : ValueList$1 ValueList :  INVOKEVIRTUAL pkg/model/ValueList$1.$javassist_write_this$0 (Lpkg/model/ValueList;)V 
00003 ValueList$1 ValueList : :  ALOAD 0 
00004 ValueList$1 ValueList : ValueList$1 :  INVOKESPECIAL java/util/AbstractList.<init>()V 
00005 ValueList$1 ValueList : :  RETURN 

我認爲沒有錯的偏移2 我們稱之爲法適當的(ValueList $ 1)和適當的參數(ValueList)。 而且ASM也沒什麼不好,因爲它的驗證器不會引發任何異常。 爲什麼JVM驗證者拒絕這個字節碼?

Hibernate版本是4.1.10.Final。 下面是相關的錯誤代碼的一部分:

public class ValueList { 
    //... 
    @Transient 
    private List<Value> safeValues = new AbstractList<Value>() { 
     // ... 
    } 
    //... 
} 

回答

2

可能是錯誤消息是誤導。 您致電super()不是構造函數主體中的第一條語句。如果您將字節碼轉換回Java,它將如下所示:

class ValueListInner extends AbstractList<Value> { 
    public ValueListInner(ValueList list) { 
     javassist_write_this_outer(list); 
     super();  
    } 

    private void javassist_write_this_outer(ValueList list) { 
     //... 
    } 
} 

這不是一個有效的Java代碼。 它應該看起來像這樣:

<init>(Lpkg/model/ValueList;)V 
00000 ValueList$1 ValueList : :  ALOAD 0 
00001 ValueList$1 ValueList : ValueList$1 :  INVOKESPECIAL java/util/AbstractList.<init>()V 
00002 ValueList$1 ValueList : :  ALOAD 0 
00003 ValueList$1 ValueList : ValueList$1 :  ALOAD 1 
00004 ValueList$1 ValueList : ValueList$1 ValueList :  INVOKEVIRTUAL pkg/model/ValueList$1.$javassist_write_this$0 (Lpkg/model/ValueList;)V 
00005 ValueList$1 ValueList : :  RETURN 
+1

該消息是正確的。在構造器鏈接達到java/lang/Object類型之前,索引爲'0'的變量數組值包含JVM類型'undefined'的值。這個類型不知道'$ javassist_write_this $ 0'這個方法(但是你可以設置這個類型的字段)。只有在調用構造函數鏈後,才能調用此類型的方法,因爲它已由實際類型替換。 –

+0

哦,我明白了,我剛剛在VM Spec中發現:「但是,在調用任何實例初始化方法之前,可以指定在當前類中聲明的實例字段。」所以,在調用super構造函數之前,編譯器會生成PUTFIELD來初始化合成域。我想我應該向javassist團隊發佈一個問題。 –

+0

@raphw,是的,如果你知道這些小細節,那就是邏輯。但同意,當你試圖意識到什麼是錯的時候,這個信息是非常混亂的。 –