WebAssembly-クイックガイド

WebAssemblyは、Web用の新しいコンピュータープログラミング言語です。WebAssemblyコードは低レベルのバイナリ形式であり、Webと互換性があり、最新のWebブラウザーで簡単に実行できます。生成されるファイルサイズは小さく、ロードと実行が高速です。C、C ++、Rustなどの言語をバイナリ形式にコンパイルできるようになり、JavaScriptと同じようにWeb上で実行できるようになりました。

WebAssemblyの定義

WebAssemblyの公式ウェブサイトによると、 https://webassembly.org/、WebAssembly(Wasmと略記)はスタックベースの仮想マシン用のバイナリ命令フォーマットとして定義されています。Wasmは、C / C ++ / Rustなどの高級言語をコンパイルするためのポータブルターゲットとして設計されており、クライアントおよびサーバーアプリケーションのWebへのデプロイを可能にします。

Web Assemblyは開発者が作成する必要のあるものではありませんが、コードはC、C ++、Rustなどの言語で作成されており、WebAssembly(wasm)にコンパイルできます。同じコードをWebブラウザ内で実行できます。

Web Assemblyは新しい言語であり、コードは低レベルのアセンブリ言語ですが、テキスト形式の機能により、コードは読み取り可能であり、必要に応じてデバッグが可能です。

WebAssemblyの目標

WebAssemblyのオープンスタンダードは、すべての主要なブラウザの代表者とW3Cワーキンググループを含むW3Cコミュニティグループで開発されています。

WebAssemblyの主な目標は以下のとおりです-

  • FasterEfficient and Portable − WebAssemblyコードは、利用可能なハードウェアを利用して、さまざまなプラットフォームでより高速に実行することを目的としています。

  • Easy to read and debug −低レベルのアセンブリ言語であるWebAssemblyは、テキスト形式をサポートしているため、問題がないかコードをデバッグしたり、必要に応じてコードを書き直したりできます。

  • Security − WebAssemblyは、アクセス許可と同一生成元ポリシーを処理するため、Webブラウザーで安全に実行できます。

WebAssemblyの利点

WebAssemblyの利点は次のとおりです-

  • Run is Modern Browsers − WebAssemblyは、利用可能な最新のWebブラウザーで問題なく実行できます。

  • Multiple Language support− C、C ++、Rust、Goなどの言語で、コードをWebAssemblyにコンパイルし、Webブラウザーで同じように実行できるようになりました。そのため、ブラウザで実行できなかった言語でも実行できるようになります。

  • Faster, Efficient and Portable −コードのサイズが小さいため、ロードと実行が高速になります。

  • Easy to understand−開発者は、WebAssemblyでコードを記述する必要がないため、WebAssemblyコーディングを理解するのにそれほどストレスをかける必要はありません。代わりに、WebAssemblyでコードをコンパイルし、Web上で同じように実行します。

  • Easy to Debug −最終的なコードは低レベルのアセンブリ言語ですが、テキスト形式でも入手できるため、読みやすく、デバッグも簡単です。

WebAssemblyのデメリット

以下はWebAssemblyの欠点です-

  • WebAssemblyはまだ作業中であり、その将来を決定するには時期尚早です。

  • WebAssemblyは、ドキュメントオブジェクトモデル(DOM)と対話するためにJavaScriptに依存しています。

WebAssemblyはWASMとも呼ばれ、2017年に最初に導入されました。WebAssemblyの起源の背後にある大手テクノロジー企業は、Google、Apple、Microsoft、Mozilla、およびW3Cです。

話題は、WebAssemblyが実行速度が速いためにJavascriptに取って代わるということですが、そうではありません。WebAssemblyとJavascriptは、複雑な問題の解決に向けて連携することを目的としています。

WebAssemblyの必要性

これまでのところ、ブラウザ内で正常に動作できるJavascriptしかありません。javascriptを使用してブラウザで実行するのが難しい非常に重いタスクがあります。

いくつか例を挙げると、画像認識、コンピューター支援設計(CAD)アプリケーション、ライブビデオ拡張、VRおよび拡張現実、音楽アプリケーション、科学的視覚化およびシミュレーション、ゲーム、画像/ビデオ編集などがあります。

WebAssemblyは、読み込みと実行を高速化できるバイナリ命令を備えた新しい言語です。上記のタスクは、C、C ++、Rustなどの高級言語で簡単に実行できます。C、C ++、Rustにあるコードをコンパイルして、Webブラウザーで使用できる方法が必要です。WebAssemblyを使用しても同じことが達成できます。

WebAssemblyコードがブラウザ内にロードされたとき。次に、ブラウザがプロセッサが理解できるマシン形式への変換を処理します。

javascriptの場合、コードをダウンロードして解析し、マシン形式に変換する必要があります。それに多くの時間がかかり、前述のような重いタスクの場合、非常に遅くなる可能性があります。

WebAssemblyの動作

C、C ++、Rustなどの高級言語は、バイナリ形式にコンパイルされます。 .wasm およびテキスト形式 .wat

C、C ++、Rustで記述されたソースコードは次のようにコンパイルされます。 .wasmコンパイラを使用します。EmscriptenSDKを使用してC / C ++をコンパイルできます。.wasm

流れは次のとおりです-

C / C ++コードは次のようにコンパイルできます .wasmEmscriptenSDKを使用します。後で、.wasm コードをhtmlファイルのjavascriptを使用して使用すると、出力を表示できます。

WebAssemblyの重要な概念

重要な概念は以下のとおりです-

モジュール

モジュールは、ブラウザによって実行可能なマシンコードにコンパイルされるオブジェクトです。モジュールはステートレスであると言われ、WindowsとWebワーカーの間で共有できます。

記憶

WebAssemblyのメモリは、 arraybufferデータを保持します。Javascript api WebAssembly.memory()を使用してメモリを割り当てることができます。

テーブル

WebAssemblyのテーブルは型付き配列であり、WebAssemblyメモリの外部にあり、ほとんどの場合関数への参照があります。関数のメモリアドレスを格納します。

インスタンス

インスタンスは、javascriptから呼び出してブラウザ内で実行できるすべてのエクスポートされた関数を持つオブジェクトです。

WebAssemblyはwasmとも呼ばれ、Javascriptの改良版です。javascriptと同じように、またnodejsを使用してブラウザー内で実行するように設計されています。C、C ++、Rustなどの高級言語をコンパイルすると、wasm出力が発生します。

次のCプログラムを考えてみましょう-

int factorial(int n) {
   if (n == 0) 
      return 1; 
   else 
      return n * factorial(n-1); 
}

で入手可能なWasmExplorerを利用してくださいhttps://mbebenita.github.io/WasmExplorer/ 以下に示すようにコンパイルされたコードを取得するには-

階乗プログラムのWebAssemblyテキスト形式は次のとおりです-

(module 
   (table 0 anyfunc) 
   (memory $0 1) (export "memory" (memory $0)) (export "factorial" (func $factorial)) (func $factorial (; 0 ;) (param $0 i32) (result i32) (local $1 i32) 
      (local $2 i32) (block $label$0 (br_if $label$0 (i32.eqz (get_local $0) 
            )
         )
         (set_local $2 (i32.const 1) ) (loop $label$1 (set_local $2 
               (i32.mul 
                  (get_local $0) (get_local $2) 
               ) 
            ) 
            (set_local $0 (tee_local $1        (i32.add 
                  (get_local $0) (i32.const -1) ) ) ) (br_if $label$1 (get_local $1) 
            ) 
         ) 
         (return 
            (get_local $2)
         ) 
      ) 
      (i32.const 1) 
   )
)

Wat2Wasmツールを使用すると、以下に説明するように、WASMコードを表示できます。

開発者は、wasmでコードを記述したり、wasmでコードを学習したりすることは想定されていません。これは、ほとんどの場合、高級言語をコンパイルするときに生成されるためです。

スタックマシンモデル

WASMでは、すべての命令がスタックにプッシュされます。引数がポップされ、結果がスタックにプッシュバックされます。

2つの数字を追加する次のWebAssemblyテキスト形式について考えてみます-

(module
   (func $add (param $a i32) (param $b i32) (result i32) 
      get_local $a get_local $b 
      i32.add
   )
   (export "add" (func $add))
)

関数の名前は $add、2つのパラメータを取ります $a and $b。結果は32ビット型の整数です。ローカル変数はget_localを使用してアクセスされ、追加操作はi32.addを使用して実行されます。

実行中に2つの数値を加算するスタック表現は次のようになります-

step 1 −get_localの実行 $a instruction, the first parameters i.e., $aがスタックにプッシュされます。

