2011-11-02 3 views
5

J'essaie (sans succès) d'imprimer seulement le contenu d'une méthode donnée. Le code suivant presque le tour est joué:Jolie impression d'une méthode 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; 
     } 
    } 

en cours d'exécution avec

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

entraînant

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


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

Ce que je voudrais obtenir était

SIPUSH 777 
    IRETURN 

sans signature, commentaires et que ce soit. Comment puis-je accomplir cela?

Répondre

4

La réponse est déjà assez vieux et implique l'écriture beaucoup de code.

Comme des instructions de la méthode d'impression asm v5 est simple:

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

Dans votre mise en œuvre de la ClassVisitor, simplement remplacer la méthode visitMethod. Voici un exemple:

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

Le TraceMethodVisitor recevra l'événement appelle pour visiter des instructions de méthode, etc. par le classVisitor. Le code sera ensuite imprimé par le TraceMethodVisitor à l'aide de l'imprimante.

+3

Jetez aussi un oeil dans le guide asm4 à la page 59. –

0

La chose la plus simple à laquelle je puisse penser est d'utiliser une regex ou un autre type de chaîne de caractères qui filtre uniquement les instructions.

Par exemple, utilisez OutputStreamWriter pour écrire à String à la place. Conservez un tableau de valeurs de chaîne de all ASM opcode types, puis si une ligne dans ce String contient une chaîne de code d'opération, la ligne est une instruction.

+0

Je préférerais une solution où j'obtiendrais la sortie propre directement des visiteurs d'ASM (ou de toute autre main fabriquée par moi-même) ... mais j'utiliserai votre approche si je ne trouve rien de mieux! –

+0

Que faire si un nom de méthode est aussi un opcode? – thejh

+0

Eh bien, je réalise que ce n'est pas une solution parfaite, mais cela fonctionnerait si vous faites correspondre le cas. Dans la plupart des cas, vous ne trouverez pas de méthode appelée BIPUSH ou ICONST_1 (avec cette majuscule). – jli

1

Dans ASM 4, il existe une nouvelle abstraction appelée Printer. Vous pouvez passer votre propre instance d'imprimante (par exemple étendre ou copier l'implémentation de Textifier) ​​dans le constructeur de TraceClassVisitor.

3

Cela semble faire l'affaire .. même si je ne comprends pas comment:

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

et on peut l'exécuter en utilisant:

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

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

Concernant cette solution. Quelle version d'ASM est requise? J'utilise org.ow2.asm asm v5.0.3 et les classes sous org.objectweb.asm.util.Printer ne sont pas trouvées – mangusbrother

Questions connexes