ブラウザからWasmモジュールを呼び出す方法

---

## はじめに

WebAssembly（Wasm）は、ブラウザ上でネイティブ級の高速コードを安全に実行できる仕組みとして知られています。「Wasmモジュールを呼び出す」という表現を聞くと、つい**HTTPベースのAPIコール**をイメージしがちですが、実際は **ブラウザ内部での“関数呼び出し”** に近い形で実行されます。

この記事では、**ブラウザ（JavaScriptエンジン）からWasmモジュールを呼び出す仕組み**をわかりやすく紹介しつつ、**「同一プロセスで動くのにセキュリティは大丈夫なの？」** という疑問にも答えます。さらに、よくある質問（FAQ）を加えています。

---

## 1. 全体の流れ

まず、**「Wasmを呼び出す」とはどんな流れなのか**を大まかに押さえておきましょう。

1. **Wasmバイナリ（.wasmファイル）をブラウザに読み込む**  
   - `fetch()` で取得、あるいはscriptタグ経由で`.js`ラッパを読み込み  
2. **WebAssemblyモジュールをインスタンス化**  
   - `WebAssembly.instantiate()` や `WebAssembly.compile()`でロードする  
   - Emscriptenの `.js` 出力を呼ぶ場合は内部で自動実行される  
3. **モジュール内のエクスポートされた関数をJavaScriptから呼ぶ**  
   - まるで **「関数呼び出し」** のように実行  
4. **引数・戻り値の受け渡し**  
   - JavaScript側とWasm側でプリミティブ型をやり取り。メモリバッファを共有することも可能

このように、**HTTPを介したネットワーク通信ではなく**、**JavaScriptとWasmの同一プロセス内で関数呼び出し**が行われます。

---

## 2. 仕組みを図で見る

```mermaid
flowchart TB
    A[ブラウザ<br>JSエンジン] -->|fetch<br>instantiate<br>Wasmバイナリ| B[Wasmモジュール]
    B -->|エクスポート<br>された関数<br>を公開| A
    A -->|JS関数<br>として<br>呼び出す| B
    B -->|計算実行| B
    B -->|結果| A
```

- JavaScript（ブラウザ）側で`.wasm`をロードし、モジュールを初期化  
- **module.exports**にある関数を**JS関数のようにコール**して結果を受け取る  
- 同じプロセス内で動くので、高速＆低レイテンシ

---

## 3. 実装例1: Emscriptenが生成したラッパを使うパターン

### 3.1 Emscriptenでコンパイル

たとえば、`myfunc.c` というCコードがあるとします。

```c
// myfunc.c
int add(int a, int b) {
  return a + b;
}
```

これをEmscriptenで以下のようにコンパイルして`.js`ラッパを生成します:

```bash
emcc myfunc.c -o myfunc.js -s MODULARIZE=1 -s EXPORT_ALL=1
```

これにより、`myfunc.js` と `myfunc.wasm` が出力され（`myfunc.js`の内部で`myfunc.wasm`をfetchする仕組み）、ブラウザで読み込めるようになります。

### 3.2 JavaScriptから呼び出す

```html
<script src="myfunc.js"></script>
<script>
  Myfunc().then(module => {
    // Emscriptenは C関数のエクスポートを "_関数名" で公開する
    const result = module._add(10, 20);
    console.log(result); // 30
  });
</script>
```

- このとき、`_add` は**C関数 `int add(int, int)`** に対応し、ほぼ「JavaScriptの関数を呼ぶ」感覚。  
- `Module._関数名` でWasm関数を呼べるイメージです。

---

## 4. 実装例2: WebAssembly標準API（直接実装）を使うパターン

```javascript
const response = await fetch('myfunc.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
  // imports: {...} // 必要なインポートがあれば追加
});

// exportsオブジェクトに、C関数が公開される
const result = instance.exports.add(3, 4);
console.log(result); // 7
```

- この場合、**`.wasm`をfetch→arrayBuffer→instantiate()`** という流れを手動で実装  
- `instance.exports.add` を**関数オブジェクト**として呼び出せる  
- HTTPベースのリクエストではなく、同プロセス内で「関数を呼ぶ」形

---

## 5. HTTPコールとは何が違う？

### 5.1 同一プロセス内での関数呼び出し

**REST API**や**GraphQL**などのHTTPベース通信は、リクエストをサーバーへ送って応答を受け取る仕組みですが、Wasmの場合は**同じブラウザプロセスでJSとWasmが動作**します。ネットワーク経由の遅延がなく、データを**メモリ上**で直接やりとり可能です。

### 5.2 低レイテンシ・メモリ共有

HTTP経由ではJSONやXMLなどのシリアライズ・デシリアライズが発生しますが、Wasm呼び出しは**バイナリレベル**でデータを受け渡しでき、とても高速です。

### 5.3 リモートサーバーとの通信はオプション

必要に応じてJavaScript側で`fetch()`や`WebSocket`などを使い、取得したデータをWasmモジュールに渡すことでサーバー連携も可能ですが、**その通信部分はJavaScriptが担う**という点がHTTP APIとの大きな違いです。

---

## 6. 同一プロセスだけど安全なの？ 〜Wasmのメモリサンドボックス〜

「同一プロセス内にJavaScriptエンジンとWasmが存在するなら、**バッファオーバーランやメモリリーク**が相互に影響しないのか？ セキュリティ上問題はないのか？」という疑問がよく挙がります。

### 6.1 WebAssemblyのサンドボックス設計

**結論**: WebAssemblyでは、**専用の線形メモリ**に隔離され、**境界チェック**によってバッファオーバーランを防止する仕組みが根幹にあるため、**同じプロセスであってもJSや他のWasmモジュールを破壊するリスクは極めて低い**です。

```mermaid
flowchart LR
    subgraph JS Engine Process
    A[JavaScript Heap<br>Objects, Arrays, etc.] -. boundary .- E[Wasm Module]
    E --> B(Wasm Linear Memory<br>ArrayBuffer-like)
    E --> C(Execution Sandbox<br>Boundary Checks)
    end
