deno 用 WASM を作って公開しようとしたけど、できない

Deno 向けの WASM を作って公開しようとしたけど、上手くいかなかったので、暫定対応と合わせてメモ。

何が起きたのか

そもそも、wasm-bindgen の Deno 向けの生成をすると、
WASM を以下のようなコードで読み込もうとする。

1
const wasmModule = new WebAssembly.Module(Deno.readFileSync(wasmFile));

ローカルに置いてある WASM を読み込むことを前提になっている。

これ故に、例えば github で公開しても動作できない。

さらに、ネットワークから .wasm を取得するとき、WebAssembly の mime type の指定もある。
application/wasm でサーバーが返さないと、エラーになる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 修正前
const file = new URL(import.meta.url).pathname;
const wasmFile =
file.substring(
0,
file.lastIndexOf(Deno.build.os === "windows" ? "\\" : "/") + 1
) + "wasm_test_bg.wasm";
const wasmModule = new WebAssembly.Module(Deno.readFileSync(wasmFile));
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
const wasm = wasmInstance.exports;

//修正後
const url = new URL(import.meta.url);
const splitedUrl = url.href.split("/");
splitedUrl.pop();
const dir = splitedUrl.join("/");

const { instance, module } = await WebAssembly.instantiateStreaming(
fetch(`${dir}/wasm_test_bg.wasm`)
);
const wasm = instance.exports;

実行すると、次のようなエラー。

1
2
3
4
import {myAdd} from "https://raw.githubusercontent.com/Octo8080X/deno-wasm-test/main/pkg-a/wasm_test.js"
Download https://raw.githubusercontent.com/Octo8080X/deno-wasm-test/main/pkg-a/wasm_test.js
Uncaught TypeError: Invalid WebAssembly content type.
at deno:extensions/fetch/26_fetch.js:498:17

こんなエラーになってしまう。

mime type は、application/wasm じゃないとエラーになる。

暫定対応

暫定で、wasm を fetch で取得してバイナリをインスタンス化すると、mime type が対応していなくても大丈夫。

1
2
3
4
5
6
7
8
9
10
11
12
13
const url = new URL(import.meta.url);

// ソースの取得先に対してのみ、 --allow--net を与える。
const permit = { name: "net", host: url.host };
await Deno.permissions.request(permit);

const splitedUrl = url.href.split("/");
splitedUrl.pop();
const dir = splitedUrl.join("/");
const response = await fetch(`${dir}/wasm_test_bg.wasm`);
const buffer = await response.arrayBuffer();
const wasmInstance = await WebAssembly.instantiate(buffer, imports);
const wasm = wasmInstance.instance.exports;

ただし、大きな欠陥があって、–allow-net が必要。
実行時のオプションで、host を渡すのは面倒なので、Deno.permissions.requestを使って対話的に処理させる。

読み込む側のコードは、次のようになる。

use_wasm.ts
1
2
import {myAdd} from "https://raw.githubusercontent.com/Octo8080X/deno-wasm-test/main/pkg-n/wasm_test.js"
console.log(myAdd(4, 2))

実行すると、以下のようになる。

1
2
3
4
5
6
7
8
9
10
# 対話的に、実行
$ deno run d.ts
Download https://raw.githubusercontent.com/Octo8080X/deno-wasm-test/main/pkg-n/wasm_test.js
Check file:///usr/src/app/d.ts
⚠️ ️Deno requests network access to "raw.githubusercontent.com". Allow? [y/n (y = yes allow, n = no deny)] y
6

# --allow-net で許可すれば対話的な確認は、発生しない
$ deno run --allow-net d.ts
6

Deno.permissions.request の便利さを再確認できた。

何も解決してない

実は、Deno Manual - WebAssembly supportに対応策は、書いてある。

Uint8Array に分解して、どうやらぶちまけるといいらしい。

今度はこれにチャレンジする。

ではでは。