2014-05-06 9 views
7

Sono nuovo ad ASM e desidero qualche aiuto correlato alla trasformazione bytecode.Aggiunta del blocco try/catch in bytecode tramite ASM

Domanda: Vorrei aggiungere il blocco try/catch per l'intero metodo in bytecode tramite ASM e voglio eseguire il metodo senza utilizzare l'opzione java -noverify. Posso aggiungere il blocco try/catch per l'intero metodo, ma quando ho provato ad eseguire il metodo sto ottenendo 'java.lang.VerifyError'. Se utilizzo l'opzione java -noverify, verrà eseguita. Mi aiuti per favore.

Di seguito sono riportati i dettagli.

public class Example { 
    public static void hello() { 
     System.out.println("Hello world"); 
    } 
} 

voglio trasformare il codice di cui sopra, come di seguito l'introduzione di blocchi try/catch, con strumentazione ASM bytecode.

public class Example { 
    public static void hello() { 
     try 
     { 
      System.out.println("Hello world"); 
     } catch(Exception ex) { 
     ex.printStackTrace(); 
     } 
    } 
} 

seguito code Aggiungi blocco try/catch, ma non riesce a eseguire il codice con l'opzione -noverify fuori java.

public class InstrumentExample { 

    /** 
    * Our custom method modifier method visitor class. It delegate all calls to 
    * the super class. Do our logic of adding try/catch block 
    * 
    */ 
    public static class ModifierMethodWriter extends MethodVisitor { 

     // methodName to make sure adding try catch block for the specific 
     // method. 
     private String methodName; 

     // below label variables are for adding try/catch blocks in instrumented 
     // code. 
     private Label lTryBlockStart; 
     private Label lTryBlockEnd; 
     private Label lCatchBlockStart; 
     private Label lCatchBlockEnd; 

     /** 
     * constructor for accepting methodVisitor object and methodName 
     * 
     * @param api: the ASM API version implemented by this visitor 
     * @param mv: MethodVisitor obj 
     * @param methodName : methodName to make sure adding try catch block for the specific method. 
     */ 
     public ModifierMethodWriter(int api, MethodVisitor mv, String methodName) { 
      super(api, mv); 
      this.methodName = methodName; 
     } 

     // We want to add try/catch block for the entire code in the method 
     // so adding the try/catch when the method is started visiting the code. 
     @Override 
     public void visitCode() { 
      super.visitCode(); 

      // adding try/catch block only if the method is hello() 
      if (methodName.equals("hello")) { 
       lTryBlockStart = new Label(); 
       lTryBlockEnd = new Label(); 
       lCatchBlockStart = new Label(); 
       lCatchBlockEnd = new Label(); 

       // set up try-catch block for RuntimeException 
       visitTryCatchBlock(lTryBlockStart, lTryBlockEnd, 
         lCatchBlockStart, "java/lang/Exception"); 

       // started the try block 
       visitLabel(lTryBlockStart); 
      } 

     } 

     @Override 
     public void visitMaxs(int maxStack, int maxLocals) { 

      // closing the try block and opening the catch block if the method 
      // is hello() 
      if (methodName.equals("hello")) { 
       // closing the try block 
       visitLabel(lTryBlockEnd); 

       // when here, no exception was thrown, so skip exception handler 
       visitJumpInsn(GOTO, lCatchBlockEnd); 

       // exception handler starts here, with RuntimeException stored 
       // on stack 
       visitLabel(lCatchBlockStart); 

       // store the RuntimeException in local variable 
       visitVarInsn(ASTORE, 2); 

       // here we could for example do e.printStackTrace() 
       visitVarInsn(ALOAD, 2); // load it 
       visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", 
         "printStackTrace", "()V", false); 

       // exception handler ends here: 
       visitLabel(lCatchBlockEnd); 
      } 

      super.visitMaxs(maxStack, maxLocals); 
     } 

    } 

    /** 
    * Our class modifier class visitor. It delegate all calls to the super 
    * class Only makes sure that it returns our MethodVisitor for every method 
    * 
    */ 
    public static class ModifierClassWriter extends ClassVisitor { 
     private int api; 

     public ModifierClassWriter(int api, ClassWriter cv) { 
      super(api, cv); 
      this.api = api; 
     } 

     @Override 
     public MethodVisitor visitMethod(int access, String name, String desc, 
       String signature, String[] exceptions) { 

      MethodVisitor mv = super.visitMethod(access, name, desc, signature, 
        exceptions); 

      // Our custom MethodWriter 
      ModifierMethodWriter mvw = new ModifierMethodWriter(api, mv, name); 
      return mvw; 
     } 

    } 

    public static void main(String[] args) throws IOException { 

     DataOutputStream dout = null; 
     try { 
      // loading the class 
      InputStream in = InstrumentExample.class 
        .getResourceAsStream("Example.class"); 
      ClassReader classReader = new ClassReader(in); 
      ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 

      // Wrap the ClassWriter with our custom ClassVisitor 
      ModifierClassWriter mcw = new ModifierClassWriter(ASM4, cw); 
      ClassVisitor cv = new CheckClassAdapter(mcw); 

      classReader.accept(cv, 0); 

      byte[] byteArray = cw.toByteArray(); 
      dout = new DataOutputStream(new FileOutputStream(new File("Example.class"))); 
      dout.write(byteArray); 

     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } finally { 
      if (dout != null) 
       dout.close(); 
     } 

    } 
} 

