大家好,我是老三,在上期 里我们介绍了轻量级规则引擎AviatorScript
的基本用法和一些使用案例,这期我们来研究一下,这么丝滑的规则脚本是怎么实现的。
概览
我们先来回顾一个简单的例子:
@Test
public void test(){
//表达式脚本
String expression = "if(a > 1) { return b+2; } else { return c; }";
//一:编译
Expression compiledExpression=AviatorEvaluator.getInstance().compile(expression);
//上下文
Map<String, Object> env = new HashMap<>();
env.put("a", 2);
env.put("b", 3);
env.put("c", 4);
//二、执行
Object result =compiledExpression.execute(env);
System.out.println(result);
}
这是一个很简单的is-else
·脚本,脚本里做了一个条件判断,分别返回不同的值。
要执行这个脚本,主要分为两步,一是编译
,二是执行
,AviatorScript的具体实现,就藏在了这两大步里,我们一起去挖掘Aviator实现的秘密吧。
编译compile()
接着上面这个例子,我们进去看一下compile()
的关键代码:
public Expression compile(final String expression) {
return compile(expression, this.cachedExpressionByDefault);
}
经过一层套一层的compile()
,我们终于看到一个代码多一些的compile()
方法。
分析一下这段代码:
-
根据传入的cached参数判断是否启用缓存。如果启用缓存,则会执行以下步骤:
a. 如果使用了LRU(最近最少使用)缓存策略,那么首先会尝试从LRU缓存中获取已经存在的FutureTask对象,该对象用于表示正在编译的任务。如果存在,就返回已编译的表达式;如果不存在,则创建一个新的编译任务(newCompileTask),并将其放入LRU缓存中。
b. 如果没有使用LRU缓存(this.expressionLRUCache为null),那么会尝试从普通的缓存(this.expressionCache)中获取已经存在的FutureTask对象。如果存在,就返回已编译的表达式;如果不存在,则创建一个新的编译任务,并将其放入缓存中。
-
如果在缓存中找到了对应的编译任务(existedTask),则执行该任务(existedTask.run())并返回已编译的表达式。
-
如果不启用缓存(cached为false),则调用innerCompile方法进行实际的编译操作,并返回编译后的表达式。
到了这里,我们终于抓住了执行实际编译的方法innerCompile()
,继续跟着源码前进。
innerCompile()
innerCompile()
这个方法就是实际用来来执行编译的方法:
/**
* 执行真正的编译操作:将给定的表达式进行词法分析、语法分析,并生成一个完整的表达式对象
* @param expression 需要编译的表达式
* @param sourceFile 源文件路径
* @param cached 是否启用缓存
* @return 表达式对象
*/
private Expression innerCompile(final String expression, final String sourceFile,
final boolean cached) {
//1.创建表达式词法分析器:通过传入当前对象和表达式,创建 ExpressionLexer 对象。ExpressionLexer 用于对表达式进行词法分析,如将脚本解析为变量、数字、字符串、注释等,并构造Token流进行后续处理。
ExpressionLexer lexer = new ExpressionLexer(this, expression);
//2.创建字节码生成器:调用 newCodeGenerator 方法创建 CodeGenerator 对象。CodeGenerator 用于生成自定义的字节码
CodeGenerator codeGenerator = newCodeGenerator(sourceFile, cached);
//3.创建表达式解析器:通过传入当前对象、Lexer 和 CodeGenerator,创建 ExpressionParser 对象。ExpressionParser 用于将词法分析得到的标记组装成一个完整的表达式。
ExpressionParser parser = new ExpressionParser(this, lexer, codeGenerator);
//4.解析表达式:调用 parser 的 parse 方法,将词法分析得到的标记解析为一个表达式对象(exp)。
Expression exp = parser.parse();
//5.设置表达式文本(可选):根据选项 Options.TRACE_EVAL 的值来判断是否需要设置表达式对象的文本。如果 Options.TRACE_EVAL 为 true,则将 expression 设置在 exp 对象中,用于跟踪和调试目的。
if (getOptionValue(Options.TRACE_EVAL).bool) {
((BaseExpression) exp).setExpression(expression);
}
//6.返回表达式对象:返回解析后的表达式对象 exp。
return exp;
}
在这段代码里,创建了几个关键的角色:
- ExpressionLexer:词法分析器,它用于将AviatorScript脚本解析成变量、数字、字符串、注释等不同的词法单元(Token),并构造一个Token流以供后续处理。
- CodeGenerator:字节码生成器,它用于动态生成自定义的字节码。在AviatorScript中,CodeGenerator负责将ExpressionLexer生成的Token流编译成表达式对象的字节码。
- ExpressionParser:表达式解析器,它用于将AviatorScript脚本编译为表达式对象(通常是ClassExpression)。ExpressionParser支持解析多种AviatorScript脚本,并且在解析过程中会利用CodeGenerator将ExpressionLexer构建的Token流编译成适当的表达式对象(比如ClassExpression)。
ExpressionLexer词法分析器
ExpressionLexer负责对AviatorScript脚本进行词法分析,将其解析为Token流。
public Token<?> scan() {
return this.scan(true);
}
这个类里代码不多,最重要的方法就是scan
,用于扫描输入的字符串并将其转换为不同类型的词法单元(Token
),它会在创建ExpressionParser
的时候被调用。
具体来说,它按照一定的规则逐个字符地读取输入,并根据字符的特征确定相应的词法单元类型。
CodeGenerator字节码生成器
CodeGenerator用于动态生成自定义的字节码。
我们先来看下它的继承体系:
-
CodeGenerator
是一个顶层接口,定义了字节码生成的方法和规范。
-
ASMCodeGenerator
是默认的实现类,它基于ASM框架进行具体的字节码生成。它实现了CodeGenerator接口中声明的操作,将代码转化为ASM指令并生成对应的字节码。
-
OptimizeCodeGenerator
是一个优化过的字节码生成器,默认情况下也使用的是ASMCodeGenerator
作为底层实现。OptimizeCodeGenerator可以提高提高执行效率,在生成字节码之前,它可以执行一些预处理的计算逻辑,然后再将结果交给ASMCodeGenerator来生成字节码。通过这种优化,可以减少运行时的计算量,提高脚本执行的效率。
-
LambdaGenerator
是一个用于生成Lambda表达式代码的代码生成器。它接受一个父级代码生成器和其他参数作为输入,并通过调用内部的codeGenerator
来生成Lambda表达式的代码。
ASM拾遗
这里再简单学习一下字节码技术,因为ASM在Aviator里扮演了非常重要的作用,是Aviator依赖的核心类库。
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
看一个简单的例子,Hello World
都写过吧:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Java代码在执行
前,需要先编译
,那么要生成和这个类编译之后相同的Java类文件(.class
文件),用ASM怎么实现呢?
稍微有点繁琐:
public class HelloWorldGenerator {
public static void main(String[] args) throws IOException {
// 1.创建一个 ClassWriter 实例,以生成一个新的类
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// 2.定义类的基本信息:访问修饰符、类名、父类、接口
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "Hello", null, "java/lang/Object", null);
// 3.定义主方法
MethodVisitor mainMethod = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
mainMethod.visitCode();
mainMethod.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mainMethod.visitLdcInsn("Hello, World!");
mainMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
"(Ljava/lang/String;)V", false);
mainMethod.visitInsn(Opcodes.RETURN);
mainMethod.visitMaxs(2, 1);
mainMethod.visitEnd();
//4.类结束
cw.visitEnd();
//5.将字节码写入文件
byte[] byteCode = cw.toByteArray();
FileOutputStream fos = new FileOutputStream("Hello.class");
fos.write(byteCode);
fos.close();
}
}
在Idea里可以直接反编译生成的字节码文件:
ASM框架,主要由这几个核心组件组成:
- ClassReader(类读取器):ClassReader用于读取已编译的Java类文件的字节码。它将字节码解析为可以访问和操作的结构化表示形式。
- ClassWriter(类写入器):ClassWriter用于生成新的Java类文件的字节码。它提供了一组API来创建类、方法、字段和指令等,并将它们转换为字节码形式。
- ClassVisitor(类访问器):ClassVisitor是一个接口,用于访问和修改正在被读取或写入的类。通过实现ClassVisitor接口,开发人员可以自定义对类的访问方式,并在访问过程中进行相应的操作。
- MethodVisitor(方法访问器):MethodVisitor是ClassVisitor的子接口,用于访问和修改正在被读取或写入的方法。通过实现MethodVisitor接口,开发人员可以自定义对方法的访问方式,并在访问过程中进行相应的操作。
- FieldVisitor(字段访问器):FieldVisitor是ClassVisitor的子接口,用于访问和修改正在被读取或写入的字段。通过实现FieldVisitor接口,开发人员可以自定义对字段的访问方式,并在访问过程中进行相应的操作。
- AnnotationVisitor(注解访问器):AnnotationVisitor是ClassVisitor的子接口,用于访问和修改正在被读取或写入的注解。通过实现AnnotationVisitor接口,开发人员可以自定义对注解的访问方式,并在访问过程中进行相应的操作。
……
简单了解一下ASM技术之后,接下来我们看看核心的代码生成器ASMCodeGenerator。
ASMCodeGenerator
ASMCodeGenerator
在Aviator中的作用是将用户提供的表达式转换为可执行的Java字节码,并且能够动态生成类和方法来存储和执行这些字节码。
包括其它几个CodeGenerator最后调用的也是ASMCodeGenerator
。
ASMCodeGenerator
是对ASM的一个封装,加入了一些定制化的逻辑判断,其实生成代码的主要流程和我们前面HelloWorld
的例子是类似的。
-
创建 ClassWriter 对象,用于生成类文件的字节码。
ASMCodeGenerator的构造方法:
public ASMCodeGenerator(final AviatorEvaluatorInstance instance, final String sourceFile,
final AviatorClassLoader classLoader, final OutputStream traceOut) {
super(instance, sourceFile, classLoader);
//创建内部类型
this.className = "Script_" + System.currentTimeMillis() + "_" + CLASS_COUNTER.getAndIncrement();
//创建ClassWriter, 自动填充栈帧信息
this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
//访问类
visitClass();
}
-
定义类名、父类、接口等基本信息,并开始访问类
visitClass()
方法:
/**
* 访问类,生成类的相关信息
*/
private void visitClass() {
// 设置类的基本信息
this.classWriter.visit(this.instance.getBytecodeVersion(), ACC_PUBLIC + ACC_SUPER,
this.className, null, "com/googlecode/aviator/ClassExpression", null);
// 设置类的源文件名
this.classWriter.visitSource(this.sourceFile == null ? this.className : this.sourceFile, null);
}
-
设置类的字段和方法,生成对应的字段和方法的字节码。
- 比如在启动代码生成过程的
start()
里生成了一个默认的构造函数,并且开始生成方法代码。
/**
* 启动代码生成过程
*/
@Override
public void start() {
//生成构造函数的代码
makeConstructor();
//用于开始生成方法的代码
startVisitMethodCode();
}
private void makeConstructor() {
// 创建公共构造函数
this.mv = this.classWriter.visitMethod(ACC_PUBLIC, CONSTRUCTOR_METHOD_NAME,
"(Lcom/googlecode/aviator/AviatorEvaluatorInstance;Ljava/util/List;Lcom/googlecode/aviator/lexer/SymbolTable;)V",
null, null);
this.mv.visitCode(); // 开始访问构造函数的代码
// 调用父类的构造函数
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitVarInsn(ALOAD, 1);
this.mv.visitVarInsn(ALOAD, 2);
this.mv.visitVarInsn(ALOAD, 3);
this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/ClassExpression",
CONSTRUCTOR_METHOD_NAME,
"(Lcom/googlecode/aviator/AviatorEvaluatorInstance;Ljava/util/List;Lcom/googlecode/aviator/lexer/SymbolTable;)V");
// 设置内部变量的值
if (!this.innerVars.isEmpty()) {
for (Map.Entry<String, String> entry : this.innerVars.entrySet()) {
String outterName = entry.getKey();
String innerName = entry.getValue();
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitTypeInsn(NEW, JAVA_TYPE_OWNER);
this.mv.visitInsn(DUP);
this.mv.visitLdcInsn(outterName);
this.mv.visitVarInsn(ALOAD, 3);
this.mv.visitMethodInsn(INVOKESPECIAL, JAVA_TYPE_OWNER, CONSTRUCTOR_METHOD_NAME,
"(Ljava/lang/String;Lcom/googlecode/aviator/lexer/SymbolTable;)V");
this.mv.visitFieldInsn(PUTFIELD, this.className, innerName,
"Lcom/googlecode/aviator/runtime/type/AviatorJavaType;");
}
}
// 设置内部方法的值
if (!this.innerMethodMap.isEmpty()) {
for (Map.Entry<String, String> entry : this.innerMethodMap.entrySet()) {
String outterName = entry.getKey();
String innerName = entry.getValue();
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitVarInsn(ALOAD, 1);
this.mv.visitLdcInsn(outterName);
this.mv.visitVarInsn(ALOAD, 3);
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/AviatorEvaluatorInstance",
"getFunction",
"(Ljava/lang/String;Lcom/googlecode/aviator/lexer/SymbolTable;)Lcom/googlecode/aviator/runtime/type/AviatorFunction;");
this.mv.visitFieldInsn(PUTFIELD, this.className, innerName,
"Lcom/googlecode/aviator/runtime/type/AviatorFunction;");
}
}
// 设置常量池中的值
if (!this.constantPool.isEmpty()) {
for (Map.Entry<Token<?>, String> entry : this.constantPool.entrySet()) {
Token<?> token = entry.getKey();
String fieldName = entry.getValue();
this.mv.visitVarInsn(ALOAD, 0);
onConstant0(token, true);
this.popOperand();
this.mv.visitFieldInsn(PUTFIELD, this.className, fieldName, OBJECT_DESC);
}
}
this.mv.visitInsn(RETURN); // 返回
this.mv.visitMaxs(4, 1); // 最大栈深度和局部变量数
this.mv.visitEnd(); // 结束访问构造函数
}
-
在方法的字节码生成过程中,根据代码逻辑和类型信息,生成相应的字节码指令和操作数。
- 比如这个方法
onTernaryLeft0
,表示处理三元表达式的左侧逻辑:
/**
* 处理三元表达式的左侧逻辑
* @param lookhead 当前处理的标记
*/
@Override
public void onTernaryLeft(final Token<?> lookhead) {
// 跳转到 peekLabel1() 标签处执行
this.mv.visitJumpInsn(GOTO, peekLabel1());
// 访问 popLabel0() 标签
visitLabel(popLabel0());
// 访问当前代码行号
visitLineNumber(lookhead);
// 弹出一个布尔值
this.popOperand();
}
在ASMCodeGenerator的方法里,只要是on开头的,基本都是根据一定的逻辑去生成对应的代码。
-
最后,结束访问类,输出字节码。
/**
* 结束方法体,对方法代码和类进行结束访问
*/
private void end(final boolean unboxObject) {
// 结束访问方法的代码
endVisitMethodCode(unboxObject);
// 结束访问类
endVisitClass();
}
这里我们就对ASMCodeGenerator
有了一个大概的了解,ASMCodeGenerator
在Aviator扮演的就是默默无闻的搬砖工,把底层的事情都干了。
ExpressionParser表达式解析器
ExpressionParser将Token流编译为表达式对象,它充当一个协调者的角色,战斗在编译工作的一线。
我们先来看下构造方法:
/**
* ExpressionParser的构造方法
*/
public ExpressionParser(final AviatorEvaluatorInstance instance, final ExpressionLexer lexer,
final CodeGenerator codeGenerator) {
super();
//创建了一个 ScopeInfo 对象,并将其赋值给 scope 属性。ScopeInfo 对象用于跟踪变量和函数的作用域信息。
this.scope = new ScopeInfo(0, 0, 0, 0, false, new ArrayDeque<DepthState>());
//将传入的 AviatorEvaluatorInstance 实例赋值给 instance 属性。
this.instance = instance;
//从 instance 中获取选项 CAPTURE_FUNCTION_ARGS 的值,并将其设置为解析器的 captureFuncArgs 属性。
this.captureFuncArgs = instance.getOptionValue(Options.CAPTURE_FUNCTION_ARGS).bool;
//将传入的 ExpressionLexer 实例赋值给 lexer 属性
this.lexer = lexer;
//⚡⚡⚡通过调用 lexer 的 scan() 方法,获取下一个 Token,并将其赋值给 lookhead 属性。lookhead 表示当前待处理的 Token。
this.lookhead = this.lexer.scan();
//如果 lookhead 不为空,则将 parsedTokens 的值加1。parsedTokens 表示已经解析的 Token 数量。
if (this.lookhead != null) {
this.parsedTokens++;
}
//从 instance 中获取选项 FEATURE_SET 的值,并将其设置为解析器的 featureSet 属性。featureSet 表示当前使用的特性集合。
this.featureSet = this.instance.getOptionValue(Options.FEATURE_SET).featureSet;
//如果 lookhead 为空,则报告语法错误,提示输入的脚本为空。
if (this.lookhead == null) {
reportSyntaxError("blank script");
}
//设置解析器的代码生成器为传入的 CodeGenerator 实例。
setCodeGenerator(codeGenerator);
//通过调用 getCodeGeneratorWithTimes() 方法获取代码生成器,并将解析器实例设置为其属性中的 parser。
getCodeGeneratorWithTimes().setParser(this);
}
在这段代码里我们可以看到前面说的调用词法分析器的部分:
//⚡⚡⚡通过调用 lexer 的 scan() 方法,获取下一个 Token,并将其赋值给 lookhead 属性。lookhead 表示当前待处理的 Token。
this.lookhead = this.lexer.scan();
parse()
接下来我们看看直接干活的parse()
方法,通过parse()
方法完成对Token的解析。
//4.解析表达式:调用 parser 的 parse 方法,将词法分析得到的标记解析为一个表达式对象(exp)。
Expression exp = parser.parse();
比如if,for,let等脚本特性,解析逻辑都是依赖parse()
方法完成的:
public Expression parse(final boolean reportErrorIfNotEOF) {
//调用statements()方法解析一系列语句,并将结果保存在statementType变量中。
//StatementType表示语句的类型,可能是三元表达式(StatementType.Ternary)或其他类型的语句。
StatementType statementType = statements();
if (this.lookhead != null && reportErrorIfNotEOF) {
if (statementType == StatementType.Ternary) {
reportSyntaxError("unexpect token '" + currentTokenLexeme()
+ "', maybe forget to insert ';' to complete last expression ");
} else {
reportSyntaxError("unexpect token '" + currentTokenLexeme() + "'");
}
}
//将代码生成器的结果作为返回值返回,即返回一个Expression对象
return getCodeGeneratorWithTimes().getResult(true);
}
这段代码,主要做了两件事情:
- 调用statements()方法解析一系列语句,并将结果保存在statementType变量中。StatementType表示语句的类型,可能是三元表达式(StatementType.Ternary)或其他类型的语句。
- 将代码生成器的结果作为返回值返回,即返回一个Expression对象
statements()
我们来看下statements()
,大体上是循环解析一系列Token,返回最后一个Token解析的StatementType。
具体的解析在私有的statement()
方法里里,源码扒出来看下:
/**
* 根据当前标记的类型选择相应的语句处理方法,并返回相应的语句类型。
* 包括条件语句、循环语句、返回语句、异常处理语句、作用域语句、函数声明语句和三元表达式。
* 根据不同的语句类型,调用相应的处理方法,并返回对应的语句类型。如果无法匹配任何语句类型,则返回StatementType.Empty表示空语句。
* @return
*/
private StatementType statement() {
if (this.lookhead == Variable.IF) {
// 确保 if 语句的功能已启用
ensureFeatureEnabled(Feature.If);
// 解析 if 语句,并返回是否成功解析了 return 语句
if (ifStatement(false, false)) {
return StatementType.Return;
} else {
return StatementType.Other;
}
} else if (this.lookhead == Variable.FOR) {
// 确保 for 循环的功能已启用
ensureFeatureEnabled(Feature.ForLoop);
// 解析 for 循环语句
forStatement();
return StatementType.Other;
} else if (this.lookhead == Variable.RETURN) {
// 确保 return 语句的功能已启用
ensureFeatureEnabled(Feature.Return);
// 解析 return 语句
returnStatement();
return StatementType.Return;
} else if (this.lookhead == Variable.BREAK) {
// 解析 break 语句
breakStatement();
return StatementType.Return;
} else if (this.lookhead == Variable.CONTINUE) {
// 解析 continue 语句
continueStatement();
return StatementType.Return;
} else if (this.lookhead == Variable.LET) {
// 确保 let 语句的功能已启用
ensureFeatureEnabled(Feature.Let);
// 解析 let 语句
letStatement();
return StatementType.Other;
} else if (this.lookhead == Variable.WHILE) {
// 确保 while 循环的功能已启用
ensureFeatureEnabled(Feature.WhileLoop);
// 解析 while 循环语句
whileStatement();
return StatementType.Other;
} else if (this.lookhead == Variable.FN) {
// 确保 fn 语句的功能已启用
ensureFeatureEnabled(Feature.Fn);
// 解析 fn 语句
fnStatement();
return StatementType.Other;
} else if (this.lookhead == Variable.TRY) {
// 确保异常处理的功能已启用
ensureFeatureEnabled(Feature.ExceptionHandle);
// 解析 try 语句
tryStatement();
return StatementType.Other;
} else if (this.lookhead == Variable.THROW) {
// 确保异常处理的功能已启用
ensureFeatureEnabled(Feature.ExceptionHandle);
// 解析 throw 语句
throwStatement();
return StatementType.Other;
} else if (expectChar('{')) {
// 确保词法作用域的功能已启用
ensureFeatureEnabled(Feature.LexicalScope);
// 解析作用域语句,并返回是否成功解析了 return 语句
if (scopeStatement()) {
return StatementType.Return;
} else {
return StatementType.Other;
}
} else if (this.lookhead == Variable.USE) {
// 确保 use 语句的功能已启用
ensureFeatureEnabled(Feature.Use);
// 解析 use 语句
useStatement();
return StatementType.Other;
} else {
if (ternary()) {
return StatementType.Ternary;
} else {
return StatementType.Empty;
}
}
}
看到这里,老三不由感慨,原来再牛的规则引擎,底层也是一堆的if-sle
。
这个方法根据当前的 Token 类型来解析不同类型的语句,并返回每个语句的类型。
ifStatement()
我们来抓住一个典型,比如代码里最常用的if
,看看Aviator是怎么处理的,试着管中窥豹,分析Aviator
的设计。
/**
* *用于处理条件语句(if语句):
* 接受两个布尔值参数isWhile和isElsif,用于确定当前条件语句是否是while语句或者是elsif语句。
* <pre>
* if(test) {
* ...if-body...
* }else {
* ...else-body...
* }
* ...statements...
* </pre>
* <p>
* ===>
*
* <pre>
* __if_callcc(test ? (lambda() -> ...if-body... end)() : (lambda() -> ...else-body... end)(),
* lambda()- >
* ...statements...
* end);
* </pre>
*/
private boolean ifStatement(final boolean isWhile, final boolean isElsif) {
// 移动到下一个 Token
if (!isWhile) {
move(true);
}
// 标记 if 语句的主体和 else 语句的返回情况
boolean ifBodyHasReturn = false;
boolean elseBodyHasReturn = false;
// 设置新的词法作用域
boolean newLexicalScope = this.scope.newLexicalScope;
this.scope.newLexicalScope = true;
// 准备调用 __if_callcc(result, statements)
getCodeGeneratorWithTimes().onMethodName(Constants.IfReturnFn);
// 解析 if 语句的条件部分
{
//对三元表达式(ternary),调用相应的代码生成方法进行处理。
if (!ternary()) {
reportSyntaxError("missing test statement for if");
}
getCodeGeneratorWithTimes().onTernaryBoolean(this.lookhead);
// 解析 if 语句的主体部分
if (expectChar('{')) {
move(true);
//调用 this.scope.enterBrace() 进入一个新的花括号作用域
this.scope.enterBrace();
//调用代码生成器的 onLambdaDefineStart() 方法,表示开始定义一个 lambda 函数
getCodeGeneratorWithTimes().onLambdaDefineStart(
getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope));
//调用代码生成器的 onLambdaBodyStart() 方法,表示 lambda 函数的主体部分开始
getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead);
//调用 statements() 方法解析 lambda 函数的主体部分,判断主体部分的类型是否为 Return,并将结果赋值给 ifBodyHasReturn 变量
ifBodyHasReturn = statements() == StatementType.Return;
//调用代码生成器的 onLambdaBodyEnd() 方法,表示 lambda 函数的主体部分结束
getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead);
//调用代码生成器的 onMethodName() 方法,使用匿名方法名
getCodeGeneratorWithTimes().onMethodName(anonymousMethodName());
// 调用代码生成器的 onMethodInvoke() 方法,生成方法调用
getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead);
//调用代码生成器的 onTernaryLeft() 方法,表示条件表达式的左侧部分
getCodeGeneratorWithTimes().onTernaryLeft(this.lookhead);
} else {
reportSyntaxError("expect '{' for if statement");
}
// 验证 if 语句的主体部分是否有正确的右大括号结束
if (!expectChar('}')) {
reportSyntaxError("missing '}' to close if body");
}
this.scope.leaveBrace();
move(true);
// 解析 else 语句
elseBodyHasReturn = elseStatement(isWhile, ifBodyHasReturn);
getCodeGeneratorWithTimes().onMethodParameter(this.lookhead);
}
// 处理 if 语句后面的陈述部分
{
if (isWhile || isElsif) {
// 直接加载 ReducerEmptyVal
getCodeGenerator().onConstant(Constants.ReducerEmptyVal);
} else {
if (expectChar(';')) {
// 陈述部分已结束,加载 ReducerEmptyVal
getCodeGenerator().onConstant(Constants.ReducerEmptyVal);
} else {
// 创建一个包含 if 语句后陈述部分的 lambda 函数
getCodeGeneratorWithTimes().onLambdaDefineStart(
getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)
.withMeta(Constants.INHERIT_ENV_META, true));
getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead);
if (statements() == StatementType.Empty) {
// 陈述部分为空,加载 ReducerEmptyVal
getCodeGenerator().onConstant(Constants.ReducerEmptyVal);
} else {
if (ifBodyHasReturn && elseBodyHasReturn && !isElsif) {
reportSyntaxError("unreachable code");
}
}
getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead);
}
}
getCodeGenerator().onMethodParameter(this.lookhead);
// 调用 __if_callcc(result, statements)
getCodeGenerator().onMethodInvoke(this.lookhead);
this.scope.newLexicalScope = newLexicalScope;
}
//返回if主体和else主体是否有返回语句的布尔值
return ifBodyHasReturn && elseBodyHasReturn;
}
在这段代码开头,作者给出了注释,这个方法会对if脚本进行转换,抽象成一个lambda表达式:
把if语句转换为使用 __if_callcc
函数的形式。转换后的代码使用了三元表达式(ternary)来选择执行 ...if-body...
或者 ...else-body...
,并在转换后的代码中使用lambda函数来表示if和else的主体部分。
这段代码比较长,主要的解析过程可以分为以下几个部分:
-
解析三元表达式:通过ternary()
方法完成对三元表达式的解析和处理(解析结果放入OptimizeCodeGenerator的Token流中,后续统一生成字节码)
-
解析if的主体部分:将if的方法体(ifBody)抽象为一个lambda表达式,并委托给lambdaGenerator进行解析;
-
解析else的主体部分:调用了elseStatement()
,实际的解析过程和ifBody的解析过程类似,也是委托给新构建的lambdaGenerator进行解析;
- 最后对对if语句的陈述部分解析:也可以理解成对整个代码块进行处理,抽象成一个lambda表达式,也是委托给lambdaGenerator进行解析;
接下来我们再研究一下lambda()
的秘密。
LambdaGenerator
我们从这段代码开始看起:
可以大体上把生成lambda脚本解析生成分为三步:
- 解析前:
-
onLambdaDefineStart()
创建并配置一个 LambdaGenerator 实例,用于处理 lambda 函数的生成和编译过程。它还负责设置 lambda 函数的词法作用域,并确保每个 lambda 函数只能在一个作用域中定义。
-
解析中:
- 具体的解析就不用多说了,因为还是接着调用
statements()
,绕来绕去又绕回去了。
-
解析后
-
onLambdaBodyEnd()
方法里,会调用getLmabdaBootstrap构造LambdaFunctionBootstrap实例对象,并且缓存起来。
- 进一步展开
getLmabdaBootstrap()
,这里把Lambda 函数的信息打包成一个 LambdaFunctionBootstrap 对象,并返回该对象。LambdaFunctionBootstrap 是一个用于表示 Lambda 函数的引导类,它包含了 Lambda 函数的类名、表达式、参数列表和是否继承环境等信息。
这里也可以看到调用了getResult()
方法,这个方法里,会调用代码生成器,去生成字节码。
getResult()
先回顾一下parse()
的代码最后一句:
//将代码生成器的结果作为返回值返回,即返回一个Expression对象
return getCodeGeneratorWithTimes().getResult(true);
Aviator脚本解析完成之后,解析结果Token流会存放到OptimizeCodeGenerator的成员变量List<Token<?>> tokenList中,getResult方法就是根据tokenList生成字节码的过程。
OptimizeCodeGeneratord
的getResult()
方法具体实现如下:
/**
* 根据给定的tokenList列表生成相应的表达式对象。
* 它根据不同类型的token,对变量、方法和常量进行处理,并根据情况返回对应的表达式对象。
* 其中包括执行字面量表达式、创建变量和常量的映射集合、调用 ASM(Java字节码框架)生成字节码等操作。
* 最后返回生成的表达式对象
*/
@Override
public Expression getResult(final boolean unboxObject) {
// 执行字面量表达式
while (execute() > 0) {
;
}
// 创建变量、方法和常量的映射集合
Map<String, VariableMeta/* metadata */> variables = new LinkedHashMap<String, VariableMeta>();
Map<String, Integer/* counter */> methods = new HashMap<String, Integer>();
Set<Token<?>> constants = new HashSet<>();
// 遍历tokenList列表
for (Token<?> token : this.tokenList) {
// 如果token是常量,则将其添加到constants集合中
if (ExpressionParser.isConstant(token, this.instance)) {
constants.add(token);
}
// 根据不同的token类型进行不同的处理
switch (token.getType()) {
case Variable:
if (SymbolTable.isReservedKeyword((Variable) token)) {
continue;
}
String varName = token.getLexeme();
VariableMeta meta = variables.get(varName);
// 如果变量在variables集合中不存在,则创建新的VariableMeta对象,并将其添加到variables集合中
if (meta == null) {
meta = new VariableMeta((CompileTypes) token.getMeta(Constants.TYPE_META), varName,
token.getMeta(Constants.INIT_META, false), token.getStartIndex());
variables.put(varName, meta);
} else {
meta.add(token);
}
break;
case Delegate:
DelegateToken delegateToken = (DelegateToken) token;
// 如果是委托类型的token,则根据不同的委托类型进行不同的处理
if (delegateToken.getDelegateTokenType() == DelegateTokenType.Method_Name) {
Token<?> realToken = delegateToken.getToken();
if (realToken == null) {
continue;
}
if (realToken.getType() == TokenType.Variable) {
String methodName = token.getLexeme();
// 统计方法出现的次数,并将其添加到methods集合中
if (!methods.containsKey(methodName)) {
methods.put(methodName, 1);
} else {
methods.put(methodName, methods.get(methodName) + 1);
}
}
} else if (delegateToken.getDelegateTokenType() == DelegateTokenType.Array) {
Token<?> realToken = delegateToken.getToken();
if (realToken.getType() == TokenType.Variable) {
varName = token.getLexeme();
VariableMeta varMeta = variables.get(varName);
// 如果变量在variables集合中不存在,则创建新的VariableMeta对象,并将其添加到variables集合中
if (varMeta == null) {
varMeta =
new VariableMeta((CompileTypes) realToken.getMeta(Constants.TYPE_META), varName,
realToken.getMeta(Constants.INIT_META, false), realToken.getStartIndex());
variables.put(varName, varMeta);
} else {
varMeta.add(realToken);
}
}
}
break;
}
}
Expression exp = null;
// 如果tokenList的大小小于等于1,则根据情况返回对应的LiteralExpression对象
if (this.tokenList.size() <= 1) {
if (this.tokenList.isEmpty()) {
exp = new LiteralExpression(this.instance, null, new ArrayList<>(variables.values()));
} else {
final Token<?> lastToken = this.tokenList.get(0);
if (ExpressionParser.isLiteralToken(lastToken, this.instance)) {
exp = new LiteralExpression(this.instance,
getAviatorObjectFromToken(lastToken).getValue(getCompileEnv()),
new ArrayList<>(variables.values()));
}
}
}
if (exp == null) {
// 调用ASM生成字节码
callASM(variables, methods, constants);
// 从ASM获取结果
exp = this.codeGen.getResult(unboxObject);
}
// 对于BaseExpression对象,设置编译环境和源文件
if (exp instanceof BaseExpression) {
((BaseExpression) exp).setCompileEnv(getCompileEnv());
((BaseExpression) exp).setSourceFile(this.sourceFile);
}
return exp;
}
这里主要包含以下几部分:
-
可以前置执行的逻辑提前执行,比如文本表达式(1+2)等,先行计算出执行结果,优化执行效率
-
初始化常量集、变量集、aviator函数实例集合,为后续ASM生成类成员变量和类构造函数使用
-
调用callASM方法生成字节码,根据不同的Token类型进行不同的asm操作
- 调用
ASMCodeGenerator
的getResult()
方法,结束代码生成过程,根据生成的字节码构造Expression子类实例(ClassExpression
)
/*
* (non-Javadoc)
*
* @see com.googlecode.aviator.code.CodeGenerator#getResult()
* 生成并返回一个表达式(Expression)对象
*/
@Override
public Expression getResult(final boolean unboxObject) {
// 调用 end(unboxObject) 方法结束代码生成过程,并获取生成的字节码数据 byte[] bytes
end(unboxObject);
// 使用 ClassDefiner.defineClass 方法动态定义一个类,并使用生成的字节码数据作为类的定义内容。
// 这里定义的类是继承自 Expression 的类。返回的是一个 Class<?> 类型的对象,表示定义的类
byte[] bytes = this.classWriter.toByteArray();
//
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)