連載目次

WebAssemblyは、Web（およびWeb以外の）プラットフォームを対象とした、サイズとロード時間の両面での効率性を高めるバイナリフォーマット。「wasm」とも呼ばれる。C／C++などで書かれたコードを、WebAssemblyをターゲットにコンパイルすることで、それらのコードをブラウザ内で実行できるようになる。W3CのWebAssembly Working Groupで標準化作業が進められている。

WebAssemblyの目標

今も述べたように、WebAssemblyはサイズとロード時間の面で効率的なバイナリフォーマットを定義するものであるが、その仕様策定と実装は段階を追って行われるようになっている。現在はWebAssemblyの初期API（MVP：Minimum Viable Product）については合意が形成され、各ブラウザベンダーでのブラウザへの実装作業が進められている。MVPは、asm.jsと同様な機能を標準化したものであり、C／C++を主要な対象としている。

その後の目標としては、スレッド機能、例外処理、SIMD、ガベージコレクション機能、ECMAScriptのモジュール機能との統合、C／C++以外の言語のサポートなどが挙げられている。また、既存のWebプラットフォームでのコードの実行、統合も目標として掲げられている。これには、JavaScriptコードとの相互運用、ブラウザが提供するJavaScript APIの利用などが含まれている。加えて、セキュアであること（サンドボックス化された実行環境でのコードの実行やSame Origin Policyの適用など）もWebAssemblyの目標の1つである。

WebAssemblyの利用箇所としては、コードの高速な実行が必要な部分。例えば、ブラウザ内での画像／映像編集、ゲーム、VRなどが考えられている。ブラウザ外でも、ゲーム配信サービス、サーバサイドアプリ、モバイルデバイス上でのJavaScriptコードとWebAssemblyコードを組み合わせての実行などが考えられている。これらの詳細については「Use Cases」ページを参照されたい。

なお、WebAssemblyはJavaScriptを置き換えるものではないことには注意しておこう。むしろ、WebAssemblyはJavaScriptを補完するものであり、C／C++をはじめとする言語で書かれた（高速性が求められる）コードをWebAssemblyコードとしてWebプラットフォーム上で実行し、JavaScriptからはそうしたコードを利用したり、HTML／CSS／JavaScriptを利用してそうしたコードに対するUIを構築するといった使い方が想定されている。

WebAssemblyを作成して、ブラウザで利用してみる

ここではCで記述した階乗を求めるコードを、WebAssemblyに変換し、それをWebブラウザ内で実行してみよう。

int fact1(int n) {

int result = 1;

for (int i = n; i > 1; i--)

result *= i;

return result;

}



int fact2(int n, int result) {

if (n == 0)

return result;

else if (n == 1)

return result;

else

return fact2(n - 1, n * result);

}

階乗を求めるコード（test.cファイル）



なお、ここではWebAssemblyへのコンパイルには、Emscriptenを利用した。Emscriptenのビルドについては「Getting Started」ページを参照してほしい。このページを参考にして、Emscriptenのビルドを行い、上のコードを「emcc test.c -O1 -s WASM=1 -s SIDE_MODULE=1 -o test.html」コマンドでコンパイルすることで、WebAssemblyを含んだtest.wasmファイルを生成した。emccコマンドのオプション「-s WASM=1」により、WebAssembly（wasm）コードが生成されるようになる。「-s SIDE_MODULE=1」はこれがEmscriptenのサイドモジュールであることを意味する。「-O1」オプションは最適化を行うオプションだ。

WebAssemblyコードを実際に利用しているのが、以下のHTMLファイルになる。

<!doctype html>

<html>

<head>

<meta charset='utf-8' />

<title>insider.net WebAssembly test</title>

</head>

<body>

<input type='text' id='num'input type='button' id='getFact' value='get Factorial' />

<p id="result1"></p>

<p id="result2"></p>

<script>

var importObject = {

env: {

memoryBase: 0,

memory: new WebAssembly.Memory({ initial: 256 }),

tableBase: 0,

table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })

}

};



fetch('test.wasm')

.then(response => response.arrayBuffer())

.then(bytes => WebAssembly.compile(bytes))

