ウェブアプリケーション開発を行う場合、ブラウザー側の実行環境といえばJS(JavaScript)のみでしたが、新たにWasm(WebAssembly)という選択肢が加わりました。Wasmの登場によって、今後はJSだけで構築されたウェブアプリケーションだけでなくJS+Wasmというハイブリッドなウェブアプリケーションが増えるでしょう。
Wasmの登場によって変わるのはブラウザーの実行環境だけではありません。Wasmは開発環境やバックエンドの実行環境にも影響を与えます。モダンなウェブアプリケーションの開発といえばTS(TypeScript)などのトランスパイル言語を用いる開発が主流ですが、今後Wasmが普及することでRustやDartなどのコンパイル言語を用いて開発を行い、ブラウザー上もサーバー上もWasmを実行ファイルとして利用する時代が来るでしょう。
本書はWasmを用いたウェブアプリケーションの作り方を紹介する本ではありません。
・Wasmとはどういうものなのか?
・Wasmで解決できること、解決できないことは何か?
・Wasmの登場によって何が変わるのか?
本書がWasmの活用を検討する際のヒントとなれば幸いです。
各章で出てくるコマンドのインストール方法や、コードの全体像は付録にまとめています。本文ではそれらのコマンドのインストール方法には言及しないため、必要に応じて付録を参照してください。
本書は1章から順に読み進めることを想定して書かれています。1章から3章でWasmに対する基本的な知識とビルド方法、実行方法を紹介し、4章でWasmをどのように活用できるのかを紹介します。
本書はWasmを用いて開発を行うための基本的な知識を提供することを目的に書かれています。「Wasmに興味があるから、とりあえずチュートリアル1をやってみよう」「チュートリアルをやってみたけれども、なんだかよくわからなかった」と思っている人にとって、本書は役に立つでしょう。また、この本は特定の言語のWasmをサポートするためのツールなどの紹介を行う本ではありません。それらのツールの使い方については、本書を読んだ後に各ツールのドキュメントなどを読むことをお勧めします。
本書に記載されている会社名、製品名などは、一般に各社の登録商標または商標、商品名です。会社名、製品名については、本文中では©、®、™マークなどは表示していません。
Wasm(WebAssembly)とは、ブラウザーなどで動作させることを目的としたバイナリフォーマットの実行ファイル形式(とその仮想マシン)のことです。Wasmのトップページ1には、次のように記載されています。
WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.
WebAssembly(Wasm)は、スタックベースの仮想マシンのバイナリ命令フォーマットです。プログラミング言語からの移植可能なコンパイル先として設計されており、クライアントやサーバーアプリケーションのウェブへの展開を可能にします。
バイナリフォーマットとは、人ではなくコンピューターが読むことを目的としたフォーマットのことです。通常、人が読むことを目的としたフォーマットのことをテキストフォーマットと呼びます。テキストフォーマットの場合、文字として扱えるコードの範囲が決まっています。ASCIIコードを例に挙げると、アルファベットのA~Zは65~90というコードに対応します。しかし、バイナリフォーマットの場合はコードの範囲がテキストフォーマットの範囲と一致しません。そのため、バイナリフォーマットをテキストエディターで開くと、文字化けして見えます。
バイナリフォーマットの表示や編集を直接行いたい場合、一般的にはバイナリエディターを用います。バイナリエディターは、16進数を用いてバイト列の表示と編集を行うツールです。リバースエンジニアリングを行うような特殊な理由がない限り、バイナリエディターを利用してバイナリフォーマットを直接編集するようなことはありません。バイナリフォーマットとは、通常はコンパイラなどのプログラムによって編集や作成が行われるからです。
wat(WebAssembly Text Format)とは、Wasmのバイナリフォーマットを人が読めるようテキストフォーマットにしたものです。watとWasmのバイナリフォーマットは相互変換可能で、バイナリフォーマットをテキストフォーマットに変換し、それを再びバイナリフォーマットに変換することが可能です。
Wasmについて紹介するにあたり、最小のWasmファイルを作成します。minimal.watというテキストファイルを用意し、wat2wasmを用いてWasmファイルに変換します。
$ cat <<EOF >> minimal.wat
> (module)
> EOF
(module)
$ wat2wasm -o minimal.wasm minimal.wat
生成されたminimal.wasmをxxdを用いて表示すると、次のような結果を得られます。
$ xxd minimal.wasm
00000000: 0061 736d 0100 0000 .asm....
この8バイトのコード0061 736d 0100 0000が、Wasmを構成する最小のバイナリとなります。前半の4バイトのコード0061 736dは、Wasmのバイナリフォーマットであることを表すマジックナンバー\0asmです。後半の4バイトのコード0100 0000は、バージョン番号を表します。Wasmのバージョン番号は32ビットの整数で表現されているため、バージョンは1となります。バージョン番号が0000 0001ではなく0100 0000と表現されているのは、Wasmが値をリトルエンディアンで扱うためです。
Wasmのバイナリフォーマットをテキストフォーマットに戻すには、wasm2watを用います。
$ wasm2wat minimal.wasm
(module)
最小のWasmのバイナリフォーマットには、Wasmであることを示すマジックナンバーとバージョン番号しかなく、実行命令は含まれていませんが、実行命令が含まれていたとしてもテキストフォーマットとバイナリフォーマットは相互に変換が可能です。
VM(Virtual Machine; 仮想マシン)とは、CPUやOSに依存しない仮想的なコンピューターのことです。GoやRustなどのコンパイル言語は、CPUやOS毎に実行ファイルとなるバイナリを生成する必要があります。一方で、VMの場合は実行ファイルのバイナリはひとつのみでよく、実行環境であるVMをCPUとOS毎に用意します。Wasm以外の有名なVMとして、JavaのJVMが挙げられます。
VMはオーバーヘッドが発生しているように見えますが、JITコンパイルの恩恵も受けられるため、ほとんどのケースでこのオーバーヘッドは無視することができます。Wasm自体もJITコンパイルを意識した設計となっているため、高いパフォーマンスを期待できます。
スタックとは、データを積み上げていくFILO(First In Last Out)と呼ばれるデータ構造です。スタックはデータ構造が単純かつ実装が簡単なため、一時的なデータを入れる変数のメモリー領域として使われるデータ構造です。
スタックを利用した有名な実装例として、後置記法(逆ポーランド記法)を用いた電卓の実装が挙げられます。2 + 3という式を後置記法で表すと、2 3 +となります。後置記法は値を順番にスタックに積んでいき、評価することで結果を得られるため、実装が簡単という特徴があります。
WasmのVMは、このスタック操作をベースとしています。
Wasmが生まれた背景として、ブラウザー上で実行するウェブアプリケーションの肥大化とそれに伴うJSの実行速度の遅さが問題視されるようになったことが挙げられます。
JSの実行速度の改善としてJITコンパイラが導入されましたが、JSが動的型付け言語であるためにJITコンパイルを有効活用できる場面が限定的という問題がありました。その状況を打破するためにWasmの前身であるasm.jsが生まれ、その後Wasmに発展しました。
Wasm自体はウェブアプリケーションの実行速度の高速化という目的からスタートしましたが、それ以外にも多くのメリットをもたらします。
・実行ファイルの軽量化
・サンドボックス化によるセキュアな実行環境
・JS以外の言語からのサポート
ウェブアプリケーションの規模が大きくなるほど、JSのファイルサイズの肥大化が問題になります。ファイルサイズが大きいほど、ネットワークの通信にかかる時間が長くなるため、最初の画面が表示されるまでが長くなってしまう問題が発生します。ウェブの世界では、この最初の画面の表示にかかる時間はUXに直結する重要な指標のひとつです。
バイナリフォーマットのプログラムは、一般的にテキストフォーマットで書かれたインタープリター言語のプログラムよりもファイルサイズがコンパクトになる傾向があります。そのため、Wasmを利用することでファイルサイズを削減することができ、表示スピードを改善できる可能性があります。
ブラウザー上でアプリケーションを動かす場合、必ずセキュリティーを意識する必要があります。WasmのVMは、外部環境であるJSのAPIやOSのリソースに対して直接アクセスすることができないサンドボックス環境で実行されます。そのため、Wasmが外部環境にアクセスするにはWasmの実行時(インスタンス生成時)に動的リンクによって、外部にアクセスするために必要な機能をインポートする必要があります。
Wasmはブラウザー上で実行することを目的に開発されていますが、サンドボックスであることやWasmの仕様自体がシンプルであることから、ブラウザー以外でも利用しようという動きがあります。
例として、プラグインとしてのWasmの活用や、CDN上でのWasmの実行などが挙げられます。また、Wasmの標準的なシステムインターフェース2を定義しようという動きもあります。
Wasmはコンパイル言語のコンパイルターゲットとなるよう設計されているため、RustやGoといった言語からWasmにコンパイルすることが可能です。そのため、Wasmの普及に伴いウェブアプリケーションの機能をTSやJSで作る必要性が少なくなり、より使い慣れた言語を用いてウェブアプリケーションを開発することが可能になります。
たとえばRustには、Yew3というウェブアプリケーションを構築するためのフレームワークがあります。これはフロントエンドも全てRustで書くことが可能なフレームワークで、Wasmを用いることで実現されています。
他にもサーバーとブラウザーでバリデーション処理を二重で管理している場合など、Wasmを用いることで同一コードでバリデーション処理を管理できるようになり、メンテナンスコストを下げられる可能性もあります。
サーバーで実行している処理をブラウザー側に持っていく場合、ライブラリーなどのライセンスの扱いに注意しましょう。特に、GPL系のライセンスは注意が必要です。