at backyard

Color my life with the chaos of trouble.

WATを触ってみることにしたのでWABTをインストールする

WebAssembly テキスト形式(WAT)を触ってみたので、備忘録を残す。

watからwasmに変換するためのツールとしてWABTがMDNにて紹介されていたのでインストールしてみた際のメモと、実際にMDNに記載されているサンプルコードをブラウザ上で動かしてみるまでのメモを載せている。

WABT

github.com

WebAssembly テキスト形式から wasm への変換

developer.mozilla.org

WABTのインストール

自身の環境

インストール手順は公式リポジトリ内の記述をそのまま実行している。
wabt ビルド用のディレクトリを別途堀り(※)、そちらで作業。
(ひとまずユーザディレクトリ配下に /Users/username/wabt-build という感じで作成)

# clone
git clone --recursive https://github.com/WebAssembly/wabt
cd wabt
git submodule update --init


# cmakeをインストール(command not found: cmakeというエラーがこの後の手順で出たので、まずはcmakeをインストールする)
brew install cmake

# build
mkdir build
cd build
cmake ..
cmake --build .

ここまで完了したら、下記ディレクトリにbuildしたものが格納されているので、こちらにPATHに通す。
(ちなみに /Users/username/wabt-build/wabt/build にも同様のものが生成されているが、ディレクトリの名称的に bin のほうが適切かと思い、こちら側にPATHを通している。生成されているものはどちらも同じよう)

/Users/username/wabt-build/wabt/bin

自分はとりあえず .zshrc に以下のように設定した。

export WABT_BIN="/Users/username/wabt-build/wabt/bin"
export PATH="$WABT_BIN:$PATH"

これでコマンドが実行できるようになった。

wasm2wat --version
# => 1.0.29 (git~1.0.29-34-g694274bc)

wat形式のファイルを実行するまで

上にも貼った下記のページのコードを実行してみる

developer.mozilla.org

作業ディレクトリにて下記のコードを simple.wat で保存。

(module
  (func $i (import "imports" "imported_func") (param i32))
  (func (export "exported_func")
    i32.const 42
    call $i
  )
)

次に先程インストールしたwabtから wat2wasm を用いて下記のコマンドを実行。

wat2wasm simple.wat -o simple.wasm

すると simple.wasm が生成される。
ちなみに中身はバイナリとなっていて、テキストエディタで開いてみても中身は読めない。

.wasmをテキスト表現(WAT)に戻す

ただし、この.wasm をさきほどのテキスト表現に戻すことができる。

wasm2wat simple.wasm -o text.wat

上記コマンドを実行した後、text.wat を開くと下記のようなコードが読める。

(module
  (type (;0;) (func (param i32)))
  (type (;1;) (func))
  (import "imports" "imported_func" (func (;0;) (type 0)))
  (func (;1;) (type 1)
    i32.const 42
    call 0)
  (export "exported_func" (func 1)))

.wasmの中身を読む

上にも書いたように .wasm ファイルは通常のテキストエディターで表示することはできないが、 wat2wasm-v オプションを使うと中身を見ることができる。

wat2wasm simple.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: 02                                        ; num types
・
・
・

watから生成したwasmを実行してみる

次は実際にこの .wasm をブラウザ上で実行してみる。

と、その前に今回サンプルとして書いているMDNに掲載されているコードが一体どのようなコードなのかを理解しておくため、コメントを付与したコードを掲載する。
なお、WATでのコメントは2重のセミコロン( ;; )となる。

(とここまで書いたものの、私はWATを完全に理解しきれているわけではないので誤りは含まれている可能性がある。その際は修正を加える予定。)

(module
  ;; imported_func という名前の関数を imports というモジュールからインポートし、$1に格納している。
  ;; このimported_func関数の引数として渡す型は32ビット整数
  (func $i (import "imports" "imported_func") (param i32))
  ;; exported_func という名称で関数をエクスポートする。これをWebアプリ上から利用する形となる
  ;; 以下ネストしている部分はexported_func内の処理
  (func (export "exported_func")
    ;; 32ビット整数を(ここでは42)を定義してスタックにプッシュしている
    i32.const 42
    ;; $1(つまりimported_func関数)を実行する。さきほどpushした42が引数として渡される
    call $i
  )
)

というわけで、ようやくweb上から実行していく。上にも書いたが下記のコマンドで .wasmコンパイルする。

wat2wasm simple.wat -o simple.wasm

次に下記のようなHTMLを書く。
WebAssembly.instantiateStreaming(fetch("simple.wasm") というような形で読み込んでいく。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple wasm example</title>
  </head>
  <body>
    <script>
      const importedFunc = (num) => console.log("num: ", + num);
      WebAssembly.instantiateStreaming(fetch("simple.wasm"), {imports : { imported_func: importedFunc }})
      .then(obj => {
        obj.instance.exports.exported_func();
      });
    </script>
  </body>
</html>

WAT上で定義した imported_func 部分は上のコードでは importedFunc として定義している。これを imported_func としてimportし、wasm側に渡している。

ローカルでサーバを立ち上げてsimple.htmlに実行することでブラウザのコンソール上に num: 42 という数値が表示されるのを確認した。

思ったよりも長くなってしまったが備忘録は以上。

WATから実際にコードを書くというケースは少なそうだが、どういうものなのかというのは少しだけでも理解はしておきたいので、ひとまずそのうちWATでfizzbuzzでも書いてみたい。

なお、WATの書き方についてはこちらの記事が参考になりそうだった。
(まだ全ては読めていない)

developer.mozilla.org