【WASM】WASIランタイム(JS用)が混沌としている

2023-04-23

WASI用に出力されたWebAssemblyバイナリをJS上で動かすためのランタイムを探ったが混沌としていた。 できればコマンドライン(node.js)からとブラウザ上の両方で動くといいんだけど、決定版みたいなものが現状ないように見える。

結論

  • @wasmer/wasi は古いバージョン(0.12.0)なら動かせるけど、最新版(1.2.2)だと期待する動作がさせられない
  • wasi-jsはコマンドラインからは動かせるが、ブラウザ上で動かせなかった
  • コマンドライン版とブラウザ版とで使うランタイムを変えることも検討したほうがいいかも

動作確認したマトリクス:

モジュール コマンドライン (node.js) ブラウザ (WebPack)
@wasmer/wasi 0.12.0
@wasmer/wasi 最新 1.2.2 ❌(実ファイルが読めない) △(標準出力を乗っ取れない)
wasi-js ❌(pathモジュールが解決できず)
node.js標準ライブラリ

テストコード:https://github.com/tyfkda/wasi-on-js-test

あらすじ

WASI上でのファイルオープンに悪戦苦闘した話 でWasm/WASIをnode.js上から動かそうとしていた続き。

実際にはコマンドライン上だけじゃなくブラウザ上でも動かしたいので、WebPack(バージョン5)して使う方法も調べた。

WasmerJS編

npmでwasiと検索するとトップの方に@wasmer/wasiが出てくるので、以前に引き続き試してみた。 現在公開されてるバージョンは1.2.2。

最新版はメモリファイル専用で、ローカルファイルは読み書きできない?

WASIをコマンドラインで動かすランライム環境としてWasmerというのがあって、そこが提供してるJavaScriptのモジュールなので機能的にも問題ないだろうし同等のことができるだろうと疑問を持つことなく使ってみた。

単純に動かす分にはいいんだが実ファイルの読み書きで行き詰まった。 いろいろ試した結果どうも最新版1.2.2ではそもそもできないっぽい。 node.js用サンプルを見ると実際のファイルじゃなくて仮想ファイルを扱ってるだけだった。 そう思ってみるとREADME.mdのAPI Docswasi.fsは思いっきりMemFS型と書いてあった。 ローカルファイルは読めないってことだったのね。

一応WebPackしてブラウザ上で仮想ファイルを扱うことはできたが、標準出力・エラーの乗っ取り方がわからなかった。 実行が終わった後にwasi.getStdoutString()で取得して処理するのは厳しい。

旧バージョン0.12.0なら動かせる

@wasmer/cli や rubyonbrowser を見たところ@wasmer/wasiを使っていた。 え?と思ってpackage.jsonを見たところ、

  • 最新バージョンではなく、0.12.0を使用
  • ローカルファイルも扱える
    • コマンドライン上で実ファイルを扱うには fs かつ preopens: {'.': '.'} を指定
    • ブラウザでは仮想ファイル、 @wasmer/wasmfs 0.12.0 を使う
      • npmに公開されてるのとwasmerio/wasmfsは違う?わけわからん…

ググってファイルが扱えるように書いてあるドキュメントはこのバージョンのことだったらしい(公式よ…)。

WebPackの際にはbufferpathも要求される(WebPack5からはPolyfillが除外された?とのことで自分で追加する)。

ブラウザ上で動かす際に標準出力・エラーをフックして違う処理をする、ということもできた (wasi.fs.writeSync を書き換える、rubyonbrowserのソース参照)。

懸念としては、バージョン1以前のもので現在はファイルの機能が切られてしまってるので、今後WasmやWASIが更新されてもサポートは望めなさそうという点。

wasi-js

他にランタイムがないかググって出てきたのがwasi-js。 READMEを読むと@wasmer/wasiの0.12.0からフォークしたものとのこと、なのでまさに上記の通り。

こちらの開発はまだアクティブなのでこちらの方がいいのではないかと思ったが、WebPackした際にprocessやらutilやら要求されて、最終的にpathが解決できなかった。 cowasmというブラウザ上でPythonを動かすプロジェクトの一部なのでできるはずだが…。

node.js標準 (Experimental)

いろいろ手間取ってる間に次の記事を見つけた: Node.js のWASIモジュールについて - kakts-log。node.js自体がWASIライブラリを提供している。 結局のところコマンドライン用とブラウザ用で動かすソースは別だったので、コマンドライン用はこちらを使うのがいいかもしれない。

まだExpermentalとのことで実行にはnodeのコマンドラインオプションに--experimental-wasi-unstable-preview1を指定する必要がある (または環境変数NODE_OPTIONSに指定しておく)。

node.jsのライブラリなので、ブラウザには持っていけない。

結論

現状WasmerJS0.12.0を使い、node.jsでExperimentalが取れたらコマンドライン版はそちらに移行するのがいいかもしれない。 またはコマンドラインはもうWasmerやWasmtimeに任せるか。

参照