2013-04-26 60 views
6

我正在嘗試通過製作精美的java-asm庫(版本4)生成代碼。 更具體地說,我想在每次回調之前將代碼追加到方法的末尾。我成功地能夠在方法的代碼之前添加代碼。但是目前我不知道如何執行上述轉換。我真的很感謝能如何實現這一點的指針。如何在每次退貨之前向消息添加代碼?

MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); 
mv.visitCode(); 
mv.visitVarInsn(Opcodes.ALOAD, 42); 
return mv; 
+1

莫非你提供了一個在方法代碼之前添加代碼的例子嗎? – CamHart 2015-03-17 06:08:32

回答

7

有兩種解決方法:

1.使用visitInsn(int opcode)方法MethodVisitor

//this is the custom method visitor 
private class InsertInitCodeBeforeReturnMethodVisitor extends MethodVisitor{ 

    public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv) { 
     super(Opcodes.ASM4, mv); 
    } 

    @Override 
    public void visitInsn(int opcode) { 
     //whenever we find a RETURN, we instert the code, here only crazy example code 
     switch(opcode) { 
      case Opcodes.IRETURN: 
     case Opcodes.FRETURN: 
     case Opcodes.ARETURN: 
     case Opcodes.LRETURN: 
     case Opcodes.DRETURN: 
     case Opcodes.RETURN: 
       mv.visitVarInsn(Opcodes.ALOAD, 42); 
       break; 
      default: // do nothing 
     } 
     super.visitInsn(opcode); 
    } 
} 

2.使用onMethodExit(int opcode)方法在AdviceAdapterorg.objectweb.asm.commons

//this is the custom method visitor 
private class InsertInitCodeBeforeReturnMethodVisitor extends AdviceAdapter{ 

    public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv, int access, String name, String desc) { 
     super(Opcodes.ASM4, mv, access, name, desc); 
    } 

    @Override 
    protected void onMethodExit(int opcode) { 
     if(opcode != Opcdoes.ATHROW) { 
      mv.visitVarInsn(Opcodes.ALOAD, 42); 
     } 
    } 
} 

我個人喜歡AdviceAdapter更好,因爲它就像你有與第一溶液做去除使得實際調用到原來的返回指令的頭痛(如super.visitInsn(opcode);)。其次,它提供了一個很好的抽象,專門用於訪問RETURN指令(和ATHORW);這在香草MethodVisitor中的visitInsn(int opcode)方法中是不正確的,在這種情況下,您必須檢測訪問RETURN指令,其中包括可能與可能或不可能與手頭問題相關的許多其他指令,如DUPs,ICONST_0等。

但是,這又取決於手頭的問題。如果這是正在執行的唯一儀器,我將堅持AdviceAdapter。如果您還有其他事情要與RETURN指示一起進行,我可能會留下簡單的MethodVisitor,因爲它可能會給我更多的靈活性。這就是說,我一直在使用AdviceAdapter一年多的時間來完成一個重型儀器驅動的項目,到目前爲止它的工作狀況很好!


編輯:

應用方法,遊客

它往往並不清楚如何使用或應用的方法,訪問者/法適配器(至少對我來說) ,所以我在這裏彙總了一個快速代碼示例:gist.github.com/VijayKrishna/1ca807c952187a7d8c4d,它顯示瞭如何通過其相應的類訪問者/類適配器使用方法適配器。在示例代碼片段中,我已經改變了我在這個答案中使用的方法適配器的名稱,但他們也做了同樣的事情。此外,該代碼片段顯示了一個擴展AdviceAdapter的方法適配器。

總之,你先「調用」之類的適配器如下:

ClassReader cr = new ClassReader(in); 
ClassWriter cw = new ClassWriter(ClassReader.EXPAND_FRAMES); 
ReturnAdapter returnAdapter = new ReturnAdapter(cw, className); 
cr.accept(returnAdapter, 0); 

你再跟進調整方法作爲類適配器的visitMethod方法如下:

MethodVisitor mv; 
mv = cv.visitMethod(access, name, desc, signature, exceptions); 
mv = new MethodReturnAdapter(Opcodes.ASM4, className, access, name, desc, mv); 
return mv; 
+1

哇!感謝這個非常詳細的答案! – nanoquack 2013-04-27 11:17:49

+0

我發現的所有文檔中缺少的是如何實際應用'AdviceAdapter'? – 2014-05-26 17:20:01

+0

@JarrodRoberson我在這裏彙總了一個快速代碼示例:https://gist.github.com/VijayKrishna/1ca807c952187a7d8c4d。看看這是否有道理。如果您有任何問題,只需將它們作爲意見發表;我會盡快回復他們。 – vijay 2014-05-26 18:32:28

3

其實它變得很容易,但我在完全錯誤的地方尋找。 爲了在任意位置轉換方法主體,您必須創建一個自定義的MethodVisitor子類,並通過它傳遞現有的MethodVisitor以執行您希望它執行的轉換。在我的例子中,每當定製MethodVisitor中發現的操作碼RETURN,它增加了代碼,像這樣:

public class ActorInterfaceTransformer extends ClassVisitor { 
    public ActorInterfaceTransformer(ClassVisitor cv) { 
     super(Opcodes.ASM4, cv); 
    } 

    @Override 
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 
     //if the method is the one we want to transform 
     if(name.equals("<init>")){ 
      //... then we pipe the method visitor 
      MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); 
      return new InsertInitCodeBeforeReturnMethodVisitor(mv); 
     } 
     return super.visitMethod(access, name, desc, signature, exceptions); 
    } 
} 

//this is the custom method visitor 
private class InsertInitCodeBeforeReturnMethodVisitor extends MethodVisitor{ 

    public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv) { 
     super(Opcodes.ASM4, mv); 
    } 

    @Override 
    public void visitInsn(int opcode) { 
     //whenever we find a RETURN, we instert the code, here only crazy example code 
     if(opcode==Opcodes.RETURN){ 
      mv.visitVarInsn(Opcodes.ALOAD, 42); 
     } 
     super.visitInsn(opcode); 
    } 
} 
+1

你忽略了非空的返回類型,比如'ARET','LRET','FRET'等 – afk5min 2013-04-26 11:04:43

+0

是的,但我想將代碼插入到constructir中,這對我來說並不重要。儘管如此,這就是爲什麼我已經接受來自維傑的答案作爲解決方案 – nanoquack 2013-04-28 07:12:20

相關問題