最近做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. spark 怎么读写 elasticsearch

    参考文章: https://www.bmc.com/blogs/spark-elasticsearch-hadoop/ https://blog.pythian.com/updating-elasti ...

  2. 快速结束 git 输出行

    在使用git命令查看操作记录等时,内容很多,想要输出内容快速结束 英文 Q 备注:通过英文Q快速结束

  3. SQL全表扫描优化基础知识

    1.模糊查询效率很低: 原因:like本身效率就比较低,应该尽量避免查询条件使用like:对于like '%...%'(全模糊)这样的条件,是无法使用索引的,全表扫描自然效率很低:另外,由于匹配算法的 ...

  4. CPP在内网穿透技术的思考

    概述 内网穿透是一种技术,用于在私有局域网(LAN)中的设备与外部网络(如互联网)之间建立通信通道,使得外部设备可以访问内网中的服务.由于内网设备通常位于防火墙或 NAT(网络地址转换)设备之后,外部 ...

  5. AD域下,环境下办公机系统时间不准确

    事件起因: 某部门一同事电脑时间和AD域控时间相差3分钟,虽然说时间相差5分钟内问题不大,但是本着有问题就解决的原则,还是花了点时间去查资料解决. (小小吐槽一下,在我看来域控机是掌管下面所有的办公机 ...

  6. C# ASP.NET Core Web API 框架 实现向手机发送验证码短信

    本文章主要是在C# ASP.NET Core Web API框架实现向手机发送验证码短信功能.这里我选择是一个互亿无线短信验证码平台,其实像阿里云,腾讯云上面也可以. 首先我们先去 互亿无线 http ...

  7. DevNow: Search with Lunrjs

    前言 假期真快,转眼国庆假期已经到了最后一天.这次国庆没有出去玩,在北京看了看房子,原先的房子快要到期了,找了个更加通透一点的房子,采光也很好. 闲暇时间准备优化下 DevNow 的搜索组件,经过上一 ...

  8. KPTI——可以缓解“熔断” (Meltdown) 漏洞的内核新特性

    Linux 内核修复办法:内核页表隔离KPTl(kernel page table isolation) 每个进程一张页表变成两张:运行在内核态和运行在用户态时分别使用各自分离的页表 Kernel页表 ...

  9. DBA面试资源合集(含Oracle、MySQL、Redis等)-墨天轮

    如今正值金九银十招聘季,众多企业开放大批岗位等待新力量的注入,各位DBA们,你们是否正在激情备战中? 作为企业数据化进程中十分重要的一环,DBA的职责越来越重要,作为高薪资岗位之一,应聘DBA的竞争也 ...

  10. webapi action 参数

    使用地址参数传递(queryString)数据:eg:http://localhost:5063/WeatherForecast?age=123 /// <summary> /// GET ...