WebAssembly テキスト形式(WAT)を触ってみたので、備忘録を残す。
watからwasmに変換するためのツールとしてWABTがMDNにて紹介されていたのでインストールしてみた際のメモと、実際にMDNに記載されているサンプルコードをブラウザ上で動かしてみるまでのメモを載せている。
WABT
WebAssembly テキスト形式から wasm への変換
WABTのインストール
自身の環境
- M1 MacBook Air (arm64)
インストール手順は公式リポジトリ内の記述をそのまま実行している。
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形式のファイルを実行するまで
上にも貼った下記のページのコードを実行してみる
作業ディレクトリにて下記のコードを 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の書き方についてはこちらの記事が参考になりそうだった。
(まだ全ては読めていない)