v2.16.0 · MIT / Apache-2.0

用纯 Rust 打造的 Lua 运行时。

单个二进制里容纳五种 Lua 方言与 MacroLua。一个零依赖的解释器核心、一个基于 Cranelift 的 trace JIT、提前编译成原生二进制,以及一个为嵌入不受信任脚本而设计的沙箱。

5.1 – 5.5 + MacroLua 核心 0 依赖 冷启动堆 ~33 KB lua.h 兼容 C ABI
luna 是什么

一个运行时,覆盖你要嵌入的每一种 Lua。

luna 用安全的 Rust 从头实现了整个 Lua 语言家族——没有 C,嵌入方接触到的表层也没有 unsafe。按需选择依赖体量,再随工作负载增长逐步用上 JIT 与 AOT 两层。

§ 方言

5.1 – 5.5 + MacroLua

所有主线 Lua 方言都在同一构建里,在构造 Vm 时逐一选定。同一进程可并发承载多种方言而互不干扰。MacroLua 在 5.4 表层之上加入编译期 @macro 展开。

§ 核心

零依赖核心

luna-core 只引入一个 crate——它自己。每次提交都由 cargo deny 关卡强制保证。这意味着极小的审计面、以秒计的构建,以及无需 mmap RWX 的干净 wasm32 目标。

§ jit

Cranelift trace JIT

一个 LuaJIT 血统的 trace 式编译器。热循环被记录成带类型的 IR,经 Cranelift 降级后作为原生机器码运行——通过 trait 接入,使解释器路径永不依赖具体后端。

§ aot

提前编译的二进制

luna-aot 把一份 Lua 源码变成自包含的原生可执行文件——字节码、预热的 trace、静态运行时全部链接进同一个二进制,宿主上无需安装 luna 即可运行。

§ 嵌入

顺手的嵌入体验

一个 mlua 风格的 Lua 门面、自动解析参数的类型化原生函数、表构造器、通过 derive 宏暴露的 userdata、Rust 侧的协程与调试钩子,以及结构化的 LuaError

§ 沙箱

为约束脚本而建

安全边界由宿主掌握。精选的标准库白名单、指令预算、近似内存上限,以及默认关闭的字节码加载——脚本能看到的每一项能力,都是 Rust 主动放行的。


方言数据表

每种方言能做什么。

特性的可用性由 src/version.rs 中的能力判定驱动。luna 生成的按方言字节码与 PUC 编译器格式一致,因此 PUC 编译出的 .luac 文件可直接加载。

特性5.15.25.35.45.5MacroLua
数值
整数子类型 Int
// 向下取整除
位运算 & | ~ << >>
十六进制浮点 0x1p4
语法
goto / ::label::
空语句 ;
字符串
\xXX / \z 转义
\u{XXXX} Unicode 转义
5.4+ 属性
local <const>
local <close>
5.5 独有
global 关键字
具名变参 function f(...name)
MacroLua 独有
@name(args) 编译期宏

完整矩阵、标准库覆盖与 C API 表面见 兼容性参考


架构

自己决定依赖表面。

luna 以一个含五个可发布 crate 的 Cargo 工作区形式交付。解释器核心独立成立;JIT 与 AOT 两层通过 trait 边界叠加其上,因此移除或替换后端都不会触碰核心 API。

luna-core

解释器

词法、语法、按方言的编译器、分派器、运行时、标准库、NaN 装箱的值、侵入式标记-清除 GC、PUC 模式引擎,以及 JIT trait 表面。零第三方 crate。

luna-jit

JIT 与 C ABI

Cranelift 后端、以 cdylib / staticlib 形式暴露的 lua.h 兼容 C ABI、luna CLI,以及 Lua 嵌入门面。

luna-aot

AOT 编译器

一个构建期工具:Lua 源码 → 独立原生二进制。它不是所产物的运行时依赖。

JIT 流水线

01 解释器分派器 —— 累加计数,检查阈值
↓ 检测到热路径
02 Trace 记录器 —— 把操作码回放为带类型的 IR
↓ 闭合的 trace,跨越 trait 边界
03 Cranelift 后端 —— 降级 IR、寄存器分配、发射
↓ mmap RWX + 符号重定位
04 原生机器码 —— 运行至一次 side-exit

源码分类

每个文件都落在三类之一,各有各的改动纪律与 review 深度:

  • 石头 —— 与业务无关的地基(模式引擎、堆、值布局)。做 fuzz、做 bench、semver 严格。
  • 钢筋 —— Lua 领域原语(编译器、分派器、JIT 后端)。逐方言与 PUC 交叉校验。
  • 水泥 —— 宿主胶水(CLI、C ABI、标准库绑定、AOT)。随嵌入方需求自由演化。

性能纪律

要么诚实的数字,要么不给数字。

luna 刻意发布一个精挑细选的头条比率。一个恰好胜出的 microbench 单元只是营销产物;真正值得追的信号,是那个落后的离群点。我们发布的是可复现的基线,以及产出它们的方法论。

33KB
冷启动峰值堆 · 空 Vm
435
分配次数 · 冷启动
~4.5MiB
AOT 二进制 · release,strip 后
0
第三方依赖 · luna-core
可测量、可复现的基线。内存在 dhat 下跨五个工作负载测量(冷启动、REPL 空转、host-root churn、分配 + GC、userdata 生命周期);任一负载稳态回归超过 5% 都会触发已跟踪的告警。二进制大小取自 cargo publish --dry-run 与 strip 后的 release 测量。复现脚本随文档一同交付——这里没有一个数字需要你凭空相信。

方法论、调优旋钮与热路径计数器详见 性能参考


快速开始

三行就能嵌入。

加一个 crate,打开你要的库,求值即可。需要时再逐步用上沙箱、原生函数与 userdata。

Cargo.toml
# 完整解释器 + Cranelift JIT + C ABI
[dependencies]
luna-jit = "2"
main.rs — hello
use luna_jit::vm::Vm;
use luna_jit::version::LuaVersion;

let mut vm = Vm::new(LuaVersion::Lua55);
let out = vm.eval("return 1 + 2")?;
// out[0] == Value::Int(3)
main.rs — 沙箱
use luna_jit::Lua;
use luna_jit::version::LuaVersion;

let mut lua = Lua::sandbox(LuaVersion::Lua54)
    .open_base().open_math().open_string()
    .with_instr_budget(1_000_000)
    .with_memory_cap(8 * 1024 * 1024)
    .build();

let add = lua.create_function(|a: i64, b: i64| a + b);
lua.set_global("add", add)?;
let r: i64 = lua.eval("return add(40, 2)")?;