2016-02-29 19 views
2
What I am trying to do? 

我想在特定方法的開始和結束處添加try/catch塊。如何覆蓋類文件(asm.ClassWriter.getCommonSuperClass)?

Why am I overriding asm.ClassWriter.getCommonSuperClass(String class1,String class2)? 

我使用的標誌COMPUTE_FRAMES,正因爲如此,asm.ClassWriter.getCommonSuperClass()這個類被調用,它試圖重新加載使用Class.forName()一些類,說的ClassNotFoundException。我讀過某處重寫此方法並確保它獲取這兩個類加載。我得到了規範對象的保持和得到了所有加載的類,但仍有未載入一些類和此方法拋出空指針異常..

Any suggesstions how to override it? 

編輯的問題基於以下響應

我明白這裏是:

1.沒有必要使用,而不是COMPUTE_MAXS COMPUTE_FRAMES,如果我想補充的方法,內容的try/catch塊。

2.如果我想爲方法內容添加try/catch塊(假設只有jdk8),那麼我只需要編寫try/catch塊的ASM部分,其餘部分應該到位。

對於一個方法,它是從一個線程調用:

public void execute()throws IOException{ 
//some code 

} 

下面的代碼應該添加try/catch塊,不應該給任何Java校驗錯誤?:

private Label startFinally = new Label(); 
    public void visitMaxs(int maxStack, int maxLocals) { 
     Label endFinally = new Label(); 
     visitTryCatchBlock(startFinally, endFinally, endFinally, "java/lang/Exception"); 
     visitLabel(endFinally); 
     visitFrame(F_NEW, 0, null, 1, new Object[]{"java/lang/Exception"}); 
     visitVarInsn(ASTORE, 1); 
     visitVarInsn(ALOAD, 1); 
     visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false); 
     visitInsn(RETURN); 
} 

    public void visitCode() {  

     mv.visitLabel(startFinally); 
     super.visitCode(); 
    } 
+0

爲什麼你認爲這些類是已經加載?班級尚未加載並不罕見。 – Holger

回答

3

當您啓動不得不處理getCommonSuperClass您正在進入堆棧圖框架設計要解決的問題。不是讓驗證者進行這樣的公共超類搜索,而是從編譯器可用的信息派生出的框架應該告訴哪種類型要承擔,並驗證正確性比執行此搜索便宜。當然,如果你使用像ASM這樣的框架並且讓它從頭開始方便地計算所有這些框架,而沒有可用的信息給編譯器,那麼你最終會做這麼昂貴的操作,甚至不得不協助ASM以防萬一類型不適用於簡單的Class.forName調用(或者您想避免加載類)。

您應該注意以下兩點:

  • 你不必加載這些類。此方法有意提供兩個字符串並期望結果字符串。如果您有可用的元信息以確定基於名稱的常見超級類型,則可以使用它

  • 當您使用Instrumentation在所有加載的類中搜索名稱時,您可能會錯過該類,因爲它可能尚未加載。更糟的是,當具有相同名稱的類都被加載不同ClassLoader小號


這時你應該想想是否秉承了原意,你可以得到一個更復雜的情況下錯誤的類使用已知信息生成正確的框架是一種選擇。當你測試堆棧映射框架是必需的版本的類時,除了異常處理程序所需的框架之外的所有框架都已經存在。對於沒有框架的舊類文件,無論如何您都不需要計算它們。

當您鏈一個ClassWriter一個ClassReader它不僅會複製成員和說明,但也堆棧幀映射,除非你指定COMPUTE_FRAMES導致ASM丟棄所有訪問過的幀,並從頭開始重新計算它們。所以,我們要做的第一件事情就是要改變COMPUTE_FRAMES回到COMPUTE_MAXS,然後根據需要插入visitFrame電話。

對於覆蓋有一個例外處理程序的整個方法中,我們需要正好一個幀,該處理程序的入口(假設有處理程序本身內沒有分支)。我們已經知道操作數堆棧包含對異常本身的唯一引用 - 異常處理程序始終是這種情況。由於保護代碼橫跨整個方法,該方法中沒有引入額外的局部變量是可用的,所以只有this(如果方法不static)和參數可用,除非該方法的代碼重用用於其他目的(普通的Java代碼通常不會)。但好消息是,除非你想使用它們,否則你不必處理它們。

因此,讓我們假設我們要覆蓋一個異常處理的整個方法,這將捕捉到了異常,打印其堆棧跟蹤和回報(假設void)。然後,我們不需要任何局部變量和整個代碼,完全訪問原代碼後應用,看起來像:

Label endFinally = new Label(); 
visitTryCatchBlock(startFinally, endFinally, endFinally, "java/lang/Exception"); 
visitLabel(endFinally); 
visitFrame(F_NEW, 0, null, 1, new Object[]{"java/lang/Exception"}); 
visitVarInsn(ASTORE, 1); 
visitVarInsn(ALOAD, 1); 
visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false); 
visitInsn(RETURN); 

的邏輯是

visitFrame(F_NEW,       // not derived from a previous frame 
    0, null,        // no local variables 
    1, new Object[]{"java/lang/Exception"} // one exception on the stack 
); 

當然的類型堆棧中的異常必須與visitTryCatchBlock的類型匹配,或者是它的超級類型。請注意,我們要在之後引入的局部變量是不相關的。如果你想重新拋出而不是返回,只需更換

visitInsn(RETURN); 

visitVarInsn(ALOAD, 1); 
visitInsn(ATHROW); 

和有關堆棧幀映射不改變的邏輯。

+0

是的,我理解了這兩點,但只是在執行代碼時確認了驗證錯誤。並可能是我得到驗證錯誤,因爲我沒有調用super.visitMax()。這裏是錯誤:線程「線程-0」中的異常java.lang.VerifyError:操作數堆棧溢出 異常詳細信息: 位置: com/asm/ASM1.printTwo(Ljava/lang/Object; Ljava/lang/Object; )V @ 3:LDC 原因: 超出最大堆棧大小。 – AKS

+0

這可能是由於缺少'super.visitMaxs'。下一次您對異常或錯誤有疑問時,請在您的問題中包含該異常/錯誤... – Holger