🥹

TinyGo でビルドしたWebAssemblyバイナリが JavaScript 側で呼び込めない時の対応

2023/02/04に公開

はじめに

go build で wasm をビルドして動かしていたものを TinyGo でビルドするように置き換えた際、 JavaScript での読み込みエラーが出たのでその解決策まとめです。

状況

Go で wasm コンパイルをする場合、以下のようにコマンドを実行すると .wasm バイナリをビルドできます。

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./wasm_exec.js
GOOS=js GOARCH=wasm go build -o output.wasm ./main.go

wasm_exec.js は、JavaScript 側で wasm を読み込むローダーです。 .wasm をビルドした環境専用のものなのなので、 go env GOROOT から引っ張って来る必要があります。

このビルドを TinyGo を使ったものに置き換えたい場合、 TinyGo公式を参考にし、

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./wasm_exec.js
tinygo build -o output.wasm -target wasm ./main.go

とすると、ビルドは通りますが、ブラウザコンソール上で、以下のような wasm 読み込みをするときにエラーが起きました。

WebAssembly.instantiateStreaming(
    fetch('output.wasm'),
    go.importObject,
).then((result) => {
    go.run(result.instance)
    // ...
})

起きたエラー

TypeError: WebAssembly.instantiate(): Import #0 module="wasi_snapshot_preview1" error: module is not an object or function

解決策

tinygo の issue に同じ事象に当たった人がいましたが、 そこについていたコメント の通りにするとうまく読み込みできるようになりました。
読み込み用のローダーの JavaScript も TinyGo のものに置き換える必要があったということです。

# 元々: cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./wasm_exec.js
cp "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" ./wasm_exec.js

最終的なコード

こんな感じになりました。

cp "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" ./wasm_exec.js
tinygo build -o output.wasm -target wasm ./main.go

教訓

GitHub の issue が全て解決する

Discussion