Skip to content

WASM 連携

WASM パッケージは Formulon の実行入口の中で最も適用範囲が広いものです。ブラウザ・Web Worker・Node から、JS API も formulon-cell も同じ @libraz/formulon バイナリで動かします。

ホスティングが重要

ブラウザでの成功はサーバーのヘッダー、Worker 形式、バンドラ挙動に依存します。ローカル開発環境だけでなく、デプロイ先で必ず確認してください。

用語: pthread worker

WASM モジュールは -pthread 付きでビルドされており、recalc scheduler が内部で Emscripten 由来の Web Worker を起動します。Worker 間で WASM heap を共有するために SharedArrayBuffer を使うため、ブラウザでは cross-origin isolation が必要になります。

用語: COOP / COEP(Cross-Origin Isolation)

ブラウザが SharedArrayBuffer を公開するために必要な 2 つの HTTP レスポンスヘッダです。両方をページに付ける必要があります。

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

どちらかでも欠けると、createFormulon() はそのままインスタンス化に失敗します ── pthread プール用の SharedArrayBuffer 生成が例外を投げ、Promise は reject されます。生の WASM ローダー自体にフォールバックはなく、createFormulon() を直接呼ぶコードは reject を自分で処理しなければなりません。formulon-cell のような上位ラッパーは、失敗する代わりに簡易エンジンへフォールバックすることを選べます(preferStub: true)が、それは formulon-cell 側の挙動であって @libraz/formulon の挙動ではありません。opt-in のフォールバック経路は formulon-cell を参照してください。

  1. ページ読み込み
  2. COOP/COEP ヘッダー確認
  3. createFormulon()
  4. SharedArrayBuffer / pthread プール
  5. Workbook.loadBytes()

Module は 1 度だけ初期化する

Worker / プロセスごとに 1 度 createFormulon() を呼び、関連処理で workbook インスタンスを再利用します。

ts
import createFormulon from '@libraz/formulon'

const Module = await createFormulon()
const result = Module.evalFormula('=SUM(1,2,3)')

createFormulon() は非同期です。ブラウザでは .wasm の取得と pthread プールの起動を伴うため、Module 参照は長寿命にしてください。

バイト列を明示的に渡す

ワークブックの入出力はバイト列で行います。UI 層・アップロード層・永続化層と計算層を切り離す設計です。

WASM Workbook ハンドルはネイティブメモリを持ち、JS の GC 対象ではありません。使い終わったら必ず delete() してください。

ts
const workbook = Module.Workbook.loadBytes(bytes)
try {
  if (!workbook.isValid()) throw new Error(Module.lastErrorMessage())
  workbook.recalc()
} finally {
  workbook.delete()
}

ライフタイムをヘルパで包む

try / finally を withWorkbook(bytes, fn) のようなヘルパに閉じ込めると、毎回の呼び出しが揃います。delete() を忘れるとリロードまで WASM heap 内で残るリークになります。

サイズ予算

WASM ビルドには厳しいサイズ予算があります。ブラウザに乗るパスへの依存追加は測定したうえで行ってください。実数値は サイズ予算 を参照。

Bundler 設定

Vite では ES module worker を指定します。

ts
export default defineConfig({
  worker: { format: 'es' },
  optimizeDeps: { exclude: ['@libraz/formulon'] },
  build: {
    target: 'es2022',
    rollupOptions: { external: [/^node:/] }
  }
})

ブラウザでは、pthread worker を有効にする場合は上記の COOP / COEP ヘッダを配信してください。formulon-cell 側の同じ設定は バンドラ設定、bundler のよくあるメッセージは トラブルシュート を参照。

次に読むもの