首页 文章

在ASM字节码中打印方法很漂亮

提问于
浏览
5

我正在尝试(没有成功)仅打印给定方法的内容 . 以下代码几乎可以解决问题:

class MyTraceMethodVisitor extends MethodVisitor {
        public MyTraceMethodVisitor(MethodVisitor mv) {
            super(Opcodes.ASM4, mv);
        }

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

    class MyClassVisitor extends ClassVisitor {
        public MyClassVisitor(ClassVisitor cv) {
            super(Opcodes.ASM4, cv);
        }

        @Override
        public FieldVisitor visitField(int access, String name, String desc,
                String signature, Object value) {
            return null;
        }

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

            if (name.equals("get777"))
                return new MyTraceMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));

            return null;
        }
    }

运行它

ClassReader classReader = new ClassReader("something.Point");
PrintWriter printWriter = new PrintWriter(System.out);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(printWriter);
MyClassVisitor myClassVisitor = new MyClassVisitor(traceClassVisitor);
classReader.accept(myClassVisitor, ClassReader.SKIP_DEBUG);

导致

// class version 50.0 (50)
// access flags 0x21
public class something/Point {


  // access flags 0x1
  public get777()I
    SIPUSH 777
    IRETURN
}

我想得到的只是

SIPUSH 777
    IRETURN

没有签名,评论和任何 . 我怎么能做到这一点?

