Я должен признать, я был смущен ...
Я думал, что посетители АСМ совершают магию, чтобы дать мне список всех звонивших
данный метод, например, трассировка стека. Вместо этого они просто анализируют классы и метод
тела. К счастью, этого вполне достаточно для моих нужд, так как я могу построить
сам вызов дерева.
Следующий код перечисляет все методы, которые вызываются другими методами, проверяя только файлы классов в данном каталоге (и подкаталогах):
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);
}
}