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 Docsにwasi.fs
は思いっきりMemFS
型と書いてあった。
ローカルファイルは読めないってことだったのね。
一応WebPackしてブラウザ上で仮想ファイルを扱うことはできたが、標準出力・エラーの乗っ取り方がわからなかった。
実行が終わった後にwasi.getStdoutString()
で取得して処理するのは厳しい。
- wasmer_wasi_js_bg.wasm がない、とエラーになる:Can’t resolve ‘wasmer_wasi_js_bg.wasm’ · Issue #290 · wasmerio/wasmer-js .wasmをリソースにするとかじゃなく、無視リストに入れると一応動く
旧バージョン0.12.0なら動かせる
@wasmer/cli や rubyonbrowser を見たところ@wasmer/wasiを使っていた。 え?と思ってpackage.jsonを見たところ、
- 最新バージョンではなく、0.12.0を使用
- ローカルファイルも扱える
- コマンドライン上で実ファイルを扱うには
fs
かつpreopens: {'.': '.'}
を指定 - ブラウザでは仮想ファイル、
@wasmer/wasmfs 0.12.0
を使う- npmに公開されてるのとwasmerio/wasmfsは違う?わけわからん…
- コマンドライン上で実ファイルを扱うには
ググってファイルが扱えるように書いてあるドキュメントはこのバージョンのことだったらしい(公式よ…)。
WebPackの際にはbuffer
やpath
も要求される(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に任せるか。
参照
- ongaeshi/rubyonbrowser: Ruby on Browser using Ruby WASM/WASI 実際に動くので一番参考になった
- @wasmer/cli - npm これも動く
- Wasmer-JS: A New Hope Rust上で使うとまた状況は違うんだろうか…
- @bjorn3/browser_wasi_shim - npm これはどうだ?