Per il debug ho usato CheckClassAdapter e ho avuto sotto problema di verifica.

Message:org.objectweb.asm.tree.analysis.AnalyzerException: Execution can fall off end of the code 
    at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source) 
    at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source) 
    at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source) 
    at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source) 
    at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source) 
    at com.mfr.instrumentation.selenium.work.InstrumentExample.main(InstrumentExample.java:166) 
hello()V 
00000 ?  : L0 
00001 ?  :  GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
00002 ?  :  LDC "Hello world" 
00003 ?  :  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
00004 ?  :  RETURN 
00005 ?  : L1 
00006 ?  :  GOTO L2 
00007 ?  : L3 
00008 ?  :  ASTORE 2 
00009 ?  :  ALOAD 2 
00010 ?  :  INVOKEVIRTUAL java/lang/Exception.printStackTrace()V 
00011 ?  : L2 
    TRYCATCHBLOCK L0 L1 L3 java/lang/Exception 

Non ho compreso il messaggio di verifica precedente.

+0

Qual è la domanda? – biziclop

+0

Domanda è come posso trasformare il mio codice iniziale con il blocco try/catch sul mio ultimo codice avendo provato/catturare il blocco usando la strumentazione bytecode ASM. –

risposta

4

L'eccezione sopra riportata è relativa al calcolo di frame stackmap. ASM ha fornito un meccanismo per fornire i frame stackmap. Dobbiamo usare il parametro flag come COMPUTE_FRAMES nel costruttore di ClassWriter.

Es: ClassWriter cw = new ClassWriter (ClassWriter.COMPUTE_FRAMES);

public static final int COMPUTE_FRAMES Flag per il calcolo automatico dei frame di stack dei metodi da zero. Se questo flag è impostato, le chiamate al metodo MethodVisitor.visitFrame (int, int, java.lang.Object [], int, java.lang.Object []) vengono ignorate e i frame di stack map vengono ricalcolati dal metodo bytecode. Anche gli argomenti del metodo visitMaxs vengono ignorati e ricalcolati dal bytecode. In altre parole, computeFrames implica computeMaxs.

dall'API ClassWriter ASM.

5

È necessario attraversare la classe e utilizzare un MethodVisitor modificato nel processo. Se si avvolge l'intero metodo in un costrutto try - catch. È possibile farlo inserendo il costrutto intercettando i callback per l'inizio e la fine del blocco di chiamata. Questi metodi sono visitCode e visitEnd che si potrebbe intercettare in questo modo:

class MyMethodVisitor extends MethodVisitor { 
    // constructor omitted 

private final Label start = new Label(), 
        end = new Label(), 
        handler = new Label(); 

    @Override 
    public void visitCode() { 
    super.visitCode(); 
    visitTryCatchBlock(start, 
     end, 
     handler, 
     "java/lang/Exception"); 
    visitLabel(start); 
    } 

    @Override 
    public void visitEnd() { 
    visitJumpInsn(GOTO, end); 
    visitLabel(handler); 
    visitMethodInsn(INVOKEVIRTUAL, 
     "java/lang/RuntimeException", 
     "printStackTrace", 
     "()V"); 
    visitInsn(RETURN); 
    visitLabel(lCatchBlockEnd); 
    super.visitEnd(); 
    } 
} 

Si noti tuttavia che questo esempio non include di stack frame mappa che è necessario aggiungere se si produce bytecode per Java 7+.

Tuttavia, questa soluzione registrerà un gestore dominante all'inizio della tabella delle eccezioni del metodo che sostituisce tutti gli altri blocchi try-catch-finally nel metodo già presenti!

+0

Ho implementato i tuoi suggerimenti e posso aggiungere con successo il blocco try/catch per l'intero metodo. L'ho decompilato e verificato. Ma quando ho provato a eseguire questo metodo ho trovato 'java.lang.VerifyError'. Se eseguo il metodo con l'opzione -noverify, posso eseguire correttamente il metodo. Ho modificato la mia domanda e ho aggiunto il codice per aggiungere try/catch. Puoi farmi sapere cosa devo fare per eseguire il codice senza usare l'opzione -noverify. –

+0

Che cosa dice il verificatore? E quale versione di Java stai usando. Come ho detto nella mia risposta, potresti aver bisogno di un frame di stack map. Asm può calcolarli per te. –

+0

Proprio ora ho aggiornato la domanda con il messaggio Verifier. Sto usando la versione di Java 7. e ASM 5.0.2 jar –