LoginSignup
3
1

More than 3 years have passed since last update.

とにかくWebAssemblyを動かしてみる

Last updated at Posted at 2019-09-17

前々から興味だけあったWebAssemblyを「どんなもんよ?」と動かしてみたので、
動かし方をメモしておきます。

とにかくWASM読み込んでみる。

MDNのサイト内にWASMファイル(WebAssemblyのファイル)を見つけたので、それを一旦動かす。

WASMファイル:simple.wasm
MDNサイト:WebAssembly JavaScript API を使用する

ファイル構成

chromeのルールに引っかかり、ローカルではうごかせなかったので、FirebaseのHostingに上げて動作を確認しました。

.
├── firebase.json
└── public
    ├── 404.html
    ├── index.html
    └── simple.wasm
index.html
    <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がこちら

test1.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ファイルを作成

test1.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分調べても分からないことは、質問しよう!」がウリのサイトに質問したらやり方が帰ってきたので、
そちらをここにまとめておきたいと思います。

実際にした質問

test2.ts

export function test(): string{
    return "test";
}
export function test_len(): int{
    return test().length
}

文字列に変換するのに、文字数が必要なようです。

index2.html
    <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に出力することができました。
正直もう少しコンパイラー側でよしなにやってもらいたいですね。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1