MicroQuickJS: 10 kBの埋め込み JavaScript エンジン

MicroQuickJS: 10 kBの埋め込み JavaScript エンジン\n\nMicroQuickJS(別名 MQuickJS)は C 言語で書かれたオープンソース JavaScript エンジンで、制限のあるデバイス向けに特に設計されています。QuickJS のコードベースをベースにしつつ、参照カウント GC をトレーシング・コンパクティングコレクタに置き換え、メモリ使用量を増やす言語機能を多く削除しました。その結果、32‑bit 実行環境で RAM が 10 kB だけ、ROM が 約 100 kB で動作するエンジンに仕上がります。\n\n## MicroQuickJS の魅力\n\n1. 極小フットプリント – コアエンジンは 32‑bit CPU 上で RAM が約 10 kB。\n2. 設計で安全性 – ES5 の厳格モードサブセットのみをサポートし、with や動的スコープなどメモリを増やす機能は除外。\n3. トレーシング GC – コンパクティングコレクタによりオブジェクトのオーバーヘッドが削減され、断片化が消滅。\n4. libc に依存しない – エンジン自体のメモリ割り当てを使用し、malloc/free を呼び出しません。ベアメタル ファームウェアに適しています。\n5. C API – ポインタの再配置を自動で処理しながら、ネイティブ C プログラムからエンジンへアクセス可能。\n6. バイトコード輸出 – スクリプトを一度コンパイルして ROM に格納できます。\n\n## 開始方法 – REPL の構築\n\nリポジトリをクローンします。\n\nbash\n git clone https://github.com/bellard/mquickjs.git\n cd mquickjs\n\n\nREPL (mqjs) をコンパイルします。\n\nbash\n make\n\n\nリポジトリルートに mqjs があることを確認します。簡単な動作確認です。\n\nbash\n ./mqjs -e \"console.log('Hello MQuickJS');\"\n # Hello MQuickJS\n\n\n### メモリ制限オプションの使用\n\n埋め込みターゲットは RAM 上限をハードに設定できます。REPL は --memory-limit を受け付けます。\n\nbash\n ./mqjs --memory-limit 10k tests/mandelbrot.js\n # Renders the Mandelbrot set in the terminal\n\n\n32‑bit ARM ボードでは64‑bit ホスト向けに32‑bit バイトコードを生成できます。\n\nbash\n ./mqjs -o mandelbrot.bin -m32 tests/mandelbrot.js\n\n\n## 事前コンパイルされたバイトコードの実行\n\n-b フラグでバイトコードを直接実行します。\n\nbash\n ./mqjs -b mandelbrot.bin\n\n\nバイトコード形式はスタックベースですが、内部的にアトムテーブルを使用しているため、同じエンディアンを共有するアーキテクチャ間でポータブルです。\n\n## C API の探索\n\nMicroQuickJS は QuickJS に極めて似た C API を公開しますが、コンパクト GC に合わせて調整されています。\n\nc\n #include \"mquickjs.h\"\n \n int main(void) {\n uint8_t mem_buf[8192];\n JSContext *ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib);\n if (!ctx) {\n fprintf(stderr, \"Cannot allocate context\\n\");\n return 1;\n }\n\n // シンプルなスクリプトを実行\n JSValue result = JS_EvalCode(ctx, \"1 + 2\", \"test.js\", JS_EVAL_TYPE_GLOBAL);\n int i = JS_ToInt32(ctx, &i, result);\n printf(\"Result: %d\\n\", i);\n\n JS_FreeContext(ctx);\n return 0;\n}\n\n\nAPI を使う際のポイント:\n\n* 明示的な解放は不要 – GC によってオブジェクトが移動されるので JS_FreeValue を呼びません。\n* JS_PushGCRef / JS_PopGCRef を使用 – 割り当て間で参照を保持したい場合。\n* カスタムライブラリmquickjs_build.c で標準ライブラリを ROM にコンパイル。\n\n## 厳格モードと言語サブセット\n\nMicroQuickJS は strict mode のみです。典型的な落とし穴を排除しています。\n\n- グローバル変数は 必ず var で宣言 。\n- Array は穴を持てません。長さを越える書き込みは TypeError を投げます。\n- eval はグローバル eval 関数を介して間接的にのみサポート。\n- new Number(1) は無効です。ボクシングはサポート外。\n- String.prototype.toLowerCase()/toUpperCase() は ASCII のみを扱います。\n- RegExp は ASCII のみのケースフォールディングを使用。\n\nこれらの制限はエンジンを予測可能にし、メモリを節約します。\n\n## パフォーマンスとベンチマーク\n\nMicroQuickJS は QuickJS のマイクロベンチマークをほぼ同等の時間で通過し、32‑bit ハードウェア上でメモリを約半分に抑えています。追加テストでは、strict mode で V8 の Octane ベンチマークを 1–2× のスピードペナルティで実行しつつ、メモリ使用量は 4× まで削減できることを示しています。\n\n\nmake test\n# All unit tests pass.\nmake microbench\n# 10 kB memory usage, script execution ~120 ms on a RasPi‑Zero.\n\n\n## まとめ\n\nMicroQuickJS は小型デバイス上で JavaScript ランタイムを必要とする開発者にとって魅力的なソリューションを提供します。10 kB の RAM フットプリント、strict ES5 サブセット、トレーシング GC は、ファームウェア、IoT ゲートウェイ、または JavaScript の柔軟性を活かしたい任意の埋め込みシステムに最適です。REPL で直接構築するか、C API をファームウェアに埋め込むか、または事前コンパイル済みバイトコードを出荷するかに関わらず、MicroQuickJS はエッジでの JavaScript 実現へのスリムな道筋を提供します。

この記事を共有