2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

wasm-tools を使って、テキスト形式(WAT)のファイルから WebAssembly Component Model のバイナリを作成してみます。

(a) WebAssembly の場合

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

前回、wasm-encoder で作成したものと同様の処理を WAT で実装すると次のようになります。

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 ファイルを生成できます。

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 となる)に注意が必要でした。

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 ファイルを生成できます。

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 の内容を確認してみます。

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 のインスタンス化に Linkerinstantiate を、f 関数の取得(参照)に get_typed_func を使用しました。

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

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(())
}
run_component/Cargo.toml
...省略

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

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

実行結果
$ cd run_component
$ cargo run ../sample_comp.wasm 6 7
...省略
result = (26,)
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?