step 2 −get_localの実行中 $b instruction, the second parameters i.e., $bがスタックにプッシュされます。

step 3− i32.addを実行すると、要素がスタックからポップされ、結果がスタックにプッシュバックされます。スタック内の最後に残る値は、関数$ addの結果です。

この章では、EmscriptenSDKをインストールしてC / C ++をコンパイルする方法を学習します。Emscriptenは、C / C ++から生成されたバイトコードを取得し、ブラウザー内で簡単に実行できるJavaScriptにコンパイルする低レベル仮想マシン(LLVM)です。

C / C ++をWebAssemblyにコンパイルするには、最初にEmscriptensdkをインストールする必要があります。

EmscriptensDKをインストールします

EmscriptensDKをインストールする手順は次のとおりです-

Step 1 − emsdkリポジトリのクローンを作成します:git clone https://github.com/emscripten-core/emsdk.git。

E:\wa>git clone https://github.com/emscripten-core/emsdk.git 
Cloning into 'emsdk'... 
remote: Enumerating objects: 14, done. 
remote: Counting objects: 100% (14/14), done. 
remote: Compressing objects: 100% (12/12), done. 
remote: Total 1823 (delta 4), reused 4 (delta 2), pack-reused 1809 receiving obje 
cts: 99% (1819/1823), 924.01 KiB | 257.00 KiB/s 
Receiving objects: 100% (1823/1823), 1.01 MiB | 257.00 KiB/s, done. 
Resolving deltas: 100% (1152/1152), done.

Step 2 −ディレクトリemsdk内に入力します。

cd emsdk

Step 3 − Windowsの場合:以下のコマンドを実行します。

emsdk install latest

For linux、このコマンドは、java、pythonなどの必要なツールをインストールするのに少し時間がかかります。以下のコードに従ってください-

./emsdk install latest

Step 4 −最新のSDKをアクティブ化するには、ターミナルで次のコマンドを実行します。

For windows、次のコマンドを実行します-

emsdk activate latest

For linux、以下のコマンドを実行します-

./emsdk activate latest

Step 5 − PATHおよびその他の環境変数をアクティブにするには、ターミナルで次のコマンドを実行します。

For windows、コマンドを実行します-

emsdk_env.bat

For linux、次のコマンドを実行します-

source ./emsdk_env.sh

emsdkのインストールが完了し、CまたはC ++コードをコンパイルできるようになりました。C / C ++のコンパイルは、次の章で行われます。

CまたはC ++コードをコンパイルするには、次のコマンドを実行します。

emcc source.c or source.cpp -s WASM=1 -o source.html

出力には、source.htmlファイル、source.js、およびsource.wasmファイルが表示されます。jsにはsource.wasmをフェッチするAPIがあり、ブラウザーでsource.htmlを押すと、出力を確認できます。

wasmファイルを取得するには、次のコマンドを使用できます。このコマンドは、source.wasmファイルのみを提供します。

emcc source.c or source.cpp -s STANDALONE_WASM

この章では、WebAssemblyを操作する際に非常に役立つ、使いやすいツールについて説明します。WebAssembly.studioツールについて学ぶことから始めましょう。

WebAssembly.studio

このツールを使用すると、C、Rust、WatからWasmなどをコンパイルできます。

まず、Empty C Project、Empty Rust Project、Empty Wat Projectをクリックして、CとRustをWASMにコンパイルします。5.5。

コードをビルドして出力を確認するためのBuild、Runがあります。ダウンロードボタンを使用すると、ダウンロードすることができます.wasmブラウザ内でのテストに使用できるファイル。このツールは、CおよびRustコードをコンパイルし、出力を確認するのに非常に役立ちます。

WebAssemblyエクスプローラー

WebAssembly Explorerを使用すると、CおよびC ++コードをコンパイルできます。リンクを参照してくださいhttps://mbebenita.github.io/WasmExplorer/詳細については。リンクをクリックすると表示される画面を以下に示します-

CおよびC ++バージョンを選択できます。CまたはC ++のソースコードはここに書かれています-

コンパイルボタンをクリックすると、WebAssemblyテキスト形式(WAT)とFirefoxx86アセンブリコードが以下のブロックに表示されます-

あなたはダウンロードすることができます .wasm ブラウザ内でテストするコード。

WASMFiddle

Wasmfiddleは、CコードをWebAssemblyにコンパイルし、出力をテストするのに役立ちます。リンクをクリックした後https://wasdk.github.io/WasmFiddle/, 次のページが表示されます-

[ビルド]をクリックしてコードをコンパイルします。Wat and Wasmをクリックすると、Wat andWasmコードをダウンロードできます。出力をテストするには、[実行]ボタンをクリックします。

WASMからWATへ

ツール wat2wasmWebAssemblyテキスト形式を入力するとwasmコードが表示されます。リンクをクリックできますhttps://webassembly.github.io/wabt/demo/wat2wasm/ デモと表示される画面は以下のとおりです。

アップロードボタンを使用して.wasmをアップロードすると、textareaにテキスト形式が表示されます。

WATからWASMへ

ツールwat2wasmは、WebAssemblyテキスト形式を入力するとwasmコードを提供します。リンクをクリックできますhttps://webassembly.github.io/wabt/demo/wat2wasm/ デモと表示される画面は以下のとおりです。

このツールは、出力もテストするのに役立つため、非常に役立ちます。WATコードを入力して、.wasmコードを確認し、コードを実行して出力を確認することもできます。

WebAssemblyは、WASMとも呼ばれ、ブラウザー内で最も効率的な方法で実行されるように開発されたバイナリ形式の低レベルコードです。WebAssemblyコードは、次の概念で構成されています-

  • Values
  • Types
  • Instructions

それでは、それらについて詳しく学びましょう。

WebAssemblyの値は、テキスト、文字列、ベクターなどの複雑なデータを格納することを目的としています。WebAssemblyは以下をサポートします-

  • Bytes
  • Integers
  • 浮動小数点
  • Names

バイト

バイトは、WebAssemblyでサポートされている最も単純な形式の値です。値は16進形式です。

For example

bとして表されるバイトは、自然数nを取ることもできます。ここで、n <256です。

byte ::= 0x00| .... |0xFF

整数

WebAssemblyでサポートされる整数は、次のとおりです。

  • i32:32ビット整数
  • i64:64ビット整数

浮動小数点

WebAssemblyでサポートされている浮動小数点数は次のとおりです-

  • f32:32ビット浮動小数点
  • f64:64ビット浮動小数点

名前

名前は文字のシーケンスであり、Unicodeで定義されたスカラー値がリンクから入手できます。 http://www.unicode.org/versions/Unicode12.1.0/ これで与えられます。

タイプ

WebAssemblyのエンティティはタイプとして分類されます。サポートされているタイプは次のとおりです-

  • 値型
  • 結果の種類
  • 関数タイプ
  • Limits
  • メモリタイプ
  • テーブルタイプ
  • グローバルタイプ
  • 外部タイプ

それらを一つずつ研究していきましょう。

値型

WebAssemblyでサポートされている値のタイプは次のとおりです-

  • i32:32ビット整数
  • i64:64ビット整数
  • f32:32ビット浮動小数点
  • f64:64ビット浮動小数点
valtype ::= i32|i64|f32|f64

結果の種類

括弧内に書き込まれた値は実行され、結果タイプ内に格納されます。結果タイプは、値で構成されるコードのブロックの実行の出力です。

resulttype::=[valtype?]

関数タイプ

関数型はパラメータのベクトルを受け取り、結果のベクトルを返します。

functype::=[vec(valtype)]--> [vec(valtype)]

制限

制限は、メモリおよびテーブルタイプにリンクされたストレージ範囲です。

limits ::= {min u32, max u32}

メモリタイプ

メモリタイプは、線形メモリとサイズ範囲を扱います。

memtype ::= limits

テーブルタイプ

テーブルタイプは、割り当てられた要素タイプによって分類されます。

tabletype ::= limits elemtype
elemtype ::= funcref

テーブルタイプは、割り当てられている最小サイズと最大サイズの制限によって異なります。

グローバルタイプ

グローバルタイプは、値を持つグローバル変数を保持します。この変数は、変更することも、同じままにすることもできます。

globaltype ::= mut valtype
mut ::= const|var

外部タイプ

外部タイプは、インポートと外部値を扱います。

externtype ::= func functype | table tabletype | mem memtype | global globaltype

指示

WebAssemblyコードは、スタックマシンモデルに従う一連の命令です。WebAssemblyはスタックマシンモデルに従うため、命令はスタックにプッシュされます。

