WebAssemblyはじめの一歩

# はじめに
WebAssemblyが熱くなって来ている感があります。

- [WebAssembly対応、主要ブラウザChrome/Firefox/Edge/Safariで整う。－ Publickey](http://www.publickey1.jp/blog/17/webassembly_browsers.html)
- [mrubyをブラウザで実行するまで (WebAssembly)
](https://qiita.com/noontage/items/da846e78eebd1d3b2a78)

時代の流れに乗っかるべく、早速WebAssemblyを軽く触ってみたのですが、ググって出てくるチュートリアルは**WebAssemblyというより、RustやC言語を動作させるEmscripten&LLVMのチュートリアルが多い**ことが分かったので、やったことをまとめ直し、**アセンブリテキストを書いて動かす最小構成**をチュートリアルとして公開します。なお初心者なので間違っている点も多いかと思いますのでご承知おき下さい（ご指摘いただけますと大変ありがたいです！）。

なお本稿は以下の環境で**のみ**動作確認をしています。

- Ubuntu 16.04
- Google Chrome 62.0

# WebAssemblyについて
今回の内容を理解するために必要な最低限の知識です。

- WebAssemblyとはブラウザが実行可能な**バイナリコード**です（アセンブリ言語ではない）。
- WebAssemblyを、人間が読みやすくしたもの（≒アセンブリ言語）を**WebAssemblyテキストフォーマット**と呼びます。
- ブラウザは、C言語やRustで書かれたプログラムを、バイナリに変換することはできません。
    - この他言語からバイナリへのコンパイルは、[Emscripten](https://github.com/kripken/emscripten)が担います。
- **ブラウザは、WebAssemblyテキストフォーマットも、バイナリに変換することはできません。**
    - このバイナリとテキストフォーマットの相互変換は、[wabt](https://github.com/webassembly/wabt)が担います。

# 公式デモ
以上の前提から、テキストフォーマットで書いたプログラムは、wabtを用いてブラウザの外でアセンブルしておく必要がありそうです。しかし、実は**wabt（C++のプログラム）そのものが、Emscriptenによってブラウザで実行可能な形式にコンパイルできます。**これを用いて[オンラインでテキストフォーマットを書いて実行できるデモ](https://cdn.rawgit.com/WebAssembly/wabt/fb986fbd/demo/wat2wasm/index.html)がwabt公式より公開されています。

デモではデフォルトで「2引数を足す関数」を持った「モジュール」が定義されています。どうやら**WebAssemblyはモジュール形式**のプログラムのようです。

```
(module
  (func $addTwo (param i32 i32) (result i32)
    get_local 0
    get_local 1
    i32.add)
  (export "addTwo" (func $addTwo)))
```

以下のチュートリアルは、この「公式デモ」の動作を自分で実装する、というものになります。

# チュートリアル
## ローカルでアセンブル & ブラウザで実行
### ローカルでアセンブル
ローカルでアセンブルをするためにはwabtをコンパイルする必要があります。Ubuntu 16.04の環境では、公式の案内通り以下のコマンドでコンパイルできました。時間はそこまで要しません。リポジトリクローン時の`--recursive`オプションを忘れないようにしてください。

```
$ git clone --recursive https://github.com/WebAssembly/wabt
$ cd wabt
$ make gcc-release
```

先ほどのデモのサンプルをコンパイルしてみましょう。まず、プログラムをaddTwo.watという名前で保存します。出力ファイルの拡張子`.wat`はWebAssemblyTextの略です。

```addTwo.wat
(module
  (func $addTwo (param i32 i32) (result i32)
    get_local 0
    get_local 1
    i32.add)
  (export "addTwo" (func $addTwo)))
```

コンパイルは以下のコマンドで行います。wabtツール群の一つで、WebAssemblyテキストフォーマットからバイナリに変換する`wat2wasm`を利用します。WebAssemblyを意味する`.wasm`という拡張子のファイルに出力します。

```
$ ./out/gcc/Release/wat2wasm addTwo.wat -o addTwo.wasm
```

### ブラウザで実行
WebAssebmlyは、JavaScript上で`fetch`などを利用して動的にロードする必要があります。ローカルのHTMLファイルを開くと`fetch`が利用できないので、今回は`input`タグを用います（分かる方は、HTTPサーバーで配信し、fetchでロードしてもOKです）。以下のファイルを保存し、ブラウザで開き、ファイル選択から先ほどの`addTwo.wasm`を選択します。

```html:index.html
<input type="file" onChange="loadWasm()">
<script>
const loadWasm = async () => {
	const readerEvent = await new Promise(resolve => {
		const file = document.querySelector('input').files[0];
		const reader = new FileReader();
		reader.addEventListener('load', resolve);
		reader.readAsArrayBuffer(file);
	});
	const buffer = readerEvent.target.result;

	window.module = WebAssembly.Module(buffer);
	window.instance = WebAssembly.Instance(module, {});
	console.log(instance.exports, module, instance);
}
</script>

```

ブラウザのコンソール画面を開くと、`instance.exports`に`addTwo`という関数が生えていることが分かるかと思います。WebAssebmlyはモジュールであり、インスタンス化することで実行され、`exports`が得られるようです。`instance.exports.addTwo`に引数を与え、正しく動くことを確認してみましょう。

なお[MDNでは`WebAssembly.Module`や`WebAssembly.Instance`による同期実行は高コストのため非推奨とされています](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module)。以下のように`WebAssembly.compile`と`WebAssembly.instantiate`を用いることで非同期に実行可能です（バイナリを何に`compile`しているのでしょうか）。


```javascript
const module = await WebAssembly.compile(buffer);
const instance = await WebAssembly.instantiate(mod);
console.log(instance.exports, module, instance);
```

また、モジュールを得る前のバッファをそのまま`instantiate`に喰わせることも可能です。この場合は`module`と`instance`をプロパティに持ったオブジェクトが得られるようです。

```javascript
const {module, instance} = await WebAssembly.instantiate(buffer);
console.log(instance.exports, module, instance);
```

詳しくは[MDNによる解説](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly)をご参照ください。

## Emscriptenでwabtをコンパイル & ブラウザでアセンブル・実行
公式デモのところでも述べましたが、ローカルで実行していたwabt（C++のプログラム）そのものが、Emscriptenによってブラウザで実行可能な形式にコンパイル可能です。**makeが全てやってくれるので大した内容ではありません**が、自分の環境でサクッとWebAssemblyテキストを実行できると嬉しいので、やってみます。

### Emscriptenでwabtをコンパイル
**先ほどクローンした`wabt`のディレクトリ直下で**[Emscripten](https://github.com/kripken/emscripten)をクローンします。そして`make`をします。それだけです。（emscriptenを別の場所に導入している場合は、クローンせずに環境変数`EMSCRIPTEN_DIR`を指定することで代用可能です。）

```
$ git clone https://github.com/kripken/emscripten
$ make emscripten-release
```

`./out/emscripten/Release/libwabt.js`にブラウザで実行可能なwabtが出力されました！なお注意事項として、Emscriptenを利用していますが、残念ながらここではWebAssemblyではなく素のJavaScriptファイルを出力するようです。

### ブラウザでアセンブル・実行
出力された`libwabt.js`を読み込んで、ブラウザ上でWebAssemblyテキストをアセンブルしてみましょう。スクリプトを読み込むと、wabtというオブジェクが利用可能になります。


```html:index.html
<textarea></textarea><button onClick="run()">RUN</button>
<script src="./out/emscripten/Release/libwabt.js"></script>
<script>
const run = () => {
	assembleAndExecute(document.querySelector('textarea').value);
}

const assembleAndExecute = async source => {
	const sourceBuffer = (new TextEncoder('utf-8')).encode(source).buffer;
	const parsed = wabt.parseWat('test', sourceBuffer);
	const buffer = parsed.toBinary({log: true}).buffer;

	window.module = await WebAssembly.compile(buffer);
	window.instance = await WebAssembly.instantiate(module);
	console.log(instance.exports, module, instance);
}
</script>
```

テキストエリアに、先ほどの(addTwoの）WebAssebmlyテキストを貼り付け実行してください！コンソールを見ると…`addTwo`が得られました！足し算ができます！！


# 締め
emccビルドのwasmを用いると、ブラウザ上でアセンブルできるので、サクッと編集して動作確認をすることができます（公式デモでも可能ですが…）。[MDNの解説](https://developer.mozilla.org/ja/docs/WebAssembly/Understanding_the_text_format)を見ながら、ぜひ色々と試行錯誤してみてください。最後までお読み頂きありがとうございました！