2010-05-19 9 views
3

J'ai besoin d'écrire un outil qui répertorie les classes qui appellent des méthodes d'interfaces spécifiées. Il sera utilisé dans le cadre du processus de construction d'une grande application Java composée de nombreux modules. L'objectif est de documenter automatiquement les dépendances entre certains modules Java.Est-ce que la méthode ASM-visiteurs peut être utilisée avec des interfaces?

J'ai trouvé plusieurs outils pour l'analyse des dépendances, mais ils ne fonctionnent pas au niveau de la méthode, uniquement pour les packages ou les jars. Enfin, j'ai trouvé ASM, qui semble faire ce dont j'ai besoin.

Le code suivant imprime les dépendances de la méthode de tous les fichiers de classe dans un répertoire donné:

import java.io.*; 
import java.util.*; 

import org.objectweb.asm.ClassReader; 

public class Test { 

    public static void main(String[] args) throws Exception { 

     File dir = new File(args[0]); 

     List<File> classFiles = new LinkedList<File>(); 
     findClassFiles(classFiles, dir); 

     for (File classFile : classFiles) { 
      InputStream input = new FileInputStream(classFile); 
      new ClassReader(input).accept(new MyClassVisitor(), 0); 
      input.close(); 
     } 
    } 

    private static void findClassFiles(List<File> list, File dir) { 
     for (File file : dir.listFiles()) { 
      if (file.isDirectory()) { 
       findClassFiles(list, file); 
      } else if (file.getName().endsWith(".class")) { 
       list.add(file); 
      } 
     } 
    } 
} 

import org.objectweb.asm.MethodVisitor; 
import org.objectweb.asm.commons.EmptyVisitor; 

public class MyClassVisitor extends EmptyVisitor { 

    private String className; 

    @Override 
    public void visit(int version, int access, String name, String signature, 
      String superName, String[] interfaces) { 
     this.className = name; 
    } 

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

     System.out.println(className + "." + name); 
     return new MyMethodVisitor(); 
    } 
} 

import org.objectweb.asm.commons.EmptyVisitor; 

public class MyMethodVisitor extends EmptyVisitor { 

    @Override 
    public void visitMethodInsn(int opcode, String owner, String name, 
      String desc) { 

     String key = owner + "." + name; 
     System.out.println(" " + key); 
    } 
} 

Le problème:

Le code fonctionne pour les classes régulières seulement! Si le fichier de classe contient une interface, visitMethod est appelée, mais pas visitMethodInsn. Je ne reçois aucune information sur les appelants des méthodes d'interface.

Des idées?

Répondre

1

Je dois admettre, je suis confus ...

Je pensais que les visiteurs asm peu de magie pour me donner la liste de tous les appelants de une méthode donnée, comme un stacktrace. Au lieu de cela ils analysent les classes et la méthode des corps. Fortunatly, c'est totalement suffisant pour mes besoins car je peux construire l'arbre d'appel par moi-même.

Le code ci-dessous répertorie toutes les méthodes qui sont appelées par d'autres méthodes, la vérification des fichiers de classe dans le répertoire donné (et sous-répertoires) seulement:


import java.io.*; 
import java.util.*; 

import org.objectweb.asm.ClassReader; 

public class Test { 

    public static void main(String[] args) throws Exception { 

     File dir = new File(args[0]); 

     Map<String, Set<String>> callMap = new HashMap<String, Set<String>>(); 

     List<File> classFiles = new LinkedList<File>(); 
     findClassFiles(classFiles, dir); 

     for (File classFile : classFiles) { 
      InputStream input = new FileInputStream(classFile); 
      new ClassReader(input).accept(new MyClassVisitor(callMap), 0); 
      input.close(); 
     } 

     for (Map.Entry<String, Set<String>> entry : callMap.entrySet()) { 
      String method = entry.getKey(); 
      Set<String> callers = entry.getValue(); 

      if (callers != null && !callers.isEmpty()) { 
       System.out.println(method); 
       for (String caller : callers) { 
        System.out.println(" " + caller); 
       } 
      } 
     } 
    } 

    private static void findClassFiles(List<File> list, File dir) { 
     for (File file : dir.listFiles()) { 
      if (file.isDirectory()) { 
       findClassFiles(list, file); 
      } else if (file.getName().endsWith(".class")) { 
       list.add(file); 
      } 
     } 
    } 
} 

import java.util.*; 

import org.objectweb.asm.MethodVisitor; 
import org.objectweb.asm.commons.EmptyVisitor; 

public class MyClassVisitor extends EmptyVisitor { 

    private String className; 
    private Map<String, Set<String>> callMap; 

    public MyClassVisitor(Map<String, Set<String>> callMap) { 
     this.callMap = callMap; 
    } 

    @Override 
    public void visit(int version, int access, String name, String signature, 
      String superName, String[] interfaces) { 
     this.className = name; 
    } 

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

     return new MyMethodVisitor(className + "." + name, callMap); 
    } 
} 

import java.util.*; 

import org.objectweb.asm.commons.EmptyVisitor; 

public class MyMethodVisitor extends EmptyVisitor { 

    private String currentMethod; 
    private Map<String, Set<String>> callMap; 

    public MyMethodVisitor(String currentMethod, 
      Map<String, Set<String>> callMap) { 
     this.currentMethod = currentMethod; 
     this.callMap = callMap; 
    } 

    @Override 
    public void visitMethodInsn(int opcode, String owner, String name, 
      String desc) { 

     String calledMethod = owner + "." + name; 

     Set<String> callers = callMap.get(calledMethod); 
     if (callers == null) { 
      callers = new TreeSet<String>(); 
      callMap.put(calledMethod, callers); 
     } 

     callers.add(currentMethod); 
    } 
} 
1

Je pense que c'est parce que les méthodes d'interface n'ont pas de corps de méthode. Essayez d'écrire une méthode vide dans le cadre d'une classe 'normale' et voyez si visitMethodInsn est invoqué. A propos, avez-vous envisagé d'utiliser java.lang.instrument pour découvrir les classes qui sont chargées à l'exécution et faire ainsi votre instrumentation, plutôt que de lire les fichiers de classe à partir du disque?

+0

Je ne pense pas qu'avoir un corps de méthode est pertinent dans ce cas. La méthode d'interface est visitée parce que * visitMethod() * est appelée et une nouvelle instance * MyMethodVisitor * est renvoyée, mais ce * MyMethodVisitor * semble être ignoré par la suite. Je lis les classes à partir du disque, car j'ai besoin d'analyser une application qui n'est pas en cours d'exécution. –