0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ComponentizeJSでWebAssemblyコンポーネントを作成する

Posted at

ComponentizeJS を使うと WebAssembly コンポーネントモデルを JavaScript で実装できます。

Rust で実装する場合と比べるとファイルサイズが大きくなる等のデメリットはありそうですが、手軽に実装できるのは大きなメリットだと思います。

インストール

インストールは @bytecodealliance/componentize-js を npm install するだけです。

インストール例
$ npm install @bytecodealliance/componentize-js

これで componentize-js コマンドを使えるようになり、下記のようにして JavaScript ファイルから WebAssembly コンポーネントを作成します。

コマンド例
componentize-js --wit <WITディレクトリ> -o <出力ファイル> <JavaScriptファイル>

サンプル実装

WIT ファイルはこのように定義してみました。
cart は emptyactive の 2種類の状態を表現するため variant を使っています。

ついでに、動作確認で実行するために wasi:cli/run を export しています。

wit/cart.wit
package example:cart;

interface types {
    type cart-id = string;
    type item-id = string;
    type quantity = u32;

    record cart-item {
        item: item-id,
        qty: quantity,
    }

    variant cart {
        empty(cart-id),
        active(tuple<cart-id, list<cart-item>>),
    }

    create: func(id: cart-id) -> cart;
    change-qty: func(state: cart, item: item-id, qty: quantity) -> option<cart>;
}

world cart {
    export types;
    export wasi:cli/run@0.2.3;
}

componentize-js コマンドは今のところ WIT の依存関係を解決してくれないようなので、wit-deps を使って wasi:cli への依存関係を処理しておきます。

wit/deps.toml
cli = "https://github.com/WebAssembly/wasi-cli/archive/refs/tags/v0.2.3.tar.gz"
WITの依存関係を処理
$ wit-deps

JavaScript による実装は例えばこのようになります。

WIT 側でインターフェースを export している場合、JavaScript 側は export const <インターフェース名> = { 関数, ・・・ } のように実装します。1

source.js
// types の実装
export const types = {
    // create 関数の実装
    create(id) {
        return [ id ]
    },
    // change-qty 関数の実装
    changeQty(state, item, qty) {
        if (!state || !item || qty < 0) {
            return null
        }

        const [ id, items ] = state

        if (items) { // active 状態時の処理
            const [ newItems, exists ] = items.reduce(
                (acc, x) => {
                    if (x.item !== item) {
                        return [ [ ...acc[0], x ], acc[1] ]
                    }

                    return (qty > 0) ? 
                        [ [ ...acc[0], { item, qty } ], true ] : 
                        [ acc[0], true ]
                }, 
                [[], false]
            )

            if (!exists && qty > 0) {
                newItems.push({ item, qty })
            }

            if (newItems.length > 0) {
                return [ id, newItems ] // active 状態
            }
            
            return [ id ] // empty 状態
        }
        else if (qty > 0) { // empty 状態時の処理
            return [ state[0], [{ item, qty }] ]
        }

        return null
    },
}
// wasi:cli/run の実装
export const run = {
    run() {
        const s1 = types.create('test-cart')
        console.log(s1)

        const s2 = types.changeQty(s1, 'item-A', 2)
        console.log(s2)
        
        const s3 = types.changeQty(s2, 'item-B', 1)
        console.log(s3)
        
        const s4 = types.changeQty(s3, 'item-A', 3)
        console.log(s4)
        
        const s5 = types.changeQty(s4, 'item-A', 0)
        console.log(s5)
        
        const s6 = types.changeQty(s5, 'item-C', 4)
        console.log(s6)

        const s7 = types.changeQty(s6, 'item-C', 0)
        console.log(s7)

        const s8 = types.changeQty(s7, 'item-B', 0)
        console.log(s8)

        const e1 = types.changeQty(s1, 'item-A', 0)
        console.log(e1)

        const e2 = types.changeQty(s1, 'item-A', -1)
        console.log(e2)
    }
}

ビルド

次のコマンドを実行して WebAssembly コンポーネントを作成します。

ビルド例
$ npx componentize-js --wit wit -o component.wasm source.js

実行

この component.wasmwasmtime で実行した結果はこのようになります。

実行結果
$ wasmtime run -S http component.wasm
["test-cart"]
["test-cart", [{ item: "item-A", qty: 2 }]]
["test-cart", [{ item: "item-A", qty: 2 }, { item: "item-B", qty: 1 }]]
["test-cart", [{ item: "item-A", qty: 3 }, { item: "item-B", qty: 1 }]]
["test-cart", [{ item: "item-B", qty: 1 }]]
["test-cart", [{ item: "item-B", qty: 1 }, { item: "item-C", qty: 4 }]]
["test-cart", [{ item: "item-B", qty: 1 }]]
["test-cart"]
null
null

なお、今回の WIT では wasi:http を import していませんが、component.wasm 内で wasi:http 等を import するようになっているので2、wasmtime で実行する際に -S http(もしくは --wasi http)オプションを指定しておかないと実行に失敗します。

  1. WIT側で関数を export している場合は、JavaScript 側で関数を export するだけです(例. export function 関数名・・・)

  2. wasi:http の types と outgoing-handler を import しています。他にも wasi:io や wasi:filesystem 等、色々と import するようになっています。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?