wasm-tools を使って、テキスト形式(WAT)のファイルから WebAssembly Component Model のバイナリを作成してみます。
(a) WebAssembly の場合
まずは、普通の WebAssembly バイナリを WAT ファイルから作成してみます。
前回、wasm-encoder で作成したものと同様の処理を 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 ファイルを生成できます。
$ wasm-tools parse -o sample.wasm sample.wat
動作確認
前回の run_wasm.js で実行した結果は次のようになりました。
$ 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
となる)に注意が必要でした。
(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 ファイルを生成できます。
$ wasm-tools parse -o sample_comp.wasm sample_comp.wat
検証
まずは、wasm-tools validate
で検証してみます。
Component Model の検証には -f all
の指定が必要でした。
$ 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 の内容を確認してみます。
$ wasm-tools component wit sample_comp.wasm
package root:component
world root {
export f: func(x: s32, y: s32) -> s32
}
動作確認
最後に、wasmtime を使って sample_comp.wasm がエクスポートしている f
関数を呼び出す処理を実装してみました。
Component のインスタンス化に Linker
の instantiate
を、f 関数の取得(参照)に get_typed_func
を使用しました。
get_typed_func の型引数を使って、呼び出し対象とする関数の引数と戻り値の型をそれぞれタプルで指定します。
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(())
}
...省略
[dependencies]
wasmtime = { version = '12', features = ['component-model'] }
実行結果はこのようになり、問題なく動作しました。
$ cd run_component
$ cargo run ../sample_comp.wasm 6 7
...省略
result = (26,)