たとえば、関数の引数値はスタックからポップされ、結果はスタックにプッシュバックされます。結局、スタックには1つの値しかなく、それが結果です。

一般的に使用される指示のいくつかは次のとおりです-

  • 数値の説明
  • 可変命令

数値の説明

数値命令は、数値に対して実行される演算です。

For example
nn, mm ::= 32|64
ibinop ::= add|sub|mul|div_sx|rem_sx|and|or|xor
irelop ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx
frelop ::= eq | ne | lt | gt | le | ge

可変命令

変数命令は、ローカル変数とグローバル変数へのアクセスに関するものです。

For example

ローカル変数にアクセスするには-

get_local $a
get_local $b

set ローカル変数-

set_local $a
set_local $b

access グローバル変数-

get_global $a
get_global $b

set グローバル変数-

set_global $a
set_global $b

この章では、WebAssemblyとJavascriptの比較について説明します。

Javascriptは言語であり、ブラウザ内で多く使用されています。現在、WebAssemblyリリースでは、ブラウザー内でWebAssemblyを使用することもできます。

WebAssemblyが誕生する理由は、javascriptを置き換えるためではなく、javascriptで処理するのが難しい特定の処理を行うためです。

For example

画像認識、CADアプリケーション、ライブビデオ拡張、VRと拡張現実、音楽アプリケーション、科学的視覚化とシミュレーション、ゲーム、画像/ビデオ編集などのタスクをjavascriptで実行することは困難です。

WebAssemblyにコンパイルできるようになったC / C ++、Rustなどの高級言語を使用すると、上記のタスクを簡単に実行できます。WebAssemblyは、ブラウザ内で簡単に実行できるバイナリコードを生成します。

これがJavascriptとWebAssemblyの間で行われた比較のリストです。

パラメーター Javascript WebAssembly

コーディング

Javascriptで簡単にコードを書くことができます。記述されたコードは人間が読める形式であり、.jsとして保存されます。ブラウザ内で使用する場合は、<script>タグを使用する必要があります。

コードはWebAssemblyでテキスト形式で記述でき、.watとして保存されます。.wat形式でコードを書くことは困難です。.watで最初から記述するのではなく、他の高級言語からコードをコンパイルすることをお勧めします。

ブラウザ内で.watファイルを実行することはできず、利用可能なコンパイラまたはオンラインツールを使用して.wasmに変換する必要があります。

実行

ブラウザ内で使用する場合にJavaScriptで記述されたコードは、ダウンロード、解析、コンパイル、および最適化する必要があります。

.wasmのWebAssemblyコードは、すでにコンパイルされており、バイナリ形式になっています。

メモリ管理

Javascriptは、変数が作成されたときにメモリを割り当て、使用されていないときにメモリが解放され、ガベージコレクションに追加されます。

WebAssemblyのメモリは、データを保持する配列バッファです。Javascript API WebAssembly.memory()を使用してメモリを割り当てることができます。

WebAssemblyメモリは配列形式で保存されます。つまり、理解と実行が容易なフラットメモリモデルです。

WebAssemblyのメモリモデルの欠点は次のとおりです。

  • 複雑な計算には時間がかかります。

  • Webassemblyは、メモリの再利用を許可しないガベージコレクションをサポートしておらず、メモリが無駄になっています。

読み込み時間とパフォーマンス

javascriptの場合、ブラウザ内で呼び出されると、javascriptファイルをダウンロードして解析する必要があります。後で、パーサーはソースコードをバイトコードに変換し、JavaScriptエンジンがブラウザーでコードを実行します。

Javascriptエンジンは非常に強力であるため、Javascriptのロード時間とパフォーマンスはWebAssemblyと比較して非常に高速です。

WebAssemblyの最も重要な目標は、JavaScriptよりも高速であることです。高級言語から生成されたWasmコードはサイズが小さいため、読み込み時間が速くなります。

ただし、GOなどの言語をwasmにコンパイルすると、小さなコードに対して大きなファイルサイズが生成されます。

WebAssemblyは、コンパイルが高速で、すべての主要なブラウザーで実行できるように設計されています。WebAssemblyは、javascriptと比較して、パフォーマンスの面でまだ多くの改善を追加する必要があります。

デバッグ

Javascriptは人間が読める形式であり、簡単にデバッグできます。ブラウザ内のJavaScriptコードにブレークポイントを追加すると、コードを簡単にデバッグできます。

WebAssemblyは、コードをテキスト形式で提供します。これは読み取り可能ですが、デバッグは非常に困難です。Firefoxでは、ブラウザ内で.wat形式のwasmコードを表示できます。

.watにブレークポイントを追加することはできません。これは、将来利用可能になる予定です。

ブラウザのサポート

Javascriptはすべてのブラウザでうまく機能します。

すべての主要なWebブラウザはWebAssemblyをサポートしています。

この章では、javascript webassembly APIを使用して、wasmコードをロードし、ブラウザーで実行する方法を理解します。

ここにいくつかの重要なAPIがあります。チュートリアル全体でwasmコードを実行するために使用します。

  • fetch()ブラウザAPI
  • WebAssembly.compile
  • WebAssembly.instance
  • WebAssembly.instantiate
  • WebAssembly.instantiateStreaming

WebAssembly javascript APIについて説明する前に、APIと出力をテストするために、次のCプログラムとwasmexplorerを使用してcプログラムから生成された.wasmコードを使用します。

Cプログラムの例は次のとおりです。

#include<stdio.h>
int square(int n) { 
   return n*n; 
}

WASMエクスプローラーを利用してwasmコードを取得します-

WASMコードをダウンロードし、それを使用してAPIをテストします。

fetch()ブラウザAPI

fetch()APIは、.wasmネットワークリソースをロードするためのものです。

<script>
   var result = fetch("findsquare.wasm");
   console.log(result);
</script>

以下に示すようにpromiseを返します-

XMLHttpRequestメソッドを使用してwasmネットワークリソースをフェッチすることもできます。

WebAssembly.compile()

APIの責任は、.wasmからフェッチされたモジュールの詳細をコンパイルすることです。

構文

構文は次のとおりです-

WebAssembly.compile(buffer);

パラメーター

Buffer − .wasmからのこのコードは、コンパイルするための入力として与える前に、型付き配列または配列バッファーに変換する必要があります。

戻り値

コンパイルされたモジュールを持つpromiseが返されます。

webAssembly.compile()を使用してコンパイルされたモジュールとして出力を提供する1つの例を見てみましょう。

<script> 
   fetch("findsquare.wasm") .then(bytes => bytes.arrayBuffer()) 
   .then(mod => {
      var compiledmod = WebAssembly.compile(mod);
      compiledmod.then(test=> {
         console.log(test); 
      })
   })
</script>

出力

console.logは、ブラウザでチェックすると、コンパイルされたモジュールの詳細を提供します-

モジュールには、インポート、エクスポート、およびcustomSectionsを持つコンストラクターオブジェクトがあります。コンパイルされたモジュールの詳細を取得するために、次のAPIを見てみましょう。

WebAssembly.instance

APIは、WebAssembly.instanceを使用して、出力を取得するためにさらに実行できるコンパイル済みモジュールの実行可能インスタンスを提供します。

構文

構文は次のとおりです-

new WebAssembly.Instance(compiled module)

戻り値

戻り値は、実行可能なエクスポート関数の配列を持つオブジェクトになります。

<script> 
   fetch("findsquare.wasm") 
      .then(bytes => bytes.arrayBuffer())
      .then(mod => WebAssembly.compile(mod)).then(module => {
         let instance = new WebAssembly.Instance(module); 
         console.log(instance); 
      })
</script>

出力

出力は、以下に示すようなエクスポート関数の配列を提供します-

コンパイルされたCコードから取得したsquare関数を確認できます。

二乗関数を実行するには、次のようにします。

<script>
   fetch("findsquare.wasm") 
   .then(bytes => bytes.arrayBuffer()) 
   .then(mod => WebAssembly.compile(mod)) 
   .then(module => { 
      let instance = new WebAssembly.Instance(module);
      console.log(instance.exports.square(15));
   })
</script>

出力は-になります

225

WebAssembly.instantiate

このAPIは、モジュールのコンパイルとインスタンス化を一緒に処理します。

構文

構文は次のとおりです-

WebAssembly.instantiate(arraybuffer, importObject)

パラメーター

arraybuffer − .wasmのコードは、インスタンス化するための入力として指定する前に、型付き配列またはarraybufferに変換する必要があります。

