yhara.jp

Recent Posts
Edit

WASMはじめの一歩 (wastを書いてブラウザで動かすところまで)

2017-05-24
Tech

こんばんわ。今日はWebAssemblyにチャレンジしたいと思います。wasm用のプログラムを書いて、それをブラウザで動かすところまでやります。

用意するもの1:.wat

まずはWebAssemblyで書かれたプログラムが必要です。拡張子は.wasmです。あいにくこれはバイナリファイルなので手書きするのは難しいです。一般的にはお好みの言語で書いたプログラムをLLVM経由で.wasmに変換するのだと思いますが、今回はWebAssemblyそのものの例としたいので、wasmのテキスト表現を使います。

以下の内容をsquare.watというファイルに保存します。.watというのはwasmのテキスト表現のための拡張子です。ツールによっては.wastという形式を受け付けるものがありますが、これは.watの非公式な上位互換拡張だそうです。

;; square(i32 i) -> i32
(module
  (func (export "square") (param $i i32) (result i32)
    (i32.mul
      (get_local $i)
      (get_local $i))))

.watのサンプルはこのへんにいくつかあります。

用意するもの2:wabt (wast2wasm)

.watはWebAssembly/wabtに入っているwast2wasmというツールで.wasm形式に変換できます。このリポジトリをgit cloneしてmakeすると./out以下にwast2wasmという実行ファイルができます。

(Note: 私の環境では一発でmakeできましたが、もしうまく行かなくて、Node.jsには詳しい場合、https://github.com/ewasm/wast2wasm というnpm packageを使うと良いかもしれません。)

(6/13追記:コンパイルしなくてもbrew install wabtで入るのですが、生成される.wasmが新しすぎてChrome 59.0で読めませんでした。Wasm decoding failed: expected version 01 00 00 00, found 0d 00 00 00 みたいなエラーが出ます。wabtの765d59だとversion 01が出るっぽくて大丈夫です)

wasmを生成する

以下のようにして.watを.wasmに変換します。

$ ~/somewhere/wast2wasm square.wat -o square.wasm

ちなみに-vを使うとバイナリを解説付きでダンプしたものが出ます。

$ ~/somewhere/wast2wasm square.wat -V
0000000: 0061 736d                                 ; WASM_BINARY_MAGIC
0000004: 0100 0000                                 ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01                                        ; section code
0000009: 00                                        ; section size (guess)
000000a: 01                                        ; num types
...

JSから読み込む

以下の内容をindex.htmlとして保存し、ブラウザで開きます (Ajaxを使って.wasmを読み込むので、Chromeの場合はlocalhostにサーバを立てるなどしてください)。

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <script type='text/javascript'>
      function get(path, cb) {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
          if (this.readyState == 4 && this.status == 200) {
            cb(this.response);
          }
        }
        xhr.open('GET', path);
        xhr.responseType = 'arraybuffer';
        xhr.send();
      }

      var importObject = {imports: {}};
      get('square.wasm', function(response){
        WebAssembly.instantiate(response, importObject)
        .then(function(result) {
          var instance = result.instance;
          console.log(instance.exports.square(24));
        });
      });
    </script>
  </body>
</html>

Developer Consoleに576と表示されたら成功です。

deveoper consoleのスクリーンショット


More posts