Aviator源码:从具体实例看Aviator属性语法糖源码分析(a.b.c)

目录

1.从测试入手直观看aviator特性

2.Aviator可选项支持

3.源码追踪

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~~

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值