```

- **JavaScriptヒープ**や**JSエンジンの内部メモリ**にはWasmコードが直接アクセス不可  
- Wasmコードが扱う「線形メモリ（バッファ）」は**境界チェック**が入り、範囲外アクセスはトラップ（例外）を発生  
- 他のWasmモジュールやホストJSオブジェクトを乗っ取れない構造

### 6.2 バッファオーバーランやメモリリークへの対処

- **バッファオーバーラン**:  
  - Wasm命令で範囲外アクセスを行うと例外（トラップ）が起き、ホストを破壊する前に停止。  
- **メモリリーク**:  
  - C/C++のmalloc/freeを誤るとWasm線形メモリ内でリークは起き得ますが、**モジュール内部のヒープ**が無駄に使われるだけで、ブラウザ全体を破壊するわけではありません。

### 6.3 セキュリティ上の仕組み

- **Wasm命令セットは制限付き**で、RCE（リモートコード実行）的な攻撃が難しい。  
- インポート/エクスポート関数が厳密に管理され、**勝手にホストJSやOS資源**を触れない。  
- 実装バグやゼロデイは別として、**設計上はネイティブコードより安全**とも言われる。

---

## 7. 比較表：Wasm関数呼び出し vs HTTP APIコール

以下は、**Wasm呼び出し**と**HTTP APIコール**（REST/GraphQLなど）の主要な違いをまとめた簡単な表です。

| 項目             | Wasm呼び出し                                                     | HTTP APIコール (REST/GraphQL)                                            |
|------------------|-------------------------------------------------------------------|---------------------------------------------------------------------------|
| データ受け渡し   | 同一プロセス内の関数呼び出し<br>バイナリ/メモリ共有が可能          | ネットワークを介したリクエスト/レスポンス<br>JSONなどテキスト形式が主流    |
| レイテンシ       | 非常に低い<br>(関数コールと同等)                                   | ネットワーク遅延あり<br>(サーバー往復)                                    |
| セキュリティモデル | Wasmサンドボックス<br>境界チェックでバッファオーバーランを防止       | HTTPS通信や認証が基本<br>サーバーサイドの認可も必要                        |
| 実装例           | JS → Wasmモジュール_exports                                     | JS (fetch/axiosなど) → サーバーのAPIエンドポイント                         |
| 用途             | ブラウザ内で高速計算<br>ローカル関数呼び出し                       | サーバー側でデータベースアクセスなど大規模処理<br>クライアント→サーバー連携  |


- このように、**Wasm呼び出し**はAPIコールに似た「呼び出し→結果」構造を持つが、**通信ではなくローカル関数**として動作する点が大きく異なります。

---

## 8. Wasmモジュールから直接gRPC-Webで外部サーバーにアクセスできるの？

ブラウザとWasmの通信方法は理解できましたが、ブラウザのWasmはソケットを開いてgRPC-Webで直接サーバーと通信できるでしょうか？

### 8.1 結論：ブラウザWasm単体では直接アクセス不可、JS経由が必要

- **ブラウザのWasm**は**低レベルソケット**や**HTTP/2**の直接操作ができません。  
- gRPC-Web通信を行う際は、**JavaScriptのHTTP/Fetch/WebSocket**等を利用する形になります。  
  - つまり、Wasmモジュールが生成したバイナリをJS側で送信し、レスポンスを受け取ってWasmに戻す、という二段階が必要です。

### 8.2 gRPC-Web + protobuf構造

```mermaid
flowchart TD
    A[Wasm Module<br>Protobuf Encode/Decode] --call JS API--> B(JavaScript gRPC-Web Client)
    B --HTTP/2<br>(またはHTTP1)<br>over fetch--> C[gRPC-Web Server]
    C --Response--> B
    B --Binary Data--> A
