最近做program analysis,需要解析Java的源代码,于是就去看了看Abstract Syntax Tree(AST,中文为抽象语法树)。有点无奈的是,网上关于这方面的资料比我想象中的少,可能是涉及的东西太底层了吧。AST一般属于编译原理方面的内容,也经常用于程序分析等等。简单来说,你写了一坨代码,编译器会把代码转化成一棵抽象语法树用于“理解”。每个树节点代表一个代码元素,也有自己的属性什么的。关于AST的细节一两句话肯定说不清楚, 不过好消息是,Eclipse JDT中有现成的ASTParser可以将源代码解析成AST,并且有很全面的节点类型和方法供用户操作。

今天就先把网上一些关于Eclipse JDT中AST相关的资料汇总再这里,等我自己摸索的差不多了会及时更新这方面的内容。

那啥,转载请注明出处哦~~~ http://blog.csdn.net/flying881114/archive/2011/02/16/6187061.aspx

---入门文档

1. Eclipse Corner Article: Abstract Syntax Tree

http://www.eclipse.org/articles/article.php?file=Article-JavaCodeManipulation_AST/index.html

(英文,AST的概述,细节代码不多)

2. 探索Eclipse的ASTParser

http://www.ibm.com/developerworks/cn/opensource/os-ast/index.html#resources

(中文,相对详细的讲解和代码)

---AST使用

1. Eclipse JDT--AST and JavaModel Tutorial

http://www.vogella.de/articles/EclipseJDT/article.html

(英文,代码很多,但需要有更多的背景知识)

2. Eclipse AST 使用指南

http://wenku.baidu.com/view/a0b8e07931b765ce050814ac.html

(pdf文档,更加详细地说明了AST 相关类与方法,可以与eclipse documentation结合使用)

3. Eclipse documentation

http://help.eclipse.org/helios/index.jsp

其实,最靠谱的还是直接看eclipse文档。在这里你可以了解你想知道的一切。

4. Yet another AST tutorial

http://sahits.ch/blog/?p=228

英文,有大量代码,从易到难讲解(这篇我还没看完~)

---使用AST相关

1. 使用AST出现问题?不知道使用什么jar包?看看这里吧:

http://lym6520.javaeye.com/blog/747840

2. 想亲眼看看你源文件的AST是什么样的?为你的Eclipse装一个ASTview插件吧!

http://www.eclipse.org/jdt/ui/astview/index.php

(小心!就连Hello World的小程序也有很复杂的AST哦!)

要解析Java源码,首先要建立ASTParser的实例。此过程最重要的一点是,你要告诉parser需要解析的内容类型。ASTParser支持对以下四种内容的解析:

1. K_COMPILATION_UNIT: 一个编译单元,一般就是完整的Java文件

2. K_STATEMENTS: Java statements,比如赋值语句,或是if语句块,while语句块等。此类型不需要文件是完整的编译单元,但需要是完整的statements。比如if语句块要作为一个完整的statement输入,否则会报错。

3. K_EXPRESSION: Java expressions

4. K_CLASS_BODY_DECLARATIONS: Java class里的元素

以下的例子以K_COMPILATION_UNIT为例创建ASTParser,也就是解析一个完整的Java文件。

  1. // Initialize ASTParser
  2. ASTParser parser = ASTParser.newParser(AST.JLS3); //initialize
  3. parser.setKind(ASTParser.K_COMPILATION_UNIT);     //to parse compilation unit
  4. parser.setSource(content.toCharArray());          //content is a string which stores the java source
  5. parser.setResolveBindings(true);
  6. CompilationUnit result = (CompilationUnit) parser.createAST(null);

其中,变量content是一个字符串,储存了读入的java源文件内容。最后一行,parser.createAST方法返回了一个ASTNode类。ASTNode是语法树各节点的抽象基类,它的子类分别代表各种类型的节点比如CompilationUnit, Statement, Expression, Comment, PackageDeclaration等等。具体参见

http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/WhileStatement.html。

这里,我们将其转换成CompilationUnit类。

