关于编译原理
在文章深入理解JVM3-1-程序编译与代码优化-前端编译与优化中,其中介绍了javac的编译原理。其中简要流程如下:
- 准备过程:初始化插入式注解处理器。
- 解析与填充符号表过程
- 词法、语法分析:将源代码的字符流转变为标记集合,构造出抽象语法树。
- 填充符号表:产生符号地址和符号信息。
- 插入式注解处理器的注解处理过程:插入式注解处理器的执行阶段。
- 分析与字节码生成过程
- 标注检查:对语法的静态信息进行检查。
- 数据流及控制流分析:对程序动态运行过程进行检查。
- 解语法糖:将简化代码编写的语法糖还原为原有的形式。
- 字节码生成:将前面各个步骤所生成的信息转化成字节码。
而下面的两个API就是关于抽象语法树,javac给出了注释处理器(processAnnotations)可以让我们读取生成的语法树节点,也干预抽象语法树的生成,我们可以在其中进行更改。
但是首先值得注意的是:下面的两个系类API都是Java私有。并不对外公开,也就是说要使用这些API,必须自行引入。并且要承担其随时改变的风险。
JCTree
JCTree是语法树元素的基类。其解析出的所有抽象语法树节点都继承自该节点。但是我们在自定义节点时,值得注意的说,我们并不能直接newJCTree节点。因为树节点总是在一个语境中,有其对应的结构。其中的pos属性就用于代表该节点在该父节点中的位置。所以JCTree的逻辑是,必须用另一组TreeMaker
API并依赖父级节点才能进行新建。
下面介绍JCTree的子类,其包含了所有Java的构成元素。
常用节点
AnnotatedTypeTree
指被注释修饰的类型。
如下面的s和d:
1 | String s |
其可以用在变量申明中,也可以用在方法参数中。
如:
1 | public class A{ |
AnnotationTree
注释树节点。
如:
1 |
ArrayAccessTree
数组访问表达式的树节点。
如:
1 | array[0] |
ArrayTypeTree
数组类型的树节点。
例如:
1 | int[] |
AssignmentTree
赋值表达式的树节点。
例如:
1 | variable = expression |
BinaryTree
二元表达式的树节点。
例如:
1 | leftOperand operator rightOperand |
BlockTree
语句块的树节点(一般指被花括号包围的语句块)。
例如:
1 | { } |
BreakTree
break语句的树节点。
例如:
1 | break; |
CaseTree
switch语句或表达式中一个case的树节点。
例如:
1 | case expression : |
CatchTree
try语句中catch块的树节点。
例如:
1 | catch ( parameter ) |
ClassTree
类、接口、枚举、记录或注释类型声明的树节点。
例如:
1 | modifiers class simpleName typeParameters |
CompoundAssignmentTree
用于复合赋值操作符的树节点。使用getKind确定操作符的类型。
例如:
1 | variable operator expression |
注意与简单赋值操作符区别在于操作符不仅仅是=
。
ConditionalExpressionTree
三目运算符?:
的树节点。
例如:
1 | condition ? trueExpression : falseExpression |
ContinueTree
continue语句的树节点。
例如:
1 | continue; |
DoWhileLoopTree
do...while
语句的树节点。
例如:
1 | do |
EnhancedForLoopTree
增强for循环语句的树节点。
例如:
1 | for ( variable : expression ) |
ExpressionStatementTree
表达式语句的树节点。
例如:
1 | expression ; |
ForLoopTree
基本的for循环语句的树节点。
例如:
1 | for ( initializer ; condition ; update ) |
IdentifierTree
标识符表达式的树节点。比较基本的树节点。比如用来在二元表达式中作为操作符左右的变量表示。
例如:
1 | name |
IfTree
if语句的树节点。
例如:
1 | if ( condition ) |
ImportTree
用于导入声明的树节点。(包括静态import)
例如:
1 | import qualifiedIdentifier ; |
InstanceOfTree
instanceof表达式的树节点。
例如:
1 | expression instanceof type |
LambdaExpressionTree
lambda表达式的树节点。
例如:
1 | ()->{} |
LambdaExpressionTree.BodyKind
lambda表达式的的body类型。
Lambda表达式有两种形式:
- expression lambda:主体是一个表达式。
- statement lambdas:主题是一个块。
LiteralTree
字面表达式的树节点。
例如:
1 | value |
MemberReferenceTree
成员引用表达式的树节点。
一般用在函数式接口的位置,可与lambda表达式混用。
例如:
1 | expression # [ identifier | new ] |
注意这里的#
与::
一致,都可以表示方法引用。
如:
1 | String::hashCode |
都表示String类的hashcode方法引用。其可以像lambda表达式一样传递。
MemberReferenceTree.ReferenceMode
有两种成员引用:
- 方法引用。
- 构造器引用。
MemberSelectTree
成员访问表达式的树节点。
例如:
1 | expression . identifier |
MethodInvocationTree
方法调用表达式的树节点。
例如:
1 | identifier ( arguments ) |
MethodTree
方法或注释类型元素声明的树节点。
例如:
1 | modifiers typeParameters type name |
ModifiersTree
用于修饰符(包括声明的注释)的树节点。
例如:
1 | flags |
NewArrayTree
用于创建数组新实例的表达式的树节点。
例如:
1 | new type dimensions initializers |
NewClassTree
声明类的新实例的树节点。
例如:
1 | new identifier ( ) |
ParameterizedTypeTree
类型参数的类型表达式的树节点。
例如:
1 | type < typeArguments > |
PrimitiveTypeTree
基本类型的树节点。
例如:
1 | primitiveTypeKind |
ReturnTree
return语句的树节点。
例如:
1 | return; |
StatementTree
用作不同类型语句的基类的树节点。
SwitchExpressionTree
switch表达式的树节点。
例如:
1 | switch ( expression ) { |
SwitchTree
switch语句的树节点。
例如:
1 | switch ( expression ) { |
SynchronizedTree
synchronized语句的树节点。
例如:
1 | synchronized ( expression ) |
ThrowTree
抛出语句的树节点。
例如:
1 | throw expression; |
TryTree
try语句的树节点。
例如:
1 | try |
TypeCastTree
类型转换表达式的树节点。
例如:
1 | ( type ) expression |
TypeParameterTree
类型参数的树节点。
例如:
1 | name |
VariableTree
用于变量声明的树节点。
例如:
1 | modifiers type name initializer ; |
WhileLoopTree
一个while循环语句的树节点。
例如:
1 | while ( condition ) |
其他节点
AssertTree
断言树节点。
例如:
1 | assert condition ; |
BindingPatternTree
预览特性,可能不稳定。
CaseLabelTree
预览特性,可能不稳定。
CompoundAssignmentTree
表示普通编译单元和模块编译单元的抽象语法树。
DefaultCaseLabelTree
预览特性,可能不稳定。
一个case标号,在case中标记default (null, default)。
DirectiveTree
模块树中所有指令的超类型。该指令一般指在module-info.java
中的指令。
EmptyStatementTree
一个空(跳过)语句的树节点。
例如:
1 | ; |
ErroneousTree
用来代替一个畸形的表达式的树节点。
ExportsTree
模块声明中’exports’指令的树节点。
如:
1 | exports package-name; |
ExpressionTree
预览特性,可能不稳定。
用作不同类型表达式的基类的树节点。
GuardedPatternTree
预览特性,可能不稳定。
守护模式树。
IntersectionTypeTree
转换表达式中交集类型的树节点。
LabeledStatementTree
带label的表达式的树节点。一般和带label的break语句一起使用。
LineMap
提供在编译单元的字符位置和行号之间进行转换的方法。
其包含以下方法:
- getColumnNumber:查找字符位置的列。
- getLineNumber:查找包含位置的行;行终止字符在它终止的行上。
- getPosition:找到对应于(行,列)的位置。
- getStartPosition:查找一行的起始位置。
ModuleTree
模块声明的树节点。
例如:
1 | annotations |
ModuleTree.ModuleKind
模块的类型。
OpensTree
模块声明中’open ‘指令的树节点。
例如:
1 | opens package-name; |
PackageTree
表示包声明的树节点。
ParenthesizedPatternTree
预览特性,可能不稳定。
圆括号模式的树节点。
ParenthesizedTree
用于圆括号表达式的树节点。注意:解析器不会保留圆括号。
例如:
1 | ( expression ) |
PatternTree
预览特性,可能不稳定。
用作不同类型模式的基类的树节点。
ProvidesTree
‘ providers ‘指令在模块声明中的树节点。
例如:
1 | provides service-name with implementation-name; |
RequiresTree
‘require ‘指令在模块声明中的树节点。
例如:
1 | requires module-name; |
Scope(作用域)
用于决定本地可以用程序元素的接口,这些元素可能是本地变量或者import。在创建元素时,Scope与给定的程序位置相关联;例如,树节点。这个位置可以用来推断一个封闭的方法和/或类。
Scope本身不包含与包含其位置的方法和类的参数、方法和字段相对应的元素的详细信息。但是,这些元素可以从外围元素中确定。
Scope可以包含在封闭作用域中。最外层Scope包含那些通过”star import”声明可用的元素;其中的作用域包含编译单元的顶级元素,包括任何命名导入。
UnionTypeTree
多包变量声明中用于union类型表达式的树节点。
UsesTree
在模块声明中用于’uses’指令的树节点。
例如:
1 | uses service-name; |
WildcardTree
通配符类型参数的树节点。
例如:
1 | ? |
YieldTree
yield语句的树节点。
例如:
1 | yield expression ; |
Blocks, Statements
Blocks是一个Statements序列、局部变量声明语句、局部类和大括号内的接口声明。
Statements也是Statements的序列。其由多种类型,比如
空语句:空语句什么也不做。
1
2EmptyStatement:
;标签语句:语句可以有标签前缀。
1
2
3
4LabeledStatement:
Identifier : Statement
LabeledStatementNoShortIf:
Identifier : StatementNoShortIf表达式语句:某些类型的表达式可以通过后跟分号作为语句来使用。
1
2
3
4
5
6
7
8
9
10ExpressionStatement:
StatementExpression ;
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpressionif语句:
1
2
3
4
5
6IfThenStatement:
if ( Expression ) Statement
IfThenElseStatement:
if ( Expression ) StatementNoShortIf else Statement
IfThenElseStatementNoShortIf:
if ( Expression ) StatementNoShortIf else StatementNoShortIfif - then - else语句
断言语句
switch语句
while语句
do语句
for语句
增强for循环语句
break语句
continue语句
return语句
throw语句
synchronized 语句
try语句
yield语句
无法到达语句(Unreachable Statements):如果语句由于无法访问而无法执行,则为编译时错误。
具体见Java Language Specification。
其继承关系如下:
TreeMaker
既然上面说到抽象语法树节点必须依赖一个context,则无法直接new。所以就有了TreeMaker,用来在指定下文中来新建抽象语法树节点。
instance(Context context)
该方法用于获取TreeMaker实例。其中context可以通过
1 | Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); |
来获取。
at
这个函数是用来改变当前树节点在context的位置。并不会产生新的树节点。源码如下:
1 | /** Reassign current position. |
Modifiers(long flags)
用于新建一个标识符。其中入参flags
可以用枚举类型com.sun.tools.javac.code.Flags
,且支持拼接(枚举值经过精心设计)。
例如:
1 | treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL); |
Import(JCTree qualid, boolean importStatic)
用来新建一个Import语句。其中qualid是要引入的类。
ClassDef
用来新建一个类语句,所有参数如下:
1 | JCClassDecl ClassDef(JCModifiers mods, // 修饰符 |
MethodDef
用来新建一个犯法语句,所有参数如下:
1 | JCMethodDecl MethodDef(JCModifiers mods, // 修饰符 |
VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init)
用来新建一个变量。
参数意义如下:
- mods:修饰符
- name:变量名
- vartype:变量类型
- init:初始化
Skip
用来新建一个;
语句块,没有参数。
Block(long flags, List<JCStatement> stats)
用来新建一个块语句。
参数意义如下:
- flags:访问标志
- stats:语句列表
DoLoop(JCStatement body, JCExpression cond)
用来新建一个do...while
语句。
参数意义如下:
- body:循环体的语句
- cond:循环条件
JCWhileLoop WhileLoop(JCExpression cond, JCStatement body)
用来新建一个while
循环。
参数意义如下:
- cond:循环条件。
- body:循环体语句。
ForLoop
用来新建一个for循环。
1 | JCForLoop ForLoop(List<JCStatement> init, // 循环初始化 |
JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body)
用来新建一个增强for循环。
其参数意义如下:
- var:每次循环的单个变量
- expr:被循环的变量
- body:循环体
JCLabeledStatement Labelled(Name label, JCStatement body)
用来新建一个标记块。
其参数意义如下:
- label:标签名
- body:块体
public JCSwitch Switch(JCExpression selector, List<JCCase> cases)
用来新建一个Switch语句。
其参数意义如下:
- selector: switch语句中被判断的表达式
- cases:switch语句的cases
public JCCase Case(JCExpression pat, List<JCStatement> stats)
用来新建一个case语句。
其参数意义如下:
- pat:case语句的条件表达式
- stats:符合条件时的语句块
public JCSynchronized Synchronized(JCExpression lock, JCBlock body)
用来新建一个synchronized语句。
其参数意义如下:
- lock:同步锁的对象
- body:synchronized的语句块
Try
用来新建一个try语句。
1 | public JCTry Try(List<JCTree> resources, // try-resource语句中的资源列表 |
public JCCatch Catch(JCVariableDecl param, JCBlock body)
用来新建一个catch语句。
其参数意义如下:
- param:catch语句的参数
- body:catch语句的主体语句块
Conditional
用来新建一个三目表达式。
1 | public JCConditional Conditional(JCExpression cond, // 条件判断 |
public JCIf If(JCExpression cond, JCStatement thenpart, JCStatement elsepart)
用来新建一个if语句。
其参数意义如下:
- cond:if条件语句
- thenpart:if的语句块
- elsepart:else的语句块
JCExpressionStatement Exec(JCExpression expr)
用来创建一个可执行语句。
其参数意义如下:
- expr:要执行的语句。
例如,TreeMaker.Apply以及TreeMaker.Assign就需要外面包一层TreeMaker.Exec来获得一个JCExpressionStatement
public JCBreak Break(?Name label)
用来创建一个break语句,可以是带标签的。
public JCContinue Continue(?Name label)
用来创建一个continue语句,可以是带标签的。
public JCReturn Return(JCExpression expr)
用来创建一个return语句,expr是要返回的表达式。
public JCThrow Throw(JCExpression expr)
用来新建一个throw语句,expr是要抛出的错误表达式。
NewClass
用来新建一个新的类实例化,如new A()
1 | public JCNewClass NewClass(JCExpression encl, // 作用域,一般是在顶层,可以通getEnclosingExpression函数来获取 |
NewArray
用来创建一个数组。例如,new int[10][10]{1,2,3}
1 | public JCNewArray NewArray(JCExpression elemtype, // 数组对的类型 |
public JCLambda Lambda(List<JCVariableDecl> params,JCTree body)
用来创建一个lambda表达式。
其参数意义如下:
- params:参数
- body:lambda表达式的方法体
public JCAssign Assign(JCExpression lhs, JCExpression rhs)
用来创建一个赋值表达式,如例a = b
其参数意义如下:
- lhs:左侧的表示
- rhs:右侧的表示
public JCAssignOp Assignop(JCTree.Tag opcode, JCTree lhs, JCTree rhs)
用来创建一个复合赋值运算符。例如:a += b
或c-= d
等操作。
其参数意义如下:
- opcode:操作符
- lhs:左侧表达式
- rhs:右侧表达式
public JCBinary Binary(JCTree.Tag opcode, JCExpression lhs, JCExpression rhs)
创建一个二元操作符。如a - b
或c + d
。
其参数意义如下:
- opcode:操作符
- lhs:左侧表达式
- rhs:右侧表达式
public JCTypeCast TypeCast(JCTree clazz, JCExpression expr)
创建一个类型强转。如(int)c
。
其参数意义如下:
- clazz:要转换的类型
- expr:要转换的变量
public JCArrayAccess Indexed(JCExpression indexed, JCExpression index)
创建一个根据数组下标获取语句,如arr[0]
。
其参数意义如下:
- indexed:被获取的数组表达式
- index:数组下标
public JCFieldAccess Select(JCExpression selected, Name selector)
创建一个对象获取语句,如obj.prop
。
其参数意义如下:
- selected:被选择的对象
- selector:要选择的对象属性
public JCIdent Ident(Name name)
创建一个标识符语句。例如:a = 10
中的a。
public JCLiteral Literal(TypeTag tag, Object value)
创建一个字面量表达式。如String a = '10'
中的'10'
。
其参数意义如下:
- tag:字面量表达式的类型
- value:字面量表达式的值
Apply
创建一个方法调用。如method1(10)
。
1 | public JCMethodInvocation Apply(List<JCExpression> typeargs, // 类型参数列表 |