title: Adding try/catch block in bytecode through ASM
body: <p>I am new to ASM and I want some help related to bytecode transformation.</p>
  
  <p>Question: I would like to add try/catch block for the entire method in bytecode through ASM and want to run the method with out using java -noverify option. I can able to add try/catch block for the entire method but when I tried to execute the method I am getting 'java.lang.VerifyError'. If I use java -noverify option then it will run. Please help me.</p>
  
  <p>Below are the details.</p>
  
  <pre><code>public class Example {
      public static void hello() {
          System.out.println("Hello world");
      }
  }
  </code></pre>
  
  <p>I want to transform the above code as below introducing try/catch blocks, with ASM bytecode instrumentation.</p>
  
  <pre><code>public class Example {
    public static void hello() {
         try
         {
            System.out.println("Hello world");
         } catch(Exception ex) {
           ex.printStackTrace();
         }
      }
  }
  </code></pre>
  
  <p>Below code add try/catch block but fails to execute the code with out java -noverify option. </p>
  
  <pre><code>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();
          }
  
      }
  }
  </code></pre>
  
  <p>For debugging I have used CheckClassAdapter and I got below verification problem.</p>
  
  <pre><code>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
  </code></pre>
  
  <p>I failed to understand the above verification message.  </p>