.then(module => WebAssembly.instantiate(module, importObject))

.then(instance => {

console.log(instance.exports);

let num = document.getElementById('num');

let btn = document.getElementById('getFact');

let result1 = document.getElementById('result1');

let result2 = document.getElementById('result2');

let fact1 = instance.exports._fact1;

let fact2 = instance.exports._fact2;

btn.addEventListener('click', () => {

result1.innerText =

`factorial of ${num.value} is ${fact1(num.value)}`;

result2.innerText =

`factorial of ${num.value} is ${fact2(num.value, 1)}`;

});

});

</script>

</body>

</html> WebAssemblyを利用しているWebページ



WebAssemblyはバイナリフォーマットであり、.wasmファイルに格納されているコードはコンパイル→モジュール生成→インスタンス生成という手順を踏むことで利用できるようになる。これを行っているのが、上のHTMLに含まれるコードのうちの次の部分である。

var importObject = {

env: {

…… WebAssemblyからインスタンスを生成するために必要な設定 ……

}

};



fetch('test.wasm')

.then(response => response.arrayBuffer())

.then(bytes => WebAssembly.compile(bytes))

.then(module => WebAssembly.instantiate(module, importObject))

.then(instance => {

…… WebAssemblyのインスタンスを利用して何かを行う ……

}); WebAssemblyをJavaScriptコードから利用する流れ



上のコード例では.wasmファイルをfetch APIで取得し、それをarrayBufferメソッドでバイト配列化してから、compileメソッドでコンパイルし、そこから得られたモジュールからinstantiateメソッドを使ってインスタンスを生成している。最後に、インスタンスを利用するという流れになっている。

instantiateメソッドで使用しているimportObjectオブジェクトには、WebAssembly.instantiateメソッドでインスタンスを生成するために必要な情報を記述する。上で記述しているenv.memoryプロパティなどについては「WebAssembly Dynamic Linking」ページなどを参照されたい。

実際に行っている処理は次のようになっていた。

console.log(instance.exports);

let num = document.getElementById('num');

let btn = document.getElementById('getFact');

let result1 = document.getElementById('result1');

let result2 = document.getElementById('result2');

let fact1 = instance.exports._fact1;

let fact2 = instance.exports._fact2;

btn.addEventListener('click', () => {

result1.innerText =

`factorial of ${num.value} is ${fact1(num.value)}`;

result2.innerText =

`factorial of ${num.value} is ${fact2(num.value, 1)}`;

}); WebAssemblyのインスタンスを使って行っている処理



WebAssemblyコードがエクスポートしているものはインスタンスのexportsプロパティから参照できる。最初の行では、fact1関数やfact2関数がエクスポートされているかを確認するために、コンソールにexportsプロパティの値を出力している。その後は、Webページに置かれたボタンなどの要素を取得している。

fact1変数とfact2変数には、このWebAssemblyがエクスポートしている2つの関数を代入している（ここでは「instance.exports._fact1」のように関数名に「_」が前置されている点に注意。これは以下の実行画面から分かるように実際に「_」が前置されているために付加した。コンパイラの処理によっては異なる名前となるかもしれない）。最後にボタンのclickイベントハンドラーでは、WebAssemblyがエクスポートしている関数を呼び出して、その結果をWebページに反映するようにしているだけだ。

実際にこれを実行した結果を以下に示す。

実行結果



デベロッパーツールの［コンソール］タブにはWebAssemblyがエクスポートしている関数が表示されていることと、実際に階乗が計算され、その結果がWebページに表示されている点に注目しよう。

WebAssemblyは、C／C++などで書かれたコードをWebページでも利用できるようにすることを念頭に置いたバイナリフォーマットである。本稿では取り上げなかったが、Node.jsの新しいバージョンでもWebAssemblyがサポートされるようになったり、.NETの世界でもWebAssemblyをターゲットとしたコード生成が行われるようになったりと、その存在感は高まっている。

参考資料 WebAssembly： 公式ページ

Getting Started： ツールのビルドとHello Worldプログラムなど

Documentation： ドキュメントのトップページ



「Dev Basics／Keyword」