Skip to main content
Fluxon 默认以解释器运行,但也支持将脚本编译为 Java 8 字节码并直接执行。编译模式的入口是 Fluxon.compile(...),产物为一组 .class 字节码:
  • 主脚本类:继承 RuntimeScriptBase,实现脚本主体逻辑。
  • 内部类:用于 def/lambda 等脚本定义(同样由字节码生成器输出)。
编译后的代码不会绕开运行时:函数调用、扩展函数匹配(target::method)、索引访问(obj[index])等依然交由运行时的 Intrinsics/Environment/注册表处理,因此语义应与解释器保持一致。

快速上手

import org.tabooproject.fluxon.Fluxon;
import org.tabooproject.fluxon.compiler.CompileResult;
import org.tabooproject.fluxon.interpreter.bytecode.FluxonClassLoader;
import org.tabooproject.fluxon.runtime.Environment;
import org.tabooproject.fluxon.runtime.FluxonRuntime;
import org.tabooproject.fluxon.runtime.RuntimeScriptBase;

FluxonRuntime runtime = FluxonRuntime.getInstance();
Environment env = runtime.newEnvironment();

CompileResult result = Fluxon.compile("1 + 2", "MyScript", env, getClass().getClassLoader());
FluxonClassLoader loader = new FluxonClassLoader(getClass().getClassLoader());

RuntimeScriptBase script = (RuntimeScriptBase) result.createInstance(loader);
Object out = script.eval(env);
System.out.println(out);
调试字节码时可用:CompileResult#dump(File) 输出 .class 文件,或用 CompileResult#defineClass(FluxonClassLoader) 手动控制类加载过程。

RuntimeScriptBase 的角色

  • 每个编译后的脚本都会生成一个继承 RuntimeScriptBase 的类。
  • 生成类会覆写 eval(Environment),直接在字节码中实现脚本逻辑。
  • 运行时错误会通过 RuntimeScriptBase#attachRuntimeError(...) 补充源码摘录,便于定位行列。
  • 生成类会实现 clone(),用于复制脚本实例(避免共享可变状态)。

索引访问器

  • IndexAccessor 抽象了 target[index] 的读写行为,既可服务解释器,也可让编译器在遇到 [] 语法时绑定到正确的宿主类型。
  • IndexAccessorRegistry 利用 Java SPI 自动加载第三方实现,并允许在运行时调用 registerAccessor 提高优先级。
  • 索引访问由 Intrinsics 统一派发到底层注册表,因此解释与编译模式共享同一套行为与错误类型(如 IndexAccessError)。

导出桥

  • ExportRegistry 扫描 @Export 方法并注册为扩展函数;底层通过 ClassBridge(字节码生成)避免反射调用。
  • 编译后的脚本在调用处仍会走相同的派发链路:
    • Intrinsics.callFunction(...)
    • Environment#getExtensionFunction(...)
    • Function#call(...)
  • 因此只要注册表一致,行为就应与解释器一致。

何时选用编译模式

  • 脚本会被频繁执行,且你希望减少解释器开销。
  • 你需要把脚本“打包”为库(配合 LibraryLoader 等机制)。
  • 你希望拿到可审计的 .class 产物用于诊断(配合 CompileResult#dump)。

调优建议

  1. 生成调试:在编译产物中打印 getClass().getName(),确认字节码是否加载到预期的 ClassLoader。
  2. 索引冲突:如遇 IndexAccessor 顺序问题,记得调用 registerAccessor 将自定义实现插入列表开头。
  3. 导出同步:任何新的 @Export 类都必须在运行时通过 ExportRegistry#registerClass 注册,并在需要时重建 Environment, 确保缓存与派发表可见。

相关链接