importObject−インポートオブジェクトには、モジュール内で使用されるメモリ、インポートされた関数の詳細が含まれている必要があります。共有するものがない場合は、空のモジュールオブジェクトにすることができます。

戻り値

モジュールとインスタンスの詳細を含むpromiseが返されます。

<script type="text/javascript">
   const importObj = {
      module: {}
   };
   fetch("findsquare.wasm")
      .then(bytes => bytes.arrayBuffer())
      .then(module => WebAssembly.instantiate(module, importObj)) 
      .then(finalcode => { 
         console.log(finalcode); console.log(finalcode.instance.exports.square(25)); 
      }); 
</script>

出力

コードを実行すると、以下の出力が得られます。

WebAssembly.instantiateStreaming

このAPIは、指定された.wasmコードからWebAssemblyモジュールをコンパイルおよびインスタンス化する処理を行います。

構文

構文は次のとおりです-

WebAssembly.instantiateStreaming(wasmcode, importObject);

パラメーター

wasmcode −wasmコードを提供してpromiseを返すfetchまたはその他のAPIからの応答。

importObject−インポートオブジェクトには、モジュール内で使用されるメモリ、インポートされた関数の詳細が含まれている必要があります。共有するものがない場合は、空のモジュールオブジェクトにすることができます。

戻り値

モジュールとインスタンスの詳細を含むpromiseが返されます。

例を以下に説明します-

<script type="text/javascript">     
   const importObj = { 
      module: {} 
   };
   WebAssembly.instantiateStreaming(fetch("findsquare.wasm"), importObj).then(obj => {
      console.log(obj); 
   }); 
</script>

ブラウザでテストすると、エラーが表示されます-

サーバー側で機能させるには、mimeタイプのapplication / wasmを追加するか、WebAssembly.instantiate(arraybuffer、importObject)を使用する必要があります。

WebAssemblyのサポートは、Chrome、Firefoxなどの現在利用可能なすべての最新ブラウザーに追加されています。Firefoxバージョン54以降では、wasmコードをデバッグするための特別な機能が提供されます。

これを行うには、wasmを呼び出すFirefoxブラウザー内でコードを実行します。たとえば、数値の2乗を見つける次のCコードについて考えてみます。

Cプログラムの例は次のとおりです。

#include<stdio.h>
int square(int n) {
   return n*n;
}

WASMエクスプローラーを使用してwasmコードを取得します-

WASMコードをダウンロードし、それを使用してブラウザーで出力を確認します。

wasmをロードするhtmlファイルは次のとおりです-

!doctype html> 
<html>
   <head>
      <meta charset="utf-8"> 
      <title>WebAssembly Square function</title> 
      <style> 
         div { 
            font-size : 30px; text-align : center; color:orange; 
         } 
      </style> 
   </head> 
   <body> 
      <div id="textcontent"></div> 
      <script> 
         let square; 
         fetch("findsquare.wasm").then(bytes => bytes.arrayBuffer()) 
            .then(mod => WebAssembly.compile(mod)) 
            .then(module => {return new WebAssembly.Instance(module) }) 
            .then(instance => {  
            square = instance.exports.square(13);
            console.log("The square of 13 = " +square);           
            document.getElementById("textcontent").innerHTML = "The square of 13 = " +square; 
         }); 
      </script> 
   </body> 
</html>

Firefoxブラウザーを開き、上記のhtmlファイルをロードして、デバッガーツールを開きます。

デバッガツールにwasm://エントリが表示されます。wasm://をクリックすると、上記のように.wat形式に変換されたwasmコードが表示されます。

エクスポートされた関数のコードを確認し、問題が発生した場合はコードをデバッグできます。Firefoxはブレークポイントを追加することも意図しているため、コードをデバッグして実行フローを確認できます。

この章では、Cで簡単なプログラムを作成し、それを.wasmに変換し、ブラウザーで同じように実行して、「HelloWorld」というテキストを取得します。

Cプログラムを.wasmに変換し、.htmlファイル内の.wasmを利用するwasmエクスプローラーツールを利用します。

で利用可能なWasmエクスプローラーツール https://mbebenita.github.io/WasmExplorer/ looks as follows −

使用するCコードは次のとおりです。

#include <stdio.h>
char *c_hello() {
   return "Hello World"; 
}

以下に示すように、wasmexplorerの最初のブロックをCコードで更新します-

以下に示すように、COMPILEボタンをクリックして、WASM、WAT、およびFirefox x86Webアセンブリにコンパイルします。

ダウンロードを使用して.wasmファイルを取得し、名前を付けて保存します firstprog.wasm

以下に示すように、firstprog.htmlという.htmlファイルを作成します-

<!doctype html>
<html>
   <head>
      <meta charset="utf-8"> 
      <title>WebAssembly Hello World</title> 
   </head> 
   <body>
      <div id="textcontent"></div>     
      <script type="text/javascript"> 
         //Your code from webassembly here
      </script> 
   </body>
</html>

ここで、firstprog.wasmを使用して、C関数c_hello()からhelloworldを読み取ります。

ステップ1

fetch()apiを使用して、firstprog.wasmコードを読み取ります。

ステップ2

.wasmコードは、を使用してarraybufferに変換する必要があります ArrayBuffer。ArrayBufferオブジェクトは、固定長のバイナリデータバッファを返します。

これまでのコードは次のようになります-

<script type="text/javascript"> 
   fetch("firstprog.wasm") .then(bytes => bytes.arrayBuffer()) 
</script>

ステップ3

ArrayBufferからのバイトは、を使用してモジュールにコンパイルする必要があります WebAssembly.compile(buffer) 関数。

コードは次のようになります-

<script type="text/javascript">
   fetch("firstprog.wasm")
   .then(bytes => bytes.arrayBuffer())
   .then(mod => WebAssembly.compile(mod))
</script>

ステップ4

モジュールを取得するには、以下に示すようにwebassembly.instanceコンストラクターを呼び出す必要があります-

<script type="text/javascript">     
   fetch("firstprog.wasm") 
   .then(bytes => bytes.arrayBuffer())
   .then(mod => WebAssembly.compile(mod))
   .then(module => {return new WebAssembly.Instance(module) }) 
</script>

ステップ5

インスタンスをコンソールして、ブラウザで詳細を確認しましょう。

<script type="text/javascript"> 
   fetch("firstprog.wasm") .then(bytes => bytes.arrayBuffer()) 
   .then(mod => WebAssembly.compile(mod)) .then(module => {
      return new WebAssembly.Instance(module) 
   }) 
   .then(instance => {
      console.log(instance);
   }); 
</script>

console.logの詳細を以下に示します-

関数c_hello()から文字列「HelloWorld」を取得するには、javascriptにコードを追加する必要があります。

まず、以下に示すようにメモリバッファの詳細を取得します-

let buffer = instance.exports.memory.buffer;;

バッファ値から値を読み取ることができるように、バッファ値を型付き配列に変換する必要があります。バッファーには、文字列HelloWorldが含まれています。

型付きに変換するには、以下に示すようにコンストラクターUint8Arrayを呼び出します-

let buffer = new Uint8Array(instance.exports.memory.buffer);

これで、forループでバッファーから値を読み取ることができます。

以下に示すように作成した関数を呼び出して、バッファーを読み取るための開始点を取得しましょう。

let test = instance.exports.c_hello();

これで、テスト変数に文字列を読み取るための開始点があります。WebAssemblyには文字列値用のものはなく、すべてが整数として格納されます。

したがって、バッファから値を読み取ると、それらは整数値になり、JavaScriptのfromCharCode()を使用して文字列に変換する必要があります。

コードは次のとおりです-

let mytext = ""; 
for (let i=test; buffer[i]; i++){ 
   mytext += String.fromCharCode(buffer[i]);
}

これで、mytextをコンソールすると、「HelloWorld」という文字列が表示されます。

完全なコードは次のとおりです-

<!doctype html> 
<html> 
   <head> 
      <meta charset="utf-8"> 
      <title>WebAssembly Add Function</title>
      <style>
         div { 
            font-size : 30px; text-align : center; color:orange; 
         } 
      </style>
   </head>
   <body>
      <div id="textcontent"></div>
      <script> 
         fetch("firstprog.wasm")
         .then(bytes => bytes.arrayBuffer())
         .then(mod => WebAssembly.compile(mod))
         .then(module => {return new WebAssembly.Instance(module)})
         .then(instance => {   
            console.log(instance); 
            let buffer = new Uint8Array(instance.exports.memory.buffer); 
            let test = instance.exports.c_hello(); 
            let mytext = ""; 
            for (let i=test; buffer[i]; i++) {
               mytext += String.fromCharCode(buffer[i]);
            }
            console.log(mytext); document.getElementById("textcontent").innerHTML = mytext; 
         });
      </script>
   </body>
