Skip to content

Wasm module serialization: structured clone + dynamic worker loader support - #6997

Open
guybedford wants to merge 4 commits into
mainfrom
gbedford/wasm-module-serialization
Open

Wasm module serialization: structured clone + dynamic worker loader support#6997
guybedford wants to merge 4 commits into
mainfrom
gbedford/wasm-module-serialization

Conversation

@guybedford

@guybedford guybedford commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

This implements serialization of WebAssembly.Module in two places: structured clone, and the dynamic worker loader modules bag — allowing compiled Wasm modules to be passed between workers without recompilation, per the WebAssembly Web API serialization steps.

The motivating flow is source phase imports into dynamic workers:

import source mod from './lib.wasm';

const worker = env.LOADER.load({
  compatibilityDate: "2026-08-13",
  mainModule: "index.js",
  modules: {
    "index.js": `
      import source mod from './lib.wasm';
      export default { /* ... WebAssembly.instantiate(mod) ... */ };
    `,
    "lib.wasm": mod,
  },
});

What was implemented:

  • jsg::Serializer/Deserializer now implement the GetWasmModuleTransferId/GetWasmModuleFromId delegate hooks, carrying v8::CompiledWasmModules in Released::wasmModules. Support is opt-in (Options::supportWasmModules) since compiled code can only be shared in-process: structuredClone() enables it; RPC and storage serialization continue to throw DataCloneError at serialize time (matching the spec's forStorage behavior).
  • The worker loader modules bag accepts a WebAssembly.Module as a module value, or { wasm: module } alongside the existing { wasm: bytes } form.
  • WorkerSource::WasmModule carries an optional v8::CompiledWasmModule; both the legacy and new module registries use FromCompiledModule when present (the new registry seeds its existing compile cache), so the loaded worker shares compiled code with zero recompilation.
  • jsg::V8Ref<v8::WasmModuleObject> is now unwrappable, with rtti and TypeScript type updates (Record<string, string | WebAssembly.Module | WorkerLoaderModule>).

Note that structuredClone(wasmModule) previously threw DataCloneError and now succeeds. This is intentionally not gated behind a compat flag as it is purely additive spec-conforming behavior, matching browsers (same-agent-cluster cloning).

Test coverage: end-to-end loader tests (source phase import → loader → import source + default import in the child, against both module registries, covering both accepted forms), and structured clone tests including instantiation of the clone and object identity deduplication.

In future, dynamic import() could work directly against these module objects as well, per the ESM Phase Imports proposal.

Adds WasmModuleObject to the V8 value subclass list so that
jsg::V8Ref<v8::WasmModuleObject> can be used as a parameter and struct
field type, along with an rtti mapping.
Implements the GetWasmModuleTransferId / GetWasmModuleFromId serializer
delegate hooks, sharing compiled code across the clone via
v8::CompiledWasmModule per the WebAssembly Web API serialization
steps. Support is opt-in via Serializer::Options::supportWasmModules
since compiled code can only be shared in-process: structuredClone()
enables it, while RPC and storage serialization continue to throw
DataCloneError at serialization time.
A module in the worker loader modules bag may now be provided as a
WebAssembly.Module directly (e.g. obtained via a source phase import),
either as the module value itself or as { wasm: module }. The compiled
code is shared with the loaded worker via v8::CompiledWasmModule rather
than recompiling the wire bytes, in both the legacy and new module
registries. Inside the loaded worker the module can be imported with
import source, matching the ESM phase imports proposal semantics.
@guybedford
guybedford requested review from a team as code owners August 13, 2026 23:19
@guybedford
guybedford requested a review from emily-shen August 13, 2026 23:19
@ask-bonk

This comment was marked as outdated.

2 similar comments
@ask-bonk

This comment was marked as outdated.

@ask-bonk

This comment was marked as outdated.

@ask-bonk

This comment was marked as outdated.

eslint's parser does not yet support import source syntax, so the
loader test using it moves to a dedicated wd-test with lint disabled,
following the module-imports-test precedent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant