開発ツールに頼らず 様々な言語から WebAssembly（第７回）

WebAssembly は多くのプログラム言語からライブラリの様に呼び出す事が出来る。
WebAssembly のコードを書く方法は多くの記事で書かれているが、その使い方はフレームワーク等のツールに頼っている事が多い様だ。
本記事では、そのような便利ツールに出来るだけ頼らず JavaScript, Rust, Python, Ruby から WebAssembly を実行する方法を記載する。

WebAssembly は新しい技術である。
目先の最先端ツールに飛びつくのもよいが、その基礎を学んで長く使える知識を身に着けないか？

本記事はシリーズの第６回である。シリーズ記事の一覧は [第１回] の  **#シリーズ記事一覧** に載せている。シリーズの記事を追加する時は [第１回] の一覧を更新して通知を行うので興味の有る人は [第１回] をストックして欲しい。

# 本記事の概要と過去記事の復習

[第６回] では 3 種類の WebAssembly モジュール(クラスの様な物) を作成し、オブジェクト指向の「委譲の様な事を行った。」
具体的には WebAssembly インスタンスの持つ抽象ポインターテーブルに移譲先のインスタンスに紐づくメソッドを保持する事で委譲先のメソッドを呼び出した。
また、登場するインスタンスは全て WebAssembly インスタンスであった。

今回は [第６回] と同様のプログラムを別の方法で実行してみる。
具体的には、WebAssembly インスタンスの持つグローバル変数に委譲先のオブジェクトの抽象ポインターを保持する。
また、委譲先のオブジェクトは外部プログラムのオブジェクトである。

イメージとしては、JavaScript の下記の様なコードを想定する。
（このコードは  [第６回] と全く同じ物である。）

```javascript
class EvenCounter {
    constructor() {
        this.counter = 0;
    }

    increment() {
        this.counter += 2;
    }

    get() {
        return this.counter;
    }
}

class OddCounter {
    constructor() {
        this.counter = 1;
    }

    increment() {
        this.counter += 2;
    }

    get() {
        return this.counter;
    }
}

class Counter {
    constructor() {
        this.counter = null;
    }

    increment() {
        this.counter.increment();
    }

    get() {
        return this.counter.get();
    }
}

let evenCounter = new EvenCounter();
let oddCounter = new OddCounter();
let counter = new Counter();

counter.counter = evenCounter;
console.log(counter.get());
counter.increment();
console.log(counter.get());

counter.counter = oddCounter;
console.log(counter.get());
counter.increment();
console.log(counter.get());
```

`EvenCounter` や `OddCounter` は、それぞれ `counter` というプロパティを持っている。
`Counter` は上記のどちらかのインスタンスをプロパティに持ち、メソッド `increment` や `get` を呼ばれた時にその処理を委譲する。

本記事では上記の様な WebAssembly のコードを実行する下記の外部プログラムを作成する。

- Vanilla JS （フレームワークやライブラリを使わない、そのままの JavaScript）から WebAssembly を実行
- Rust, Python, Ruby から低レイヤーのライブラリ [wasmtime] だけを使って WebAssembly を実行

なお、説明の都合上 [Vanilla JS から実行](#vanilla-js-から実行) の章は JavaScript に興味が無くとも読んでほしい。
（そんなに難しい事はやっていないので JavaScript を知らなくとも本質的な部分は理解できるはずだ。）
それ以外の興味の無い言語の章については読み飛ばして問題無い。

ところで「外部プログラムへの参照を行う抽象ポインター」には、「これが無ければ WebAssembly の価値が半減する」と言える程の重要な役割が存在する。
そこについては最後の [まとめ](#まとめ) 少し書いたので気が向いたら読んで欲しい。

# WebAssembly コードの準備

過去記事同様、WebAssembly Text Format で動作確認をする WebAssembly のコードを作成し、wat2wasm でコンパイルする。

## wabt のインストール

[github](https://github.com/WebAssembly/wabt) の README.md を読んでコンパイルする。
C++ のコンパイル環境が必要なので注意。

wat2wasm というコマンドがコンパイルされるので、必要に応じて Path を通しておくと良いだろう。

## wasm ファイルの準備

wasm ファイルとは、WebAssembly の規格に沿ったバイナリファイルの事である。
最初に各種言語から動かして動作確認するための wasm ファイルを用意しよう。

最初にテキストエディタで以下の様な内容のファイルを作成し `/tmp/counter.wat` という名前で保存する。

```wat
(module
    (import "host" "get" (func $delegate_get (param externref) (result i64)))
    (import "host" "increment" (func $delegate_increment (param externref)))

    (global $delegated (import "host" "delegated") externref)

    (func (export "get") (result i64)
        global.get $delegated
        call $delegate_get)

    (func (export "increment")
        global.get $delegated
        call $delegate_increment))
```

この wat ファイルは以下の事を行っている。
なお、下記の `i64` とは符号付き 64 bit 整数、`externref` とは外部プログラムのオブジェクトを指す抽象ポインターの事である。

- 外部プログラムから `host.get` という物を import する。これは関数であり、引数に `externref` を取り、`i64` を返す。この関数に、この wasm ファイル内部から `$delegate_get` という名前でアクセスできるようにする。
- 外部プログラムから `host.increment` という物を import する。これは関数であり、引数に `externref` を取り、値を返さない。値も無い。この関数に、この wasm ファイル内部から `delegate_increment` という名前でアクセスできるようにする。
- グローバル変数（プロパティ）を宣言。このグローバル変数に、この wasm ファイル内部から `$delegated` という名前でアクセスできるようにする。このグローバル変数は `externref` で外部プログラムの `host.delegated` という物を import する。
- 関数を宣言。この関数には外部から `get` という名前でアクセス可能。この関数は引数を取らず `i64` 型の値を 1 個返す。この関数は `$delegate_get` に `$delegated` を渡して実行する。
- 関数を宣言。この関数には外部から `increment` という名前でアクセス可能。この関数は引数も戻り値も無い。この関数は `$delegate_increment` に `$delegated` を渡して実行する。

WebAssembly 内部から `externref` のメソッドを直接実行する事は出来ない。
メソッド実行方法は外部プログラムの仕様によって異なるからだ。
そのため、メソッドを実行するラッパー関数を外部からインポートする必要がある。

また、`$delegated` の型が `mut externref` では無く `externref` である事にも注意しよう。
つまり、この参照は「不変」なのだ。

関数 `increment` を実行すると、 `externref` の指し示すオブジェクトは変化する。
しかし、変更されるのはあくまで「指し示しているオブジェクト」であり `externref` 自身は変更されない。
（「 `externref` を変更する」とは「 `externref` の指し示す先を別のオブジェクトに変更する」という事である。）

この wat ファイルを以下の様に wat2wasm コマンドでコンパイルすると `/tmp` 以下に `counter.wasm` というバイナリファイルが出来る。

```sh
$ wat2wasm -o /tmp/counter.wasm /tmp/counter.wat
```

# Vanilla JS から実行

説明の都合上、本章は JavaScript に興味の無い人でも斜めに読んで欲しい。

## 筆者の JavaScript 環境

- nodejs v18.13.0

## Vanilla JS のコード

nodejs から WebAssembly を実行するコードは、例えば下記の様になる。
（冗長なので、EvenCounter を使った例だけを表示する。）

```javascript
// Declare delegated classes
class EvenCounter {
    constructor() {
        this.counter = BigInt(0);
    }

    increment() {
        this.counter += BigInt(2);
    }
    get() {
        return this.counter;
    }
}

// Build Module
const fs = require('fs');
const wasm = fs.readFileSync('/tmp/counter.wasm');
const mod = new WebAssembly.Module(wasm);

// Build Instance holding EvenCounter
const innerCounter = new EvenCounter();
const importObject = {
    host: {
        get: (externref) => {
            return externref.get();
        },
        increment: (externref) => {
            externref.increment();
        },
        delegated: innerCounter,
    }
};
const counter = new WebAssembly.Instance(mod, importObject);

// Operation Check
console.log(`The count is ${counter.exports.get()} now.`);
console.log("Increment the count.")
counter.exports.increment();
console.log(`The count is ${counter.exports.get()} now.`);
```

上記のファイルを `call_wasm.js` という名前で保存し実行すると、下記の様な結果を得る。

```sh
$ node call_wasm.js
The count is 0 now.
Increment the count.
The count is 2 now.
```

JavaScript のコードを少しずつ読んでみる。
（以下の **太字リンク** は JavaScript のクラス名（コンストラクター名）である事を示す。リンク先は [MDN Web Docs](https://developer.mozilla.org/en-US/) 。）

最初に以下を読んでみる。

```javascript
// Declare delegate classes
class EvenCounter {
    constructor() {
        this.counter = BigInt(0);
    }

    increment() {
        this.counter += BigInt(2);
    }
    get() {
        return this.counter;
    }
}
```

これは委譲先の class を宣言している。
WebAssembly とは関係ないので説明を省略する。

次に、以下の行を読んでみる。

```javascript
// Build Module
const fs = require('fs');
const wasm = fs.readFileSync('/tmp/counter.wasm');
const mod = new WebAssembly.Module(wasm);

// Build Instance holding EvenCounter
const innerCounter = new EvenCounter();
const importObject = {
    host: {
        get: (externref) => {
            return externref.get();
        },
        increment: (externref) => {
            externref.increment();
        },
        delegated: innerCounter,
    }
};
const counter = new WebAssembly.Instance(mod, importObject);
```

ここは [第２回] とほとんど同じだ。
 [第２回] でも外部プログラムから関数を import したが、今回は `EvenCounter` のオブジェクトも一緒に import している。

最後に、以下の行を読んでみる。

```javascript
// Operation Check
console.log(`The count is ${counter.exports.get()} now.`);
console.log("Increment the count.")
counter.exports.increment();
console.log(`The count is ${counter.exports.get()} now.`);
```

ここでは [第１回] と同じ方法で関数 `get` と `increment` を実行している。
より詳細に説明すると、以下だ。

1. 関数 `get` を実行。（ `evenCounter` のグローバル変数 `$counter` の初期値 `0` が表示される。）
1. 関数 `increment` を実行。（ `evenCounter` のグローバル変数 `$counter` の値が `2` 増加する。）
1. 関数 `get` を実行。（ `evenCounter` のグローバル変数 `$counter` の現在値 `2` が表示される。）

# wasmtime から実行

以降の章では wasmtime を使って [Rust から実行](#rust-から実行), [Python から実行](#python-から実行), [Ruby から実行](#ruby-から実行) 方法について、それぞれ説明している。
不要な章は読み飛ばしても大丈夫だ。

## Rust から実行

### 筆者の Rust 環境

- cargo 1.73.0
- rustup 1.26.0
- rustc 1.73.0 stable-x86_64-unknown-linux-gnu

### Rust のコード

下記の様に call_counter というプロジェクトを作成する。

```sh
$ cargo new call_counter
$ cd call_counter
```

Cargo.toml の `[dependencies]` セクションに wasmtime をの設定を加える。
筆者の環境では Cargo.toml は下記の様になった。

```toml
[package]
name = "call_counter"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
wasmtime = "13.0.0"
```

Rust から WebAssembly を実行するコードは、例えば下記の様になる。

```rust
use std::fs;
use std::sync::atomic::{AtomicI64, Ordering};
use wasmtime::*;

struct EvenCounter {
    counter: AtomicI64,
}

impl EvenCounter {
    fn new() -> Self {
        Self {
            counter: AtomicI64::new(0),
        }
    }

    fn get(&self) -> i64 {
        self.counter.load(Ordering::SeqCst)
    }

    fn increment(&self) {
        self.counter.fetch_add(2, Ordering::SeqCst);
    }
}

fn main() -> wasmtime::Result<()> {
    // Build `Engine`
    let engine = Engine::default();

    // Build `Module`
    let wasm = fs::read("/tmp/counter.wasm").unwrap();
    let module = Module::new(&engine, wasm)?;

    // Build `Store`
    let mut store = Store::new(&engine, ());

    // Create functions for wasm to import
    let get = Func::wrap(&mut store, |externref: Option<ExternRef>| -> i64 {
        let externref = externref.unwrap();
        let even_counter = externref.data().downcast_ref::<EvenCounter>().unwrap();
        even_counter.get()
    });

    let increment = Func::wrap(&mut store, |externref: Option<ExternRef>| {
        let externref = externref.unwrap();
        let even_counter = externref.data().downcast_ref::<EvenCounter>().unwrap();
        even_counter.increment();
    });

    // Create instance for wasm to import
    let inner_counter = EvenCounter::new();
    let externref = ExternRef::new(inner_counter);
    let ty = GlobalType::new(ValType::ExternRef, Mutability::Const);
    let global = Global::new(&mut store, ty, Val::ExternRef(Some(externref)))?;

    // Build `Instance`. (`Linker` is builder class.)
    let mut linker = Linker::new(&engine);
    linker.define(&store, "host", "get", get)?;
    linker.define(&store, "host", "increment", increment)?;
    linker.define(&store, "host", "delegated", global)?;
    let instance = linker.instantiate(&mut store, &module)?;

    // Operation check
    let get = instance.get_typed_func::<(), i64>(&mut store, "get")?;
    let increment = instance.get_typed_func::<(), ()>(&mut store, "increment")?;
    println!("The count is {} now.", get.call(&mut store, ())?);
    println!("Increment the count.");
    increment.call(&mut store, ())?;
    println!("The count is {} now.", get.call(&mut store, ())?);

    Ok(())
}
```

上記の内容で `src/main.rs` を上書きして実行すると、下記の様な結果を得る。

```sh
$ cargo run
   Compiling call_counter v0.1.0 (/home/wbcchsyn/tmp/call_counter)
    Finished dev [unoptimized + debuginfo] target(s) in 4.45s
     Running `target/debug/call_counter`
The count is 0 now.
Increment the count.
The count is 2 now.
```

上記の Rust コードを少しずつ解説していく。
以下で **太字のリンク** は wasmtime で定義されたクラスである事を示す。リンク先は [Rust 版 wasmtime の公式ドキュメント](https://docs.rs/wasmtime/latest/wasmtime/index.html) 。

まずはコードの全体像を把握しよう。

```rust
use std::fs;
use wasmtime::*;

struct EvenCounter {
    ...
}

impl EvenCounter {
    ...
}

fn main() -> wasmtime::Result<()> {
...
}
```

見ての通り、本コードは main 関数の他に `EvenCounter` が定義してある。

以下、コードを少しずつ読んでみる。

```rust
use std::fs;
use std::sync::atomic::{AtomicI64, Ordering};
use wasmtime::*;

struct EvenCounter {
    counter: AtomicI64,
}

impl EvenCounter {
    fn new() -> Self {
        Self {
            counter: AtomicI64::new(0),
        }
    }

    fn get(&self) -> i64 {
        self.counter.load(Ordering::SeqCst)
    }

    fn increment(&self) {
        self.counter.fetch_add(2, Ordering::SeqCst);
    }
}
```

これは委譲先の struct とその method を宣言している。
WebAssembly とは関係ないので詳しく説明しないが、1 点だけ補足が有る。

wasmtime は thread を考慮に入れている。
基本的に wasmtime の抽象ポインターが指し示す struct は `Sync` trait を実装している必要が有る。
また、wasmtime は mutable な参照を得ることが出来ない。

なので、プロパティーの型に `i64` や `std::cell::Cell<i64>` をそのまま使う事は出来ない。
そのため、ここでは `std::sync::atomic::AtomicI64` を使った。

続いて main 関数の中身を上から少しずつ読んでみる。

```rust
    // Build `Engine`
    let engine = Engine::default();

    // Build `Module`
    let wasm = fs::read("/tmp/counter.wasm").unwrap();
    let module = Module::new(&engine, wasm)?;

    // Build `Store`
    let mut store = Store::new(&engine, ());
```

ここは [第１回] とほとんど同じだ。

続いて以下の行を読んでみる。

```rust
    // Create functions for wasm to import
    let get = Func::wrap(&mut store, |externref: Option<ExternRef>| -> i64 {
        let externref = externref.unwrap();
        let even_counter = externref.data().downcast_ref::<EvenCounter>().unwrap();
        even_counter.get()
    });

    let increment = Func::wrap(&mut store, |externref: Option<ExternRef>| {
        let externref = externref.unwrap();
        let even_counter = externref.data().downcast_ref::<EvenCounter>().unwrap();
        even_counter.increment();
    });
```

ここでは [第２回] と同様に WebAssembly に import する関数を定義しようとしている。

続いて、以下の行を読んでみる。

```rust
    // Create a global variable holding a reference to EvenCounter.
    let inner_counter = EvenCounter::new();
    let externref = ExternRef::new(inner_counter);
    let ty = GlobalType::new(ValType::ExternRef, Mutability::Const);
    let global = Global::new(&mut store, ty, Val::ExternRef(Some(externref)))?;
```

ここでは [第４回] と同様に `externref` を含むグローバル変数と、その指し示す先の実態 (`inner_counter`) を作成している。

[`wasm ファイルの準備`](#wasm-ファイルの準備) でも説明したように、このグローバル変数は不変である。
なので `ty` 作成時に `Mutability::Const` を指定している事に注意しよう。

続いて以下の行を読んでみる。

```rust
    // Build `Instance`. (`Linker` is builder class.)
    let mut linker = Linker::new(&engine);
    linker.define(&store, "host", "get", get)?;
    linker.define(&store, "host", "increment", increment)?;
    linker.define(&store, "host", "delegated", global)?;
    let instance = linker.instantiate(&mut store, &module)?;
```

ここでは [第２回] と同様の方法で **[Instance][Instance_rs]** を作成している。

最後に以下の行を読んでみる。

```rust
    // Operation check
    let get = counter.get_typed_func::<(), i32>(&mut store, "get")?;
    let increment = counter.get_typed_func::<(), ()>(&mut store, "increment")?;
    println!("{}", get.call(&mut store, ())?);
    increment.call(&mut store, ())?;
    println!("{}", get.call(&mut store, ())?);
```

ここでは [第１回] と同じ方法で関数 `get` と `increment` を実行している。
より詳細に説明すると、以下だ。

1. 関数 `get` を実行。（ `even_counter` のグローバル変数 `$counter` の初期値 `0` が表示される。）
1. 関数 `increment` を実行。（ `even_counter` のグローバル変数 `$counter` の値が `2` 増加する。）
1. 関数 `get` を実行。（ `even_counter` のグローバル変数 `$counter` の現在値 `2` が表示される。）

## Python から実行

### 筆者の Python 環境

- Python 3.11.2
- pip 23.0.1

### Python のコード

最初に Python の version と環境に応じた方法で wasmtime をインストールする。

```sh
$ pip install wasmtime
Collecting wasmtime
  Downloading wasmtime-13.0.2-py3-none-manylinux1_x86_64.whl (7.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.1/7.1 MB 1.7 MB/s eta 0:00:00
Installing collected packages: wasmtime
Successfully installed wasmtime-13.0.2
```

Python から WebAssembly を実行するコードは、例えば下記の様になる。

```python
from wasmtime import Engine, Func, FuncType, Linker, Module, Store, ValType, Val, Global, GlobalType


class EvenCounter:
    def __init__(self):
        self.count = 0

    def get(self):
        return self.count

    def increment(self):
        self.count += 2


# Build Engine.
engine = Engine()

# Build Module.
wasm = open('/tmp/counter.wasm', 'rb').read()
module = Module(engine, wasm)

# Build Store.
store = Store(engine)

# Create functions to import.
i64 = ValType.i64()
externref = ValType.externref()
get = Func(store, FuncType([externref], [i64]), lambda exref: exref.get())
increment = Func(store, FuncType([externref], []), lambda exref: exref.increment())

# Create a global variable holding a reference to EvenCounter.
innerCounter = EvenCounter()
exref = Val.externref(innerCounter)
ty = GlobalType(externref, False)
exref_global = Global(store, ty, exref)

# Build Instance. (Linker is builder class.)
linker = Linker(engine)
linker.define(store, "host", "get", get)
linker.define(store, "host", "increment", increment)
linker.define(store, "host", "delegated", exref_global)
instance = linker.instantiate(store, module)

# Operation check.
print("The count is %d now" % instance.exports(store)['get'](store))
print("Increment the count")
instance.exports(store)['increment'](store)
print("The count is %d now" % instance.exports(store)['get'](store))
```

上記のファイルを `call_wasm.py` という名前で保存し実行すると、下記の様な結果を得る。

```sh
$ python call_wasm.py
The count is 0 now
Increment the count
The count is 2 now
```

上記の Python コードを少しずつ解説していく。
以下で **太字のリンク** は wasmtime で定義されたクラスである事を示す。リンク先は [Python 版 wasmtime の公式ドキュメント](https://bytecodealliance.github.io/wasmtime-py/) 。

最初は少しまとめて読んで見る。

```python
from wasmtime import Engine, Func, FuncType, Linker, Module, Store, ValType, Val, Global, GlobalType


class EvenCounter:
    def __init__(self):
        self.count = 0

    def get(self):
        return self.count

    def increment(self):
        self.count += 2
```

ここでは委譲先の class を宣言している。
WebAssembly とはあまり関係無いので説明を省略する。

続いて以下の行を読んでみる。

```python
# Build Engine.
engine = Engine()

# Build Module.
wasm = open('/tmp/counter.wasm', 'rb').read()
module = Module(engine, wasm)

# Build Store.
store = Store(engine)
```

ここは [第１回] とほとんど同じだ。

続いて以下の行を読んでみる。

```python
# Create functions to import.
i64 = ValType.i64()
externref = ValType.externref()
get = Func(store, FuncType([externref], [i64]), lambda exref: exref.get())
increment = Func(store, FuncType([externref], []), lambda exref: exref.increment())
```

ここでは [第２回] と同様に WebAssembly に import する関数を定義しようとしている。

続いて以下の行を読んでみる。

```python
# Create a global variable holding a reference to EvenCounter.
inner_counter = EvenCounter()
exref = Val.externref(inner_counter)
ty = GlobalType(externref, False)
exref_global = Global(store, ty, exref)
```

ここでは [第４回] と同様に `externref` を含むグローバル変数と、その指し示す先の実態 (`inner_counter`) を作成している。

[`wasm ファイルの準備`](#wasm-ファイルの準備) でも説明したように、このグローバル変数は不変である。
なので **[GlobalType][GlobalType_py]** の引数の 2 個目が `False` である事に注意をしよう。

続いて以下の行を読んでみる。

```python
# Build Instance. (Linker is builder class.)
linker = Linker(engine)
linker.define(store, "host", "get", get)
linker.define(store, "host", "increment", increment)
linker.define(store, "host", "delegated", exref_global)
instance = linker.instantiate(store, module)
```

ここでは [第２回] と同様の方法で **[Instance][Instance_py]** を作成している。

最後に以下の行を読んでみる。

```python
# Operation check.
print("The count is %d now" % instance.exports(store)['get'](store))
print("Increment the count")
instance.exports(store)['increment'](store)
print("The count is %d now" % instance.exports(store)['get'](store))
```

ここでは [第１回] と同じ方法で関数 `get` と `increment` を実行している。
より詳細に説明すると、以下だ。

1. 関数 `get` を実行。（ `even_counter` のグローバル変数 `$counter` の初期値 `0` が表示される。）
1. 関数 `increment` を実行。（ `even_counter` のグローバル変数 `$counter` の値が `2` 増加する。）
1. 関数 `get` を実行。（ `even_counter` のグローバル変数 `$counter` の現在値 `2` が表示される。）

## Ruby から実行

### 筆者の Ruby 環境

- ruby 3.1.2p20

### Ruby のコード

最初に gem で wasmtime をインストールする。
（必要に応じて bundler 等を使うと良いだろう。）

```sh
$ gem install wasmtime
Fetching wasmtime-13.0.0-x86_64-linux.gem
Successfully installed wasmtime-13.0.0-x86_64-linux
Parsing documentation for wasmtime-13.0.0-x86_64-linux
Installing ri documentation for wasmtime-13.0.0-x86_64-linux
Done installing documentation for wasmtime after 0 seconds
1 gem installed
```

Ruby から WebAssembly を実行するコードは、例えば下記の様になる。

```ruby
require 'wasmtime'

class EvenCounter
  def initialize
    @count = 0
  end

  def get
    @count
  end

  def increment
    @count += 2
  end
end

# Build Engine
engine = Wasmtime::Engine.new

# Build Module
wasm = open('/tmp/counter.wasm', 'rb').read
mod = Wasmtime::Module.new(engine, wasm)

# Build Store
store = Wasmtime::Store.new(engine)

# Create functions to import from WebAssembly
get = Wasmtime::Func.new(store, [:externref], [:i64]) { |_caller, extref|
  extref.get
}
increment = Wasmtime::Func.new(store, [:externref], []) { |_caller, extref|
  extref.increment
}

# Create a global variable holding a reference to EvenCounter.
inner_counter = EvenCounter.new
exref_global = Wasmtime::Global.const(store, :externref, inner_counter)

# Build instance (Linker is builder Class)
linker = Wasmtime::Linker.new(engine)
linker.define(store, "host", "get", get)
linker.define(store, "host", "increment", increment)
linker.define(store, "host", "delegated", exref_global)
counter = linker.instantiate(store, mod)

# Operation check
puts("The count is %d now." % counter.export('get').to_func.call)
puts("Increment the count.")
counter.export('increment').to_func.call
puts("The count is %d now." % counter.export('get').to_func.call)
```

上記の Ruby コードを少しずつ解説していく。
以下で **太字のリンク** は wasmtime で定義されたクラスである事を示す。リンク先は [Ruby 版 wasmtime の公式ドキュメント](https://bytecodealliance.github.io/wasmtime-rb/latest/top-level-namespace.html) 。

最初は少しまとめて読んで見る。

```ruby
require 'wasmtime'

class EvenCounter
  def initialize
    @count = 0
  end

  def get
    @count
  end

  def increment
    @count += 2
  end
end
```

ここでは委譲先の class を宣言している。
WebAssembly とはあまり関係無いので説明を省略する。

続いて以下の行を読んでみる。

```ruby
# Build Engine
engine = Wasmtime::Engine.new

# Build Module
wasm = open('/tmp/counter.wasm', 'rb').read
mod = Wasmtime::Module.new(engine, wasm)

# Build Store
store = Wasmtime::Store.new(engine)
```

ここは [第１回] とほとんど同じだ。

続いて以下の行を読んでみる。

```ruby
# Create functions to import from WebAssembly
get = Wasmtime::Func.new(store, [:externref], [:i64]) { |_caller, extref|
  extref.get
}
increment = Wasmtime::Func.new(store, [:externref], []) { |_caller, extref|
  extref.increment
}
```

ここでは [第２回] と同様に WebAssembly に import する関数を定義しようとしている。

続いて以下の行を読んでみる。

```ruby
# Create a global variable holding a reference to EvenCounter.
inner_counter = EvenCounter.new
exref_global = Wasmtime::Global.const(store, :externref, inner_counter)
```

ここでは [第４回] と同様に `externref` を含むグローバル変数と、その指し示す先の実態（ `inner_counter` ）を作成している。

[`wasm ファイルの準備`](#wasm-ファイルの準備) でも説明したように、このグローバル変数は不変である。
なので **[Wasmtime::Global.const][Global_const_rb]** を使ってグローバル変数を作成している事に注意しよう。

続いて以下の行を読んでみる。

```ruby
# Build instance (Linker is builder Class)
linker = Wasmtime::Linker.new(engine)
linker.define(store, "host", "get", get)
linker.define(store, "host", "increment", increment)
linker.define(store, "host", "delegated", exref_global)
counter = linker.instantiate(store, mod)
```

ここでは [第２回] と同様の方法で **[Instance][Instance_rb]** を作成している。

最後に以下の行を読んでみる。

```ruby
# Operation check
puts("The count is %d now." % counter.export('get').to_func.call)
puts("Increment the count.")
counter.export('increment').to_func.call
puts("The count is %d now." % counter.export('get').to_func.call)
```

ここでは [第１回] と同じ方法で関数 `get` と `increment` を実行している。
より詳細に説明すると、以下だ。

1. 関数 `get` を実行。（ `even_counter` のグローバル変数 `$counter` の初期値 `0` が表示される。）
1. 関数 `increment` を実行。（ `even_counter` のグローバル変数 `$counter` の値が `2` 増加する。）
1. 関数 `get` を実行。（ `even_counter` のグローバル変数 `$counter` の現在値 `2` が表示される。）

# まとめ

今回は [第６回] と同様の事を、別の方法で行った。
そして [第６回] では「説明のために書いたが、あまり乱用しない方が良い」と戒めるような事を書いたと思う。

今回紹介した「外部プログラムのオブジェクトを指す抽象ポインター」（以下、 `externref` ）も、使わずに済むならば使わない方が良いと思う。
ただ、現実問題として `externref` は使わざるを得ないだろう。

本シリーズでは、説明のために意図的に「簡単な例」のみを挙げている。
扱うデータは整数がほとんどで、時々 String が出てくるくらいだ。

しかし実用的なプログラムを作る場合は、そうはいかないだろう。
例えばファイルを扱う場合を考えてみよう。

ファイルを扱うためには、WebAssembly は外部プログラムの力を借りる必要がある。
なぜならファイルを扱う際は「システムコール」を実行する必要が有り、WebAssembly では禁止されているのだ。

（ [第１回](https://qiita.com/wbcchsyn/items/e1f699bb86c466411e36#%E5%BE%A9%E7%BF%92) の冒頭に WebAssembly の制限を簡潔に記載したので、忘れた人は読んで欲しい。）

その際は（一部の例外を除き、）ほとんどの外部プログラムは「ファイルハンドル」や「ファイルポインター」と呼ばれるオブジェクトを扱うはずだ。

ならば WebAssembly は、それらのオブジェクトを扱う必要が有る。
その際には `externref` を使うしかない。

筆者は `externref` は「避けては通れない物」と認識している。

[wasmtime]: https://github.com/bytecodealliance/wasmtime
[第１回]: https://qiita.com/wbcchsyn/items/e1f699bb86c466411e36
[第２回]: https://qiita.com/wbcchsyn/items/925d11c6fd9111b29851
[第４回]: https://qiita.com/wbcchsyn/items/c5906430fa478345a5e9
[第６回]: https://qiita.com/wbcchsyn/items/e2e2d944034be6dc195f

[Instance_rs]: https://docs.rs/wasmtime/latest/wasmtime/struct.Instance.html

[GlobalType_py]: https://bytecodealliance.github.io/wasmtime-py/#wasmtime.GlobalType
[Instance_py]: https://bytecodealliance.github.io/wasmtime-py/#wasmtime.Instance

[Global_const_rb]: https://bytecodealliance.github.io/wasmtime-rb/latest/Wasmtime/Global.html#const-class_method
[Instance_rb]: https://bytecodealliance.github.io/wasmtime-rb/latest/Wasmtime/Instance.html