Как поймать исключение времени выполнения в байт-коде через ASM

Я пытаюсь поймать исключение времени выполнения через исключение. Я могу фиксировать обычные события выхода из метода. Но управление никогда не достигает opcode==Opcodes.ATHROW .

Я думаю, что я делаю что-то неправильно в вызове событий.

Вот мой пример кода:

 public void visitCode() { 
//          mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 
//          mv.visitLdcInsn("Entering method "  + fQMethodName); 
//          mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream","println","(Ljava/lang/String;)V"); 
            }


     @Override
    public void visitInsn(int opcode)
    {



         if (opcode == Opcodes.ATHROW)
         {
                          mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");

          mv.visitLdcInsn("Exiting on exception " );         
             mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                    "(Ljava/lang/String;)V");
         }


        super.visitInsn(opcode);
    }


         public void visitMethodInsn(int opcode, String owner, String name,
                   String desc) {


                  super.visitMethodInsn(opcode, owner, name, desc);
                    //                
                  if (opcode == Opcodes.ATHROW)
                  {
                                              mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");

                   mv.visitLdcInsn("Exiting on exception " + name);         
                      mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                            "(Ljava/lang/String;)V");
                  }
                  else if (!name.equals("println")
                    && !name.equals("<init>")
                    && (opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKESPECIAL
                      || opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKEDYNAMIC)) {




                      this.currentMethod = name;

                   onFinally(opcode);
                  }
                 }

                 private void onFinally(int opcode) {
                                          mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
                    "Ljava/io/PrintStream;");
                  mv.visitLdcInsn("Returning to " + fQMethodName + " from " + currentMethod);
                  mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                    "(Ljava/lang/String;)V");
                 }

Во время исключения во время выполнения существующее содержимое стека очищается, и остается только бросаемый объект. Могу ли я получить верхний элемент стека (т.е. кадр стека в основном) через ASM?

Заранее спасибо.

ИЗМЕНИТЬ:

Вот моя модифицированная программа после добавления блока try-catch.

@Holger: Спасибо за помощь. Действительно ценю это.

Итак, я пытаюсь вставить динамический блок try-catch в MethodVisitor ASM.

Вот мой код:

public void visitCode() 
    {

        super.visitCode();

        this.visitTryCatchBlock(lblTryBlockStart, lblTryBlockEnd, lblCatchExceptionBlockStart, "java/lang/Exception");
        this.visitLabel(lblTryBlockStart);

    }  



    public void visitMaxs(int maxStack, int maxLocals)
    {
        // visit try block end label
        this.visitLabel(lblTryBlockEnd);
        // visit normal execution exit block
        this.visitJumpInsn(Opcodes.GOTO, exitBlock);

        // visit catch exception block
        this.visitLabel(lblCatchExceptionBlockStart);
        // store the exception
        this.visitVarInsn(Opcodes.ASTORE, 1);
        // load the exception
        this.visitVarInsn(Opcodes.ALOAD, 1);
        // call printStackTrace()
        //this.visitInsn(Opcodes.ATHROW);
        this.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V");


        // exit from this dynamic block
        this.visitLabel(exitBlock);

        super.visitMaxs(maxStack+2, maxLocals+2);

    }

Мое намерение состоит в том, чтобы добавить динамический блок try-catch, и если возникнет какое-либо исключение, распечатать его.


person DukeLover    schedule 20.09.2013    source источник
comment
Знаете ли вы, где выбрасывается фактическое исключение RuntimeException? Если это вызвано самой JRE, это не сработает, поскольку (я предполагаю), что вы не преобразуете классы в пакете java (чего вам в любом случае не следует делать).   -  person Emiel    schedule 20.09.2013
comment
Ваше последнее обновление работает? Я тоже хотел добавить блок try catch в метод. Пожалуйста, ответь.   -  person AKS    schedule 04.02.2016


Ответы (1)


RuntimeExceptions не нуждаются в инструкции athrow для запуска. Инструкция idiv или irem может выдать ArithmeticException, инструкция getfield, putfield или invoke… может выдать NullPointerException, и это лишь некоторые примеры. Таким образом, вы не найдете инструкции по броску в этих местах.

Единственный способ перехватить их все — внедрить обработчик исключений, который обрабатывает ваш код и повторно выдает исключение, если вы хотите.

person Holger    schedule 20.09.2013
comment
Я добавил Label операторов и посетил их. И я также пробовал метод visitTryCatchBlock. Но, после этого я не мог двигаться дальше. Не могли бы вы немного помочь мне в этом? - person DukeLover; 21.09.2013
comment
Это правильно ?mv.visitTryCatchBlock(lTryBlockStart, lTryBlockEnd, lCatchBlockStart, "java/lang/Exception"); super.visitMethodInsn(opcode, owner, name, desc); mv.visitLabel(lTryBlockEnd); mv.visitJumpInsn(org.objectweb.asm.Opcodes.GOTO,lCatchBlockEnd); mv.visitLabel(lCatchBlockStart); mv.visitVarInsn(org.objectweb.asm.Opcodes.ASTORE, 1); mv.visitVarInsn(org.objectweb.asm.Opcodes.ALOAD, 1); mv.visitMethodInsn(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V"); mv.visitLabel(lCatchBlockEnd); } - person DukeLover; 22.09.2013
comment
Это выходит за рамки комментариев; Я рекомендую открыть новый вопрос для этого. - person Holger; 23.09.2013
comment
Я отредактировал вопрос. Пожалуйста, проверьте . Заранее спасибо. - person DukeLover; 25.09.2013
comment
Вы написали, что сделали, но не сказали, в чем проблема с этим кодом. - person Holger; 25.09.2013
comment
Я хочу зафиксировать исключение с полной трассировкой стека. Сейчас моя проблема в том, что я не могу захватить объект исключения. Я могу сделать this.visitInsn(Opcodes.ATHROW) . Но я не могу его сохранить. Я делаю this.visitInsn(Opcodes.ASTORE,1) , но думаю, что делаю что-то не так. - person DukeLover; 25.09.2013
comment
Вам следует поработать над описанием проблемы. Что означает «Я не могу это сохранить»? В любом случае, ваша последовательность должна быть astore_1 aload_1 invokevirtual (printStackTrace) aload_1 athrow. См. также: docs.oracle.com /javase/specs/jvms/se7/html/ - person Holger; 25.09.2013
comment
Спасибо за ваше предложение, обязательно возьму на заметку. Теперь, перейдя к проблеме, я хочу передать исключение одному из моих написанных методов, который принимает исключение в качестве аргумента, поэтому я должен вызвать visitLdcInsn, верно? Вот где я застрял - person DukeLover; 25.09.2013
comment
ldc не имеет ничего общего с вызовом методов. Если ваш метод public static void exceptionOccurred(Throwable) {} в классе foo.Bar, просто выполните aload_1, чтобы поместить аргумент в стек, а затем visitMethodInsn(Opcodes.INVOKESTATIC, "foo/Bar", "exceptionOccurred", "(Ljava/lang/Throwable;)V"); - person Holger; 25.09.2013
comment
давайте продолжим это обсуждение в чате - person DukeLover; 25.09.2013
comment
@Holger, я получаю эту ошибку. JSR/RET не поддерживаются в параметре calculateFrame. Какие-либо предложения? - person AKS; 25.02.2016