C/C++に組み込める軽量JavaScriptエンジン “QuickJS” を試す

# はじめに

**QuickJS** は C/C++ に組み込める軽量な JavaScript エンジンである。ライセンスは MIT license。JavaScript を組み込みたいけれど [V8](https://v8.dev/) はオーバースペックすぎる、という時に有用と思われる。

デザインがシンプルすぎてかえって信頼感のある公式ページはこちら。

* [QuickJS Javascript Engine](https://bellard.org/quickjs/)

> QuickJS is a small and embeddable Javascript engine. It supports the ES2020 specification including modules, asynchronous generators, proxies and BigInt.
（訳：QuickJS は小さい・組み込み可能な JavaScript エンジンである。モジュール・非同期ジェネレータ・プロキシ・BigInt を含めた ES2020 仕様に対応している）

ちなみに QuickJS 作者の Fabrice Bellard 氏は qemu や ffmpeg の作者でもある。行動力の化身…（画像略）

また GitHub 上に非公式のミラーがある。これは “Unofficial git mirror” として公式ページからリンクされている。ただし記事執筆現在、最新のリリース (2020-01-05) が反映されておらず 2019-10-27 版のままになっている。

* [horhof/quickjs: Unpacked tarball of Fabrice Bellard's QuickJS](https://github.com/horhof/quickjs)

<p><ins>2021-06-10 追記: 現在は以下の公式リポジトリが存在する。最新のリリースは 2021-03-27。</ins></p>

* [bellard/quickjs: Public repository of the QuickJS Javascript Engine. Pull requests are not accepted. Use the mailing list to submit patches.](https://github.com/bellard/quickjs)

この記事では C/C++ にどうやって QuickJS を組み込んでいくかを概説する。半ば私の備忘録のようなものであり体系的・網羅的ではないのであらかじめご理解のほどを……。

# 動作確認環境

* Linux
    * 最新の Arch Linux (kernel: v5.4.11)
    * gcc v9.2.0
    * clang v9.0.1 （ついで）
* macOS （ついで）
    * Catalina (v10.15.2)
    * Apple clang v11.0.0 (clang-1100.0.33.17)
* QuickJS のバージョンは 2020-01-05
* あとインストール時に curl とか GNU tar とか GNU make とかを適宜使っている

# インストール

tarball を展開して `make install` するいつもの流れでインストールできる。デフォルトでは `/usr/local` 下にインストールされるが `prefix` 指定で変更可能。以下 `~/.local` にインストールする前提で書くので適宜読み替えること。

```bash
# ソースをダウンロード
curl -LO https://bellard.org/quickjs/quickjs-2020-01-05.tar.xz
# tarball を展開
tar axvf quickjs-2020-01-05.tar.xz
# ビルドして ~/.local 下にインストールする例
# -j (--jobs) は並列実行数なので適宜調整
make -C quickjs-2020-01-05 -j 2 prefix="${HOME}/.local" install
```

`qjs` コマンドを使いたい場合は `PATH` 環境変数を適当に通す。

```bash
PATH="${HOME}/.local/bin:${PATH}"
export PATH
```

Arch Linux 使いは AUR 、macOS 使いは Homebrew 経由でインストールすることもできる。

* [AUR (en) - quickjs](https://aur.archlinux.org/packages/quickjs/)
* [quickjs — Homebrew Formulae](https://formulae.brew.sh/formula/quickjs)

# `qjs` / `qjsc` コマンドを使う

## REPL を起動する

`qjs` コマンドを無引数で呼ぶと REPL が起動する。

```console
$ qjs
QuickJS - Type "\h" for help
qjs > \h
\h          this help
\x          hexadecimal number display
\d         *decimal number display
\t          toggle timing display
\clear      clear the terminal
\q          exit
qjs > 3**2 + 4**2
25
qjs > 2n ** 256n
115792089237316195423570985008687907853269984665640564039457584007913129639936n
qjs > const name = "world"
undefined
qjs > `Hello, ${name}`
"Hello, world"
qjs > /^(a)(b*)(c+)(d?)$/.exec("abbbcc")
[ "abbbcc", "a", "bbb", "cc", "" ]
qjs >
```

## JS ファイルを実行する

`qjs` コマンドにファイル名を与えるとそのファイルを実行する。`import` / `export` もできる。

```js:greeter.js
export function greet(name) {
  console.log(`Hello, ${name}!`);
}
```

```js:index.js
import { greet } from "./greeter.js";

greet("Alice");
```

```console
$ qjs index.js 
Hello, Alice!
```

## JS ファイルをコンパイルする

`qjsc` コマンドを使うと JavaScript を実行可能ファイルにできる。

```console
$ qjsc index.js
$ strip a.out
$ file a.out 
a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=315625503ecf284b44cab3b6f1d3dea6df4dedc7, for GNU/Linux 3.2.0, stripped
$ stat -c '%s' a.out | numfmt --to=iec-i
832Ki
$ ./a.out 
Hello, Alice!
```

# QuickJS を C/C++ プログラムに埋め込む

ここからが本題。

C API についての公式ドキュメントは以下の数十行のセクションしかない。

* [3.4 QuickJS C API - QuickJS Javascript Engine](https://bellard.org/quickjs/quickjs.html#QuickJS-C-API)

まあヘッダ (`quickjs.h`) からだいたい挙動は察せられるしだいたいその通りに動く。あとは REPL のソース (`qjs.c`) やらライブラリ本体の実装 (`quickjs.c`) やらを読めばそれなりに利用方法はわかってくる。

なお、今回例示するソースコードの全体は以下のリポジトリに置いている。

* [taskie/quickjs-exercise - GitHub](https://github.com/taskie/quickjs-exercise)


## C 側から JS の関数を呼ぶ

手始めに JS で定義した `foo` 関数を C API を使って呼んでみる。コードは以下の通り（ちなみにこれは [Lua (programming language) - Wikipedia (en) の C API](https://en.wikipedia.org/wiki/Lua_(programming_language)#C_API) のコード例に対応している）。

```c:simple.c
#include <stddef.h>
#include <stdio.h>
#include <string.h>

#include <quickjs.h>

int main(void) {
  JSRuntime *rt = JS_NewRuntime();
  JSContext *ctx = JS_NewContext(rt);

  char *const fooCode = "function foo(x, y) { return x + y; }";
  if (JS_IsException(JS_Eval(ctx, fooCode, strlen(fooCode), "<input>", JS_EVAL_FLAG_STRICT))) {
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return -1;
  }

  JSValue global = JS_GetGlobalObject(ctx);
  JSValue foo = JS_GetPropertyStr(ctx, global, "foo");
  JSValue argv[] = { JS_NewInt32(ctx, 5), JS_NewInt32(ctx, 3) };
  JSValue jsResult = JS_Call(ctx, foo, global, sizeof(argv) / sizeof(JSValue), argv);
  int32_t result;
  JS_ToInt32(ctx, &result, jsResult);
  printf("Result: %d\n", result);

  JSValue used[] = { jsResult, argv[1], argv[0], foo, global };
  for (int i = 0; i < sizeof(used) / sizeof(JSValue); ++i) {
    JS_FreeValue(ctx, used[i]);
  }

  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}
```

上記のソースを以下のコマンドでビルドする。

```bash
# コンパイル（-Os でサイズ重視で最適化）
gcc -c -Os -Wall -I"${HOME}/.local/include" simple.c
# リンク（-Wl,-s で strip）
gcc -Wl,-s -L"${HOME}/.local/lib/quickjs" simple.o -l quickjs -l m -o simple
```

実行するとめでたく `5 + 3` の結果が表示される。

```console
$ ./simple
Result: 8
```

このコードはだいたい以下のような流れで処理を行っている。

* `JS_NewRuntime` でランタイムの生成
    * ランタイム ≈ ブラウザで言うところの Worker 単位と考えてよさそう
    * ランタイム内部はシングルスレッドでしか動作しない
    * ランタイム間で値を直接共有することはできない
* `JS_NewContext` でコンテクストの生成
    * それぞれのコンテクストが別のグローバルオブジェクトとシステムオブジェクトを持つ
    * コンテクスト間では値を直接共有できる
* `JS_Eval` でコードの実行
    * `JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags);` というシグネチャ
* `JS_GetGlobalObject` でグローバルオブジェクトの取得
* `JS_GetPropertyStr` でプロパティの取得
* `JS_NewInt32` で整数値を JS 側で使えるようラップ
* `JS_Call` で JS の関数を呼び出し
    * `JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst *argv);` というシグネチャ
* `JS_ToInt32` で JS の値を C の int に変換
* `JS_FreeValue` で JS のオブジェクトの解放（参照カウンタを減らす）
    * GC は参照カウント + Mark & Sweep
    * C 側で値を使い終わったことを JS 側に伝える必要がある
    * きちんと解放しないと ``JS_FreeRuntime: Assertion `list_empty(&rt->gc_obj_list)' failed.`` のような怒られが発生する
    * `JS_NewInt32` で生成したものは参照型でなく値型なので実は `JS_FreeValue` を呼ばなくともよいが、一貫性のため呼んでおくに越したことはない
        * 同様に `true`, `undefined`, `null` のような特別な値を `JS_FreeValue` しても害はない
    * ちなみに参照カウンタを増やしたい場合は `JS_DupValue` を呼ぶ
    * `JS_FreeValue` しなければならないパターン・してはいけないパターンについては次の章で後述
* `JS_FreeContext` でコンテクストの解放
* `JS_FreeRuntime` でランタイムの解放

## JS 側から C の関数を呼ぶ

コマンドライン引数として与えられた JavaScript を実行して結果を標準出力に表示するアプリケーションを作る例。動作イメージは以下の通り。

```console
$ ./jseval '3**2 + 4**2'
25
$ ./jseval foo
ReferenceError: foo is not defined
$ ./jseval 'undefined'
$ ./jseval '[3, 4, 5].map(x => x ** 10).forEach(x => console.log(x))' 
59049
1048576
9765625
```

コードは以下の通り。`console.log` / `console.error` を C で実装し JS から使えるようにしている。

```c:jseval.c
#include <stdio.h>
#include <string.h>

#include <quickjs.h>

JSValue jsFprint(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv, FILE *f) {
  for (int i = 0; i < argc; ++i) {
    if (i != 0) {
      fputc(' ', f);
    }
    const char *str = JS_ToCString(ctx, argv[i]);
    if (!str) {
      return JS_EXCEPTION;
    }
    fputs(str, f);
    JS_FreeCString(ctx, str);
  }
  fputc('\n', f);
  return JS_UNDEFINED;
}

JSValue jsPrint(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
  return jsFprint(ctx, jsThis, argc, argv, stdout);
}

JSValue jsPrintErr(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
  return jsFprint(ctx, jsThis, argc, argv, stderr);
}

void initContext(JSContext *ctx) {
  JSValue global = JS_GetGlobalObject(ctx);

  // globalThis に console を追加
  JSValue console = JS_NewObject(ctx);
  JS_SetPropertyStr(ctx, global, "console", console);
  // console.log を設定
  JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, jsPrint, "log", 1));
  // console.error を設定
  JS_SetPropertyStr(ctx, console, "error", JS_NewCFunction(ctx, jsPrintErr, "error", 1));

  JS_FreeValue(ctx, global);
}

int main(int argc, char const *argv[]) {
  int exitCode = 0;

  JSRuntime *rt = JS_NewRuntime();
  JSContext *ctx = JS_NewContext(rt);

  initContext(ctx);

  for (int i = 1; i < argc; ++i) {
    JSValue ret = JS_Eval(ctx, argv[i], strlen(argv[i]), "<input>", JS_EVAL_FLAG_STRICT);
    if (JS_IsException(ret)) {
      JSValue e = JS_GetException(ctx);
      jsPrintErr(ctx, JS_NULL, 1, &e);
      JS_FreeValue(ctx, e);
      exitCode = 1;
      break;
    } else if (JS_IsUndefined(ret)) {
      // nop
    } else {
      jsPrint(ctx, JS_NULL, 1, &ret);
    }
    JS_FreeValue(ctx, ret);
  }

  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return exitCode;
}
```

* `JS_SetPropertyStr` でプロパティの設定
    * <strong>`JS_SetPropertyStr` に渡した値（上の例では `console` 変数や `JS_NewCFunction` で作成した値）は内部で解放されるため、呼び出し側で解放すると二重解放になることに注意</strong>
* `JS_NewCFunction` で C の関数を JS の関数として扱えるようにする
    * `JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, int length)` というシグネチャ
        * `JSCFunction` の定義は `typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);`
            * `this` と引数（個数と配列先頭のポインタ）を受け取って値を返す関数
            * `JSValueConst` は「`JS_FreeValue` しなくてよい `JSValue`」
                * 「JS 側が所有しているオブジェクトを借用している」という見方もできる
* `JS_IsException(JS_Evalの戻り値)` が truthy なら `JS_GetException` でコンテクスト内で発生した例外のオブジェクトを取得して表示

<p><ins>2021-06-10 追記: 値を <code>JS_FreeValue</code> すべきかそうでないかについては以下のような指針に従ってやっていくとよい。</ins></p>

* 一般的に QuickJS ランタイムから獲得した値については呼び出し側で解放 (`JS_FreeValue`) が必要
    * 例:
        * `JS_NewObject` 等で作成した値
        * `JS_NewGlobalObject` で獲得したグローバルオブジェクト
        * `JS_GetProperty` / `JS_GetPropertyStr` で獲得した値
        * `JS_Call` や `JS_Eval` の戻り値
* 一部の関数は内部で引数を `Free` するため呼び出し側で解放してはいけない
    * 例:
        * `JS_SetProperty` / `JS_SetPropertyStr` でセットする値（`val` 引数）
            * 値のセット先（`this_obj` 引数）の方は内部で `Free` されないことにも注意
        * `JS_DefinePropertyValue` の `val` 引数および `JS_DefinePropertyGetSet` の `getter`/`setter` 引数
            * これらの内部実装にもなっている `JS_DefineProperty` の方は `Free` されない
        * `JS_Throw` の `obj` 引数
    * このように内部で `Free` されるパターン（値を消費する関数）は全体的に見るとかなり例外的
        * 他は `JS_EvalFunction` の `fun_obj` 引数などが `Free` されるがかなりマイナーな状況
    * 基本的に呼び出し側で `Free` しておいてエラーが出たら内部実装 (`quickjs.c`) を見る、という感じでよいと思う
* C++ のデストラクタや Rust の `drop` などで `JS_FreeValue` を行う場合、上記の例外的な関数を呼ぶ前に `JS_DupValue` する（参照カウンタをインクリメントしておく）、という形とすると都合がよい

## C/C++ のデータを JS 側に管理させる

ここから突如例示コードが C でなく C++ になる。色々面倒になったので……。

例えば C++の [`std::mt19937`](https://cpprefjp.github.io/reference/random/mt19937.html) （擬似乱数生成器）を JavaScript 側で以下のように使いたい、という例を考える。

```js
const mt = new Mt19937();
for (let i = 0; i < 10; ++i) {
  console.log(mt.generate());  // 乱数 (BigInt) を出力する
}
```

```text:出力
3499211612
581869302
3890346734
3586334585
545404204
4161255391
3922919429
949333985
2715962298
1323567403
```

これは以下のように書ける。

```c++:mt19937.cc
// Mt19937 class の一意なID（後段で初期化）
// 簡単のためグローバルに定義するが、複数 Runtime を同時に動かした時に破綻するので適宜やっていく必要がある
static JSClassID jsMt19937ClassID;

// Mt19937.prototype.generate
JSValue jsMt19937Generate(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
  std::mt19937 *p = static_cast<std::mt19937 *>(JS_GetOpaque(jsThis, jsMt19937ClassID));
  return JS_NewBigUint64(ctx, (*p)());
}

// Mt19937.prototype
const JSCFunctionListEntry jsMt19937ProtoFuncs[] = {
  JS_CFUNC_DEF("generate", 1, jsMt19937Generate),
};

// Mt19937 の constructor
JSValue jsMt19937New(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
  // インスタンスを生成
  JSValue obj = JS_NewObjectClass(ctx, jsMt19937ClassID);
  bool fail = false;
  if (argc == 0) {
    JS_SetOpaque(obj, new std::mt19937());
  } else if (argc == 1) {
    // ... (シード値を設定したい場合。本質的でないので省略)
  } else {
    fail = true;
  }
  if (fail) {
    JS_FreeValue(ctx, obj);  // 忘れがち
    return JS_EXCEPTION;
  }
  return obj;
}

// Mt19937 object が GC に回収された際に呼ばれる
void jsMt19937Finalizer(JSRuntime *rt, JSValue val) {
  std::mt19937 *p = static_cast<std::mt19937 *>(JS_GetOpaque(val, jsMt19937ClassID));
  delete p;
}

// Mt19937 class の定義
// JS 側に表出していないオブジェクト間の依存がある場合 .gc_mark もアレコレする必要があるっぽい
JSClassDef jsMt19937Class = {
  "Mt19937",
  .finalizer = jsMt19937Finalizer,
};

void initContext(JSContext *ctx) {
  // ...
  // Mt19937 class の ID を初期化
  JS_NewClassID(&jsMt19937ClassID);
  // ランタイムにクラスを登録
  JS_NewClass(JS_GetRuntime(ctx), jsMt19937ClassID, &jsMt19937Class);
  // prototype 設定
  JSValue mt19937Proto = JS_NewObject(ctx);
  JS_SetPropertyFunctionList(ctx, mt19937Proto, jsMt19937ProtoFuncs, std::extent_v<decltype(jsMt19937ProtoFuncs)>);
  JS_SetClassProto(ctx, jsMt19937ClassID, mt19937Proto);
  // globalThis に Mt19937 を追加
  JS_SetPropertyStr(ctx, global, "Mt19937", JS_NewCFunction2(ctx, jsMt19937New, "Mt19937", 1, JS_CFUNC_constructor, 0));
  // ...
}
```

肝は `JS_NewClass` / `JS_NewObjectClass` と `JS_GetOpaque` / `JS_SetOpaque`。このようにして `std::mt19937 *` の生存期間の管理を GC に任せることができる。

## モジュールを利用する

モジュールの `import` / `export` を行いたい場合、`JS_Eval` 時に `JS_EVAL_TYPE_MODULE` フラグが必要となる。このフラグが有効な場合、戻り値 `ret` は `JS_UNDEFINED` か `JS_EXCEPTION` のいずれかになる。

```c
    JSValue ret = JS_Eval(ctx, argv[i], strlen(argv[i]), "<input>", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_STRICT);
```

`qjs` コマンドのようにファイルシステムから JS ファイルをモジュールとしてロードしたい場合、 `quickjs-libc.c` の `js_module_loader` のような関数を `JS_SetModuleLoaderFunc` で登録する必要がある。

## C/C++ 側でモジュールを定義する

前々節の `Mt19937` について、グローバルプロパティとして与えるのではなく `rand` モジュールから `import` するという形にしたい、という場合を考える。

```js
import { Mt19937 } from "rand";
```

これは以下のように書ける。

```c++:rand.cc
// rand module 内の関数一覧
static const JSCFunctionListEntry randFuncs[] = {
  JS_CFUNC_SPECIAL_DEF("Mt19937", 1, constructor, jsMt19937New),  // new Mt19937() できるようにする
  // JS_CFUNC_DEF("Mt19937", 1, jsMt19937New),  // Mt19937() としたいならこっち
};

// rand module の初期化（JS の import 時に呼ばれるやつ）
int initRand(JSContext *ctx, JSModuleDef *m) {
  JS_NewClassID(&jsMt19937ClassID);
  JS_NewClass(JS_GetRuntime(ctx), jsMt19937ClassID, &jsMt19937Class);
  // prototype 設定
  JSValue mt19937Proto = JS_NewObject(ctx);
  JS_SetPropertyFunctionList(ctx, mt19937Proto, jsMt19937ProtoFuncs, std::extent_v<decltype(jsMt19937ProtoFuncs)>);
  JS_SetClassProto(ctx, jsMt19937ClassID, mt19937Proto);
  // 最後の引数は sizeof(randFuncs) / sizeof(JSCFunctionListEntry) の意
  return JS_SetModuleExportList(ctx, m, randFuncs, std::extent_v<decltype(randFuncs)>);
}

// rand module の定義
JSModuleDef *initRandModule(JSContext *ctx, const char *moduleName) {
  JSModuleDef *m = JS_NewCModule(ctx, moduleName, initRand);
  if (!m) {
    return nullptr;
  }
  JS_AddModuleExportList(ctx, m, randFuncs, std::extent_v<decltype(randFuncs)>);
  return m;
}

void initContext(JSContext *ctx) {
  // ...
  initRandModule(ctx, "rand");
  // ...
}

int main(int argc, char const *argv[]) {
    // ...
    // JS_EVAL_TYPE_MODULE でないと import できない
    JSValue ret = JS_Eval(ctx, argv[i], strlen(argv[i]), "<input>", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_STRICT);
    // ...
}
```

`JSCFunctionListEntry[]` を作って `JS_NewCModule` してよしなにやるとモジュールをコンテクストに登録できる。

## モジュールを共有ライブラリにして `qjs` で実行可能にする

モジュール初期化関数の名前を `js_init_module` とし、共有ライブラリとしてコンパイルすると `qjs` コマンドで実行するスクリプトから `import` できる。この機能は `quickjs-libc.c` の `js_module_loader_so` 関数で実装されている。

```js:利用イメージ
import { Mt19937 } from "./librand.so";
```

```c++:rand.cc
// rand module の定義
extern "C" JSModuleDef *js_init_module(JSContext *ctx, const char *moduleName) {
  JSModuleDef *m = JS_NewCModule(ctx, moduleName, initRand);
  if (!m) {
    return nullptr;
  }
  JS_AddModuleExportList(ctx, m, randFuncs, std::extent_v<decltype(randFuncs)>);
  return m;
}
```

```bash
g++ -c -fPIC -Os -std=c++17 -Wall -I"${HOME}/.local/include/quickjs" rand.cc
# macOS では -undefined dynamic_lookup も付ける
g++ -shared -Wl,-s -L"${HOME}/.local/lib/quickjs" rand.o -o librand.so
```

# おわりに

QuickJS は完全な JavaScript の処理系が数個の C ソースファイルだけで完結しているのですごい（感想）。ただし 1 ファイルにつき数千〜数万行あるけど。中身は割と読みやすく書かれていそうなのでコードリーディングもしてみたい。

C/C++ に組み込める軽量言語としては他に [Lua](https://www.lua.org/), [mruby](http://mruby.org/), [Squirrel](http://squirrel-lang.org/) 等があるが、代わりに JavaScript (QuickJS) を使うというのも選択肢に入ってくる気がする。例えばアプリケーションの設定ファイルやゲームのスクリプトを JS で書けるようにすると eslint + prettier が使えたり tsc や babel で TypeScript / JSX から変換できたりするのでなんか便利で面白いことができるのではなかろうか（適当）。

あとは実用上 C/C++ ⇆ JavaScript のバインディングを楽に行うやつが必要。Lua で言うところの [tolua++](https://github.com/LuaDist/toluapp) とか [luabind](https://github.com/luabind/luabind) とかに相当する層（使ったことないけど今は [sol](https://sol2.readthedocs.io/en/latest/) が主流？）。個人的には Rust のマクロでいい感じにやってくれるやつがほしい。現在 `quick-js` というラッパーがあるがやや機能が不足しているので自分でも実装したりしなかったりしていきたい。

* [quick-js - crates.io: Rust Package Registry](https://crates.io/crates/quick-js)
    * [libquickjs-sys - crates.io: Rust Package Registry](https://crates.io/crates/libquickjs-sys) (FFI bindings)

# 参考情報

* [2019 Javascript engine 俯瞰 - abcdefGets](http://abcdef.gets.b6n.ch/entry/2019/12/11/121840)
    * QuickJS インタプリタの内部動作について触れられている