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?

  1. 极小占用 – 核心引擎在 32 位 CPU 上大约 10 kB RAM。
  2. 严格且安全的设计 – 单独支持严格的 ES5 子集;省略了 with 或动态作用域等功能。
  3. 跟踪 GC – 压缩式收集器降低对象开销并消除碎片。
  4. 无 libc 依赖 – 引擎使用自身分配器,不调用 malloc/free,适用于裸机固件。
  5. C API – 将引擎暴露给本地 C 程序,并自动处理指针迁移。
  6. 字节码导出 – 脚本可以一次编译后存入 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 提供了简洁高效的路径。

原创文章: 查看原文

分享本文