Part 2 of #3275 — see its Phase 2 section for the full spec. Blocked by #3295 (Phase 1).
Verified against the installed quickjs-emscripten@0.32.0: the sync release variant @jitl/quickjs-wasmfile-release-sync (RELEASE_SYNC) is already installed — no new dependency. The sync QuickJSContext exposes evalCode() + newPromise(), and its .runtime exposes executePendingJobs() / setInterruptHandler() / setMemoryLimit() / setMaxStackSize() — identical to the async path.
Change (packages/runtime/src/sandbox/quickjs-runner.ts):
newAsyncContext() → const mod = await newQuickJSWASMModule(); const vm = mod.newContext(); — a fresh isolated module (own linear memory) per invocation, so the isolation model is UNCHANGED (confirmed in the .d.ts; only newContext() on a shared module shares memory, which we do not do).vm.evalCodeAsync(wrapped) → vm.evalCode(wrapped).- Imports/types:
newAsyncContext/QuickJSAsyncContext → newQuickJSWASMModule/QuickJSContext. - Disposal: hold both
mod and vm; in finally dispose the context then the module (verify whether disposing the context alone frees the module in 0.32 — dispose the module explicitly to be safe; it owns the linear memory).
Why it's safe now: the original crashes were an ASYNCIFY limitation (one suspended stack per module); the deferred-promise + pump redesign already removed all wasm-stack suspension (installApiMethod uses plain newPromise, not newAsyncifiedFunction). See #3275 §Phase 2.
Acceptance criteria
Refs: #3275 (parent) · #3295 (Phase 1).
🤖 Generated with Claude Code
Part 2 of #3275 — see its Phase 2 section for the full spec. Blocked by #3295 (Phase 1).
Verified against the installed
quickjs-emscripten@0.32.0: the sync release variant@jitl/quickjs-wasmfile-release-sync(RELEASE_SYNC) is already installed — no new dependency. The syncQuickJSContextexposesevalCode()+newPromise(), and its.runtimeexposesexecutePendingJobs()/setInterruptHandler()/setMemoryLimit()/setMaxStackSize()— identical to the async path.Change (
packages/runtime/src/sandbox/quickjs-runner.ts):newAsyncContext()→const mod = await newQuickJSWASMModule(); const vm = mod.newContext();— a fresh isolated module (own linear memory) per invocation, so the isolation model is UNCHANGED (confirmed in the.d.ts; onlynewContext()on a shared module shares memory, which we do not do).vm.evalCodeAsync(wrapped)→vm.evalCode(wrapped).newAsyncContext/QuickJSAsyncContext→newQuickJSWASMModule/QuickJSContext.modandvm; infinallydispose the context then the module (verify whether disposing the context alone frees the module in 0.32 — dispose the module explicitly to be safe; it owns the linear memory).Why it's safe now: the original crashes were an ASYNCIFY limitation (one suspended stack per module); the deferred-promise + pump redesign already removed all wasm-stack suspension (
installApiMethoduses plainnewPromise, notnewAsyncifiedFunction). See #3275 §Phase 2.Acceptance criteria
quickjs-runner.bench.ts,npx vitest bench) shows the expected compile/instantiate + per-instruction win vs asyncify.@objectstack/runtimepatch/minor).Refs: #3275 (parent) · #3295 (Phase 1).
🤖 Generated with Claude Code