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

使用ANTLR对Java源文件进行静态分析,java,antlr,Java,Antlr,是否有人拥有使用ANTLR语法文件和Java源代码分析Java源代码的完整实现(可能是github或googlecode)。例如,我希望能够简单地计算变量、方法等的数量 还使用了最新版本的ANTLR。我想我应该在午餐休息时尝试一下。这可能不能完全解决你的问题,但它可能会给你一个开始的地方。本例假设您在同一目录中执行所有操作 从GitHub下载。来自ANTLR站点的预编译的“complete”JAR包含一个已知的bug。GitHub回购协议已经解决了这个问题 取出ANTLR焦油球 % tar xz

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


还使用了最新版本的ANTLR。

我想我应该在午餐休息时尝试一下。这可能不能完全解决你的问题,但它可能会给你一个开始的地方。本例假设您在同一目录中执行所有操作

  • 从GitHub下载。来自ANTLR站点的预编译的“complete”JAR包含一个已知的bug。GitHub回购协议已经解决了这个问题

  • 取出ANTLR焦油球

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

  • 下载一个。还有其他的,但我知道这个很有效

  • 将语法编译为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
    % javac -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/*.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
    % 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;
    import org.antlr.runtime.*; import org.antlr.runtime.tree.*;
    import com.habelitz.jsobjectizer.unmarshaller.antlrbridge.generated.*;
    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); } }
    % java -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main VAR_DECLARATOR Main.java
    6
  • 选择要计数的Java源的类型;例如,
    VAR\u声明符
    函数\u方法\u DECL
    ,或
    VOID\u方法\u DECL

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

    import java.io.IOException;
    import java.util.List;
    import org.antlr.runtime.*; import org.antlr.runtime.tree.*;
    import com.habelitz.jsobjectizer.unmarshaller.antlrbridge.generated.*;
    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); } }
    % java -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main VAR_DECLARATOR Main.java
    6
  • 当然,这是不完美的。如果仔细观察,您可能已经注意到增强的
    for
    语句的局部变量没有被计算在内。为此,您需要为每个使用类型
    ,而不是
    VAR\u声明符

    您需要对Java源代码的元素有很好的理解,并且能够合理地猜测这些元素如何与这个特定语法的定义相匹配。您也将无法进行引用计数。声明很容易,但计算字段的使用,例如,需要引用解析。
    p.C.f
    是指包
    p
    中类
    C
    的静态字段
    f
    ,还是指由类
    p
    的静态字段
    C
    存储的对象的实例字段
    f
    ?基本解析器不会解析像Java这样复杂的语言的引用,因为一般情况下可能非常困难。如果您想要这种级别的控制,您需要使用编译器(或更接近它的东西)。Eclipse编译器是一种流行的选择


    我还应该提到,除了ANTLR,您还有其他选择。JavaCC是另一个解析器生成器。静态分析工具PMD使用JavaCC作为解析器生成器,允许您编写自定义规则,用于指定的计数类型。

    我假设您可以直接从ANTLR网站下载Java解析器/AST生成器(因此满足“ANTLR的最新版本”)。编写一个树爬虫来计算方法和字段是相当容易的。“等”部分意味着没有人能猜出你还想要什么;为什么一个标准的度量工具不能做到呢?如果您正在考虑对错误或安全性进行静态分析,那么您可能需要的不仅仅是ANTLR提供的AST和树重写规则。您可能希望结合使用ANTLR。我不知道有谁有一个免费的公众版本的你所寻求的。好问题。如果你想要专业质量工具,请查看Ira的个人资料。通过查看编译后的字节码(例如使用ASM字节码框架),计算变量和方法会容易得多。@JörnHorstmann:检查字节码得到的计数可能与检查源代码得到的计数不同。内联编译时常量、桥接方法等将导致不同的数字。无论嵌套类型的变量/方法等是否计入封闭类型的总数,事情都变得更加复杂。为什么不将其作为起点呢?它不使用ANTLR,而是使用javacc。顺便说一句,它可能已经生成了您正在寻找的所有度量。从简单计算语法实例(例如#变量声明)的角度来看,这是一个很好的答案。OP对于他想做的其他事情是不透明的;静态分析通常不仅仅是计数,通常需要一个符号表,并且通常需要流分析。如果他只需要数数,你的解决方案就是桃子(so+1);如果他需要更多,这不会解决问题,Java的名称解析更不用说流分析了。@IraBaxter:同意我的解决方案可能只是部分解决方案。我怀疑他的需求相对来说并不复杂,再加上重新使用ANTLR很有趣。这是一个模糊的问题,因为我从来没有用Java语法来处理ANTLR的最新版本。语法与2.0版和3.0版相比发生了很大变化。很多在线文档都是针对旧版本的。它把我甩了。我主要对antlr的语法和方法感兴趣。我的实际任务相当基本。谢谢,我终于重新审视了这一点。我有很多编译错误,试图从源代码构建antlr。我最终只构建了我需要的模块(antlr complete,等等)。作为构建的一部分,我还必须关闭gpg。除此之外,语法也如预期的那样起作用。