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

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


# とにかくWASM読み込んでみる。
MDNのサイト内にWASMファイル（WebAssemblyのファイル）を見つけたので、それを一旦動かす。

WASMファイル：[simple.wasm](https://github.com/mdn/webassembly-examples/raw/master/js-api-examples/simple.wasm)
MDNサイト：[WebAssembly JavaScript API を使用する](https://developer.mozilla.org/ja/docs/WebAssembly/Using_the_JavaScript_API)    


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

```tree
.
├── firebase.json
└── public
    ├── 404.html
    ├── index.html
    └── simple.wasm
```


```HTML: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のコードを早くしよう](https://qiita.com/chikoski/items/d98ed0a98acfa896869f) 

こちらを参考にAssemblyScriptをインストールしました。

```Terminal
$ 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ファイルをコンパイルします。

 ```Terrminal
$ 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分調べても分からないことは、質問しよう！」がウリのサイトに質問したらやり方が帰ってきたので、
そちらをここにまとめておきたいと思います。  
[実際にした質問](https://teratail.com/questions/212030)  

```TypeScript:test2.ts

export function test(): string{
    return "test";
}
export function test_len(): int{
    return test().length
}
```
文字列に変換するのに、文字数が必要なようです。

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