目录
3.1 通过ASM 字节码技术构造ClassExpression对象
3.2 调用ClassExpression的execute0方法获取结果值
1.从测试入手直观看aviator特性
a.b.c测试代码如下:
public class BillingEngineTest extends TransactionalTestBase {
@Test
public void test() {
Student student = new Student();
student.setName("张三");
student.setAge(20);
student.setBirthday(DateUtils.parse("1995-01-01 10:00:00", DateUtils.YYYYMMDDHHMMSS));
student.setCardId(1000L);
student.setAliases(Lists.newArrayList("李四", "王五"));
student.setScore(new BigDecimal("100.00"));
student.setMonitor(true);
student.setLevel("A");
Map env = Maps.newHashMap();
env.put("student", student);
Object name = AviatorEvaluator.execute("student.name", env);
assertThat(name, is("张三"));
}
}
运行结果:
"张三",测试通过;
可以看出通过变量逐级引用的方式,可以正确获取对应字段的属性值,下面从aviator源码入手来具体分析aviator是怎么支持的(How);
2.Aviator可选项支持
属性语法糖在aviator中是默认开启支持的,在Options类中可以看到:
3.源码追踪
这里获取脚本的计算结果主要分为2步:
1): 通过ASM 字节码技术构造ClassExpression对象;
2): 调用ClassExpression的execute0方法获取结果值;
3.1 通过ASM 字节码技术构造ClassExpression对象
首先说明几个主要用到的类:
ExpressionLexer:表达式词法分析器,用来对aviator脚本进行词法解析,如将脚本解析为变量、数字、字符串、注释等;
CodeGenerator:字节码生成器,用于动态生成自定义的字节码;
ExpressionParser:表达式解析器,用于将脚本编译为表达式对象(BaseExpression)
下面是aviator中通过ASM工具动态生成字节码的过程,如下:
1.生成ClassExpression子类字节码
public ASMCodeGenerator(final AviatorEvaluatorInstance instance, final String sourceFile,
final AviatorClassLoader classLoader, final OutputStream traceOut) {
this.classLoader = classLoader;
this.instance = instance;
this.compileEnv = new Env();
this.sourceFile = sourceFile;
this.compileEnv.setInstance(this.instance);
// Generate inner class name
this.className = "Script_" + System.currentTimeMillis() + "_" + CLASS_COUNTER.getAndIncrement();
// Auto compute frames
this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// if (trace) {
// this.traceClassVisitor = new TraceClassVisitor(this.clazzWriter, new PrintWriter(traceOut));
// this.classWriter = new CheckClassAdapter(this.traceClassVisitor);
// } else {
// this.classWriter = new CheckClassAdapter(this.clazzWriter);
// }
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);
}
2.生成ClassExpression子类成员变量,f1,类型AviatorJavaType
public void initVariables(final Map<String, VariableMeta/* counter */> vars) {
this.variables = vars;
this.innerVars = new HashMap<>(this.variables.size());
for (String outterVarName : this.variables.keySet()) {
// Use inner variable name instead of outter variable name
String innerVarName = getInnerName(outterVarName);
this.innerVars.put(outterVarName, innerVarName);
this.classWriter.visitField(ACC_PRIVATE + ACC_FINAL, innerVarName,
"Lcom/googlecode/aviator/runtime/type/AviatorJavaType;", null, null).visitEnd();
}
}
3.生成ClassExpression子类默认构造函数:
/**
* Make a default constructor
*/
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();
}
4.构造ClassExpression子类方法execute0():
private void startVisitMethodCode() {
this.mv = this.classWriter.visitMethod(ACC_PUBLIC + +ACC_FINAL, "execute0",
"(Lcom/googlecode/aviator/utils/Env;)Ljava/lang/Object;",
"(Lcom/googlecode/aviator/utils/Env;)Ljava/lang/Object;", null);
this.mv.visitCode();
}
进入方法体,并生成方法体内容:
// Get field at first time
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitFieldInsn(GETFIELD, this.className, innerVarName,
"Lcom/googlecode/aviator/runtime/type/AviatorJavaType;");
// Variable is used more than once,store it to local
if (this.variables.get(outterVarName).getRefs() > 1) {
this.mv.visitInsn(DUP);
int localIndex = getLocalIndex();
this.mv.visitVarInsn(ASTORE, localIndex);
if (name2Index == null) {
name2Index = new HashMap<>();
this.labelNameIndexMap.put(this.currentLabel, name2Index);
}
name2Index.put(innerVarName, localIndex);
this.pushOperand(3);
this.popOperand(2);
} else {
this.pushOperand(2);
this.popOperand();
}
loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, OBJECT_OWNER, "getValue",
"(Ljava/util/Map;)Ljava/lang/Object;");
this.mv.visitInsn(ARETURN);
this.popOperand();
this.popOperand();
this.mv.visitMaxs(this.maxStacks, this.maxLocals);
this.mv.visitEnd();
至此,通过ASM字节码技术生成字节码完毕。
根据ASM指令语义,可以推导出上述动态生成的字节码反编译结果如下:
public class SubClassExpression extends ClassExpression {
private final AviatorJavaType f1;
public SubClassExpression(final AviatorEvaluatorInstance instance, final List<String> varNames,
final SymbolTable symbolTable){
super(instance, varNames, symbolTable);
f1 = new AviatorJavaType("student.name", symbolTable);
}
public final Object execute0(Env env){
return f1.getValue(env);
}
}
5.字节码构造完毕,根据字节码生成Expression class对象:
@Override
public Expression getResult(final boolean unboxObject) {
end(unboxObject);
byte[] bytes = this.classWriter.toByteArray();
try {
Class<?> defineClass =
ClassDefiner.defineClass(this.className, Expression.class, bytes, this.classLoader);
Constructor<?> constructor =
defineClass.getConstructor(AviatorEvaluatorInstance.class, List.class, SymbolTable.class);
ClassExpression exp = (ClassExpression) constructor.newInstance(this.instance,
new ArrayList<VariableMeta>(this.variables.values()), this.symbolTable);
exp.setLambdaBootstraps(this.lambdaBootstraps);
exp.setFuncsArgs(this.funcsArgs);
exp.setSourceFile(this.sourceFile);
return exp;
} catch (ExpressionRuntimeException e) {
throw e;
} catch (Throwable e) {
if (e.getCause() instanceof ExpressionRuntimeException) {
throw (ExpressionRuntimeException) e.getCause();
}
throw new CompileExpressionErrorException("define class error", e);
}
}
3.2 调用ClassExpression的execute0方法获取结果值
1.调用BaseExpression的execute方法
/**
* Execute a text expression with environment
*
* @param expression text expression
* @param env Binding variable environment
* @param cached Whether to cache the compiled result,make true to cache it.
*/
public Object execute(final String expression, final Map<String, Object> env,
final boolean cached) {
Expression compiledExpression = compile(expression, cached);
if (compiledExpression != null) {
return compiledExpression.execute(env);
} else {
throw new ExpressionNotFoundException("Null compiled expression for " + expression);
}
}
execute方法实现如下:
@Override
public Object execute(Map<String, Object> map) {
if (map == null) {
map = Collections.emptyMap();
}
Env env = genTopEnv(map);
EnvProcessor envProcessor = this.instance.getEnvProcessor();
if (envProcessor != null) {
envProcessor.beforeExecute(env, this);
}
try {
return executeDirectly(env);
} finally {
if (envProcessor != null) {
envProcessor.afterExecute(env, this);
}
}
}
调用ClassExpression的executeDirectly方法:
@Override
public Object executeDirectly(final Map<String, Object> env) {
try {
Object result = execute0((Env) env);
if (RuntimeUtils.isTracedEval(env)) {
RuntimeUtils.printlnTrace(env, "Result : " + result);
}
return result;
} catch (ExpressionRuntimeException e) {
throw e;
} catch (Throwable t) {
throw Reflector.sneakyThrow(t);
}
}
这里可以看到调用了execute0方法,即是字节码构造的execute0方法,也及AviatorJavaType.getValue方法
2.调用AviatorJavaType.getValue方法
@Override
public Object getValue(final Map<String, Object> env) {
return this.getValueFromEnv(this.name, this.containsDot, env, true);
}
public Object getValueFromEnv(final String name, final boolean nameContainsDot,
final Map<String, Object> env, final boolean throwExceptionNotFound) {
if (env != null) {
if (nameContainsDot && !env.containsKey(name) && RuntimeUtils.getInstance(env)
.getOptionValue(Options.ENABLE_PROPERTY_SYNTAX_SUGAR).bool) {
if (this.subNames == null) {
// cache the result
this.subNames = SPLIT_PAT.split(name);
}
return getProperty(name, this.subNames, env, throwExceptionNotFound, this, false);
}
return env.get(name);
}
return null;
}
getProperty方法里面首先根据student从env中获取到student对象,然后通过反射获取到对象student的字段name的值,Over~~