wasm-tools を使ってテキストファイルから WebAssembly Component Model を作成

[wasm-tools](https://github.com/bytecodealliance/wasm-tools) を使って、テキスト形式（WAT）のファイルから WebAssembly Component Model のバイナリを作成してみます。

# (a) WebAssembly の場合

まずは、普通の WebAssembly バイナリを WAT ファイルから作成してみます。

[前回](https://qiita.com/fits/items/c4d17afa95942b73444b)、wasm-encoder で作成したものと同様の処理を WAT で実装すると次のようになります。

```lisp:sample.wat
(module
  (func (export "calc") (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add
    i32.const 2
    i32.mul
  )
)
```

## バイナリ化 （wasm ファイル化）

このファイルを wasm-tools parse で処理すると wasm ファイルを生成できます。

```sh:wasm ファイル化
$ wasm-tools parse -o sample.wasm sample.wat
```

## 動作確認

[前回](https://qiita.com/fits/items/c4d17afa95942b73444b)の run_wasm.js で実行した結果は次のようになりました。

```sh:動作確認
$ deno run --allow-read run_wasm.js sample.wasm calc 2 3
10
```

# (b) WebAssembly Component Model の場合

(a) を組み込んだ Component Model の WAT ファイルを次のように作りました。

* (1) 通常 WebAssembly の module を ```core module``` として組み込む
* (2) ```core instance``` を使って (1) をインスタンス化
* (3) ```func``` でコンポーネントの関数を定義し、(2) で定義したインスタンスの関数（注）を ```canon lift``` で適用
* (4) ```export``` で (3) の関数をエクスポート

```
（注） (1)で定義した calc 関数
```

(3) で引数を定義する箇所では引数の名称が必要だったり、値の型が (1) とは違う点（i32 は ```s32``` となる）に注意が必要でした。

```lisp:sample_comp.wat
(component
  (core module $A
    (func (export "calc") (param i32 i32) (result i32)
      local.get 0
      local.get 1
      i32.add
      i32.const 2
      i32.mul
    )
  )
  (core instance $a (instantiate $A))
  (func $f (param "x" s32) (param "y" s32) (result s32) (canon lift (core func $a "calc")))
  (export "f" (func $f))
)
```

## バイナリ化 （wasm ファイル化）

このファイルを (a) と同様に wasm-tools parse で処理すると wasm ファイルを生成できます。

```sh:wasm ファイル化
$ wasm-tools parse -o sample_comp.wasm sample_comp.wat
```

## 検証

まずは、```wasm-tools validate``` で検証してみます。
Component Model の検証には ```-f all``` の指定が必要でした。

```sh:検証
$ wasm-tools validate -f all -v sample_comp.wasm 

[2023-09-19T13:21:51Z INFO ] module structure validated in 203.292µs
[2023-09-19T13:21:51Z INFO ] functions validated in 255.583µs
```

次に、```wasm-tools component wit``` で WIT の内容を確認してみます。

```sh:wit の確認
$ wasm-tools component wit sample_comp.wasm

package root:component

world root {
  export f: func(x: s32, y: s32) -> s32
}
```

## 動作確認

最後に、[wasmtime](https://github.com/bytecodealliance/wasmtime) を使って sample_comp.wasm がエクスポートしている ```f``` 関数を呼び出す処理を実装してみました。

Component のインスタンス化に ```Linker``` の ```instantiate``` を、f 関数の取得（参照）に ```get_typed_func``` を使用しました。

get_typed_func の型引数を使って、呼び出し対象とする関数の引数と戻り値の型をそれぞれタプルで指定します。

```rust:run_component/src/main.rs
use wasmtime::component::{Component, Linker};
use wasmtime::{Config, Engine, Store};

use std::env;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

fn main() -> Result<()> {
    let mut args = env::args().skip(1);

    let file = args.next().unwrap_or_default();
    let p1: i32 = args.next().unwrap_or("1".to_string()).parse()?;
    let p2: i32 = args.next().unwrap_or("2".to_string()).parse()?;

    let mut config = Config::new();
    config.wasm_component_model(true);

    let engine = Engine::new(&config)?;

    let component = Component::from_file(&engine, file)?;

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

    let linker = Linker::new(&engine);
    // Component のインスタンス化
    let instance = linker.instantiate(&mut store, &component)?;
    // 関数の取得
    let f = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f")?;
    // 関数呼び出し
    let r = f.call(&mut store, (p1, p2))?;

    println!("result = {:?}", r);

    Ok(())
}
```

```toml:run_component/Cargo.toml
...省略

[dependencies]
wasmtime = { version = '12', features = ['component-model'] }
```

実行結果はこのようになり、問題なく動作しました。

```sh:実行結果
$ cd run_component
$ cargo run ../sample_comp.wasm 6 7
...省略
result = (26,)
```