</html>

divを追加し、コンテンツをdivに追加したので、文字列がブラウザに表示されます。

出力

出力は以下に記載されています-

c / c ++コードから.wasmファイルを取得する方法を見てきました。この章では、wasmをWebAssemblyモジュールに変換し、ブラウザーで同じように実行します。

以下に示すように、C ++階乗コードを使用しましょう-

int fact(int n) {
   if ((n==0)||(n==1))
      return 1;
   else
      return n*fact(n-1);
}

で利用可能なWasmExplorerを開きます。 https://mbebenita.github.io/WasmExplorer/ as shown below −

最初の列にはC ++階乗関数があり、2番目の列にはWebAssemblyテキスト形式があり、最後の列にはx86アセンブリコードがあります。

WebAssemblyテキスト形式-

(module
   (table 0 anyfunc)
   (memory $0 1)
   (export "memory" (memory $0)) (export "_Z4facti" (func $_Z4facti))
   (func $_Z4facti (; 0 ;) (param $0 i32) (result i32)
      (local $1 i32) (set_local $1
         (i32.const 1)
      )
      (block $label$0
         (br_if $label$0
            (i32.eq
               (i32.or
                  (get_local $0) (i32.const 1) ) (i32.const 1) ) ) (set_local $1
            (i32.const 1)
         )
         (loop $label$1
            (set_local $1 (i32.mul (get_local $0)
                  (get_local $1) ) ) (br_if $label$1 (i32.ne (i32.or (tee_local $0
                        (i32.add
                           (get_local $0) (i32.const -1) ) ) (i32.const 1) ) (i32.const 1) ) ) ) ) (get_local $1)
   )
)

C ++関数 fact_Z4facti」をWebAssemblyテキスト形式で。

ダウンロードボタンをクリックしてwasmコードをダウンロードし、ファイルをfactorial.wasmとして保存します。

ここで、.wasmコードをモジュールに変換するには、次のことを行う必要があります。

ステップ1

を使用して.wasmをarraybufferに変換します ArrayBuffer. ArrayBufferオブジェクトは、固定長のバイナリデータバッファを返します。

ステップ2

ArrayBufferからのバイトは、を使用してモジュールにコンパイルする必要があります WebAssembly.compile(buffer) 関数。

ザ・ WebAssembly.compile() 関数は、指定されたバイトからWebAssembly.Moduleをコンパイルして返します。

これは、ステップ1と2で説明したJavascriptコードです。

<script type="text/javascript">
   let factorial;
   fetch("factorial.wasm")
      .then(bytes => bytes.arrayBuffer())
      .then(mod => WebAssembly.compile(mod))
      .then(module => {return new WebAssembly.Instance(module) })
      .then(instance => {
      
      factorial = instance.exports._Z4facti;
      console.log('Test the output in Brower Console by using factorial(n)');
   });
</script>

コードの説明

  • javascriptブラウザAPIフェッチは、factorial.wasmのコンテンツを取得するために使用されます。

  • 内容は、arrayBuffer()を使用してバイトに変換されます。

  • モジュールは、WebAssembly.compile(mod)を呼び出すことによってバイトから作成されます。

  • モジュールのインスタンスは、newを使用して作成されます

    WebAssembly.Instance(module)

  • 階乗関数export_Z4factiは、WebAssembly.Module.exports()を使用して変数階乗に割り当てられます。

ここに、javascriptコードと一緒にmodule.htmlがあります-

module.html

<!doctype html>
<html>
   <head>
      <meta charset="utf-8">
      <title>WebAssembly Module</title>
   </head>
   <body>
      <script>
      let factorial;
      fetch("factorial.wasm")
      .then(bytes => bytes.arrayBuffer())
      .then(mod => WebAssembly.compile(mod))
      .then(module => {return new WebAssembly.Instance(module) })
      .then(instance => {
         factorial = instance.exports._Z4facti;
         console.log('Test the output in Browser Console by using factorial(n)');
      });
      </script>
   </body>
</html>

出力

ブラウザでmodule.htmlを実行して、出力を確認します-

この章では、.wasm出力を検証するwebassembly.validate()関数について説明します。.wasmは、C、C ++、またはrustコードをコンパイルするときに使用できます。

次のツールを使用して、wasmコードを取得できます。

  • Wasm Fiddlerは、次のURLで入手できます。 https://wasdk.github.io/WasmFiddle/

  • WebAssembly Explorerは、次のURLで入手できます。 https://mbebenita.github.io/WasmExplorer/.

構文

構文は次のとおりです-

WebAssembly.validate(bufferSource);

パラメーター

bufferSource− bufferSourceには、C、C ++、またはRustプログラムのいずれかからのバイナリコードがあります。typedarrayまたはArrayBufferの形式です。

戻り値

この関数は、.wasmコードが有効な場合はtrueを返し、そうでない場合はfalseを返します。

一例を見てみましょう。Wasmフィドラーに移動します。https://wasdk.github.io/WasmFiddle/, 選択したCコードを入力し、wasmコードを入力します。

赤でマークされたブロックはCコードです。中央の[ビルド]ボタンをクリックして、コードを実行します。

Wasmボタンをクリックして、.wasmコードをダウンロードします。.wasmを最後に保存し、検証に同じものを使用します。

例えば: validate.html

<!doctype html>
<html>
   <head> 
      <meta charset="utf-8">
      <title>Testing WASM validate()</title>
   </head>
   <body>
      <script> 
         fetch('program.wasm').then(res => res.arrayBuffer() ).then(function(testbytes) {
         var valid = WebAssembly.validate(testbytes); 
            if (valid) {
               console.log("Valid Wasm Bytes!"); 
            } else {
               console.log("Invalid Wasm Code!"); 
            }
         }); 
      </script> 
   </body>
</html>

上記の.htmlファイルをダウンロード.wasmファイルと一緒にwampサーバーでホストしました。これは、ブラウザでテストしたときの出力です。

出力

出力は以下のとおりです-

WebAssemblyには、WASMと呼ばれるバイナリ形式のコードがあります。WebAssemblyでテキスト形式を取得することもできます。これはWAT(WebAssemblyテキスト形式)と呼ばれます。開発者は、WebAssemblyでコードを作成する必要はありません。代わりに、C、C ++、Rustなどの高級言語をWebAssemblyにコンパイルする必要があります。

WATコード

WATコードを段階的に記述しましょう。

Step 1 − WATの開始点は、モジュールを宣言することです。

(module)

Step 2 −ここで、関数の形でいくつかの機能を追加しましょう。

関数は次のように宣言されます-

(func <parameters/result> <local variables> <function body>)

関数はfuncキーワードで始まり、その後にパラメーターまたは結果が続きます。

パラメータ/結果

結果としてのパラメータと戻り値。

パラメータは、wasm −でサポートされている次のタイプを持つことができます。

  • i32:32ビット整数
  • i64:64ビット整数
  • f32:32ビットフロート
  • f64:64ビット浮動小数点

関数のパラメータは次のように記述されます-

  • (param i32)
  • (param i64)
  • (param f32)
  • (param f64)

結果は次のようになります-

  • (結果i32)
  • (結果i64)
  • (結果f32)
  • (結果f64)

パラメータと戻り値を持つ関数は次のように定義されます-

(func (param i32) (param i32) (result i64) <function body>)

ローカル変数

ローカル変数は、関数で必要な変数です。関数のローカル値は次のように定義されます-

(func (param i32) (param i32) (local i32) (result i64) <function body>)

機能本体

関数本体は、実行されるロジックです。最終的なプログラムは次のようになります-

(module (func (param i32) (param i32) (local i32) (result i64) <function body>) )

Step 3 −パラメータとローカル変数を読み取って設定します。

パラメータとローカル変数を読み取るには、次を利用します get_local そして set_local コマンド。

Example

(module 
   (func (param i32) (param i32) (local i32) (result i64) get_local 0 
      get_local 1 
      get_local 2 
   ) 
)

関数シグネチャに従って、

  • get_local 0 与える param i32

  • get_local 1 次のパラメータを与えます param i32

  • get_local 2 あげる local value i32

0、1、2などの数値を使用してパラメーターとローカルを参照する代わりに、パラメーターの前に名前を使用して、名前の前にドル記号を付けることもできます。

次の例は、パラメータとローカルで名前を使用する方法を示しています。

Example

(module 
   (func 
      (param $a i32) (param $b i32) 
      (local $c i32) (result i64) get_local $a get_local $b get_local $c 
   ) 
)

