Dynamic `import.defer()` semantics
7f06296
JSTests/modules/import-defer-dynamic-evaluation.js
+const ns = await import.defer("./import-defer/eval-tracker.js");
+shouldBe(JSON.stringify(globalThis.deferEvaluations), "[]");
+
+// A symbol-keyed lookup must not trigger evaluation either.
+shouldBe(ns[Symbol.toStringTag], "Deferred Module");
+shouldBe(JSON.stringify(globalThis.deferEvaluations), "[]");
+
+// First non-symbol access evaluates the module synchronously.
+shouldBe(ns.value, 42);
+shouldBe(JSON.stringify(globalThis.deferEvaluations), JSON.stringify(["eval-tracker"]));
+
+// Subsequent import.defer() calls hand back the same namespace and never re-evaluate.
+const ns2 = await import.defer("./import-defer/eval-tracker.js");
+shouldBe(ns2, ns);
The TC39 Deferred Module Evaluation proposal separates module linking from module execution: a deferred namespace proxy is returned immediately and the module body only runs on first non-Symbol property access. JSC's module pipeline is split across the engine (graph traversal, bytecode, microtask scheduling) and WebCore bindings (ScriptModuleLoader, JSDOMGlobalObject), so the new "import phase" parameter must be propagated through both layers.
This commit implements the dynamic import.defer(specifier) form. The module graph is loaded and linked but the deferred root is not executed; GatherAsynchronousTransitiveDependencies() collects unexecuted top-level-await (TLA) modules in post-order, evaluates them through two new internal microtasks (DynamicImportDeferLoadSettled, DynamicImportDeferDependencySettled), and resolves the returned promise to a deferred module namespace once all settle. The AND-join that waits for all TLA dep promises reuses JSPromiseCombinatorsGlobalContext as a shared counter cell, a pattern borrowed from Promise.all internals but with the critical spec constraint that then must never be looked up on the dependency promises.
import.defer(specifier)
├─► load() + link()
├─► GatherAsyncTransitiveDeps() → [dep1, dep2, ...]
├─► evaluate(depN) ──► AND-join counter (DynamicImportDeferDependencySettled)
└─► resolve(deferredNamespace)
Namespace proxy dispatch:
Symbol key ──► return value (NO evaluation)
String key ──► evaluate() ──► return value
Significance
This change introduces new JS-visible API, new microtask types, a new promise AND-join, and new namespace proxy dispatch semantics spanning every layer of the JSC module system — historically the highest-density area for security bugs in JS engines.
Audit directions
a Aaaaaaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa a Aaaaaa Aaaaaaa Aaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaa Aaaa Aaa Aaaaaaa Aaa Aaaa Aa Aaaaa Aaa Aaaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaa Aaa Aaaa Aaaaaaaa Aaa Aaaa Aa Aaaaa Aaaa Aa Aaaaaa Aa Aaa Aaaaaaaa a Aaa Aaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aa Aaaaaa Aaaa Aaaa Aaa Aaaaaaaaaa Aaaaaaaaaa
a Aaaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaa Aaaaaa Aaaa Aaaaa Aaaaaaa Aaaaaaaaaaa Aaaaaaaaaa Aaaaaa Aaaaaa Aaaaa Aaaa Aaaaa Aaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaa Aaaa Aaaaa Aaa Aaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaa Aaaa a Aaaaaaaaaaaaa Aaaaaaa Aa Aaaaaaaaa Aaaaa Aaaaaaa Aaaa Aaaaaa Aaa Aaaaaaa Aaaaaaaaaaaa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaa Aaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaa Aaaa Aaaaaaaaa Aaaaaa Aaaaaaa Aa Aaa Aaaaaaaaaaa Aa Aaa Aaaaaaaaa Aaaaaaaaaaa Aa Aa Aaaaaa Aaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaa Aaaaaaa Aaaaa Aaaaa Aa Aaaaaaa Aaaaaaaaaaaaaaaaa Aa Aaaaa Aaaaaaaa Aaaaaaaaaa
a Aaaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaaa Aaaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaa Aaaaaaaa Aaa Aaaaa Aaaaaaaa Aaaaaaaa a Aaaaaa Aaaa Aaaaaa Aaaaaa Aaaaaaaaa
a Aaaaaaaaaaa Aaaaaaaa Aaa Aa Aaaaaaaaaaaaaa Aa a Aa Aaaa Aaaaaaa Aaa Aaaa Aaaa Aaa Aaa Aaaaaa Aaaaa Aaaaaaaaaa Aa Aaa Aaaaaaaa Aaaaaaaaa Aaaaaa Aa Aaa Aaaaaaaaaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaa Aa Aaaaaaaaaa Aaa Aaa Aaaaaaa Aaaaa Aaaaaa Aa Aaaaaaa Aaa Aa Aaaaaaa Aaaaaaaaaaaaa
🔒New promise AND-join mechanics, namespace proxy dispatch boundaries, and cross-layer import phase propagation all have audit-worthy angles.
Subscribe to read more