使用ANTLR对Java源文件进行静态分析

9

有没有一个完整的实现(可能是github或googlecode),用于使用ANTLR语法文件和Java源代码分析Java源代码。例如,我想简单地能够计算变量、方法等数量。

同时使用最新版本的ANTLR。


4
我假设你可以直接从ANTLR官网下载Java解析器/AST构建器(因此满足“最新版本的ANTLR”)。编写一个遍历树来计算方法和字段数量的程序相当容易。 “等等”一部分意味着没有人能猜出你想要什么其他内容;为什么不使用标准的度量工具呢? - Ira Baxter
如果您正在考虑进行静态分析以检测错误或安全问题,那么您可能需要比ANTLR提供的AST和树重写规则更多的东西。您可能希望将ANTLR与Stratego/XT结合使用。我不知道是否有人拥有您所寻找的免费公共版本。好问题。如果您想要专业质量的工具,请查看Ira的个人资料。 - Guy Coder
1
通过查看编译后的字节码,例如使用ASM字节码框架,计算变量和方法会更容易。 - Jörn Horstmann
1
@JörnHorstmann:从检查字节码得到的计数可能与从检查源代码得到的计数不同。内联编译时常量、桥接方法等会导致不同的数字。当嵌套类型的变量/方法等是否计入封闭类型的总数时,情况会变得更加复杂。 - Nathan Ryan
为什么不使用http://www.kclee.de/clemens/java/javancss/作为起点?它不使用ANTLR而是javacc。顺便说一句,它可能已经生成了您正在寻找的所有指标。 - Yves Martin
1个回答

12

我想在午餐时间试着解决这个问题。这可能并不能完全解决你的问题,但可以帮助你入门。示例假设你把所有东西都放在同一个目录下。

  1. 从GitHub下载ANTLR源代码。来自ANTLR官网的预编译“complete”JAR文件存在已知的bug。GitHub上的存储库有修复。

  2. 解压ANTLR tarball。

% tar xzf antlr-antlr3-release-3.4-150-g8312471.tar.gz
  • 构建 ANTLR "complete" JAR。

  • % cd antlr-antlr3-8312471
    % mvn -N install
    % mvn -Dmaven.test.skip=true
    % mvn -Dmaven.test.skip=true package assembly:assembly
    % cd -
  • 下载一个Java语法文件。虽然有其他的选择,但我知道这个是有效的。

  • 将语法文件编译成Java源代码。

  • % mkdir com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated
    % mv *.g com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated
    % java -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar org.antlr.Tool -o com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated Java.g
  • 编译Java源代码。

  • % javac -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/*.java
    将以下源文件Main.java添加。
    import java.io.IOException;
    import java.util.List;<br>
    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;<br>
    import com.habelitz.jsobjectizer.unmarshaller.antlrbridge.generated.*;<br>
    public class Main {
        public static void main(String... args) throws NoSuchFieldException, IllegalAccessException, IOException, RecognitionException {
            JavaLexer lexer = new JavaLexer(new ANTLRFileStream(args[1], "UTF-8"));
            JavaParser parser = new JavaParser(new CommonTokenStream(lexer));
            CommonTree tree = (CommonTree)(parser.javaSource().getTree());
            int type = ((Integer)(JavaParser.class.getDeclaredField(args[0]).get(null))).intValue();
            System.out.println(count(tree, type));
        }
        private static int count(CommonTree tree, int type) {
            int count = 0;
            List children = tree.getChildren();
            if (children != null) {
                for (Object child : children) {
                    count += count((CommonTree)(child), type);
                }
            }
            return ((tree.getType() != type) ? count : count + 1);
        }
    }
  • 编译。

    % javac -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main.java
  • 选择您想要计数的Java源代码类型;例如,VAR_DECLARATORFUNCTION_METHOD_DECLVOID_METHOD_DECL

  • % cat com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/Java.tokens
  • 运行任何文件,包括最近创建的 Main.java 文件。

  • % java -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main VAR_DECLARATOR Main.java
    6
    当然,这还是有瑕疵的。如果你仔细看,你可能已经注意到增强型for语句的局部变量并没有被计算在内。要解决这个问题,你需要使用类型为FOR_EACH的语句,而不是VAR_DECLARATOR
    你需要对Java源代码的元素有很好的理解,并能够合理地猜测它们如何匹配到特定语法的定义。你也无法对引用进行计数。例如,一个字段的使用次数需要进行引用解析。例如,p.C.f是指包p内的一个类C的静态字段f,还是指由类p的静态字段存储的对象中的实例字段f?基本解析器不能解析像Java这样复杂的语言的引用,因为一般情况下可能非常困难。如果您想要这种级别的控制,您需要使用编译器(或更接近编译器的东西)。Eclipse编译器是一个流行的选择。
    我还应该提到,除了ANTLR之外,你还有其他选择。JavaCC是另一个解析器生成器。静态分析工具PMD使用JavaCC作为其解析器生成器,允许您编写自定义规则,可以用于您所指定的计数类型。

    从简单地计算语法实例的角度来看(例如,#变量声明),这是一个很好的答案。 OP不透露他想要做什么其他事情; 静态分析通常远远超出了仅仅计数的范畴,并且通常需要符号表并且经常需要流程分析。 如果他只需要计数,那么您的解决方案就很好(所以+1); 如果他需要更多,则此方法行不通,更别说Java的名称解析,更不用说流程分析了。 - Ira Baxter
    @IraBaxter:同意我的解决方案可能只是部分的。我怀疑他的需求相对简单,而且重新回到 ANTLR 进行深入探究也很有趣。 - Nathan Ryan
    这是一个含糊的问题,因为我从未真正使用过最近版本的antlr与Java语法配合工作。从2.0版本到3.0版本,语法发生了很大变化。许多在线文档都是针对旧版本而编写的,这让我感到困惑。我主要关注antlr的语法和方法。我的实际任务非常基础。 - Berlin Brown
    谢谢,我终于重新审视了这个问题。在尝试从源代码构建ANTLR时,出现了许多编译错误。最终,我只构建了我需要的模块(如antlr-complete等)。我还不得不在构建过程中关闭gpg。除此之外,语法按预期工作。 - Berlin Brown

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接