Step 4 −関数本体と実行の命令。

wasmでの実行は、スタック戦略に従います。実行された命令は、スタック上で1つずつ送信されます。たとえば、命令get_local $ aは値をプッシュし、スタックで読み取ります。

のような命令 i32.add これにより、スタックから要素がポップされます。

(func (param $a i32) (param $b i32) get_local $a 
   get_local $b 
   i32.add
)

の指示 i32.add です ($a+$b)。i32.addの最終値はスタックにプッシュされ、結果に割り当てられます。

関数シグニチャに結果が宣言されている場合、実行の最後にスタックに1つの値が存在する必要があります。結果パラメータがない場合、スタックは最後に空である必要があります。

したがって、関数本体を含む最終的なコードは次のようになります。

(module 
   (func (param $a i32) (param $b i32) (result i32) get_local $a
      get_local $b 
      i32.add
   )
)

Step 5 −関数を呼び出します。

関数本体を含む最終的なコードは、手順4に示すとおりです。次に、関数を呼び出すために、関数をエクスポートする必要があります。

関数をエクスポートするには、0、1などのインデックス値を使用して実行できますが、名前を付けることもできます。名前の前には$が付き、funcキーワードの後に​​追加されます。

Example

(module 
   (func $add (param $a i32) (param $b i32) (result i32) get_local $a 
      get_local $b i32.add
   ) 
)

以下に示すように、exportキーワードを使用して、関数$ addをエクスポートする必要があります。

(module 
   (func $add (param $a i32) 
      (param $b i32) (result i32) get_local $a get_local $b i32.add ) (export "add" (func $add))
)

上記のコードをブラウザでテストするには、バイナリ形式(.wasm)に変換する必要があります。変換する方法を示す次の章を参照してください.WAT to .WASM.

前の章では、でコードを書く方法を見てきました .watつまり、WebAssemblyテキスト形式です。WebAssemblyテキスト形式はブラウザ内で直接機能しないため、ブラウザ内で機能するようにバイナリ形式、つまりWASMに変換する必要があります。

WATからWASMへ

.WATを.WASMに変換しましょう。

使用するコードは次のとおりです-

(module 
   (func $add (param $a i32) (param $b i32) (result i32) get_local $a 
      get_local $b i32.add ) (export "add" (func $add)) 
)

さあ、 WebAssembly Studio、で入手可能です https://webassembly.studio/。

リンクをクリックすると、次のように表示されます。

Empty Watプロジェクトをクリックし、下部にある[作成]ボタンをクリックします。

以下に示すように、空のプロジェクトに移動します-

main.watをクリックし、既存のコードを自分のコードに置き換えて、[保存]ボタンをクリックします。

保存したら、ビルドをクリックして.wasm −に変換します

ビルドが成功すると、以下に示すように.wasmファイルが作成されます。

main.wasmファイルを下に移動し、それを.htmlファイル内で使用して、以下に示すような出力を確認します。

例- add.html

<!doctype html>
<html>
   <head>
      <meta charset="utf-8">
      <title>WebAssembly Add Function</title>
   </head>
   <body>
      <script> 
         let sum; 
         fetch("main.wasm")
            .then(bytes => bytes.arrayBuffer()) 
            .then(mod => WebAssembly.compile(mod)) .then(module => {
            
            return new WebAssembly.Instance(module) 
         })
         .then(instance => {
            sum = instance.exports.add(10,40); 
            console.log("The sum of 10 and 40 = " +sum); 
         }); 
      </script>
   </body>
</html>

関数addは、コードに示されているようにエクスポートされます。渡されるパラメータは2つの整数値10と40であり、その合計を返します。

出力

出力はブラウザに表示されます。

ダイナミックリンクは、実行時に2つ以上のモジュールがリンクされるプロセスです。

ダイナミックリンクがどのように機能するかを示すために、Cプログラムを使用し、Ecmascriptsdkを使用してwasmにコンパイルします。

だからここにあります-

test1.c

int test1(){ 
   return 100; 
}

test2.c

int test2(){ 
   return 200; 
}

main.c

#include <stdio.h>

int test1(); 
int test2();
int main() { 
   int result = test1() + test2(); 
   return result; 
}

main.cコードでは、test1.cおよびtest2.c内で定義されているtest1()およびtest2()を利用します。WebAssemblyでこれらのモジュールをリンクする方法を確認しましょう。

上記のコードをコンパイルするコマンドは次のとおりです。コマンドに示されているように、ダイナミックリンクにSIDE_MODULE = 1を使用します。

emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm

WasmtoWatの使用。 https://webassembly.github.io/wabt/demo/wasm2wat/、maintest.wasmのWebAssemblyテキスト形式を取得します。

(module 
   (type $t0 (func (result i32))) (type $t1 (func)) 
   (type $t2 (func (param i32))) (type $t3 (func (param i32 i32) (result i32))) 
   (import "env" "stackSave" (func $env.stackSave (type $t0))) 
   (import "env" "stackRestore" (func $env.stackRestore (type $t2))) 
   (import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32)) 
   (import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref)) 
   (func $f2 (type $t1) 
      (call $__wasm_apply_relocs) ) (func $__wasm_apply_relocs (export "__wasm_apply_relocs") (type $t1)) (func $test1 (export "test1") (type $t0) (result i32) (local $l0 i32) 
      (local.set $l0 (i32.const 100) ) (return (local.get $l0)
      )
   )
   (func $test2 (export "test2") (type $t0) (result i32) 
      (local $l0 i32) (local.set $l0 
         (i32.const 200)) 
      (return 
         (local.get $l0) ) ) (func $__original_main 
      (export "__original_main") 
      (type $t0) (result i32) (local $l0 i32) 
      (local $l1 i32) (local $l2 i32) 
      (local $l3 i32) (local $l4 i32) 
      (local $l5 i32) (local $l6 i32) 
      (local $l7 i32) (local $l8 i32) 
      (local $l9 i32) (local.set $l0(call $env.stackSave)) (local.set $l1 (i32.const 16))
      (local.set $l2 (i32.sub (local.get $l0) (local.get $l1))) (call $env.stackRestore (local.get $l2) ) (local.set $l3(i32.const 0)) 
      (i32.store offset=12 (local.get $l2) (local.get $l3)) 
      (local.set $l4 (call $test1)) 
      (local.set $l5 (call $test2)) 
      (local.set $l6 (i32.add (local.get $l4) (local.get $l5))) (i32.store offset=8 (local.get $l2) (local.get $l6)) (local.set $l7 (i32.load offset=8 (local.get $l2))) (local.set $l8 (i32.const 16)) 
      (local.set $l9 (i32.add (local.get $l2) (local.get $l8))) (call $env.stackRestore (local.get $l9)) (return(local.get $l7))
   )
   (func $main (export "main") (type $t3) 
      (param $p0 i32) (param $p1 i32) 
      (result i32) 
      (local $l2 i32) (local.set $l2 
      (call $__original_main)) (return (local.get $l2))
   ) 
   (func $__post_instantiate (export "__post_instantiate") (type $t1) (call $f2)) (global $__dso_handle (export "__dso_handle") i32 (i32.const 0))
)

WebAssemblyテキスト形式には、以下に示すように定義されたいくつかのインポートがあります-

(import "env" "stackSave" (func $env.stackSave (type $t0)))       
(import "env" "stackRestore" (func $env.stackRestore (type $t2))) 
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32)) 
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))

これは、emcc(emscripten sdk)によるコードのコンパイル中に追加され、WebAssemblyでのメモリ管理を処理します。

インポートとエクスポートの操作

出力を確認するには、.watコードで確認できるインポートを定義する必要があります-

(import "env" "stackSave" (func $env.stackSave (type $t0)))       
(import "env" "stackRestore" (func $env.stackRestore (type $t2))) 
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32)) 
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))

上記の用語は次のように説明されます-

  • env.stackSave − emscriptenでコンパイルされたコードによって提供される機能であるスタック管理に使用されます。

  • env.stackRestore − emscriptenでコンパイルされたコードによって提供される機能であるスタック管理に使用されます。

  • env.__memory_base−これは不変のi32グローバルオフセットです。つまり、env.memoryで使用され、wasmモジュール用に予約されています。モジュールは、データセグメントのイニシャライザでこのグローバルを使用できるため、正しいアドレスにロードされます。

  • env.__table_base−これは不変のi32グローバルオフセットです。つまり、env.tableで使用され、wasmモジュール用に予約されています。モジュールは、テーブル要素セグメントの初期化子でこのグローバルを使用できるため、正しいオフセットでロードされます。

  • env.memory −これには、wasmモジュール間で共有する必要のあるメモリの詳細が含まれます。

  • env.table −これには、wasmモジュール間で共有する必要のあるテーブルの詳細が含まれます。

