MicroQuickJS: 10 kB 嵌入式 JavaScript 引擎
MicroQuickJS: 10 kB 嵌入式 JavaScript 引擎
MicroQuickJS(又名 MQuickJS)是一款用 C 语言编写的开源 JavaScript 引擎,专为受限设备设计。它基于 QuickJS 代码库,但将引用计数 GC 替换为跟踪式压缩式收集器,并移除许多会导致内存膨胀的语言特性。结果得到一个 32 位运行时,在 Arm Thumb‑2 目标上仅需 10 kB RAM 与 约 100 kB ROM。
为什么选择 MicroQuickJS?
- 极小占用 – 核心引擎在 32 位 CPU 上大约 10 kB RAM。
- 严格且安全的设计 – 单独支持严格的 ES5 子集;省略了
with或动态作用域等功能。 - 跟踪 GC – 压缩式收集器降低对象开销并消除碎片。
- 无 libc 依赖 – 引擎使用自身分配器,不调用
malloc/free,适用于裸机固件。 - C API – 将引擎暴露给本地 C 程序,并自动处理指针迁移。
- 字节码导出 – 脚本可以一次编译后存入 ROM。
入门 – 构建 REPL
克隆仓库:
git clone https://github.com/bellard/mquickjs.git
cd mquickjs
编译 REPL (mqjs):
make
你应该会在仓库根目录看到 mqjs。快速验证一下:
./mqjs -e \"console.log('Hello MQuickJS');\"
# Hello MQuickJS
使用记忆限制选项
嵌入式目标可设置硬性 RAM 上限。REPL 接受 --memory-limit:
./mqjs --memory-limit 10k tests/mandelbrot.js
# Renders the Mandelbrot set in the terminal
对于 32 位 ARM 板,你甚至可以为 64 位宿主生成 32 位字节码:
./mqjs -o mandelbrot.bin -m32 tests/mandelbrot.js
运行预编译字节码
-b 标志允许直接运行字节码:
./mqjs -b mandelbrot.bin
字节码格式是基于栈的,但内部使用原子表,因而在共享相同字节序的架构间具有可移植性。
探索 C API
MicroQuickJS 暴露的 C API 与 QuickJS 极为相似,但已针对紧凑 GC 进行了适配。
#include \"mquickjs.h\"
int main(void) {
uint8_t mem_buf[8192];
JSContext *ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib);
if (!ctx) {
fprintf(stderr, \"Cannot allocate context\n\");
return 1;
}
// Run a simple script
JSValue result = JS_EvalCode(ctx, \"1 + 2\", \"test.js\", JS_EVAL_TYPE_GLOBAL);
int i = JS_ToInt32(ctx, &i, result);
printf(\"Result: %d\\n\", i);
JS_FreeContext(ctx);
return 0;
}
使用 API 的要点:
- 无需显式释放 – 对象由 GC 移动,因此你永远不需要调用
JS_FreeValue。 - 使用
JS_PushGCRef/JS_PopGCRef– 在分配间保持引用存活。 - 自定义库 – 用
mquickjs_build.c将标准库编译到 ROM。
严格模式与语言子集
MicroQuickJS 是 严格模式仅。一些典型陷阱被消除:
- 所有全局变量 必须 使用
var声明。 Array不能有空洞;超出长度写入会抛出 TypeError。eval仅通过全局eval函数间接支持。- 无
new Number(1)。包装(boxing)不受支持。 String.prototype.toLowerCase()/toUpperCase()仅处理 ASCII。RegExp使用仅 ASCII 的大小写折叠。
性能与基准
MicroQuickJS 在 32 位硬件上使用约一半内存,在时间上与 QuickJS 的 micro‑benchmark 相当。额外测试显示,它可以在严格模式下运行 V8 Octane 基准,速度略慢 1–2 倍,但内存占用小 4 倍。
make test
# All unit tests pass.
make microbench
# 10 kB memory usage, script execution ~120 ms on a RasPi‑Zero.
总结
MicroQuickJS 为需要在极小设备上运行 JavaScript 运行时的开发者提供了有吸引力的解决方案。它 10 kB 的 RAM 占用、严格 ES5 子集以及跟踪 GC 使其非常适合固件、IoT 网关或任何受益于 JavaScript 灵活性而不想承受大型引擎开销的嵌入式系统。无论你直接使用 REPL 编写、将 C API 嵌入固件,还是预编译并运行字节码,MicroQuickJS 都为边缘 JavaScript 提供了简洁高效的路径。