其实以上这四种类型是存在层次关系的:一个完整的编译单元包括imported packages, class。Class里又包括field和methods,而method又可以被分成一系列statements。也就是说,一个完整的java文件被分成几个基本部分,每个部分又可以继续往下分。

比如说,Statement的子类包括IfStatement, WhileStatement, ExpressionStatement等等。IfStatment类提供getElseStatement, getThenStatment,还有提取if条件等各种方法。像是ExpressionStatement类,支持通过getExpression方法得到源文件对应的Expression,而Expression类则包括如Assignment, MethodInvocation,ArrayCreation种种类型。

关于Statement的类层次结构和各类的方法可以参见:

http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/WhileStatement.html

至此,利用createAST,java的源文件已经被解析成AST并且储存于Compilation的实例result中了。我会在下一篇文章介绍一些使用AST的具体操作。

创建ASTParser并将Java源文件解析成AST:

  1. // Initialize ASTParser
  2. ASTParser parser = ASTParser.newParser(AST.JLS3); //initialize
  3. parser.setKind(ASTParser.K_COMPILATION_UNIT);     //to parse compilation unit
  4. parser.setSource(content.toCharArray());          //content is a string which stores the java source
  5. parser.setResolveBindings(true);
  6. CompilationUnit result = (CompilationUnit) parser.createAST(null);

调用imports()方法得到源文件的一系列import声明:

  1. //show import declarations in order
  2. List importList = result.imports();
  3. System.out.println("import:");
  4. for(Object obj : importList) {
  5. ImportDeclaration importDec = (ImportDeclaration)obj;
  6. System.out.println(importDec.getName());
  7. }

调用types()方法得到类名称:

  1. //show class name
  2. List types = result.types();
  3. TypeDeclaration typeDec = (TypeDeclaration) types.get(0);
  4. System.out.println("className:"+typeDec.getName());

调用TypeDeclaration的getField方法得到类里定义的field:

  1. //show fields
  2. FieldDeclaration fieldDec[]=typeDec.getFields();
  3. System.out.println("Fields:");
  4. for(FieldDeclaration field: fieldDec)
  5. {
  6. System.out.println("Field fragment:"+field.fragments());
  7. System.out.println("Field type:"+field.getType());
  8. }