インポートは、JavaScriptで次のように定義する必要があります-

var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536}); 
const importObj = { 
   env: {
      stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => {
         throw new Error('overflow'); 
      }, 
      table: new WebAssembly.Table({ 
         initial: 0, maximum: 65536, element: 'anyfunc' 
      }), __table_base: 0,
      memory: wasmMemory, __memory_base: 256 
   } 
};

以下は、WebAssembly.instantiate内のimportObjを利用するJavaScriptコードです。

<!DOCTYPE html> 
<html>
   <head>
      <meta charset="UTF-8">
   </head>
   <body>
      <script>
         var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536}); 
         const importObj = {
            env: {
               stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => {
                  throw new Error('overflow'); 
               }, 
               table: new WebAssembly.Table({ 
                  initial: 0, maximum: 65536, element: 'anyfunc' 
               }), __table_base: 0,
               memory: wasmMemory, __memory_base: 256 
            } 
         };
         fetch("maintest.wasm") .then(bytes => bytes.arrayBuffer()) .then(
            module => WebAssembly.instantiate(module, importObj)
         )
         .then(finalcode => {        
            console.log(finalcode);     
            console.log(WebAssembly.Module.imports(finalcode.module)); 
            console.log(finalcode.instance.exports.test1());    
            console.log(finalcode.instance.exports.test2());   
            console.log(finalcode.instance.exports.main()); 
         });
      </script>
   </body>
</html>

出力

出力は次のとおりです-

WebAssembly.orgの公式ウェブサイトによると、次のURLで入手できます。 https://webassembly.org/docs/security/ セキュリティの観点からのWebAssemblyの主な目標は次のとおりです-

WebAssemblyのセキュリティモデルには2つの重要な目標があります-

  • バグのあるモジュールや悪意のあるモジュールからユーザーを保護し、

  • (1)の制約内で、安全なアプリケーションを開発するための有用なプリミティブと緩和策を開発者に提供します。

コンパイルされたコード、つまりC / C ++ / RustのWASMはブラウザ内で直接実行されず、JavascriptAPIを利用します。WASMコードはサンドボックス化されます。つまり、Javascript APIラッパーを介して実行され、ブラウザーはAPIを使用してWASMと通信します。

これは、ブラウザ内で.wasmファイルを使用する例です。

例- C Program

#include<stdio.h> 
int square(int n) { 
   return n*n; 
}

WASMエクスプローラーを使用してwasmコードを取得します-

WASMコードをダウンロードし、それを使用してAPIをテストします。

<script type="text/javascript"> 
   const importObj = {
      module: {}
   }; 
   fetch("findsquare.wasm")      
      .then(bytes => bytes.arrayBuffer())          
      .then(module => WebAssembly.instantiate(module,importObj))                 
      .then(finalcode => {
         
      console.log(finalcode); console.log(finalcode.instance.exports.square(25)); 
   }); 
</script>

出力

次の出力が得られます-

エクスポートオブジェクトには、呼び出される関数への参照があります。関数squareを呼び出すには、次のようにする必要があります-

console.log(finalcode.instance.exports.square(25));

WASMコンパイル済みコードの問題

以下はWASMコンパイル済みコードの問題です-

  • コードをwasmにコンパイルする際に、悪意のあるコードが挿入されているかどうかを確認することは困難です。現在、コードを検証するために利用できるツールはありません。

  • Wasmは分析が難しく、バグのある/悪意のあるコードはブラウザ内で簡単に実行できます。

この章では、単純なCプログラムをJavaScriptにコンパイルし、ブラウザーで実行します。

例- C Program

#include<stdio.h> 
int square(int n) { 
   return n*n; 
}

wa /フォルダーにemsdkをインストールしました。同じフォルダーに別のフォルダーcprog /を作成し、上記のコードをsquare.cとして保存します。

前の章ですでにemsdkをインストールしました。ここでは、emsdkを使用して上記のcコードをコンパイルします。

以下に示すように、コマンドプロンプトでtest.cをコンパイルします-

emcc square.c -s STANDALONE_WASM –o findsquare.wasm

emccコマンドは、コードのコンパイルを処理し、.wasmコードを提供します。.wasmファイルのみを提供するSTANDALONE_WASMオプションを使用しました。

例- findsquare.html

<!doctype html> 
<html>
   <head>
      <meta charset="utf-8">
      <title>WebAssembly Square function</title>
      <style>
         div { 
            font-size : 30px; text-align : center; color:orange; 
         } 
      </style>
   </head> 
   <body>
      <div id="textcontent"></div>
      <script> 
      let square; fetch("findsquare.wasm").then(bytes => bytes.arrayBuffer()) 
      .then(mod => WebAssembly.compile(mod)) .then(module => {
         return new WebAssembly.Instance(module) 
      }) 
      .then(instance => {
         square = instance.exports.square(13); 
         console.log("The square of 13 = " +square);         
         document.getElementById("textcontent").innerHTML = "The square of 13 = " +square; 
      }); 
      </script>
   </body>
</html>

出力

出力は以下のとおりです-

この章では、単純なC ++プログラムをJavaScriptにコンパイルし、ブラウザーで同じものを実行します。

C ++プログラム-指定された数を逆にします。

#include <iostream> 
int reversenumber(int n) { 
   int reverse=0, rem; 
   while(n!=0) { 
      rem=n%10; reverse=reverse*10+rem; n/=10; 
   } 
   return reverse; 
}

wa /フォルダーにemsdkをインストールしました。同じフォルダーに別のフォルダーcprog /を作成し、上記のコードをreverse.cppとして保存します。

前の章ですでにemsdkをインストールしました。ここでは、emsdkを使用して上記のcコードをコンパイルします。

以下に示すように、コマンドプロンプトでtest.cをコンパイルします-

emcc reverse.cpp -s STANDALONE_WASM –o reverse.wasm

emccコマンドは、コードのコンパイルを処理し、.wasmコードを提供します。

例- reversenumber.html

<!doctype html> 
<html>
   <head> 
      <meta charset="utf-8">
      <title>WebAssembly Reverse Number</title>
      <style>
         div { 
            font-size : 30px; text-align : center; color:orange; 
         } 
      </style>
   </head>
   <body>
      <div id="textcontent"></div>
      <script> 
         let reverse; 
         fetch("reverse.wasm")  
            .then(bytes => bytes.arrayBuffer()) 
            .then(mod => WebAssembly.compile(mod)) 
            .then(module => {return new WebAssembly.Instance(module) })
            .then(instance => { 
            
            console.log(instance); 
            reverse = instance.exports._Z13reversenumberi(1439898); 
            console.log("The reverse of 1439898 = " +reverse); 
            document.getElementById("textcontent")
               .innerHTML = "The reverse of 1439898 = " +reverse; 
         }); 
      </script>
   </body>
</html>

出力

出力は次のとおりです-

RUSTコンパイルコードを取得するには、WebAssembly.studioツールを使用します。

移動WebAssembly.studioに行く時に利用可能ですhttps://webassembly.studio/ 以下のような画面が表示されます−

Empty RustProjectをクリックします。完了すると、src /フォルダーに3つのファイルが作成されます-

main.rsファイルを開き、選択したコードを変更します。

私は2つの与えられた数を追加する次の関数を追加しています-

fn add_ints(lhs: i32, rhs: i32) -> i32 {
   lhs+rhs
}

main.rsで利用可能なコードは次のとおりです-

#[no_mangle]
pub extern "C" fn add_one(x: i32) -> i32 {
   x + 1
}

以下に示すように、fnadd_oneを自分のものに置き換えます-

#[no_mangle]
pub extern "C" fn add_ints(lhs: i32, rhs: i32) -> i32 {
   lhs+rhs
}

main.jsで、関数名をadd_oneからadd_intsに変更します

fetch('../out/main.wasm').then(
   response =>
   response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes)).then(results => {
   instance = results.instance;
   document.getElementById("container").textContent = instance.exports.add_one(41);
}).catch(console.error);

instance.exports.add_oneをinstance.exports.add_ints(100,100)に置き換えます

fetch('../out/main.wasm').then(
   response =>
   response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes)).then(results => {
   instance = results.instance;
   document.getElementById("container").textContent = instance.exports.add_ints(100,100)
}).catch(console.error);

webassembly.studio UIで利用可能なビルドボタンをクリックして、コードをビルドします。

