Open6

WebAssembly APIについてまとめる

泡沫京水泡沫京水

WebAssembly Moduleの読み込み

Streaming読み込み

Firefox 58からの新機能で、WebAssemblyモジュールをソースから直接コンパイルおよびインスタンス化する機能が追加されている
https://developer.mozilla.org/ja/docs/WebAssembly/JavaScript_interface/compileStreaming_static
https://developer.mozilla.org/ja/docs/WebAssembly/JavaScript_interface/instantiateStreaming_static

WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
  (obj) => obj.instance.exports.exported_func(),
);

メリット

  • バイトコードを直接Module/Instanceインスタンスに変換できる
    • fetch()からのResponseArrayBufferに変換する必要がない

通常読み込み

上記のStreaming

  • 対応していない
  • 使用したくない事情があるとき

代わりに

https://developer.mozilla.org/ja/docs/WebAssembly/JavaScript_interface/compile_static
https://developer.mozilla.org/ja/docs/WebAssembly/JavaScript_interface/instantiate_static

こっちはバイトコードに直接アクセスできないため、

  1. WebAssembly Moduleをfetch()関数で取得し
  2. 取得したResponseArrayBufferにする
    という手順が必要
fetch("simple.wasm")
  .then((response) => response.arrayBuffer())
  .then((bytes) => WebAssembly.instantiate(bytes, importObject))
  .then((results) => {
    results.instance.exports.exported_func();
  });
泡沫京水泡沫京水

WebAssemblyでのメモリの仕様

WebAssemblyの低レベルのメモリのモデルは
線形メモリと呼ばれる

  • 型なし
  • 連続したバイト列

として表現される。
それらのメモリに格納されたデータは
モジュール内のロード、ストア命令を使用して読み書きされる

このメモリモデルだと、任意のロード、ストア命令は線形メモリ全体の任意のバイトにアクセス可能。
(ここなんかアセンブラ動かしてるみたいで面白そう)

ただし、利用可能なメモリ範囲がプロセス全体に及ぶネイティブのC/C++プログラムとは異なる。

WebAssemblyインスタンスがアクセス可能なメモリの範囲は
WebAssembly Memoryオブジェクトが含む、特定(潜在的に非常に小さい)範囲に制限されている

Memoryインスタンスはリサイズ可能なArrayBuffer(または共有メモリの場合はSharedArrayBuffer)とみなすことが可能なため、ArrayBufferと同様にして、多くの独立したMemoryオブジェクトを作成可能である。

また、Memoryオブジェクトは以下のようにして作成できる

new WebAssembly.Memory({
    initial: 10, // 初期サイズ(必須)
    maximum: 100 // 最大サイズ(省略可能)
})

initialmaximumの単位はWebAssemblyページ
でこれらが64KBに固定されている。
つまり上記のインスタンスは、

  • 初期サイズ:640KB
  • 最大サイズ:6.4MB
    であることを意味する
泡沫京水泡沫京水

WebAssemblyメモリが持つバイト列は
ArrayBufferとしてBuffer Getter/Setterから公開されているため

const memory = new WebAssembly.Memory({
    initial: 10, // 初期サイズ(必須)
    maximum: 100 // 最大サイズ(省略可能)
})
new Uint32Array(memory.buffer)[0] = 42
// 線形メモリの先頭ワードに直接、42を書き込む

こういうコードを書く

泡沫京水泡沫京水

メモリ拡張

メモリインスタンスは、grow()を呼び出すことが拡張可能

try {
    memory.grow(1) // 引数はWebAssemblyページ単位で指定
} catch(e){
    // Memoryインスタンス作成時に最大値を指定していて、
    // それを超えた拡張をするとエラーがでる
}
泡沫京水泡沫京水

関数と同様にして、線形メモリは
モジュール内で定義したり、インポートすることも可能
また、任意でメモリをエクスポートすることも可能
これは、JavaScriptがWebAssemblyインスタンスに対して新しく作成したWebAssembly.Memoryをインポートで渡したり、
Memoryのエクスポートから(Instance.exportsを介して)受け取りが可能