github地址:aviator
Aviator的特性
Aviator的限制:
asm实现:直接修改或生成.class
例子代码
package com.googlecode.aviator;
import com.googlecode.aviator.asm.ClassWriter;
import com.googlecode.aviator.asm.MethodVisitor;
import com.googlecode.aviator.asm.Opcodes;
/**
* @author dingqi on 2023/5/29
* @since 1.0.0
*/
public class TestAsm extends ClassLoader{
/**
* 生成一个Class类
* public class User {
*
* public static void main(String[] args) {
* System.out.println("Hello World");
* }
*
* public int add(int a, int b){
* return a + b;
* }
* }
*/
public static byte[] generateClazz() {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cw.visit(
Opcodes.V1_7,
Opcodes.ACC_PUBLIC,
"com/googlecode/aviator/User",
null,
"java/lang/Object",
null
);
MethodVisitor mv = cw.visitMethod(
Opcodes.ACC_PUBLIC,
"" ,
"()V",
null,
null
);
// 开始访问方法code
mv.visitCode();
// 局部变量进栈
mv.visitVarInsn(Opcodes.ALOAD, 0);
// 执行特殊实例方法(构造方法)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "" , "()V");
// 方法返回
mv.visitInsn(Opcodes.RETURN);
// 最大栈大小值、最大方法本地参数值
mv.visitMaxs(1, 1);
// 方法结束
mv.visitEnd();
mv = cw.visitMethod(
Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
"main",
"(Ljava/lang/String;)V",
null,
null
);
// 开始访问方法code
mv.visitCode();
// 访问static类字段out,参数类型Ljava/io/PrintStream;
mv.visitFieldInsn(
Opcodes.GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;"
);
// 常量加载进栈
mv.visitLdcInsn("Hello World");
// 调用对象的实例方法println,方法参数String数组(Ljava/lang/String;)V
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/String;)V"
);
// 方法返回
mv.visitInsn(Opcodes.RETURN);
// 最大栈大小值、最大方法内本地参数值
mv.visitMaxs(2, 1);
// 方法结束
mv.visitEnd();
// 再添加方法
mv = cw.visitMethod(
Opcodes.ACC_PUBLIC,
"add",
"(II)I",
null,
null
);
// 入参
mv.visitVarInsn(Opcodes.ILOAD,1);
mv.visitVarInsn(Opcodes.ILOAD,2);
mv.visitInsn(Opcodes.IADD); //2个int类型相加
//返回int 类型
mv.visitInsn(Opcodes.IRETURN);
// 设置操作数栈的深度和局部变量的大小:2个数计算,加上this 总共3个变量
mv.visitMaxs(2, 3);
mv.visitEnd();
cw.visitEnd();
return cw.toByteArray();
}
public static void main(String[] args) throws Exception {
TestAsm testAsm = new TestAsm();
byte[] code = TestAsm.generateClazz();
String className = "com.googlecode.aviator.User";
Class<?> clazz = testAsm.defineClass(className, code, 0, code.length);
clazz.getMethods()[0].invoke(null, new Object[]{null});
Object o = clazz.newInstance();
Integer ans = (Integer)clazz.getMethods()[1].invoke(o, 1, 2);
System.out.println("add ans:" + ans);
ans = (Integer)clazz.getMethods()[1].invoke(o, 1, 3);
System.out.println("add ans:" + ans);
}
}
/** 输出
Hello World
add ans:3
add ans:4
*/
public class AviatorEvaluatorInstanceUnitTest {
protected AviatorEvaluatorInstance instance;
@Before
public void setup() {
this.instance = AviatorEvaluator.newInstance();
}
@Test
public void testExec() {
String exp1 = "b-c+a";
assertEquals(8, this.instance.exec(exp1, 6, 2, 4));
}
}
新生成了一个类Script_1685399425946_58
:
执行execute0方法,参数:com.googlecode.aviator.utils.Env
然后执行成功,得到结果
debug可以看到生成的类确实有方法:public final java.lang.Object Script_1685400413476_58.execute0(com.googlecode.aviator.utils.Env)
依据
String exp1 = "b-c+a";
assertEquals(8, this.instance.exec(exp1, 6, 2, 4));
解析完变量后的asm生成逻辑:
private void callASM(final Map<String, VariableMeta/* metadata */> variables,
final Map<String, Integer/* counter */> methods, final Set<Token<?>> constants) {
this.codeGen.initConstants(constants);
this.codeGen.initVariables(variables);
this.codeGen.initMethods(methods);
this.codeGen.setLambdaBootstraps(this.lambdaBootstraps);
this.codeGen.start();
生成execute0
方法
@Override
public void start() {
makeConstructor();
startVisitMethodCode();
}
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();
}
可以debug写到class文件查看, className文件名Script_1685767852489_58
:
b-c+a
生成的class文件public class Script_1685767852489_58 extends ClassExpression {
private final AviatorJavaType f0;
private final AviatorJavaType f1;
private final AviatorJavaType f2;
public Script_1685767852489_58(AviatorEvaluatorInstance var1, List var2, SymbolTable var3) {
super(var1, var2, var3);
this.f2 = new AviatorJavaType("a", var3);
this.f0 = new AviatorJavaType("b", var3);
this.f1 = new AviatorJavaType("c", var3);
}
public final Object execute0(Env var1) {
return this.f0.sub(this.f1, var1).add(this.f2, var1).getValue(var1);
}
}
参数通过构造函数设置好,然后一个exceute0
方法内部就是执行b-c+a
的逻辑