◆ wasmer のパッケージをブラウザで使おうとするとうまく動かない

こういうの見つけました
https://github.com/wapm-packages/python

WebAssembly で動く Python のようです
WebAssembly ということはブラウザで動くんでしょうか
ブラウザ上で Python 動かせるというのは面白そうなので試してみることにしました

wasmer なのでブラウザで動かすにはこういうことをすればできるようです
https://docs.wasmer.io/integrations/js/wasi/browser/examples/hello-world

こういうパッケージをインストールして

import { WASI } from '@wasmer/wasi'
import browserBindings from '@wasmer/wasi/lib/bindings/browser'
import { WasmFs } from '@wasmer/wasmfs'

wasi のセットアップ

const wasmFs = new WasmFs()
let wasi = new WASI({
args: [wasmFilePath, echoStr],
env: {},
bindings: {
...browserBindings,
fs: wasmFs.fs
}
})

コンパイルとインスタンス化して実行

let response  = await fetch(pathToWasmFile)
let wasmBytes = new Uint8Array(await response.arrayBuffer())

let wasmModule = await WebAssembly.compile(wasmBytes);
let instance = await WebAssembly.instantiate(wasmModule, {
...wasi.getImports(wasmModule)
});

wasi.start(instance)

コンパクトにするためにコメントは除去してます
そのへんも見るならリンク先で

パッケージは npm のものなのでブラウザで動かすには import の依存解決が必要です
この例では parcel を使うようです

とりあえずこの通りやってみたのですが 動きません
実行以前の Parcel の依存解決でエラーです

ドキュメントに書かれてるバージョンと最新のバージョンで色々変わってそうなので @wasmer/wasi パッケージのリポジトリを直接見ることにしました
https://github.com/wasmerio/wasmer-js

けっこう違ってます
ブラウザ用の binding とかはいらないみたいで wasmfs もデフォルトで wasi に含まれてるみたいです
wasi.fs で MemFS 型のオブジェクトを取得できて ここの readDir とか rename とか open メソッドを使ってメモリ内ファイルシステムの読み書きをするみたいです

とりあえず @wasmer/wasi だけを残して

import { init, WASI } from "@wasmer/wasi"

const main = async () => {
await init()

const wasm_file = "/python.wasm"

const wasi = new WASI({
args,
env: {},
})

const module = await WebAssembly.compileStreaming(fetch(wasm_file))
await wasi.instantiate(module, {})
const exit_code = wasi.start()
const stdout = wasi.getStdoutString()
console.log({ exit_code, stdout })
}

main()

という感じで実行しました
ロードするところまでは動いてるのですが instantiate でエラーです
ランタイムエラーとしか出てなくて何が悪いのかわかりません

そもそもブラウザで本当に動くのかってところも怪しかったので 例にもあった helloworld.wasm を試します
https://github.com/wasmerio/docs.wasmer.io/blob/master/integrations/shared/wat/wasi/helloworld.wasm

すると 問題なく動きました

{exit_code: 0, stdout: 'hello world\n'}

エラーもなく 標準出力に hello world が出てます

なんで Python だと謎のエラーなんでしょう
wasmer-js のリポジトリ内の例はブラウザではなく Node.js だったので同じパッケージをそのまま使って Node.js で試してみます
.js ファイルは基本同じですが fetch で wasm ファイルを取得する部分を Node.js 用に変更します

const buf = fs.readFileSync("./python.wasm")
const module = await WebAssembly.compile(new Uint8Array(buf))
await wasi.instantiate(module, {})

これで試すと Node.js でもエラーでしたが場所が違って instantiate は成功していました
その後の start の中でランタイムエラーのようです
start はできている以上 標準エラー出力になにか出てないかと見てみると

{
stdout: '',
stderr: 'Fatal Python error: failed to get random numbers to initialize Python\n\n'
}

と Python のエラーが出ていました
エラーは出ているものの一応 Python は動いてそうです
なぜブラウザだとここまで行かないのか不明ですが そもそもこの Python の wasm が動くのかもわからないので 本来の cli で wasmer コマンドを使うことにしました

公式ドキュメントのとおりインストールします
https://docs.wasmer.io/ecosystem/wasmer/getting-started

パッケージマネージャはなくて shell script でのインストールみたいです

curl https://get.wasmer.io -sSfL | sh

fedora35 だと依存ファイルがなくてエラーになったので libxkbcommon-x11 と ncurses-compat-libs を dnf で入れると正常にインストールできました
これで helloworld.wasm は動きます

python.wasm を試してみると PYTHONHOME などの環境変数が無いことのエラーが出ていますが Node.js のときよりも動いています
ただ最終的に locale がどうこうで encodings モジュールをロードしようとして見つからないとエラーになっていました
標準ライブラリは wasm に含まれるわけではなく 別の方法でライブラリの py ファイルにアクセスできるようにしないといけないみたいです
wasmer コマンドだとフォルダのマッピングがありますが ブラウザで動作させることを考えると難しそうです
標準ライブラリの全部のファイルをブラウザがダウンロードして メモリ内ファイルシステムに配置するとかやろうとは思わないです

Node.js 版でのみ出るエラーやブラウザ版でなぜかインスタンス化できないなどの問題もありますし それらを解決できても標準ライブラリの問題もあると考えると 簡単にできるものじゃなさそうです
最初からブラウザを想定されてない WebAssembly パッケージはブラウザで使うのは難しそうです