```

- Wasmモジュールがprotobufライブラリを使い、メッセージのencode/decodeだけを行う  
- **実際のHTTP通信**はJSのgRPC-Webライブラリが`fetch()`や`XMLHttpRequest`を使って実行  
- **Wasmはソケットに直接アクセスできない**ため、こうしたラッパ構造になる

### 8.3 サーバーサイドWasmなら可能性あり

- サーバーサイドのWasmランタイム（WASIなど）で**TCP/UDPソケット**がサポートされれば、ネイティブgRPCクライアントを移植し、**直接外部サーバーにアクセス**できる可能性があります。  
- ただしブラウザWasmとは違い、**ホストOSへの依存やランタイムの実装状況**に左右される点に注意。

---

## 9. FAQ

### Q1. サーバーとの通信は不要なのですか？
A1. 必要なら`fetch()`や`WebSocket`等でサーバーとデータをやり取りし、Wasmに受け渡すことが多いです。**Wasm自体はHTTPコールを行わず、JSが橋渡し**する形になります。

### Q2. 逆アセンブルなどでコードが漏れませんか？
A2. WebAssemblyはバイナリ形式ですが、解析・逆アセンブルは可能です。ソース保護には難読化やライセンス対策が必要な場合があります。

### Q3. メモリ容量を無制限に使われる危険は？
A3. Wasmの線形メモリには**初期サイズ**と**最大サイズ**を指定できます。Emscriptenで`-s INITIAL_MEMORY`や`-s MAXIMUM_MEMORY`を設定しておけば暴走を防げます。

### Q4. メモリリークが起きたらブラウザ全体に影響しませんか？
A4. 原則、Wasmモジュール内のヒープを無駄に消費するだけで、ブラウザ本体や他のモジュールを破壊するわけではありません。極端に大きなメモリ要求はブラウザの制限で失敗する場合もあります。

### Q5. ネイティブコードより安全と言われるのは本当？
A5. **Wasmは境界チェック**や**制限付き命令セット**で、従来のC/C++コードによるバッファオーバーラン攻撃を大幅に減らせます。ランタイムやブラウザ実装にバグが無い限り、ネイティブより安全と評価されることも多いです。

### Q6. 大規模な数値計算ならサーバーサイドのほうが良いのでは？
A6. ユースケースによります。ブラウザでリアルタイム応答やUI連携が必要ならWasmが有利。大規模処理やDBアクセスが必須ならサーバー側で実行（Wasm or ネイティブ）のほうが適しています。

### Q7. ブラウザWasmからgRPC-Web/Protobufを“直接”呼び出すのは？
A7. ブラウザでは**ソケットアクセスが禁止**されており、HTTP/2のネイティブ利用は不可です。実際には **JavaScriptライブラリ（gRPC-Webなど）** を経由し、WasmはProtobufのencode/decodeだけ行う構造になります。

### Q8. サーバーサイドWasmならネットワークソケットが使える？
A8. **WASI**などがネットワークAPIをサポートすれば、ネイティブのgRPCクライアントをWasmに移植し、**直接外部サーバーにアクセス**することも可能と考えられます。ただしブラウザ環境とは別物です。

---

## 9. まとめ

- **ブラウザからWasmモジュールを呼び出す**流れは、**同プロセス内**の「関数呼び出し」のようなモデルで動作し、RESTやGraphQLのようなHTTP通信は介在しません。  
- このため**非常に低レイテンシ**かつ**メモリを共有**した高速な処理が可能。  
- 「同じプロセスならバッファオーバーラン等で破壊されるのでは？」という懸念に対しては、**WebAssemblyのサンドボックス設計**がしっかり対策を講じており、バッファオーバーランやメモリリークが**JSエンジンや他のWasmモジュールに波及するリスク**は極めて低いです。  
- **gRPC-Web**通信に関しては、ブラウザWasm単体でソケットを直接扱えないため、**JavaScriptのgRPC-Webライブラリ**を介してHTTPリクエストを行う必要がある。その結果、WasmからはProtobufのエンコード/デコード処理を担い、実際のHTTP通信はJSが担当する構造を取る。  
- もし必要ならJavaScript側でHTTP通信を行い、Wasmにデータを渡すことでサーバーとの連携も可能になります。
- サーバーサイドWasm（WASIなど）ならソケットAPIを直接使えるかもしれないが、ブラウザWasm環境とは別物。

**結論**:  
- 「APIコール」に似た形で呼び出すが、実態は**ローカル関数呼び出し**として同プロセスで動作する。  
- **Wasmの境界チェック**によりメモリアクセスは安全性が高く、JSヒープや他モジュールを破壊しない。  
- HTTP通信やPWA、サーバーレスなどとも組み合わせでき、ブラウザ上でネイティブ級の性能と安全性を両立できる点が大きな魅力。

---

### 参考リンク

- [WebAssembly公式サイト](https://webassembly.org/)  
- [MDN: Using the WebAssembly JavaScript API](https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API)  
- [Emscripten公式サイト](https://emscripten.org/)  
- [HackerNews discussion: “Is WebAssembly safer than native code?”](https://news.ycombinator.com/)  

---

以上