【问题标题】:Getting qualified method name by the line number通过行号获取合格的方法名称
【发布时间】:2016-03-26 23:48:49
【问题描述】:

这个问题是特定于 Java 和 Maven 的。请注意下面的附加限制,因为它们与其他问题不同。

我有几个 Maven (Java) 项目要分析。我所拥有的是:

  • 源代码
  • maven 编译的 Jave 代码,在 target/ 文件夹中有二进制文件

问题是: 给定一个源代码文件 (.java) 和那里的行号,如何获得跨越该行的方法的完全限定名称?如果该行不在方法中,则仅输出 null。可接受的实现此功能的语言是:Java、ruby 或 python。

您能用以下两种方式之一回答问题吗?

  1. 使用二进制文件并提取该行的qualified方法名称。 (这可能涉及调试信息的编织,但这很好。)

  2. 直接使用给定的源文件,尝试解析并使用AST。

使用特定的库(如 BCEL)或任何第 3 方库(只要它们有良好的文档记录且可用)也是可以的。

非常感谢您的大力帮助!

【问题讨论】:

  • 由于源代码不保证可编译,所以可能是不可能的,而且行号可能根本不在方法中。
  • 嗯,好吧,让我确定它可以编译。

标签: java maven compiler-construction abstract-syntax-tree fully-qualified-naming


【解决方案1】:

有许多开源 Maven 插件可以分析源代码,并基于每个方法进行报告。仔细研究其中一些可能是您最好的选择。

示例包括 Checkstyle、FindBugs、PMD、JDepend、JavaNCSS。

还可以看看 SonarQube。

【讨论】:

  • 感谢您的建议,斯图尔特。
【解决方案2】:

很遗憾,您的问题充满了缺点:

  • 当然,您可以解析输入源(通过 Javacc 或 ANTLR 解析器),直到到达所需的行。但是,由于您已经拥有 .class 文件,因此解析相同的源似乎是浪费精力。
  • 所以,分析.class 文件似乎更好。但不幸的是,您无法保证这是您的行所在的类,因为在同一个源文件中可以定义多个类

啊!这让我想到了一种复杂的解决方案:

我将声明一个包含所有登录信息的类:

public class SourceMethodsIndexer
{
    private final SortedMap<Integer, List<Method>> indexOfMethodsByFirstLineNumber;
}

构造函数会是这样的:

public SourceMethodsIndexer(File sourceFile)

...并且应该完成这些任务:

1.浏览目标包相关的类目录。

File targetPackageDir=getTargetPackageDir(sourceFile);
File[] classFiles=targetPackageDir.listFiles(new FileFilter(){
    public boolean accept(File dir, String name){
        return name.endsWith(".class");
}

});

2.使用Apache BCEL收集所有属于你输入源文件的非公共类(你可以调用JavaClass.getSourceFileName()过滤类),加上你输入源文件名对应的公共类。

Collection<JavaClass> targetClasses=getNonPublicClasses(classFiles, sourceFile.getName());
targetClasses.add(publicClass);

3.然后收集每个类中的所有方法。

Set<Method> targetMethods=new HashSet<Method>(1024);
for (JavaClass javaClass:targetClasses)
{
    targetMethods.addAll(Arrays.asList(javaClass.getMethods()));
}

4.现在您可以直接搜索您的行号,或者先按行号索引方法以便稍后更快地访问它们:JavaClass.getMethods()[n].getLineNumberTable().getSourceLine(0)(注意可能存在重复值)。

this.indexOfMethodsByFirstLineNumber=new TreeMap<Integer, List<Method>>((int)(1.7d*methods.size()));
for (Method method: methods)
{
    // Note: The -1 in this line stands to make the SortedMap work properly when searching for ranges.
    int firstLine=getLineNumberTable().getSourceLine(0)-1;
    List<Method> methodsInTheSameLine=indexOfMethodsByFirstLineNumber.get(firstLine);
    if (methodsInTheSameLine==null)
    {
        methodsInTheSameLine=new ArrayList<Method>();
        indexOfMethodsByFirstLineNumber.put(firstLine,methodsInTheSameLine);
    }
    methodsInTheSameLine.add(method);
}

5.公开一个方法来做搜索:

public Method getMethodByLine(int lineNumber)
{
    Set<Method> methodsInTheSameLine=this.indexOfMethodsByFirstLineNumber.headMap(lineNumber).lastKey();
    if (methodsInTheSameLine.size()==0)
    {
        // There are no methods method in that line: Absurd.
    }
    else if (methodsInTheSameLine.size()>1)
    {
        // There are more than one method in that line. Hardly probable, but possible.
    }
    else
    {
        // There is one method in that line:
        return methods.get(0);
    }
}

【讨论】:

  • 非常感谢,小。这个答案非常接近我的需要。你介意详细说明你的答案吗?我看到你那里有大约 4 个步骤,但只是最后一个,直接搜索行号的方法是什么?您可以假设调试信息在字节码中。
  • 当然:我已经用我的提案的更多细节更新了我的答案。我刚刚命名但未实现的方法我认为它们足够简单。
  • 我认为从检查生成的字节码的角度来看,这足以作为答案。不过,我最终解析了 AST 并获得了它的完全限定名称。正如所承诺的,让我接受这个答案来奖励你的帮助。谢谢,小。
猜你喜欢
  • 2016-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-27
  • 1970-01-01
  • 2023-02-16
  • 1970-01-01
相关资源
最近更新 更多