Settle WebAssembly promise APIs in the extension runtime (#1056) (#1158)

JavaScriptCore settles WebAssembly.compile/instantiate from a run-loop
timer on the thread that owns the VM, and the runtime's queue never
spins one, so the promises stayed pending forever. sql.js loads that
way: Zotero's Search Database sat on Loading… with no error.

Route compile, instantiate and the streaming forms through the
synchronous Module and Instance constructors, and cover them in
ext-test with a module that must instantiate both ways and an invalid
one that must reject with CompileError.

Co-authored-by: Richard Demann <richard.demann@gmail.com>
This commit is contained in:
Abue Ammar
2026-09-25 17:35:53 +06:00
committed by GitHub
co-authored by Richard Demann
parent 5c9ffd4aee
commit ab9d0edf15
4 changed files with 69 additions and 9 deletions
+23
View File
@@ -71,6 +71,29 @@ if (!g.queueMicrotask) {
};
}
// ─── WebAssembly ────────────────────────────────────────────────────
// JSC settles the promise forms on a run loop the JS queue never spins; constructors don't wait.
if (g.WebAssembly) {
const { Module, Instance } = g.WebAssembly;
const compile = (bytes) => new Promise((resolve) => resolve(new Module(bytes)));
const instantiate = (source, imports) =>
new Promise((resolve) => {
if (source instanceof Module) {
resolve(new Instance(source, imports));
return;
}
const module = new Module(source);
resolve({ module, instance: new Instance(module, imports) });
});
const bytesOf = async (source) => new Uint8Array(await (await source).arrayBuffer());
g.WebAssembly.compile = compile;
g.WebAssembly.instantiate = instantiate;
g.WebAssembly.compileStreaming = async (source) => compile(await bytesOf(source));
g.WebAssembly.instantiateStreaming = async (source, imports) =>
instantiate(await bytesOf(source), imports);
}
// ─── Error reporting ────────────────────────────────────────────────
let uncaughtSink = (error) => log("error", ["Uncaught:", error]);