调用TypeDeclaration的getMethods方法得到类里定义的list of methods;同理,可以调用MethodDeclaration里的各种方法得到method的方法名,参数,返回类型等等:

  1. //show methods
  2. MethodDeclaration methodDec[] = typeDec.getMethods();
  3. System.out.println("Method:");
  4. for (MethodDeclaration method : methodDec)
  5. {
  6. //get method name
  7. SimpleName methodName=method.getName();
  8. System.out.println("method name:"+methodName);
  9. //get method parameters
  10. List param=method.parameters();
  11. System.out.println("method parameters:"+param);
  12. //get method return type
  13. Type returnType=method.getReturnType2();
  14. System.out.println("method return type:"+returnType);

一个方法的内容对应一个block,可以用getBody()得到;一个block又可以被分解成一系列statements,可以用statements()方法得到:

  1. //get method body
  2. Block body=method.getBody();
  3. List statements=body.statements();   //get the statements of the method body
  4. Iterator iter=statements.iterator();
  5. while(iter.hasNext())
  6. {
  7. //get each statement
  8. Statement stmt=(Statement)iter.next();

接下来需要根据每个statement的类型来对源码进行相应的解析。Statement有很多子类,这里只给出其中的几类介绍(具体可以参考我上一篇文章:ASTParser介绍;最好直接去Eclipse documentation官网查看相关文档)。基本流程就是先判断statement是哪种类型的实例,做出相应的ClassCast,再调用相应的方法。

1. ExpressionStatement (又包括Assignement, MethodInvocation等子类)。

  1. if(stmt instanceof ExpressionStatement)
  2. {
  3. ExpressionStatement expressStmt=(ExpressionStatement) stmt;
  4. Expression express=expressStmt.getExpression();
  5. if(express instanceof Assignment)
  6. {
  7. Assignment assign=(Assignment)express;
  8. System.out.println("LHS:"+assign.getLeftHandSide()+"; ");
  9. System.out.println("Op:"+assign.getOperator()+"; ");
  10. System.out.println("RHS:"+assign.getRightHandSide());
  11. }
  12. else if(express instanceof MethodInvocation)
  13. {
  14. MethodInvocation mi=(MethodInvocation) express;
  15. System.out.println("invocation name:"+mi.getName());
  16. System.out.println("invocation exp:"+mi.getExpression());
  17. System.out.println("invocation arg:"+mi.arguments());
  18. }
  19. System.out.println();
  20. }

2. IfStatement

  1. else if(stmt instanceof IfStatement)
  2. {
  3. IfStatement ifstmt=(IfStatement) stmt;
  4. InfixExpression wex=(InfixExpression) ifstmt.getExpression();
  5. System.out.println("if-LHS:"+wex.getLeftOperand()+"; ");
  6. System.out.println("if-op:"+wex.getOperator()+"; ");
  7. System.out.println("if-RHS:"+wex.getRightOperand());
  8. System.out.println();
  9. }

3. VariableDeclarationStatement

  1. else if(stmt instanceof VariableDeclarationStatement)
  2. {
  3. VariableDeclarationStatement var=(VariableDeclarationStatement) stmt;
  4. System.out.println("Type of variable:"+var.getType());
  5. System.out.println("Name of variable:"+var.fragments());
  6. System.out.println();
  7. }

4. ReturnStatement

  1. else if(stmt instanceof ReturnStatement)
  2. {
  3. ReturnStatement rtstmt=(ReturnStatement) stmt;
  4. System.out.println("return:"+rtstmt.getExpression());
  5. System.out.println();
  6. }

ASTParser 作为programming language编译器的一部分,功能机制可以说是相当强大。以上的例子只是冰山一角。使用ASTParser可以对java的源文件进行各种解析,用户只需要指明源文件,调用一下ASTParser的createAST方法就能得到完整的AST了,剩下的就是用户根据实际需要对AST进行各种manipulate了。

Eclipse documentation详细记录了AST相关的各类描述,继承关系等等。另外,如果对AST机制或者在Eclipse里使用ASTParser有什么疑问的话,可以参考我的另一篇文章:Eclipse JDT--AST入门,里面整理了很多相关资料。

估计接下来一段时间就是继续研究AST了,还请各位高手多多指教~~~

Eclipse JDT--AST入门的更多相关文章

  1. Atitit org.eclipse.jdt 的ast 架构 Eclipse JDT API spec

    Atitit org.eclipse.jdt 的ast 架构 Eclipse JDT API spec 继承树1 Expression的子类1 获取子类2 继承树 Astnode>express ...

  2. RCP: JDT 根据org.eclipse.jdt.core.IJavaElement对象获取org.eclipse.jdt.core.dom.ASTNode对象

    JDT中有两套Java文件模型映射. 其核心类\接口分别为: org.eclipse.jdt.core.IJavaElement和org.eclipse.jdt.core.dom.ASTNode IJ ...

  3. 使用JDT.AST解析java源码

    在做java源码的静态代码审计时,最基础的就是对java文件进行解析,从而获取到此java文件的相关信息: 在java文件中所存在的东西很多,很复杂,难以用相关的正则表达式去一一匹配.但是,eclip ...

  4. java.lang.RuntimeException: wrong class format Caused by: org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException: null

    Spring Boot 启动的时候报的错 使用Drools 5.6版本,Spring Boot1.5.8版本,JAVA8版本,Eclipse4.4.2版本. Google后在Stack上发现一个,中文 ...

  5. 使用eclipse JDT compile class,解决 无法确定 X 的类型参数;对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最大实例

    ant 命令行方式执行build javac编译class出现 泛型无法转换 无法确定 <X>X 的类型参数:对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最 ...

  6. Compilation err ororg.eclipse.jdt.internal.compiler.classfmt.ClassFormatException

    严重: Compilation errororg.eclipse.jdt.internal.compiler.classfmt.ClassFormatExceptionat org.eclipse.j ...

  7. Eclipse报错Resource '/.org.eclipse.jdt.core.external.folders/.link5' already exists.

    Eclipse查看源码出现source not found,重新Build Path选择jdk的jar包时,出现Resource '/.org.eclipse.jdt.core.external.fo ...

  8. (办公)plug-in org.eclipse.jdt.ui was unable to load class org.eclipse.jdt.internal

    今天上午开发环境遇到这个问题,解决方案如下,(解决了之后,项目并没有丢失.) 因为Eclipse的这个plug-in org.eclipse.jdt.ui was unable to load cla ...

  9. org.eclipse.jdt.internal.compiler包下的类找不到

    到maven库上下载jar包:org.eclipse.jdt.core-3.13.jar <!-- https://mvnrepository.com/artifact/org.eclipse. ...

  10. Eclipse:Could not create the view: Plug-in org.eclipse.jdt.ui was unable to load class org.eclipse.

    今天电脑死机了2次,重启电脑开eclipse后,发现项目环境坏了.百度后得到的答案是删除.metadata目录.但觉得麻烦,后在stackoverflow发现最佳的方式是 把 .metadata/.p ...

随机推荐

  1. Unrecognized SSL message, plaintext connection?

    报错:Unrecognized SSL message, plaintext connection? 修改:把 requestContext.setScheme(Scheme.HTTPS);修改为 r ...

  2. CM3和ARM7的差异

    此文章由文心一言生成,引用请标注作者:文心一言CM3通常指的是Cortex-M3,它是ARM公司设计的一种基于ARMv7-M架构的32位处理器内核,主要用于嵌入式系统.而ARM7则是ARM公司早期设计 ...

  3. 淘宝打单发货接口,淘宝打单发货API

    许多做系统功能的小伙伴经常面对的一个功能是对接淘宝开放平台,在自己系统中进行打单发货. 但是,目前淘宝开放平台,已经关闭了相关的相关的权限申请,具体可查看相关公告.有需要这个权限的,可以站内信联系我, ...

  4. WPF 实现一个吃豆豆的Loading加载动画

    运行的效果如下 先引入一下我们需要的库 在nuget上面搜一下"expression.Drawing",安装一下这个包 我们再创建一个Window,引入一下这个包的命名空间 我们设 ...

  5. Vs Code, Visual Studio 2022, Angular and Live Server Running Through Https and IP Address

    前言 之前就写过 angular cli, vs code liveserver, vs 2019 iis express 10, vs code kestrel 使用 https + ip. 但写的 ...

  6. 全面掌握 Jest:从零开始的测试指南(下篇)

    在上一篇测试指南中,我们介绍了Jest 的背景.如何初始化项目.常用的匹配器语法以及钩子函数的使用.这一篇篇将继续深入探讨 Jest 的高级特性,包括 Mock 函数.异步请求的处理.Mock 请求的 ...

  7. Maven 依赖项管理&&依赖范围

    依赖管理   使用坐标导入jar包     1.在pom.xml 中编写 <dependencies> 标签     2.在 <dependencies> 标签中使用 < ...

  8. Flutter Engage 活动精彩回顾 | 中文字幕视频

    在 Flutter Engage 预告之后,无数开发者充满期待并且在社区中积极讨论交流,分享见解.今天,我们正式发布 Flutter 2.0,并在 Flutter Engage 活动 中详细介绍了这一 ...

  9. 音视频入门-5-音频编码原理、编码器、ADTS格式

    1.  概念 音频压缩技术指的是对原始数字音频信号流(PCM编码)运用的数字信号处理技术. 2. 背景 和 压缩的必要性 研究发现,直接采用PCM码流进行存储和传输存在非常大的冗余度. 以CD为例,其 ...

  10. Qt中当程序结束时线程的退出

    在Qt程序结束时应该如何退出正在运行的任务子线程? 因个人经验和能力有限,本文仅供参考,有错误或者考虑不完善的地方请各位批评指正. 一.正常情况下如何创建和退出线程 1.继承QThread,重写run ...