TL;DR

WebAssembly の相互運用性を高めるための仕様。WebAssembly Module で起きていた、データ表現、関数呼び出し規約、メモリ管理、例外処理、開発体験の問題を解消する。

なぜ必要か

WebAssembly Module は、構造を持つデータの表現を定めていない。そのため、関数を呼び出す側と呼び出される側の両方が、同じルールに従う必要があった。

たとえば、次の Rust のコードを WASM にビルドし、JavaScript から呼び出すとする。

struct A {
    x: u32,
    y: u8,
    z: u16,
}
 
pub fn get_x(value: &A) -> u32 { value.x }
pub fn get_y(value: &A) -> u8  { value.y }
pub fn get_z(value: &A) -> u16 { value.z }

次のように呼び出したくなるが、WebAssembly Instance はサンドボックスで動いており、a が置かれたメモリに get_x はアクセスできないので、これは動かない。

const a = { x: 1, y: 2, z: 3 }
const x = instance.exports.get_x(a)

動かすには、インスタンスの線形メモリにオブジェクトをコピーし、その先頭の位置を引数に渡す必要がある。このとき呼び出し側は、x、y、z の順に値が並ぶこと、バイトオーダがリトルエンディアンであること、値以外のデータが置かれないことを前提にする。しかし、このルールが守られる保証はない。コンパイラや設定によっては x、z、y の順に並ぶこともあり、Rust では #[repr(C)] で C と同じ並びを指定する必要がある。

使い方が単純で小規模なうちは、作る人と使う人が話し合うか、ツールをそろえれば済んだ。しかし、複数の WASM ファイルを使い、あるモジュールの関数を別のモジュールにインポートするような大きなプロジェクトが増え、WASM を出力できるツールも増えると、問題が表に出てきた。Component Model は、この 5 つの問題をすべて解決するために作られた。

仕組み

Component Model は、次の 3 つの仕様をまとめたものである。

  • ABI(Application Binary Interface)
  • IDL(インタフェース記述言語)。Component Model では WIT を使う
  • バイナリフォーマット

この仕様に従って作った WASM ファイルが WebAssembly Component である。WASI の Preview 2 も、Component Model のインタフェースに従って作られている。

例

  • cargo-component: Rust のコードを WebAssembly Component としてビルドする
  • Wasmtime: WebAssembly Component を実行するクレートと CLI

注意点

2024 年 10 月時点では Phase 1(Feature Proposal)の段階だが、すでに多くのツールが作られ、広く受け入れられている。

関連

出典