ASM方法访问者可以与接口一起使用吗?

时间:2010-05-19 15:14:37

标签: java assembly static-analysis java-bytecode-asm

我需要编写一个工具,列出调用指定接口方法的类。它将用作由许多模块组成的大型Java应用程序的构建过程的一部分。目标是自动记录某些java模块之间的依赖关系。

我发现了几种用于依赖性分析的工具,但它们不适用于方法级别,仅适用于包或jar。最后我找到了 ASM ,这似乎就是我需要的。

以下代码打印给定目录中所有类文件的方法依赖项:

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

问题:

该代码仅适用于常规课程!如果类文件包含接口,则调用 visitMethod ,但不调用 visitMethodInsn 。我没有得到任何有关接口方法调用者的信息。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

我认为这是因为Interface方法没有方法体。尝试将空方法编写为“普通”类的一部分,并查看是否调用了visitMethodInsn。

顺便说一下,您是否考虑过使用java.lang.instrument来发现在运行时加载的类并以这种方式执行检测,而不是从磁盘读取类文件?

答案 1 :(得分:1)

我不得不承认,我很困惑......

我认为asm访客做了一些魔术给我列出了所有来电者 给定的方法,如堆栈跟踪。相反,他们只是解析类和方法 身体。幸运的是,这完全足以满足我的需求,因为我可以建立 我自己打电话。

以下代码列出了其他方法调用的所有方法,仅检查给定目录(和子目录)中的类文件:


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