2014-05-06 4 views
7

के माध्यम से बाइटकोड में कोशिश/पकड़ ब्लॉक जोड़ना मैं एएसएम के लिए नया हूं और मुझे बाइटकोड परिवर्तन से संबंधित कुछ मदद चाहिए।एएसएम

प्रश्न: मैं एएसएम के माध्यम से बाइटकोड में पूरी विधि के लिए प्रयास/पकड़ ब्लॉक जोड़ना चाहता हूं और जावा-नॉवरिफ़ विकल्प का उपयोग करके विधि को चलाने के लिए चाहता हूं। मैं पूरी विधि के लिए कोशिश/पकड़ ब्लॉक जोड़ने में सक्षम हो सकता हूं लेकिन जब मैंने विधि को निष्पादित करने का प्रयास किया तो मुझे 'java.lang.VerifyError' मिल रहा है। अगर मैं जावा-नॉवरिफा विकल्प का उपयोग करता हूं तो यह चलाएगा। क्रिप्या मेरि सहायता करे।

नीचे विवरण हैं।

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

मैं उपरोक्त कोड को एएसएम बाइटकोड उपकरण के साथ प्रयास/पकड़ ब्लॉक को शुरू करने के रूप में नीचे परिवर्तित करना चाहता हूं।

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

कोड के नीचे कोड को जोड़ने/पकड़ने के लिए जोड़ें लेकिन जावा-नोवरिफ़ विकल्प के साथ कोड निष्पादित करने में विफल रहता है।

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(); 
     } 

    } 
} 

डीबगिंग के लिए मैंने चेक क्लास एडाप्टर का उपयोग किया है और मुझे सत्यापन समस्या से नीचे मिला है।

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 

मैं उपरोक्त सत्यापन संदेश को समझने में विफल रहा।

+0

सवाल क्या है? – biziclop

+0

प्रश्न यह है कि मैं अपने प्रारंभिक कोड को एएसएम बाइटकोड इंस्ट्रूमेंटेशन का उपयोग करके कोशिश/पकड़ने वाले ब्लॉक को अपने बाद वाले कोड में आउट/कैच ब्लॉक के साथ कैसे बदल सकता हूं। –

उत्तर

4

उपरोक्त अपवाद कंप्यूटिंग स्टैकमैप फ्रेम से संबंधित है। एएसएम ने स्टैकमैप फ्रेम स्वयं प्रदान करने के लिए तंत्र प्रदान किया है। हमें क्लासवाइटर कन्स्ट्रक्टर में COMPUTE_FRAMES के रूप में पैरामीटर ध्वज का उपयोग करने की आवश्यकता है।

पूर्व: क्लासवाइटर cw = new classWriter (ClassWriter.COMPUTE_FRAMES);

सार्वजनिक स्थिर अंतिम int COMPUTE_FRAMES स्वचालित रूप से स्क्रैच से विधियों के स्टैक मानचित्र फ़्रेम की गणना करने के लिए ध्वजांकित करें। यदि यह ध्वज सेट किया गया है, तो MethodVisitor.visitFrame (int, int, java.lang.Object [], int, java.lang.Object []) पर कॉल को अनदेखा कर दिया जाता है, और स्टैक मैप फ़्रेम को पुनः संकलित किया जाता है विधियों बाइटकोड। विज़िटमैक्स विधि के तर्कों को भी नजरअंदाज कर दिया जाता है और बाइटकोड से पुनः संयोजित किया जाता है। दूसरे शब्दों में, computeFrames computeMaxs का तात्पर्य है। एएसएम क्लासवाइटर एपीआई से

5

आपको अपनी कक्षा को ट्रैवर्स करने और प्रक्रिया में संशोधित MethodVisitor का उपयोग करने की आवश्यकता है। यदि आप अपनी पूरी विधि को try - catch निर्माण में लपेटते हैं। आप प्रारंभ और कॉल ब्लॉक के अंत के लिए कॉलबैक को अवरुद्ध करके निर्माण को सम्मिलित करके ऐसा कर सकते हैं।

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(); 
    } 
} 

हालांकि, ध्यान दें कि यह उदाहरण के ढेर नक्शा फ्रेम जो आप जोड़ना अगर आप जावा के लिए बाइट कोड का उत्पादन 7+ जरूरत है शामिल नहीं है: इन विधियों visitCode और visitEnd जो आप इस तरह रोकना कर सकते हैं।

हालांकि ध्यान दें कि यह समाधान आपकी विधि की अपवाद तालिका की शुरुआत में एक प्रमुख हैंडलर पंजीकृत करेगा जो आपकी विधि में मौजूद सभी अन्य प्रयास-अंततः ब्लॉक को ओवरराइड करता है जो पहले से मौजूद हैं!

+0

मैंने आपके सुझावों को लागू किया है और मैं पूरी विधि के लिए सफलतापूर्वक प्रयास/पकड़ ब्लॉक जोड़ने में सक्षम हूं। मैंने इसे संकुचित कर दिया है और इसे सत्यापित किया है। लेकिन जब मैंने उस विधि को चलाने की कोशिश की तो मुझे 'java.lang.VerifyError' मिल रहा है। यदि मैं विधि के साथ विधि चलाता हूं तो मैं विधि को सफलतापूर्वक निष्पादित करने में सक्षम हूं। मैंने अपना प्रश्न संपादित किया है और कोशिश/पकड़ जोड़ने के लिए कोड जोड़ा है। क्या आप मुझे बता सकते हैं कि -नोवरिफ़ विकल्प का उपयोग करके कोड को निष्पादित करने के लिए मुझे क्या करना चाहिए। –

+0

सत्यापनकर्ता क्या कहता है? और आप किस जावा संस्करण का उपयोग कर रहे हैं। जैसा कि मैंने अपने जवाब में कहा था, आपको एक स्टैक मैप फ्रेम की आवश्यकता हो सकती है। एएसएम आपके लिए उनकी गणना कर सकता है। –

+0

अभी अभी मैंने सत्यापनकर्ता संदेश के साथ प्रश्न अपडेट किया है। मैं जावा 7 संस्करण का उपयोग कर रहा हूँ। और एएसएम 5.0.2 जार –

संबंधित मुद्दे