2011-11-02 17 views
5

Estoy intentando (sin éxito) imprimir solo el contenido de un método dado. El código casi siguiente hace el truco:Bonito imprimir un método en ASM Bytecode

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

ejecutarlo con

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

resultando en

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


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

Lo que me gustaría conseguir era sólo

SIPUSH 777 
    IRETURN 

sin firma, comentarios y lo que sea. ¿Cómo puedo lograr eso?

Respuesta

4

La respuesta ya es bastante viejo y consiste en escribir mucho código

A partir de asm v5 instrucciones método de impresión es sencillo:

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

En su aplicación de la ClassVisitor, simplemente reemplazar el método visitMethod. Aquí un ejemplo:

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

El TraceMethodVisitor recibirá el evento llama para visitar las instrucciones del método etc. por el classVisitor. El código será impreso por TraceMethodVisitor con la ayuda de la impresora.

+3

También eche un vistazo en la guía asm4 en la página 59. –

0

Lo más fácil que se me ocurre es usar una expresión regular u otro tipo de coincidencia de cadenas que filtra solo las instrucciones.

Por ejemplo, utilice un OutputStreamWriter para escribir en un String. Mantenga una matriz de valores de cadena de all ASM opcode types, y luego si una línea en ese String contiene una cadena de código de operación, entonces la línea es una instrucción.

+0

Preferiría una solución en la que obtendría la salida limpia directamente de los visitantes de ASM (o cualquier mano derivada creada por mí mismo) ... ¡pero usaré su enfoque si no encuentro nada mejor! –

+0

¿Qué pasa si el nombre de un método es también un código de operación o más? – thejh

+0

Bueno, me doy cuenta de que esta no es una solución perfecta, sin embargo, funcionaría si combinas la carcasa también. En la mayoría de los casos, no encontrará un método llamado BIPUSH o ICONST_1 (con esa capitalización). – jli

1

En ASM 4, hay una nueva abstracción llamada Impresora. puede pasar su propia instancia de impresora (por ejemplo, extender o copiar la implementación de Textifier) ​​en el constructor de TraceClassVisitor.

3

Esto parece hacer el truco .. aunque yo no entiendo como:

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

y uno puede ejecutarlo usando:

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

    for (String op : ops) 
     System.out.println(op); 
} 
+0

Acerca de esta solución. ¿Qué versión de ASM se requiere? Estoy usando org.ow2.asm asm v5.0.3 y las clases en org.objectweb.asm.util.Printer no se encuentran – mangusbrother