ビルドが完了したら、UIで使用可能な[実行]ボタンをクリックして、出力を確認します-

instance.exports.add_ints(100,100)を渡したため、出力は200になります。

同様に、rust用に別のプログラムを作成し、webassembly.studioでコンパイルすることができます。

Goは、バージョン1.1以降のWebAssemblyのサポートを追加しました。最初にダウンロードしてテストするには、Goを実行します。

で利用可能なgolangサイトに移動します https://golang.org/dl/そして、DownloadGoをクリックします。オペレーティングシステムに従って、Goをダウンロードしてインストールします。

完了したら、goで2つの数値を追加する簡単なプログラムを作成します。

testnum.go

package main
import "fmt"
func main() { 
   var a int = 100 
   var b int = 200 
   var ret int 
   ret = sum(a, b) 
   fmt.Printf( "Sum is : %d\n", ret ) 
}
 
/* function returning the max between two numbers */ 
func sum(num1, num2 int) int { 
   return num1+num2 
}

上記のコードをwasmにコンパイルするには、最初にGoで環境変数を設定します。

次のコマンドを実行する必要があります-

Set GOOS=js
GOARCH=wasm

完了したら、以下のコマンドを実行します-

go build -o testnum.wasm testnum.go

コマンドが実行されると、testnum.wasmファイルを取得する必要があります。

ブラウザでコードをテストしてみましょう。これを行うには、goでインストールされるwasm_exec.jsを取得する必要があります。

ファイルwasm_exec.jsは、goのmisc / wasm /フォルダー内で利用できます。

これは、wasm_exec.jsとtestnum.wasmを利用するtestgo.htmlのコードです。

<html> 
   <head> 
      <meta charset="utf-8"/>
      <script src="wasm_exec.js"></script>
   </head>
   <body>
      <script type="text/javascript"> 
         const importObj = {
            module: {} 
         };
         const go = new Go(); 
         async function fetchAndInstantiate() { 
            const response = await fetch("testnum.wasm"); 
            const buffer = await response.arrayBuffer(); 
            const obj = await WebAssembly.instantiate(buffer, go.importObject); 
            console.log(obj); 
            go.run(obj.instance); 
         } 
         fetchAndInstantiate(); 
      </script>
   </body>
</html>

出力

出力は次のとおりです-

Javascriptには、wasmコードで動作するAPIがたくさんあります。APIはnodejsでもサポートされています。

NODEJSをシステムにインストールします。Factorialtest.jsファイルを作成します。

以下に示すように、C ++階乗コードを使用しましょう-

int fact(int n) {
   if ((n==0)||(n==1))
      return 1;
   else
      return n*fact(n-1);
}

WasmExplorerを開きます。 https://mbebenita.github.io/WasmExplorer/ 以下に示すように-

最初の列にはC ++階乗関数があり、2番目の列にはWebAssemblyテキスト形式があり、最後の列にはx86アセンブリコードがあります。

WebAssemblyのテキスト形式は次のとおりです-

(module
   (table 0 anyfunc)
   (memory $0 1) (export "memory" (memory $0))
   (export "_Z4facti" (func $_Z4facti)) (func $_Z4facti (; 0 ;) (param $0 i32) (result i32) (local $1 i32)
      (set_local $1(i32.const 1)) (block $label$0 (br_if $label$0 (i32.eq (i32.or (get_local $0)
                  (i32.const 1)
               )
               (i32.const 1)
            )
         )
         (set_local $1 (i32.const 1) ) (loop $label$1 (set_local $1
               (i32.mul
                  (get_local $0) (get_local $1)
               )
            )
            (br_if $label$1
               (i32.ne
                  (i32.or
                     (tee_local $0 (i32.add (get_local $0)
                           (i32.const -1)
                        )
                     )
                     (i32.const 1)
                  )
                  (i32.const 1)
               )
            )
         )
      )
      (get_local $1)
   )
)

C ++関数ファクトは「_Z4facti」をWebAssemblyテキスト形式で。

Factorialtest.js

const fs = require('fs');
const buf = fs.readFileSync('./factorial.wasm');
const lib = WebAssembly.instantiate(new Uint8Array(buf)).
   then(res => {
      for (var i=1;i<=10;i++) {
         console.log("The factorial of "+i+" = "+res.instance.exports._Z4facti(i))
      }
   }
);

コマンドラインで、コマンドノードfactorialtest.jsを実行すると、出力は次のようになります。

C:\wasmnode>node factorialtest.js
The factorial of 1 = 1
The factorial of 2 = 2
The factorial of 3 = 6
The factorial of 4 = 24
The factorial of 5 = 120
The factorial of 6 = 720
The factorial of 7 = 5040
The factorial of 8 = 40320
The factorial of 9 = 362880
The factorial of 10 = 3628800

この章では、WebAssemblyに関する例について説明します。

例1

以下は、最大要素を取得するためのCプログラムの例です。

void displaylog(int n);
/* function returning the max between two numbers */ 
int max(int num1, int num2) {
   /* local variable declaration */ int result; 
   if (num1 > num2) 
      result = num1; 
   else result = num2;
      displaylog(result);
   return result; 
}

wasm fiddleでコードをコンパイルし、.wasmおよび.watコードをダウンロードします。

Wat code

ワットコードは次のとおりです-

(module 
   (type $FUNCSIG$vi (func (param i32))) (import "env" "displaylog" (func $displaylog (param i32))) 
   (table 0 anyfunc) 
   (memory $0 1) (export "memory" (memory $0)) 
   (export "max" (func $max)) (func $max (; 1 ;) (param $0 i32) (param $1 i32) (result i32) 
      (call $displaylog (tee_local $0 
            (select 
               (get_local $0) (get_local $1) 
               (i32.gt_s (get_local $0) (get_local $1)) 
            )
         )
      )
      (get_local $0) 
   )
)

.wasmコードをダウンロードし、以下に示すように.htmlファイルで使用しましょう-

<!DOCTYPE html> 
<html>
   <head>
      <meta charset="UTF-8">
   </head>
   <body>
      <script>
         const importObj = {
            env: { 
               displaylog: n => alert("The max of (400, 130) is " +n) 
            } 
         };
         fetch("testmax.wasm") .then(bytes => bytes.arrayBuffer()) 
            .then(module => WebAssembly.instantiate(module, importObj)) 
            .then(finalcode => { 
            console.log(finalcode); 
            console.log(finalcode.instance.exports.max(400,130)); 
         }); 
      </script> 
   </body>
</html>

出力

出力は次のとおりです-

例2

以下は、指定された数のフィボナッチ数列を取得するためのC ++コードです。

#include <iostream>>
void displaylog(int n); 
int fibonacciSeries(int number) {
   int n1=0,n2=1,n3,i; 
   for(i=2;i<number;++i) { 
      n3=n1+n2; displaylog(n); n1=n2; n2=n3;
   }
   return 0; 
}

私はwasmexplorerを使用してコードをコンパイルしています。WatとWasmをダウンロードし、ブラウザで同じものをテストします。

下記のコードを使用できます-

<!DOCTYPE html> 
<html>
   <head> 
      <meta charset="UTF-8">
   </head>
   <body>
      <script> 
         const importObj = { 
            env: { _Z10displaylogi: n => console.log(n) } 
         };
         fetch("fib.wasm") 
            .then(bytes => bytes.arrayBuffer()) 
            .then(module => WebAssembly.instantiate(module, importObj)) 
            .then(finalcode => { 
            console.log(finalcode); 
            console.log(finalcode.instance.exports._Z15fibonacciSeriesi(10)); 
         });
      </script> 
   </body>
</html>

出力

出力は次のとおりです-

例3

以下は、特定の配列に要素を追加するためのRustコードです。

fn add_array(x: i32) -> i32 { 
   let mut sum = 0; 
   let mut numbers = [10,20,30]; for i in 0..3 { 
      sum += numbers[i]; 
   } 
   sum 
}

WebAssembly Studioを使用して、RUSTをwasmにコンパイルします。

コードをビルドしてwasmファイルをダウンロードし、ブラウザーで同じように実行します。

<!DOCTYPE html> 
<html>
   <head> 
      <meta charset="UTF-8">
   </head>
      <body>
      <script> 
         const importObj = { 
            env: {
            } 
         };
         fetch("add_array.wasm") .then(bytes => bytes.arrayBuffer())
            .then(module => WebAssembly.instantiate(module, importObj)) 
            .then(finalcode => { 
            console.log(finalcode); 
            console.log(finalcode.instance.exports.add_array());
         }); 
      </script> 
   </body> 
</html>

出力

出力は以下のようになります-