前々から興味だけあったWebAssemblyを「どんなもんよ?」と動かしてみたので、
動かし方をメモしておきます。
とにかくWASM読み込んでみる。
MDNのサイト内にWASMファイル(WebAssemblyのファイル)を見つけたので、それを一旦動かす。
WASMファイル:simple.wasm
MDNサイト:WebAssembly JavaScript API を使用する
ファイル構成
chromeのルールに引っかかり、ローカルではうごかせなかったので、FirebaseのHostingに上げて動作を確認しました。
.
├── firebase.json
└── public
├── 404.html
├── index.html
└── simple.wasm
<script>
var importObject = {
imports: {
imported_func: function(arg) {
console.log(arg);
}
}
};
fetch('simple.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
results.instance.exports.exported_func();
});
</script>
とりあえず、42という数字が返ってくる。
WASMファイルを作成してみる。
WASMを読み込むことには成功しているが、これでは実用には程遠い。
次は自分でなんらかの関数を作成してその関数をWASM化して、
それをJS側で読み込み使用するというのをやってみたい。
WASMはバイナリーなので、当然コンパイルを必要とするが、
これを書いている時点で、WASMの作成は、
・Emscripten
・AssemblyScript
この二つが主流っぽい印象。
違いは何からWASMを作るかというもので、
・Emscripten <-- RUST
・AssemblyScript <-- TypeScript
という印象だった。(※もちろん他の言語の対応もある)
個人的にはJSが好き→TSはJSの上位互換→JSの拡張子をTSにすればJSをWASMにすることができるかも!!!
という考えで、AssemblyScriptを使ってWASMファイルを作ることにしました。
AssemblyScriptをインストールする。
AssemblyScriptを使ってTypeScriptのコードを早くしよう
こちらを参考にAssemblyScriptをインストールしました。
$ npm install --save-dev assemblyscript
$ npm install -g assemblyscript
$ asc -v
Version 0.3.0
Syntax: asc [options] entryFile // オプションにバージョン表示がなかったので
WASMの元になるTSファイルを作成
もともとAssemblyScriptを選んだ理由がJSで書きたいからだったが、
ここで予定が狂う。
調べたところ、WebAssemblyにはTSのany型が対応していない。
結果、明示的な型指定が必要!ということに気づいた。
というわけで、作成したTSがこちら
export function exported_func(): int{
return 33;
}
33を返すだけの関数です。
唯一気になることが、 int
が使われていること、
Number
型でコンパイルしようとするとエラーが出ます。
どうやらWebAssemblyでは整数はintを使わなければいけないようでした。
この辺りは、AssemblyScriptの方でよしなにやってほしい。正直!
自作したTSファイルをWASMにコンパイルする
上記のTSファイルをコンパイルします。
$ asc --noRuntime -o test1.wasm test1.ts
これで、test1.wasmが作成されます。
これを読み込むhtmlファイルを作成
<script>
fetch('test1.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes)
).then(results => {
var num = results.instance.exports.exported_func();
console.log(num);
});
</script>
前回同様Firebaseにアップロードして動作確認すると無事33を返しました。
文字列を受け取る
無事にWASMファイル内の関数を呼び出し、戻り値を受け取ることに成功したのですが、
その後で文字列を返す関数を作ったらうまくいきませんでした。
「15分調べても分からないことは、質問しよう!」がウリのサイトに質問したらやり方が帰ってきたので、
そちらをここにまとめておきたいと思います。
実際にした質問
export function test(): string{
return "test";
}
export function test_len(): int{
return test().length
}
文字列に変換するのに、文字数が必要なようです。
<script> fetch('test2.wasm').then(response => response.arrayBuffer() ).then(bytes =>
WebAssembly.instantiate(bytes) ).then(obj => {
const buffer = obj.instance.exports.memory.buffer;
const offset = obj.instance.exports.test();
const test_len = obj.instance.exports.test_len();
const array = new Uint16Array( buffer, offset+8, test_len);
const str = new TextDecoder('utf-16').decode(array);
console.log(str);
});
</script>
offsetには8を足します!教えてくれた人も理由がわからないらしいので、当然私もわかりません。
こちらをコンパイルすると文字列をconsoleに出力することができました。
正直もう少しコンパイラー側でよしなにやってもらいたいですね。