Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pretty printing a method in ASM Bytecode

I am trying (with no success) to print only the contents of a given method. The following code almost does the trick:

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

running it with

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

resulting in

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


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

What I'd like to get was just

    SIPUSH 777
    IRETURN

without signature, comments and whatsoever. How can I accomplish that?

like image 274
devoured elysium Avatar asked Nov 02 '11 16:11

devoured elysium


3 Answers

The answer is already pretty old and involves writing much code.

As of asm v5 printing method instructions is straightforward:

// 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);

In your implementation of the ClassVisitor, simply override the visitMethod method. Here an example:

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

The TraceMethodVisitor will receive the event calls for visiting method instructions etc. by the classVisitor. The code will then be printed by the TraceMethodVisitor with the help of the Printer.

like image 162
Leo Gerber Avatar answered Nov 20 '22 19:11

Leo Gerber


This seems to do the trick.. although I don't understand how:

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) {
        }
    }
}

and one can run it using:

@Test
public void someTest() throws IOException {
    String[] ops = BytecodePrettyPrinter.getMethod("java.lang.String", "<init>", null);

    for (String op : ops)
        System.out.println(op);
}
like image 37
devoured elysium Avatar answered Nov 20 '22 20:11

devoured elysium


In ASM 4, there is a new abstraction called Printer. you can pass your own Printer instance (e.g. extend or copy Textifier implementation) in constructor of the TraceClassVisitor.

like image 1
Eugene Kuleshov Avatar answered Nov 20 '22 20:11

Eugene Kuleshov