4 回答

  • 3

    答案已经很老了,需要编写很多代码 .

    截至asm v5打印方法说明很简单:

    // Setup for asm ClassReader, ClassWriter and your implementation of the ClassVisitor(e.g.: YourClassVisitor)
    final ClassReader reader = new ClassReader(classBytes);
    final ClassWriter writer = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
    final ClassVisitor visitor =new YourClassVisitor(Opcodes.ASM5, visitor);
    

    在您的ClassVisitor实现中,只需覆盖visitMethod方法即可 . 这是一个例子:

    public class YourClassVisitor extends ClassVisitor {
        public InstrumentationClassVisitor(int api, ClassVisitor cv) {
            super(api, cv);
        }
    
        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
            Printer p = new Textifier(Opcodes.ASM5) {
                @Override
                public void visitMethodEnd() {
                    print(new PrintWriter(System.out)); // print it after it has been visited
                }
            };
            return new TraceMethodVisitor(mv, p);
        }
    }
    

    TraceMethodVisitor将通过classVisitor接收访问方法指令等的事件调用 . 然后,TraceMethodVisitor将在打印机的帮助下打印代码 .

  • 1

    这似乎可以解决问题..虽然我不明白如何:

    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.util.List;
    
    import org.objectweb.asm.Attribute;
    import org.objectweb.asm.ClassReader;
    import org.objectweb.asm.ClassVisitor;
    import org.objectweb.asm.Handle;
    import org.objectweb.asm.Label;
    import org.objectweb.asm.MethodVisitor;
    import org.objectweb.asm.Opcodes;
    import org.objectweb.asm.util.Printer;
    import org.objectweb.asm.util.Textifier;
    import org.objectweb.asm.util.TraceClassVisitor;
    
    
    public class BytecodePrettyPrinter {
        /**
         * Gets us the bytecode method body of a given method.
         * @param className The class name to search for.
         * @param methodName The method name.
         * @param methodDescriptor The method's descriptor. 
         *                         Can be null if one wishes to just get the first 
         *                         method with the given name.
         * @throws IOException
         */
        public static String[] getMethod(String className, String methodName, String methodDescriptor) throws IOException {
            ClassReader classReader = new ClassReader(className);
            StringWriter stringWriter = new StringWriter();
            PrintWriter printWriter = new PrintWriter(stringWriter);
            TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, new SourceCodeTextifier(), printWriter);
            MethodSelectorVisitor methodSelectorVisitor = new MethodSelectorVisitor(traceClassVisitor, methodName, methodDescriptor);
            classReader.accept(methodSelectorVisitor, ClassReader.SKIP_DEBUG);
    
            return toList(stringWriter.toString());
        }
    
        /**
         * Gets us the bytecode method body of a given method.
         * @param className The class name to search for.
         * @param methodName The method name.
         * @throws IOException
         */
        public static String[] getMethod(String className, String methodName) throws IOException {
            return getMethod(className, methodName, null);
        }
    
        private static String[] toList(String str) {
            //won't work correctly for all OSs
            String[] operations = str.split("[" + "\n" + "]");
    
            for (int i = 0; i < operations.length; ++i) {
                operations[i] = operations[i].trim();
            }
    
            return operations;
        }
    
        private static class MethodSelectorVisitor extends ClassVisitor {
            private final String methodName;
            private final String methodDescriptor;
    
            public MethodSelectorVisitor(ClassVisitor cv, String methodName, String methodDescriptor) {
                super(Opcodes.ASM4, cv);
                this.methodName = methodName;
                this.methodDescriptor = methodDescriptor;
            }
    
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc,
                    String signature, String[] exceptions) {
    
                if (methodName.equals(name)) {
                    if (methodDescriptor == null)
                        return new MaxVisitFilterMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
    
                    if (methodDescriptor.equals(desc))
                        return new MaxVisitFilterMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
                }
    
                return null;
            }
        }
    
        private static class MaxVisitFilterMethodVisitor extends MethodVisitor {
            public MaxVisitFilterMethodVisitor(MethodVisitor mv) {
                super(Opcodes.ASM4, mv);
            }
    
            @Override
            public void visitMaxs(int maxStack, int maxLocals) {
            }
        }
    
    
        private static class SourceCodeTextifier extends Printer {
            public SourceCodeTextifier() {
                this(Opcodes.ASM4);
            }
    
            protected SourceCodeTextifier(final int api) {
                super(api);
            }
    
            @Override
            public void visit(
                final int version,
                final int access,
                final String name,
                final String signature,
                final String superName,
                final String[] interfaces)
            {
            }
    
            @Override
            public void visitSource(final String file, final String debug) {
            }
    
            @Override
            public void visitOuterClass(
                final String owner,
                final String name,
                final String desc)
            {
            }
    
            @Override
            public Textifier visitClassAnnotation(
                final String desc,
                final boolean visible)
            {
                return new Textifier();
            }
    
            @Override
            public void visitClassAttribute(final Attribute attr) {
            }
    
            @Override
            public void visitInnerClass(
                final String name,
                final String outerName,
                final String innerName,
                final int access)
            {
            }
    
            @Override
            public Textifier visitField(
                final int access,
                final String name,
                final String desc,
                final String signature,
                final Object value)
            {
                return new Textifier();
            }
    
            @Override
            public Textifier visitMethod(
                final int access,
                final String name,
                final String desc,
                final String signature,
                final String[] exceptions)
            {
                Textifier t = new Textifier();
                text.add(t.getText());
                return t;
            }
    
            @Override
            public void visitClassEnd() {
            }
    
            @Override
            public void visit(final String name, final Object value) {
            }
    
    
            @Override
            public void visitEnum(
                final String name,
                final String desc,
                final String value)
            {
            }
    
            @Override
            public Textifier visitAnnotation(
                final String name,
                final String desc)
            {
                return new Textifier();
            }
    
            @Override
            public Textifier visitArray(
                final String name)
            {
                return new Textifier();
            }
    
            @Override
            public void visitAnnotationEnd() {
            }
    
            @Override
            public Textifier visitFieldAnnotation(
                final String desc,
                final boolean visible)
            {
                return new Textifier();
            }
    
            @Override
            public void visitFieldAttribute(final Attribute attr) {
                visitAttribute(attr);
            }
    
            @Override
            public void visitFieldEnd() {
            }
    
            @Override
            public Textifier visitAnnotationDefault() {
                return new Textifier();
            }
    
            @Override
            public Textifier visitMethodAnnotation(
                final String desc,
                final boolean visible)
            {
                return new Textifier();
            }
    
            @Override
            public Textifier visitParameterAnnotation(
                final int parameter,
                final String desc,
                final boolean visible)
            {
                return new Textifier();
            }
    
            @Override
            public void visitMethodAttribute(final Attribute attr) {
            }
    
            @Override
            public void visitCode() {
            }
    
            @Override
            public void visitFrame(
                final int type,
                final int nLocal,
                final Object[] local,
                final int nStack,
                final Object[] stack)
            {
            }
    
            @Override
            public void visitInsn(final int opcode) {
            }
    
            @Override
            public void visitIntInsn(final int opcode, final int operand) {
            }
    
            @Override
            public void visitVarInsn(final int opcode, final int var) {
            }
    
            @Override
            public void visitTypeInsn(final int opcode, final String type) {
            }
    
            @Override
            public void visitFieldInsn(
                final int opcode,
                final String owner,
                final String name,
                final String desc)
            {
            }
    
            @Override
            public void visitMethodInsn(
                final int opcode,
                final String owner,
                final String name,
                final String desc)
            {
            }
    
            @Override
            public void visitInvokeDynamicInsn(
                String name,
                String desc,
                Handle bsm,
                Object... bsmArgs)
            {
            }
    
            @Override
            public void visitJumpInsn(final int opcode, final Label label) {
            }
    
            @Override
            public void visitLabel(final Label label) {
            }
    
            @Override
            public void visitLdcInsn(final Object cst) {
            }
    
            @Override
            public void visitIincInsn(final int var, final int increment) {
            }
    
            @Override
            public void visitTableSwitchInsn(
                final int min,
                final int max,
                final Label dflt,
                final Label... labels)
            {
            }
    
            @Override
            public void visitLookupSwitchInsn(
                final Label dflt,
                final int[] keys,
                final Label[] labels)
            {
            }
    
            @Override
            public void visitMultiANewArrayInsn(final String desc, final int dims) {
            }
    
            @Override
            public void visitTryCatchBlock(
                final Label start,
                final Label end,
                final Label handler,
                final String type)
            {
            }
    
            @Override
            public void visitLocalVariable(
                final String name,
                final String desc,
                final String signature,
                final Label start,
                final Label end,
                final int index)
            {
            }
    
            @Override
            public void visitLineNumber(final int line, final Label start) {
            }
    
            @Override
            public void visitMaxs(final int maxStack, final int maxLocals) {
            }
    
            @Override
            public void visitMethodEnd() {
            }
    
            public void visitAttribute(final Attribute attr) {
            }
        }
    }
    

    并且可以使用以下命令运行它:

    @Test
    public void someTest() throws IOException {
        String[] ops = BytecodePrettyPrinter.getMethod("java.lang.String", "<init>", null);
    
        for (String op : ops)
            System.out.println(op);
    }
    
  • 4

    在ASM 4中,有一个名为Printer的新抽象 . 您可以在TraceClassVisitor的构造函数中传递您自己的Printer实例(例如,扩展或复制Textifier实现) .

  • 0

    我能想到的最简单的事情是使用正则表达式或其他类型的字符串匹配来过滤掉指令 .

    例如,使用 OutputStreamWriter 来写入 String . 保持all ASM opcode types的字符串值数组,然后如果 String 中的一行包含操作码字符串,则该行是一条指令 .

相关问题