abc:可扩展的 AspectJ 编译器
1. 引言
abc 编译器基于 Polyglot 可扩展编译器框架和 Soot 字节码分析与转换框架构建。它能处理源文件和类文件输入,在 Jimple 中间表示上进行织入操作。这种设计使得开发者能轻松更新依赖库版本,也便于 AspectJ 扩展的作者升级 abc 版本。
2. 架构
2.1 整体架构概述
abc 的架构主要由前端的 Polyglot 和后端的 Soot 组成。输入的类可以是源代码或类文件,abc 均能进行织入。前端 Polyglot 处理源文件,仅读取类文件的签名部分用于类型检查;后端 Soot 在 Jimple 中间表示上进行织入和优化。
下面是 abc 整体设计的 mermaid 流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A[.java]:::process --> B(Polyglot 解析器):::process
C[.class]:::process --> D(Polyglot AST 转换):::process
B --> E(Java AST):::process
D --> E
E --> F(AspectInfo):::process
E --> G(Soot 骨架生成):::process
F --> H(骨架织入):::process
G --> H
H --> I(Jimple 骨架):::process
I --> J(Soot Jimple 主体生成):::process
J --> K(Advice 织入):::process
F --> K
K --> L(分析与优化):::process
L --> M(最终 Jimple):::process
M --> N(Soot 字节码生成):::process
N --> O[.class]:::process
2.2 Polyglot
Polyglot 是用于实现 Java 语言扩展的前端。它将 Java 源代码解析为 AST,并进行静态检查,输出带有类型信息的 Java AST。在 abc 中,Polyglot 的最终阶段将 AspectJ 程序分离为纯 Java 代码和后端指令。
Polyglot 适合编写扩展的特点如下:
-
语法扩展
:允许通过修改现有语法的方式定义新语法,新语法在单独的规范文件中指定。
-
模块化设计
:大量使用接口和工厂,易于扩展或替换部分组件,如类型系统、作用域规则和 AST 重写阶段。
-
AST 节点扩展
:每个 AST 节点使用扩展和委托机制,可在现有类层次结构中添加或替换方法。
2.3 Soot
Soot 作为 abc 的后端,是 Java 字节码分析和转换框架。其核心优势在于 Jimple 中间表示,它是一种类型化、无栈的三地址代码,简化了 Advice 织入过程。
Soot 的主要功能包括:
-
中间表示转换
:提供 Jimple、Java 字节码和 Java 源代码之间的转换模块。
-
优化实现
:包含标准编译器优化,abc 在织入后应用这些优化,已取得显著的速度提升。
-
分析工具
:提供控制流图构建器、定义/使用链、定点流分析框架和方法内联器等工具,便于实现需要了解程序过程内行为的扩展。
2.4 Polyglot 与 Soot 的连接
连接 Polyglot 和 Soot 的关键是将 AspectJ AST 分离为纯 Java AST 和 AspectInfo 结构。Java AST 包含去除 AspectJ 特定构造的代码,但可能引用未编织程序中不存在或不可访问的类成员。
Soot 将 Java 转换为 Jimple 分两个阶段进行:
1.
构建类层次结构骨架
:创建一个包含方法存根的 Jimple 程序骨架,不包含方法体。
2.
填充方法体
:通过转换类文件的字节码或编译 AST 节点来填充方法体。
这种两阶段织入方式与 ajc 类似,由 AspectJ 语言设计决定:静态织入影响类型层次结构,Advice 织入影响运行时行为,且必须先调整类型层次结构才能生成代码。
2.5 Advice 织入器
Advice 织入器的任务是根据 AspectInfo 中的指令修改 Jimple 代码,使 Advice 体在相应的切入点匹配当前执行的连接点时执行。
其架构步骤如下:
1.
识别连接点阴影
:找出 Jimple 代码中可能对应程序执行中连接点的所有位置。
2.
匹配切入点
:将每个阴影与程序中的所有切入点进行匹配。如果确定某个切入点可能在特定阴影处匹配连接点,匹配器会发出织入指令。
3.
生成织入指令
:织入指令包括要织入的阴影、要织入的 Advice 和动态残差,动态残差指定了额外的运行时检查和参数绑定信息。
4.
执行织入
:根据织入指令进行实际织入,生成包含所有 Advice 体在适当时间执行的 Jimple 程序,最后将其转换为字节码写入目标类文件。
下面是 Advice 织入器设计的 mermaid 流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(Jimple IR for Bytecode):::process --> B(Shadow Finder):::process
C(Pointcuts):::process --> D(IR for Matcher):::process
B --> E(Shadows):::process
E --> D
D --> F(Weaving Instructions):::process
F --> G(Optimiser):::process
G --> H(Weaver):::process
I(Analyser):::process --> J(Analysis Results):::process
J --> G
H --> K(Woven Jimple):::process
K --> L(Bytecode Generator):::process
L --> M[Bytecode]:::process
3. 定义扩展
3.1 语法
实现新扩展的第一步通常是定义其引入的额外语法。这涉及修改 abc 使用的词法分析器和解析器。
词法分析器
abc 的词法分析器支持有限形式的可扩展性,扩展可以修改识别的关键字集和遇到关键字时的操作。由于 AspectJ 解析涉及多种语言,词法分析器是有状态的,在不同上下文中识别不同的标记。例如,对于字符串 “if . 1.Foo+.new(..)”,Java 代码和切入点定义的解释不同。
设计有状态词法分析器的关键是在不增加过多复杂性的情况下指定状态转换。一般通过维护状态栈,在遇到相应的结束括号字符时识别状态结束。
解析器
abc 解析器由 PPG 生成,PPG 是 Polyglot 中包含的用于可扩展语法的 LALR 解析器生成器。它允许对现有语法进行修改,实现新扩展的语法支持。
下面是一个总结 abc 扩展语法实现步骤的表格:
|步骤|描述|
|----|----|
|定义额外语法|确定新扩展要引入的语法元素|
|修改词法分析器|调整关键字识别和操作,可通过状态管理处理不同语言上下文|
|修改解析器|使用 PPG 生成支持新语法的解析器|
3.2 语义分析
在定义了扩展的语法之后,需要进行语义分析以确保扩展代码符合语言规则。这通常涉及在 Polyglot 的语义分析阶段添加新的检查和转换。
类型检查
扩展可能引入新的类型或类型约束,需要在语义分析中进行类型检查。例如,新的切入点可能需要特定类型的参数,或者新的 Advice 可能返回不同的类型。在 abc 中,可以通过添加自定义的类型检查器来处理这些情况。
作用域规则
扩展可能会影响 Java 源代码的作用域规则。例如,新的构造可能引入新的变量或方法,需要确保这些元素在正确的作用域内可见。在 Polyglot 中,可以通过修改作用域规则和重写阶段来处理这些变化。
语义转换
有时,扩展需要对代码进行语义转换,以确保其在 AspectJ 环境中正确执行。例如,新的切入点可能需要转换为标准的 AspectJ 切入点表达式。可以在 Polyglot 的 AST 重写阶段添加这些转换。
下面是一个总结语义分析步骤的表格:
|步骤|描述|
|----|----|
|类型检查|添加自定义类型检查器,处理新类型和类型约束|
|作用域规则|修改作用域规则和重写阶段,确保新元素在正确的作用域内|
|语义转换|在 AST 重写阶段添加转换,将扩展代码转换为标准的 AspectJ 代码|
3.3 代码生成
扩展的最后一步是生成可执行的代码。这通常在 Soot 的后端完成,基于 Jimple 中间表示进行代码生成。
静态织入
静态织入主要影响类型层次结构,在 Soot 的第一阶段生成 Jimple 骨架时完成。这包括应用 declare parents 和 intertype 声明,更新类层次结构和成员引用。
动态织入
动态织入影响运行时行为,在 Jimple 代码填充方法体后进行。根据 Advice 织入器生成的织入指令,将 Advice 体插入到适当的位置。
下面是一个总结代码生成步骤的表格:
|步骤|描述|
|----|----|
|静态织入|在 Jimple 骨架生成阶段,应用 declare parents 和 intertype 声明,更新类层次结构|
|动态织入|在 Jimple 代码填充方法体后,根据织入指令插入 Advice 体|
3.4 优化与分析
为了提高扩展代码的性能和质量,可以在织入后进行优化和分析。Soot 提供了丰富的工具和优化,abc 可以利用这些资源。
编译器优化
Soot 包含标准的编译器优化,如常量折叠、死代码消除等。abc 在织入后应用这些优化,已取得显著的速度提升。
自定义分析
扩展可能需要进行自定义分析,以满足特定的需求。例如,新的切入点可能需要分析程序的控制流图。Soot 提供了控制流图构建器、定义/使用链等工具,便于实现这些分析。
下面是一个总结优化与分析步骤的表格:
|步骤|描述|
|----|----|
|编译器优化|应用 Soot 提供的标准编译器优化,提高代码性能|
|自定义分析|使用 Soot 工具实现自定义分析,满足扩展的特定需求|
3.5 扩展示例
为了更好地理解如何在 abc 中实现扩展,下面给出一个简单的扩展示例。
新的切入点
假设我们要实现一个新的切入点,用于匹配所有在特定类中定义的方法。
- 语法定义 :在词法分析器中添加新的关键字,在解析器中定义新的语法规则。
- 语义分析 :添加类型检查和作用域规则,确保新的切入点在语义上正确。
- 代码生成 :在 Advice 织入器中,根据新的切入点生成织入指令。
- 优化与分析 :应用编译器优化和自定义分析,提高扩展的性能和质量。
新的 Advice
假设我们要实现一个新的 Advice,用于在方法调用前后记录日志。
- 语法定义 :定义新的语法,用于声明新的 Advice。
- 语义分析 :进行类型检查和作用域规则检查,确保新的 Advice 在语义上正确。
- 代码生成 :在 Jimple 代码中插入新的 Advice 体。
- 优化与分析 :应用优化和分析,提高扩展的性能和质量。
下面是一个总结扩展示例步骤的表格:
|扩展类型|语法定义|语义分析|代码生成|优化与分析|
|----|----|----|----|----|
|新的切入点|添加关键字和语法规则|类型检查和作用域规则|生成织入指令|应用优化和分析|
|新的 Advice|定义新语法|类型检查和作用域规则|插入 Advice 体|应用优化和分析|
3.6 总结
abc 作为一个可扩展的 AspectJ 编译器,通过结合 Polyglot 和 Soot 框架,提供了强大的扩展能力。通过定义新的语法、进行语义分析、生成代码和应用优化,开发者可以轻松实现各种 AspectJ 扩展。
整个扩展过程可以总结为以下 mermaid 流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A[定义扩展语法]:::process --> B[语义分析]:::process
B --> C[代码生成]:::process
C --> D[优化与分析]:::process
这个流程图展示了从定义扩展语法到最终优化与分析的整个过程,各个步骤紧密相连,确保扩展的正确性和性能。通过这种方式,abc 为开发者提供了一个灵活且高效的平台,用于实现和扩展 AspectJ 功能。
超级会员免费看
45

被折叠的 条评论
为什么被折叠?



