如何使用 ASM 字节码框架修改捕获块代码



我正在尝试更改方法中现有try/catch块的catch块的代码内容。

public static void hello(Throwable throwable) {
    try{
        System.out.println("in try");
    }catch(Exception e){
        System.out.println("in catch");
    }
}

我的目的是在 catch 块中添加一个方法调用。像这样,

public static void hello(Throwable throwable) {
    try{
        System.out.println("in Try");
    }catch(Exception e){
        *passException(e);*
        System.out.println("in catch");
    }
}

注意:我已经尝试覆盖visitTryCatchBlock MethodVisitor方法。并尝试以多种方式访问标签,但没有任何帮助。我在网上的任何文档/指南/示例中都找不到这个。我希望我已经清楚地解释了我在尝试了所有内容后发布这个问题。

如果你在ASM中使用树API,你可以获取类的MethodNode,然后是MethodNode的指令(InsnList)。使用 InsnList 的 toArray() 方法,您可以遍历单个指令。要编辑说明,您可以执行以下操作:

for (MethodNode method : classNode.methods) {
    method.instructions.set(insn, otherInsn); // Sets the instruction to another one
    method.instructions.remove(insn); //Removes a given instruction
    method.instructions.add(insn); //Appends to end
    method.instructions.insert(insn, otherInsn); // Inserts an instruction after the given insn
    method.instructions.insertBefore(insn, otherInsn); // Inserts an instruction before the given insn
}

我个人认为这是编辑方法主体的最简单方法。

目前

尚不清楚您面临的实际障碍是什么,因为您对尝试的描述指向正确的方向,visitTryCatchBlockvisitLabel。这是一个独立的示例,可以完成这项工作:

import java.io.IOException;
import java.lang.reflect.Method;
import org.objectweb.asm.*;
public class EnhanceExceptionHandler {
    static class Victim {
        public static void hello(boolean doThrow) {
            try {
                System.out.println("in try");
                if(doThrow) {
                    throw new Exception("just for demonstration");
                }
            } catch(Exception e){
                System.out.println("in catch");
            }
        }
        static void passException(Exception e) {
            System.out.println("passException(): "+e);
        }
    }
    public static void main(String[] args)
        throws IOException, ReflectiveOperationException {
        Class<EnhanceExceptionHandler> outer = EnhanceExceptionHandler.class;
        ClassReader classReader=new ClassReader(
            outer.getResourceAsStream("EnhanceExceptionHandler$Victim.class"));
        ClassWriter classWriter=new ClassWriter(classReader,ClassWriter.COMPUTE_FRAMES);
        classReader.accept(new ClassVisitor(Opcodes.ASM5, classWriter) {
            private String className;
            @Override
            public void visit(int version, int access, String name, String signature,
                String superName, String[] interfaces) {
                className=name;
                super.visit(version, access, name, signature, superName, interfaces);
            }
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc,
                String signature, String[] exceptions) {
                MethodVisitor visitor
                    = super.visitMethod(access, name, desc, signature, exceptions);
                if(name.equals("hello")) {
                    visitor=new MethodVisitor(Opcodes.ASM5, visitor) {
                        Label exceptionHandler;
                        @Override
                        public void visitLabel(Label label) {
                            super.visitLabel(label);
                            if(label==exceptionHandler) {
                                super.visitInsn(Opcodes.DUP);
                                super.visitMethodInsn(Opcodes.INVOKESTATIC, className,
                                    "passException", "(Ljava/lang/Exception;)V", false);
                            }
                        }
                        @Override
                        public void visitTryCatchBlock(
                            Label start, Label end, Label handler, String type) {
                            exceptionHandler=handler;
                            super.visitTryCatchBlock(start, end, handler, type);
                        }
                    };
                }
                return visitor;
            }
        }, ClassReader.SKIP_FRAMES|ClassReader.SKIP_DEBUG);
        byte[] code=classWriter.toByteArray();
        Method def=ClassLoader.class.getDeclaredMethod(
            "defineClass", String.class, byte[].class, int.class, int.class);
        def.setAccessible(true);
        Class<?> instrumented=(Class<?>)def.invoke(
            outer.getClassLoader(), outer.getName()+"$Victim", code, 0, code.length);
        Method hello=instrumented.getMethod("hello", boolean.class);
        System.out.println("invoking "+hello+" with false");
        hello.invoke(null, false);
        System.out.println("invoking "+hello+" with true");
        hello.invoke(null, true);
    }
}

如您所见,这很简单,只需在visitTryCatchBlock中记录异常处理程序的标签,并在遇到visitLabel中的代码位置后立即注入所需的代码。其余的是批量代码,用于读取和转换类文件并加载结果以进行测试。

最新更新