diff --git a/files/ja/webassembly/c_to_wasm/index.html b/files/ja/webassembly/c_to_wasm/index.html deleted file mode 100644 index d1f297e6b23499..00000000000000 --- a/files/ja/webassembly/c_to_wasm/index.html +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: C/C++からWebAssemblyにコンパイルする -slug: WebAssembly/C_to_wasm -translation_of: WebAssembly/C_to_wasm ---- -
C / C ++のような言語でコードを書いたら、Emscripten のようなツールを使って WebAssembly にコンパイルすることができます。 どのように動作するかを見てみましょう。
- -まず、必要な開発環境をセットアップしましょう。
- -Emscripten SDKを取得します。以下の指示にしたがってください。https://emscripten.org/docs/getting_started/downloads.html
- -環境を設定した後は、C のサンプルコードを Emscripten にコンパイルする方法を見てみましょう。 Emscripten でコンパイルするときにはいくつかのオプションがありますが、この記事でカバーする主な2つのシナリオは次のとおりです:
- -2つについて見てみましょう。
- -最も簡単なケースを見てみましょう。コードを WebAssembly としてブラウザで実行するための全てを Emscripten で生成するようにします。
- -hello.c
としてローカルドライブの新しいディレクトリに保存してください:
-
- #include <stdio.h> - -int main(int argc, char ** argv) { - printf("Hello World\n"); -}-
hello.c
ファイルと同じディレクトリに移動して、次のコマンドを実行します:
- emcc hello.c -s WASM=1 -o hello.html-
このコマンドで渡されたオプションは次のとおりです:
- --s WASM=1
— 出力を wasm に指定します。指定しない場合、Emscripten はデフォルトでは asm.js として出力します。-o hello.html
— コードを実行するための HTML ページを指定します。wasm モジュールとそれをウェブ環境で使用できるようにコンパイル、インスタンス化するための JavaScript グルーコードも出力に含まれます。この時点でソースディレクトリに以下のファイルが出力されているはずです:
- -hello.wasm
)hello.js
)hello.html
)WebAssembly をサポートしているブラウザで hello.html
をロードするだけです。WebAssembly は Firefox 52+ と Chrome 57+/最新の Opera でデフォルトで有効になっています (Firefox 47+では about:config で javascript.options.wasm
flag を有効にすることで、Chrome (51+) と Opera (38+) では chrome://flags に飛んで Experimental WebAssembly フラグを有効にすることで wasm コードを実行することができます) 。
全てが計画通りに機能していれば、ウェブページ上の Emscripten コンソールに "Hello world" の出力が表示されるはずです。おめでとうございます、ようやくCを WebAssembly にコンパイルしてブラウザで実行することができました!
- -場合によっては、カスタム HTML テンプレートを使用することもできます。 どうやってできるかを見てみましょう。
- -まず、次の C のコードを hello2.c
として新しいディレクトリに保存します:
#include <stdio.h> - -int main(int argc, char ** argv) { - printf("Hello World\n"); - -}-
shell_minimal.html
をあなたの emsdk レポジトリから探します。先程作成した新しいディレクトリに html_template
というサブディレクトリを作って、そこにコピーします。
新しいディレクトリに移動して (Emscripten コンパイラ環境があるターミナルウィンドウで) 、次のコマンドを実行します:
- -emcc -o hello2.html hello2.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html- -
今回渡したオプションは少しだけ異なります:
- --o hello2.html
と指定したことで、今回コンパイラは JavaScript グルーコードと .html
を出力します。--shell-file html_template/shell_minimal.html
と指定しました — これは例を実行する HTML を生成するための、HTML テンプレートパスです。この例を実行してみましょう。上記のコマンドで hello2.html が生成されます。これは生成された wasm コードに対してロード、実行などを行うグルーコードを含むテンプレートと同じ内容を持ちます。ブラウザを開いて最後の例と同じ出力であることを確認してください。
-注: 例えば、 emcc -o hello2.js hello2.c -O3 -s WASM=1
のように -o
フラグにHTMLファイルの代わりに .js ファイルを渡すことで JavaScript で出力することを指定できます。そのあとに、スクラッチで独自のカスタム HTML を作ることができます。しかし、これはおすすめできません — Emscripten はメモリ割り当て、メモリリークやその他の問題を扱うための JavaScript グルーコードが多数必要になります。これは提供されたテンプレートにすでに含まれています。全てをあなた自身で書くよりも簡単に使用できます。していること全てに習熟してきたら、必要に応じて独自のカスタマイズバージョンを作りましょう。
C で定義された関数があって、それを JavaScript から呼び出したい場合、Emscripten の ccall()
関数と EMSCRIPTEN_KEEPALIVE
宣言 (対象の関数をエクスポートする関数リストに加えるものです (Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process? を参照してください)) を使用します。これがどのように働くか見てみましょう。
はじめに、次のコードを hello3.c
として新しいディレクトリに保存します:
#include <stdio.h> -#include <emscripten/emscripten.h> - -int main(int argc, char ** argv) { - printf("Hello World\n"); -} - -#ifdef __cplusplus -extern "C" { -#endif - -void EMSCRIPTEN_KEEPALIVE myFunction(int argc, char ** argv) { - printf("MyFunction Called\n"); -} - -#ifdef __cplusplus -} -#endif- -
デフォルトでは、Emscripten が生成したコードは常に main()
を呼び出し、他のデッドコードは削除されます。関数名の前に EMSCRIPTEN_KEEPALIVE
を置くことによって、これが起こらなくなります。また、EMSCRIPTEN_KEEPALIVE
を使用するために emscripten.h
をインポートする必要があります。
注: #ifdef
ブロックを加えたことによって、C++ のコードからこの例をインクルードしようとしても動作するでしょう。 C と C++ の間でのマングリング規則によって、他の場合では壊れることもありますが、ここでは C++ を使用している場合に、外部の C の関数として扱うように設定しています。
便宜上、この新しいディレクトリに html_template/shell_minimal.html
(もちろん、このファイルはあなたの実際の開発環境に置きます)を加えます。
さて、再びコンパイル手順を実行しましょう。あなたの最新のディレクトリの中 (そして、Emscripten コンパイラ環境の入っているターミナルウィンドウ) で、このように C のコードをコンパイルします (NO_EXIT_RUNTIMEオプションを付与してコンパイルする必要があることに注意してください。そうしない場合、 main() 関数が終了したときにランタイムもシャットダウンされてしまい、コンパイルされたコードが正しく呼ばれなくなる可能性があります - 例えば、atexitの呼び出しなどの適切なCのエミュレーションに必要です):
- -emcc -o hello3.html hello3.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']"
- 例をブラウザでロードしたら、前と同じものが見られるでしょう。
-JavaScript から新しい myFunction()
関数を呼び出す必要があります。まずは、 以下のような{{htmlelement("button")}} を最初の <script type='text/javascript'>
タグの上に加えましょう。
<button class="mybutton">Run myFunction</button>-
そして、{{htmlelement("script")}} 要素内の最後に次のコードを追加します ( </script>
タグの直前):
document.querySelector('.mybutton').addEventListener('click', function(){ - alert('check console'); - var result = Module.ccall('myFunction', // name of C function - null, // return type - null, // argument types - null); // arguments -});-
これはエクスポートされた関数をどのようにして ccall()
を使用して呼び出すかを示しています。
警告: 実験的な {{jsxref("WebAssembly.Module")}} IndexedDB のシリアル化サポートがブラウザから削除されています。{{bug("1469395")}} とこの仕様の問題を参照してください。
-キャッシングはアプリケーションのパフォーマンスを向上させるのに役立ちます — コンパイルされた WebAssembly モジュールをクライアントに格納することによって、毎回ダウンロードしてコンパイルする必要がなくなります。この記事では、キャッシングまわりのベストプラクティスについて解説します。
- -IndexedDB はクライアント側で構造化されたデータを格納、検索できるトランザクショナルデータベースシステムです。 これはテキスト、blob、他のクローン可能なオブジェクトをアプリケーションのステートの保存、アセットの永続化をするのに理想的な方法です。
- -これには wasm モジュール ({{jsxref("WebAssembly.Module")}} JavaScript オブジェクト) も含まれます。
- -IndexedDB はやや昔ながらの API です。まず、私たちは今日のよりモダンな API にあわせて、キャッシュするコードを素早く書け、よりよく機能させるためのライブラリ関数を提供します。
- -wasm-utils.js ライブラリスクリプト内で instantiateCachedURL()
を見つけることができます— この関数は url
と dbVersion
から wasm モジュールをフェッチし、importObject
を指定してインスタンス化を行います。そして、成功時に wasm インスタンスを渡すプロミスを返します。さらに、コンパイルされた wasm モジュールをキャッシュするデータベースの作成、新しいモジュールのデータベースへの格納、事前にキャッシュされたモジュールのデータベースからの取得 (再度ダウンロードする必要がなくなります) を行います。
注: サイト全体の wasm のキャッシュ (指定されたURLだけではありません) は関数に渡す dbVersion
によってバージョニングされます。wasm モジュールコードが更新された場合や、URLが変更された場合は dbVersion
を更新する必要があります。以降 instantiateCachedURL()
を呼び出すと、キャッシュ全体がクリアされ、期限切れのモジュールの使用を避けることができます。
この関数はいくつかの必要な定数を定義することから始まります:
- -function instantiateCachedURL(dbVersion, url, importObject) { - const dbName = 'wasm-cache'; - const storeName = 'wasm-cache';- -
instantiateCachedURL()
に含まれる最初のヘルパー関数 — openDatabase()
— は wasm モジュールを格納するためのオブジェクトストアを作成し、 dbVersion
が更新された場合はデータベースをクリアする処理をハンドリングも行います。これは解決時に新しいデータベースを渡すプロミスを返します。
function openDatabase() { - return new Promise((resolve, reject) => { - var request = indexedDB.open(dbName, dbVersion); - request.onerror = reject.bind(null, 'Error opening wasm cache database'); - request.onsuccess = () => { resolve(request.result) }; - request.onupgradeneeded = event => { - var db = request.result; - if (db.objectStoreNames.contains(storeName)) { - console.log(`Clearing out version ${event.oldVersion} wasm cache`); - db.deleteObjectStore(storeName); - } - console.log(`Creating version ${event.newVersion} wasm cache`); - db.createObjectStore(storeName) - }; - }); - }- -
次の関数 — lookupInDatabase()
— は上で作成したオブジェクトストアから指定した url
で検索するプロミスベースの操作を提供します。解決時に格納されたコンパイル済モジュール、棄却時にエラーを渡すプロミスを返します。
function lookupInDatabase(db) { - return new Promise((resolve, reject) => { - var store = db.transaction([storeName]).objectStore(storeName); - var request = store.get(url); - request.onerror = reject.bind(null, `Error getting wasm module ${url}`); - request.onsuccess = event => { - if (request.result) - resolve(request.result); - else - reject(`Module ${url} was not found in wasm cache`); - } - }); - }- -
次に storeInDatabase()
関数を定義します。この関数は非同期に指定された wasm モジュールを指定したデータベースに保存します。
function storeInDatabase(db, module) { - var store = db.transaction([storeName], 'readwrite').objectStore(storeName); - var request = store.put(module, url); - request.onerror = err => { console.log(`Failed to store in wasm cache: ${err}`) }; - request.onsuccess = err => { console.log(`Successfully stored ${url} in wasm cache`) }; - }- -
プロミスベースのヘルパー関数が全て定義されました。これらを使用して IndexDB のキャッシュルックアップするコアロジックを表現できるようになりました。データベースをオープンし、url
をキーとして db
に格納されているコンパイル済モジュールの有無を確認してみましょう:
return openDatabase().then(db => { - return lookupInDatabase(db).then(module => {- -
成功したら、インポートオブジェクトを指定してモジュールをインスタンス化します:
- -console.log(`Found ${url} in wasm cache`); - return WebAssembly.instantiate(module, importObject); - },- -
失敗した場合、スクラッチでコンパイルします。次回以降に使用するためにコンパイルしたモジュールを url をキーとしてデータベースに格納します:
- -errMsg => { - console.log(errMsg); - return WebAssembly.instantiateStreaming(fetch(url)).then(results => { - storeInDatabase(db, results.module); - return results.instance; - }); - }) - },- -
注: {{jsxref("WebAssembly.instantiate()")}} は {{jsxref("WebAssembly.Module()", "Module")}} と {{jsxref("WebAssembly.Instance()", "Instance")}} の両方を返します。Module はコンパイルされたコードを表し、IDB に格納したり、postMessage()
を通じて ワーカーとの間で共有することができます。Instance はステートフルで、呼び出し可能な JavaScript の関数を含んでいるため、格納/共有することは出来ません。
データベースをオープンすることに失敗した場合(例えば、パーミッションやクォータ等の原因による)、モジュールをフェッチしてコンパイルするだけにし、結果を格納しないでください (格納するデータベースがないため) 。
- -errMsg => { - console.log(errMsg); - return WebAssembly.instantiateStreaming(fetch(url)).then(results => { - return results.instance - }); - }); -}- -
上で定義されたライブラリ関数を使用して、wasm モジュールインスタンスの取得やエクスポートされた機能を使用することが (その間にバックグラウンドでキャッシュのハンドリングをしています) 、次のパラメータで呼び出すだけのシンプルなものになります:
- -const wasmCacheVersion = 1; - -instantiateCachedURL(wasmCacheVersion, 'test.wasm').then(instance => - console.log("Instance says the answer is: " + instance.exports.answer()) -).catch(err => - console.error("Failure to instantiate: " + err) -);- -
ソースコードと例は GitHub の indexeddb-cache.html (動作例) を参照してください。
- -現時点では、WebAssembly モジュールの構造化された複製をサポートしているため、この手法は Firefox と Edge で動作します。
- -Chrome は WebAssembly 構造化クローンサポートフラグの背後にサポートが実装されていますが、いくつかの懸念があるため、デフォルトではまだ有効にしていません (たとえば、この説明を参照)。
- -Safari ではまだ実装されていません。
diff --git a/files/ja/webassembly/caching_modules/index.md b/files/ja/webassembly/caching_modules/index.md new file mode 100644 index 00000000000000..08d742b3725676 --- /dev/null +++ b/files/ja/webassembly/caching_modules/index.md @@ -0,0 +1,168 @@ +--- +title: コンパイルされた WebAssembly モジュールをキャッシュする +slug: WebAssembly/Caching_modules +tags: + - Caching + - IndexedDB + - JavaScript + - Module + - WebAssembly + - compile + - wasm +translation_of: WebAssembly/Caching_modules +--- +{{WebAssemblySidebar}} + +> **Warning:** **警告**: 実験的な {{jsxref("WebAssembly.Module")}} IndexedDB のシリアル化サポートがブラウザから削除されています。{{bug("1469395")}} と[この仕様の問題](https://github.com/WebAssembly/spec/issues/821)を参照してください。 + +キャッシングはアプリケーションのパフォーマンスを向上させるのに役立ちます — コンパイルされた WebAssembly モジュールをクライアントに格納することによって、毎回ダウンロードしてコンパイルする必要がなくなります。この記事では、キャッシングまわりのベストプラクティスについて解説します。 + +## IndexedDB を用いたキャッシング + +[IndexedDB](/ja/docs/Web/API/IndexedDB_API) はクライアント側で構造化されたデータを格納、検索できるトランザクショナルデータベースシステムです。 これはテキスト、blob、他のクローン可能なオブジェクトをアプリケーションのステートの保存、アセットの永続化をするのに理想的な方法です。 + +これには wasm モジュール ({{jsxref("WebAssembly.Module")}} JavaScript オブジェクト) も含まれます。 + +## キャッシュライブラリをセットアップする + +IndexedDB はやや昔ながらの API です。まず、私たちは今日のよりモダンな API にあわせて、キャッシュするコードを素早く書け、よりよく機能させるためのライブラリ関数を提供します。 + +wasm-utils.js ライブラリスクリプト内で `instantiateCachedURL()` を見つけることができます— この関数は `url` と `dbVersion` から wasm モジュールをフェッチし、`importObject` を指定してインスタンス化を行います。そして、成功時に wasm インスタンスを渡すプロミスを返します。さらに、コンパイルされた wasm モジュールをキャッシュするデータベースの作成、新しいモジュールのデータベースへの格納、事前にキャッシュされたモジュールのデータベースからの取得 (再度ダウンロードする必要がなくなります) を行います。 + +> **Note:** **注**: サイト全体の wasm のキャッシュ (指定された URL だけではありません) は関数に渡す `dbVersion` によってバージョニングされます。wasm モジュールコードが更新された場合や、URL が変更された場合は `dbVersion` を更新する必要があります。以降 `instantiateCachedURL()` を呼び出すと、キャッシュ全体がクリアされ、期限切れのモジュールの使用を避けることができます。 + +この関数はいくつかの必要な定数を定義することから始まります: + +```js +function instantiateCachedURL(dbVersion, url, importObject) { + const dbName = 'wasm-cache'; + const storeName = 'wasm-cache'; +``` + +### データベースをセットアップする + +`instantiateCachedURL()` に含まれる最初のヘルパー関数 — `openDatabase()` — は wasm モジュールを格納するためのオブジェクトストアを作成し、 `dbVersion` が更新された場合はデータベースをクリアする処理をハンドリングも行います。これは解決時に新しいデータベースを渡すプロミスを返します。 + +```js + function openDatabase() { + return new Promise((resolve, reject) => { + var request = indexedDB.open(dbName, dbVersion); + request.onerror = reject.bind(null, 'Error opening wasm cache database'); + request.onsuccess = () => { resolve(request.result) }; + request.onupgradeneeded = event => { + var db = request.result; + if (db.objectStoreNames.contains(storeName)) { + console.log(`Clearing out version ${event.oldVersion} wasm cache`); + db.deleteObjectStore(storeName); + } + console.log(`Creating version ${event.newVersion} wasm cache`); + db.createObjectStore(storeName) + }; + }); + } +``` + +### データベースからモジュールを検索する + +次の関数 — `lookupInDatabase()` — は上で作成したオブジェクトストアから指定した `url` で検索するプロミスベースの操作を提供します。解決時に格納されたコンパイル済モジュール、棄却時にエラーを渡すプロミスを返します。 + +```js + function lookupInDatabase(db) { + return new Promise((resolve, reject) => { + var store = db.transaction([storeName]).objectStore(storeName); + var request = store.get(url); + request.onerror = reject.bind(null, `Error getting wasm module ${url}`); + request.onsuccess = event => { + if (request.result) + resolve(request.result); + else + reject(`Module ${url} was not found in wasm cache`); + } + }); + } +``` + +### モジュールの保存とインスタンス化 + +次に `storeInDatabase()` 関数を定義します。この関数は非同期に指定された wasm モジュールを指定したデータベースに保存します。 + +```js + function storeInDatabase(db, module) { + var store = db.transaction([storeName], 'readwrite').objectStore(storeName); + var request = store.put(module, url); + request.onerror = err => { console.log(`Failed to store in wasm cache: ${err}`) }; + request.onsuccess = err => { console.log(`Successfully stored ${url} in wasm cache`) }; + } +``` + +### ヘルパー関数を使用する + +プロミスベースのヘルパー関数が全て定義されました。これらを使用して IndexDB のキャッシュルックアップするコアロジックを表現できるようになりました。データベースをオープンし、`url` をキーとして `db` に格納されているコンパイル済モジュールの有無を確認してみましょう: + +```js + return openDatabase().then(db => { + return lookupInDatabase(db).then(module => { +``` + +成功したら、インポートオブジェクトを指定してモジュールをインスタンス化します: + +```js + console.log(`Found ${url} in wasm cache`); + return WebAssembly.instantiate(module, importObject); + }, +``` + +失敗した場合、スクラッチでコンパイルします。次回以降に使用するためにコンパイルしたモジュールを url をキーとしてデータベースに格納します: + +```js + errMsg => { + console.log(errMsg); + return WebAssembly.instantiateStreaming(fetch(url)).then(results => { + storeInDatabase(db, results.module); + return results.instance; + }); + }) + }, +``` + +> **Note:** **注**: {{jsxref("WebAssembly.instantiate()")}} は {{jsxref("WebAssembly.Module()", "Module")}} と {{jsxref("WebAssembly.Instance()", "Instance")}} の両方を返します。Module はコンパイルされたコードを表し、IDB に格納したり、[`postMessage()`](/ja/docs/Web/API/MessagePort/postMessage) を通じて ワーカーとの間で共有することができます。Instance はステートフルで、呼び出し可能な JavaScript の関数を含んでいるため、格納/共有することは出来ません。 + +データベースをオープンすることに失敗した場合(例えば、パーミッションやクォータ等の原因による)、モジュールをフェッチしてコンパイルするだけにし、結果を格納しないでください (格納するデータベースがないため) 。 + +```js + errMsg => { + console.log(errMsg); + return WebAssembly.instantiateStreaming(fetch(url)).then(results => { + return results.instance + }); + }); +} +``` + +## wasm モジュールをキャッシュする + +上で定義されたライブラリ関数を使用して、wasm モジュールインスタンスの取得やエクスポートされた機能を使用することが (その間にバックグラウンドでキャッシュのハンドリングをしています) 、次のパラメータで呼び出すだけのシンプルなものになります: + +- キャッシュバージョン — 詳細は上で説明されています — wasm モジュールが更新されたときや別 URL に移動したときに更新する必要があります。 +- インスタンス化したい wasm モジュールの URL。 +- インポートオブジェクト (必要ならば) + +```js +const wasmCacheVersion = 1; + +instantiateCachedURL(wasmCacheVersion, 'test.wasm').then(instance => + console.log("Instance says the answer is: " + instance.exports.answer()) +).catch(err => + console.error("Failure to instantiate: " + err) +); +``` + +ソースコードと例は GitHub の [indexeddb-cache.html](https://github.com/mdn/webassembly-examples/blob/master/other-examples/indexeddb-cache.html) ([動作例](https://mdn.github.io/webassembly-examples/other-examples/indexeddb-cache.html)) を参照してください。 + +## ブラウザのサポート + +現時点では、WebAssembly モジュールの構造化された複製をサポートしているため、この手法は Firefox と Edge で動作します。 + +Chrome は WebAssembly 構造化クローンサポートフラグの背後にサポートが実装されていますが、いくつかの懸念があるため、デフォルトではまだ有効にしていません (たとえば、[この説明を参照](https://github.com/WebAssembly/design/issues/972))。 + +Safari ではまだ実装されていません。 diff --git a/files/ja/webassembly/concepts/index.html b/files/ja/webassembly/concepts/index.html deleted file mode 100644 index 9e94a90ddbaf54..00000000000000 --- a/files/ja/webassembly/concepts/index.html +++ /dev/null @@ -1,160 +0,0 @@ ---- -title: WebAssembly の概要 -slug: WebAssembly/Concepts -tags: - - C - - C++ - - Emscripten - - JavaScript - - Web プラットフォーム - - WebAssembly - - rust -translation_of: WebAssembly/Concepts ---- -この記事では、 WebAssembly がどのように機能しているか、その目標、解決している問題、ウェブブラウザーのレンダリングエンジン内での動作などの概念について説明します。
- -WebAssembly は最近のウェブブラウザーで動作し、新たな機能と大幅なパフォーマンス向上を提供する新しい種類のコードです。基本的に直接記述ではなく、C、C++、Rust 等の低水準の言語にとって効果的なコンパイル対象となるように設計されています。
- -この機能はウェブプラットフォームにとって大きな意味を持ちます。 — ウェブ上で動作するクライアントアプリで従来は実現できなかった、ネイティブ水準の速度で複数の言語で記述されたコードをウェブ上で動作させる方法を提供します。
- -それ以上に、その利点を享受するために利用者は WebAssembly のコードをどのように作成するのか知る必要さえ有りません。 WebAssembly モジュールはウェブ (あるいは Node.js) アプリにインポートする事ができ、 WebAssembly の機能は JavaScript を経由して他の領域から利用できる状態になります。 JavaScript 製フレームワークでは大幅なパフォーマンス改善と開発中の新機能をウェブ開発者が容易に利用できるようにするために WebAssembly を用いることができます。
- -WebAssembly は W3C WebAssembly Community Group 内で策定されるオープン標準として以下を目標に定めて作成されています:
- -メモ: WebAssembly はまたウェブの領域外の JavaScript 環境での利用も行います (Non-web embeddings を参照)。
-ウェブプラットフォームは 2 つの領域からなると考える事ができます:
- -歴史的に、仮想マシンは JavaScript を読み込む事しかできませんでした。 JavaScript が今日のウェブで人々が遭遇する多くの問題を解決する上で十分にパワフルであるため私達にとって仮想マシンはとても有効でした。しかしながら私達は JavaScript をもっと厳しい状況、3D ゲーム、VR に AR、コンピュータービジョン、画像/動画編集等ネイティブの性能が要求されるような多くの領域 (これら以外の利用アイディアに関しては WebAssembly use cases を参照) において用いる際にパフォーマンス上の問題に遭遇するようになっています。
- -加えて巨大な JavaScript アプリケーションのダウンロード、構造の解析とコンパイルのコストは異常に高いものになりえます。モバイルや他のリソースが限られたプラットフォームではこのようなパフォーマンスのボトルネックの影響をずっと強く受ける事になります。
- -WebAssembly は JavaScript と異なる言語ですが、その置き換えを意図していません。その代わり、JavaScript の足りない所を補填して併用し、ウェブ開発者に双方の以下のような利益を提供する事を狙いとしています:
- -ブラウザーにおいての WebAssembly の登場によって、私たちが先述したような仮想マシンはこれから 2 つの種類の言語をロードして実行することになります — JavaScript と WebAssembly です。
- -必要に応じてこの異なったコードは互いを呼び出し合う事ができます — WebAssembly JavaScript API はエクスポートした WebAssembly のコードを普遍的に呼び出せる JavaScript 関数でラップし、WebAssembly のコードは通常の JavaScript 関数をインポートして同期的に呼び出せます。実際、WebAssembly のコードの基本単位はモジュールと呼ばれ、 WebAssembly のモジュールは ES2015 のモジュールと多くの対になる概念を持っています。
- -ブラウザー上で WebAssembly がどのように動作するかを理解するため必要となるキーコンセプトがいくつか存在します。これらのコンセプトはそれぞれが WebAssembly JavaScript API に一対一で対応しています。
- -postMessage()
を経由して ) 共有する事ができます。モジュールは ES2015 のモジュールのように import と export の宣言を行います。 JavaScript API はモジュール、メモリー、テーブルおよびインスタンスを作る機能を開発者に提供します。 WebAssembly のインスタンスが与えられれば、 JavaScript はその中で export されたオブジェクトを、一般的な JavaScript で渡せる状態にされた関数同様に、同期的に呼び出すことができます。また任意の JavaScript の関数はその関数を WebAssembly のインスタンスに import する事で WebAssembly のコードから同期的に呼び出されるようにする事もできます。
- -JavaScript は WebAssembly のコードがどのようにダウンロードされ、コンパイルされて実行されるかを完全に制御できるため、JavaScript 開発者は WebAssembly を単に効果的に高いパフォーマンスを発揮する JavaScript の機能のようにとらえることもできます。
- -将来的には、 WebAssembly モジュールは (<script type='module'>
を利用して) ES2015 モジュールのようにロード可能 となり、これは JavaScript が WebAssembly モジュールを ES2015 モジュールと同じくらい簡単に取得、コンパイル、インポートできるようになる事を意味します。
ここまで私たちは WebAssembly がウェブプラットフォームに付加する基本的な原則について話しました。つまりコードのバイナリー形式とバイナリーコードを読み込み実行する API について。ここからは実際にこれらの原則をどのように活かすのかについて話します。
- -WebAssembly のエコシステムはまだ黎明期の状態にあります。もっと多くのツール群によってこの状況が進展するのは間違いありません。現時点では主に4つの入口があります。
- -これらの選択肢について考えてみましょう。
- -WASM コードを作成するための多くのオプションのうちの 2 つは、オンラインの Wasm アセンブラまたは Emscripten です。 WASM のオンラインアセンブラには、次のようなものがあります。
- -これらは、どこから始めるべきかを把握しようとしている人にとっては素晴らしいリソースですが、 Emscripten のツールと最適化には欠けています。
- -Emscripten ツールは C/C++ ソースコードを取得し、それを .wasm モジュール、加えてモジュールを読みだして実行するために必要な JavaScript に "glue" コードとコードの結果を表示するための HTML 文章にコンパイルおよび出力します。
- - - -簡潔に言えば、このプロセスは以下のような物になります。
- -メモ: 将来的に WebAssembly に直接ウェブ API を呼ばせる事を許容する 計画があります。
-JavaScript グルーコードは多くの人が想像するほど簡単な構造をしていません。はじめに、 Emscripten は SDL、 OpenGL、 OpenAL および POSIX の一部といった主な C/C++ ライブラリを組み込みます。これらのライブラリ群はウェブ API の観点から組み込まれ、各々が WebAssembly を既存のウェブ API に接続するためにいくつかの JavaScript グルーコードを必要とします。
- -そのためグルーコードの一部は C/C++ コードによって利用されるそれぞれのライブラリの機能を組み込みます。グルーコードはまた .wasm ファイルを取得、ロード、実行するため先述した WebAssembly JavaScript API を呼び出すロジックも含んでいます。
- -生成された HTML 文章は JavaScript グルーコードのファイルを読み込んで {{htmlelement("textarea")}} に標準出力を書き出します。もしアプリケーションが OpenGL を利用している場合、その HTML はまた出力先となる {{htmlelement("canvas")}} 要素を含みます。Emscripten の出力結果を修正して必要とするウェブアプリに変換するのは非常に簡単です。
- -Emscripten に関する完全なドキュメントは emscripten.org で参照でき、このツールチェインの組み込みと自身の C/C++ アプリを wasm へとコンパイルするガイドとしては C/C++ を WebAssembly にコンパイルする が参考になります。
- -独自のコンパイラ、ツール、あるいは WebAssembly を実行時に生成する JavaScript のライブラリを作りたいとお考えですか ?
- -実際のアセンブリ言語同様、 WebAssembly バイナリー形式はテキスト表現を持っています — これらは一対一で対応しています。なんらかの WebAssemby テキスト表現バイナリー変換ツール を用いることでテキスト表現を直接記述してバイナリー形式に変換することができます。
- -この手順に関しては、 WebAssembly テキスト表現を wasm 形式に変換する の項目を参照ください。
- -Rust WebAssembly ワーキンググループの不断の仕事のおかげで、Rust コードを書いて WebAssembly にコンパイルすることも可能です。必要なツールチェーンをインストールし、サンプル Rust プログラムを WebAssembly npm パッケージにコンパイルし、ウェブアプリケーションのサンプルを使用して、 Rust から WebAssembly へのコンパイルの記事を読むことができます。
- -C や Rust を学ぶ必要なく WebAssembly を試してみたいウェブ開発者には、 AssemblyScript が最良の選択肢です。これは小さなバンドルを生成し、 C や Rust と比較するとやや遅い程度です。ドキュメントは https://assemblyscript.org/ でチェックすることができます。
- -本項目では WebAssembly が何であるか、どういった利便性が有るか、どのようにしてウェブに適用するかとどのように活用するかについて説明しました。
- -エクスポートされた WebAssembly 関数は WebAssembly 関数が JavaScript でどのように表現されるのか、この記事では、もう少し詳しく説明します。
- -エクスポートされた WebAssembly 関数は WebAssembly 関数を表現する JavaScript ラッパーです。それらを呼び出したときにバックグラウンドでいくつかの動作を行います。引数を wasm で使える型に(例えば、JavaScript の数値を Int32 に)変換し、wasm モジュール内の関数に渡し、実行し、結果を変換して JavaScript 側に戻します。
- -エクスポートされた WebAssembly 関数は次の 2 つの方法で取得できます:
- -Table.prototype.get()
を呼び出す。Instance.exports
を通してエクスポートされた関数にアクセスする。いずれにしても、同じ種類の内在する関数のラッパーを取得できます。JavaScript からみると、すべての wasm 関数は JavaScript 関数のようにみえます。しかし、これは wasm 関数オブジェクトインスタンスによってカプセル化されており、アクセスする方法は限られています。
- -物事を明らかにするために例を見ていきましょう(例は GitHub の table-set.html と 動作例、wasm の テキスト表現 を参照してください):
- -var otherTable = new WebAssembly.Table({ element: "anyfunc", initial: 2 }); - -WebAssembly.instantiateStreaming(fetch('table.wasm')) -.then(function(obj) { - var tbl = obj.instances.exports.tbl; - console.log(tbl.get(0)()); // 13 - console.log(tbl.get(1)()); // 42 - otherTable.set(0,tbl.get(0)); - otherTable.set(1,tbl.get(1)); - console.log(otherTable.get(0)()); - console.log(otherTable.get(1)()); -});- -
ここでは、{{jsxref("WebAssembly.Table")}} コンストラクタを使用して JavaScript からテーブル(otherTable
)を作成し、{{jsxref("WebAssembly.instantiateStreaming()")}} ユーティリティ関数を使用して table.wasm をページに読み込みます。
そのあと、モジュールからエクスポートされた関数を取得し、関数の参照を tbl.get()
を通して取り出し、それぞれを実行した結果をコンソールに出力します。次に、 set()
を使用して、tbl
テーブルと同じ関数への参照を otherTable
テーブルに含まれるようにします。
確認するために、otherTable
から参照を取得し直し、その結果もコンソールに出力します(同じ結果が得られます)。
前の例で、Table.prototype.get()
のそれぞれの返り値はエクスポートされた WebAssembly 関数でした。まさに私達が話していたことです。
これらは WebAssembly 関数のラッパーであるのに加えて本当の JavaScript 関数 であることに注意してください。上の例を WebAssembly をサポートするブラウザーでロードして、コンソール上で実行すると:
- -var testFunc = otherTable.get(0); -typeof testFunc;- -
結果として関数が返されます。この関数は他の JavaScript の他の 関数 と同じように扱うことができます(call()、
bind()、その他
)。testFunc.toString()
は興味深い結果を返します:
function 0() { - [native code] -}- -
これはラッパー型の性質のアイデアの多くを提供します。
- -エクスポートされた WebAssembly 関数について他の注意事項は:
- -toString()
の結果で見えている wasm モジュール内での関数のインデックスです。WebAssembly はモダンなウェブブラウザーで実行できる新しいタイプのコードです。ネイティブに近いパフォーマンスで動作するコンパクトなバイナリー形式の低レベルなアセンブリ風言語です。さらに、 C/C++ や Rust のような言語のコンパイル対象となって、それらの言語をウェブ上で実行することができます。 WebAssembly は JavaScript と並行して動作するように設計されているため、両方を連携させることができます。
- -WebAssembly はウェブプラットフォームに大きな影響を与えます — 以前ではできなかったようなウェブ上で動作するクライアントアプリケーションのために、複数の言語で記述されたコードをウェブ上でネイティブに近いスピードで実行する方法を提供します。
- -WebAssembly は JavaScript を補完、並行して動作するように設計されています — WebAssembly JavaScript API を使用して、 WebAssembly モジュールを JavaScript アプリケーションにロードし、2 つの間で機能を共有できます。これにより、WebAssembly コードの記述方法を知らなくても、 WebAssembly のパフォーマンスとパワー、JavaScript の表現力と柔軟性を同じアプリケーションで活用できます。
- -さらに、W3C WebAssembly Working Group と Community Group を介して、ウェブ標準として開発されており、主要なブラウザーベンダーも積極的に参加しています。
- -WebAssembly.Global
オブジェクトは JavaScript と1つ以上の {{jsxref("WebAssembly.Module")}} インスタンス(のインポート/エクスポート可能な値)を横断してアクセスできるグローバル変数のインスタンスを表現します。これによって、複数モジュールでの動的リンクを実現できます。WebAssembly.Module
オブジェクトにはブラウザーでコンパイルされたステートレスな WebAssembly コードが含まれており、効率的に Worker で共有したり、IndexedDB にキャッシュしたり、複数回インスタンス化したりすることができます。WebAssembly.Instance
オブジェクトはステートフルで、実行可能な Module
のインスタンスです。Instance
オブジェクトには JavaScript から WebAssembly コードを呼び出すことを許可されたエクスポートされた WebAssembly 関数が含まれます。WebAssembly.instantiate()
関数は WebAssembly コードをコンパイル、インスタンス化するための主要な API で、Module
と、その最初の Instance
を返します。WebAssembly.Memory
オブジェクトはリサイズ可能な {{jsxref("Global_objects/ArrayBuffer", "ArrayBuffer")}} で、 Instance
からアクセスされる生のバイト列を保持します。WebAssembly.Table
オブジェクトは Instance
からアクセスされる関数参照などの不透明値のリサイズ可能な型付き配列です。CompileError
オブジェクトを生成します。LinkError
オブジェクトを生成します。RuntimeError
オブジェクトを生成します。仕様書 | -状態 | -備考 | -
---|---|---|
{{SpecName('WebAssembly JS')}} | -{{Spec2('WebAssembly JS')}} | -JavaScript API の初回ドラフト定義。 | -
{{Compat("javascript.builtins.WebAssembly")}}
- -JavaScript で WebAssembly を使用するには、まずコンパイル/インスタンス化の前にモジュールをメモリにプルする必要があります。この記事では、WebAssembly バイトコードをフェッチするために使用できるさまざまなメカニズムのリファレンスと、それをコンパイル/インスタンス化して実行する方法について説明します。
- -WebAssemblyは <script type='module'>
または ES2015 の import
文とまだ統合されていないため、インポートを使用してブラウザでモジュールをフェッチする組み込みの方法はありません。
以前の {{jsxref("WebAssembly.compile")}}/{{jsxref("WebAssembly.instantiate")}} メソッドでは、生のバイトをフェッチした後 WebAssembly モジュールのバイナリを含む {{domxref("ArrayBuffer")}} を作成し、コンパイル/インスタンス化する必要があります。これは文字列(JavaScript ソースコード) をバイトの配列バッファ (WebAssembly ソースコード)で置き換えることを除いて、new Function(string)
に似ています。
新しい {{jsxref("WebAssembly.compileStreaming")}}/{{jsxref("WebAssembly.instantiateStreaming")}} メソッドは、より効率的です。ネットワークからの生のバイトストリームに対して直接アクションを実行し、 {{domxref("ArrayBuffer")}} ステップの必要性がなくなりました。
- -では、どのようにバイト列を配列バッファに読み込んでコンパイルするのでしょうか? 次の章で説明します。
- -Fetch はネットワークリソースを取得するための便利でモダンな API です。
- -wasm モジュールをフェッチする最も簡単で効率的な方法は、新しい {{jsxref("WebAssembly.instantiateStreaming()")}} メソッドを使用することです。このメソッドは最初の引数として fetch()
を呼び出すことができ、1つのステップでフェッチ、モジュールをインスタンス化し、サーバからストリームされる生のバイトコードにアクセスします。
WebAssembly.instantiateStreaming(fetch('simple.wasm'), importObject) -.then(results => { - // Do something with the results! -});- -
直接ストリームでは動作しない古い {{jsxref("WebAssembly.instantiate()")}} メソッドを使用した場合、フェッチされたバイトコードを {{domxref("ArrayBuffer")}} に変換する必要があります。次のようにです:
- -fetch('module.wasm').then(response => - response.arrayBuffer() -).then(bytes => - WebAssembly.instantiate(bytes, importObject) -).then(results => { - // コンパイルされた結果(results)で何かする! -});- -
{{jsxref("WebAssembly.instantiate()")}} 関数は2つのオーバーロードを持ちます — 1つ目 (上の例を参照)はバイトコードを受け取ってプロミスを返します。解決されたプロミスでコンパイルされたモジュールと、それをインスタンス化したものを含むオブジェクトとして受け取ります。オブジェクトの構造は以下のようになります:
- -{ - module : Module // コンパイルされた WebAssembly.Module オブジェクト, - instance : Instance // モジュールオブジェクトから生成された WebAssembly.Instance -}- -
注: 通常はインスタンスのみを気にしますが、キャッシュする場合や、postMessage()
を使用して別のワーカーやウィンドウと共有する場合や、インスタンスをさらに作成したい場合に備えて、モジュールを用意すると便利です。
注: 2番目のオーバーロードフォームは {{jsxref("WebAssembly.Module")}} オブジェクトを引数としてとり、結果としてインスタンスオブジェクトを直接含む Promise を返します。2番目のオーバーロードの例を参照してください。
-JavaScript 内で WebAssembly インスタンスが 有効になったら {{jsxref("WebAssembly.Instance/exports", "WebAssembly.Instance.exports")}} プロパティを通してエクスポートされた機能を使い始める事ができます。コードは以下のようになるでしょう:
- -WebAssembly.instantiateStreaming(fetch('myModule.wasm'), importObject) -.then(obj => { - // Call an exported function: - obj.instance.exports.exported_func(); - - // or access the buffer contents of an exported memory: - var i32 = new Uint32Array(obj.instance.exports.memory.buffer); - - // or access the elements of an exported table: - var table = obj.instance.exports.table; - console.log(table.get(0)()); -})- -
注: WebAssembly モジュールからのエクスポートの仕組みの詳細については WebAssembly JavaScript API の使用 と WebAssemblyテキストフォーマットを理解する を参照してください。
-XMLHttpRequest
は Fetch よりやや古いですが、引き続き型付き配列を取得するために適切に使用することができます。繰り返しますが、モジュール名は simple.wasm
とします:
GET
に設定し、フェッチするためのパスを宣言します。'arraybuffer'
にすることです。最終的なコードは以下のようになります:
- -request = new XMLHttpRequest(); -request.open('GET', 'simple.wasm'); -request.responseType = 'arraybuffer'; -request.send(); - -request.onload = function() { - var bytes = request.response; - WebAssembly.instantiate(bytes, importObject).then(results => { - results.instance.exports.exported_func(); - }); -};- -
注: 動作例は xhr-wasm.html を参照してください。
-