From bc7934b1a2fdc88d64764732c113baed6bbae3e3 Mon Sep 17 00:00:00 2001 From: Ronnie Ayesigwa <98583453+TheBoyWhoLivedd@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:05:39 +0300 Subject: [PATCH 1/2] Fix sequential awaited calls in Asyncify runtimes Generate async cwrap bindings whenever an exported function may suspend, including in release builds. Add an asynchronous pending-job driver so guest jobs can safely resume through multiple awaited host calls without corrupting the runtime. --- .../src/runtime-asyncify.ts | 25 ++++++++ .../quickjs-emscripten-core/src/runtime.ts | 9 +++ .../quickjs-emscripten/src/quickjs.test.ts | 28 +++++++++ .../src/ffi.ts | 57 ++++++++++--------- .../src/ffi.ts | 57 ++++++++++--------- .../src/ffi.ts | 57 ++++++++++--------- .../src/ffi.ts | 57 ++++++++++--------- .../src/ffi.ts | 57 ++++++++++--------- scripts/generate.ts | 14 ++--- 9 files changed, 216 insertions(+), 145 deletions(-) diff --git a/packages/quickjs-emscripten-core/src/runtime-asyncify.ts b/packages/quickjs-emscripten-core/src/runtime-asyncify.ts index 8d99bd75..fa315dca 100644 --- a/packages/quickjs-emscripten-core/src/runtime-asyncify.ts +++ b/packages/quickjs-emscripten-core/src/runtime-asyncify.ts @@ -2,6 +2,7 @@ import type { QuickJSAsyncEmscriptenModule, QuickJSAsyncFFI, JSContextPointer, + JSContextPointerPointer, JSRuntimePointer, } from "@jitl/quickjs-ffi-types" import { QuickJSAsyncContext } from "./context-asyncify" @@ -21,6 +22,7 @@ import type { } from "./types" import { intrinsicsToFlags } from "./types" import { Lifetime } from "./lifetime" +import type { ExecutePendingJobsResult } from "./runtime" export class QuickJSAsyncRuntime extends QuickJSRuntime { declare public context: QuickJSAsyncContext | undefined @@ -92,4 +94,27 @@ export class QuickJSAsyncRuntime extends QuickJSRuntime { public override setMaxStackSize(stackSize: number): void { return super.setMaxStackSize(stackSize) } + + /** + * Asyncified version of {@link QuickJSRuntime.executePendingJobs}. + * + * Pending jobs may call an asyncified host function, so the synchronous + * version cannot safely drive a runtime created from an Asyncify build. + */ + async executePendingJobsAsync( + maxJobsToExecute: number | void = -1, + ): Promise { + const ctxPtrOut = this.memory.newMutablePointerArray(1) + try { + const valuePtr = await this.ffi.QTS_ExecutePendingJob_MaybeAsync( + this.rt.value, + maxJobsToExecute ?? -1, + ctxPtrOut.value.ptr, + ) + const ctxPtr = ctxPtrOut.value.typedArray[0] as JSContextPointer + return this.resolveExecutePendingJobsResult(valuePtr, ctxPtr) + } finally { + ctxPtrOut.dispose() + } + } } diff --git a/packages/quickjs-emscripten-core/src/runtime.ts b/packages/quickjs-emscripten-core/src/runtime.ts index e404f064..da1acdfe 100644 --- a/packages/quickjs-emscripten-core/src/runtime.ts +++ b/packages/quickjs-emscripten-core/src/runtime.ts @@ -3,6 +3,7 @@ import type { JSContextPointer, JSContextPointerPointer, JSRuntimePointer, + JSValuePointer, EitherFFI, EitherModule, } from "@jitl/quickjs-ffi-types" @@ -253,6 +254,14 @@ export class QuickJSRuntime extends UsingDisposable implements Disposable { const ctxPtr = ctxPtrOut.value.typedArray[0] as JSContextPointer ctxPtrOut.dispose() + return this.resolveExecutePendingJobsResult(valuePtr, ctxPtr) + } + + /** @private */ + protected resolveExecutePendingJobsResult( + valuePtr: JSValuePointer, + ctxPtr: JSContextPointer, + ): ExecutePendingJobsResult { if (ctxPtr === 0) { // No jobs executed. this.ffi.QTS_FreeValuePointerRuntime(this.rt.value, valuePtr) diff --git a/packages/quickjs-emscripten/src/quickjs.test.ts b/packages/quickjs-emscripten/src/quickjs.test.ts index f603c256..1a7f85de 100644 --- a/packages/quickjs-emscripten/src/quickjs.test.ts +++ b/packages/quickjs-emscripten/src/quickjs.test.ts @@ -1224,6 +1224,34 @@ function asyncContextTests( }) describe("asyncify functions", () => { + it("supports sequential awaited host calls in one evaluation", async () => { + let asyncFunctionCalls = 0 + vm.newAsyncifiedFunction("get", async (pathHandle) => { + asyncFunctionCalls++ + const path = vm.getString(pathHandle) + return vm.newString(JSON.stringify({ path })) + }).consume((fn) => vm.setProp(vm.global, "get", fn)) + + const result = await vm.evalCodeAsync(` + (async () => { + const a = JSON.parse(await get("/a")) + const b = JSON.parse(await get("/b")) + const c = JSON.parse(await get("/c")) + return [a, b, c] + })() + `) + + const promise = vm.unwrapResult(result).consume((handle) => vm.resolvePromise(handle)) + vm.unwrapResult(await vm.runtime.executePendingJobsAsync()) + const resolved = vm.unwrapResult(await promise) + assert.deepEqual(vm.dump(resolved), [{ path: "/a" }, { path: "/b" }, { path: "/c" }]) + resolved.dispose() + assert.equal(asyncFunctionCalls, 3) + + const reuseResult = await vm.evalCodeAsync("1 + 1") + assert.equal(vm.unwrapResult(reuseResult).consume(vm.dump), 2) + }) + it("sees Promise as synchronous", async () => { let asyncFunctionCalls = 0 const asyncFn = async () => { diff --git a/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts b/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts index e89c9b41..0c2cced8 100644 --- a/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts @@ -210,6 +210,7 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], + { async: true }, ) QTS_IsGlobalSymbol: ( @@ -239,6 +240,7 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], + { async: true }, ) QTS_GetProp: ( @@ -253,11 +255,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetProp", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -271,11 +274,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetPropNumber", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_SetProp: ( ctx: JSContextPointer, @@ -291,12 +295,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ - "number", - "number", - "number", - "number", - ]) + ) => void | Promise = this.module.cwrap( + "QTS_SetProp", + null, + ["number", "number", "number", "number"], + { async: true }, + ) QTS_DefineProp: ( ctx: JSContextPointer, @@ -346,6 +350,7 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], + { async: true }, ) QTS_Call: ( @@ -364,13 +369,12 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ - "number", - "number", - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Call", "number", - ]) + ["number", "number", "number", "number", "number"], + { async: true }, + ) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -389,6 +393,7 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], + { async: true }, ) QTS_Eval: ( @@ -416,14 +421,12 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ - "number", - "number", - "number", - "string", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Eval", "number", - ]) + ["number", "number", "number", "string", "number", "number"], + { async: true }, + ) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts b/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts index e89c9b41..0c2cced8 100644 --- a/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts @@ -210,6 +210,7 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], + { async: true }, ) QTS_IsGlobalSymbol: ( @@ -239,6 +240,7 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], + { async: true }, ) QTS_GetProp: ( @@ -253,11 +255,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetProp", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -271,11 +274,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetPropNumber", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_SetProp: ( ctx: JSContextPointer, @@ -291,12 +295,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ - "number", - "number", - "number", - "number", - ]) + ) => void | Promise = this.module.cwrap( + "QTS_SetProp", + null, + ["number", "number", "number", "number"], + { async: true }, + ) QTS_DefineProp: ( ctx: JSContextPointer, @@ -346,6 +350,7 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], + { async: true }, ) QTS_Call: ( @@ -364,13 +369,12 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ - "number", - "number", - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Call", "number", - ]) + ["number", "number", "number", "number", "number"], + { async: true }, + ) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -389,6 +393,7 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], + { async: true }, ) QTS_Eval: ( @@ -416,14 +421,12 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ - "number", - "number", - "number", - "string", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Eval", "number", - ]) + ["number", "number", "number", "string", "number", "number"], + { async: true }, + ) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts b/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts index e89c9b41..0c2cced8 100644 --- a/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts @@ -210,6 +210,7 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], + { async: true }, ) QTS_IsGlobalSymbol: ( @@ -239,6 +240,7 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], + { async: true }, ) QTS_GetProp: ( @@ -253,11 +255,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetProp", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -271,11 +274,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetPropNumber", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_SetProp: ( ctx: JSContextPointer, @@ -291,12 +295,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ - "number", - "number", - "number", - "number", - ]) + ) => void | Promise = this.module.cwrap( + "QTS_SetProp", + null, + ["number", "number", "number", "number"], + { async: true }, + ) QTS_DefineProp: ( ctx: JSContextPointer, @@ -346,6 +350,7 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], + { async: true }, ) QTS_Call: ( @@ -364,13 +369,12 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ - "number", - "number", - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Call", "number", - ]) + ["number", "number", "number", "number", "number"], + { async: true }, + ) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -389,6 +393,7 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], + { async: true }, ) QTS_Eval: ( @@ -416,14 +421,12 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ - "number", - "number", - "number", - "string", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Eval", "number", - ]) + ["number", "number", "number", "string", "number", "number"], + { async: true }, + ) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts b/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts index e89c9b41..0c2cced8 100644 --- a/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts @@ -210,6 +210,7 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], + { async: true }, ) QTS_IsGlobalSymbol: ( @@ -239,6 +240,7 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], + { async: true }, ) QTS_GetProp: ( @@ -253,11 +255,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetProp", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -271,11 +274,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetPropNumber", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_SetProp: ( ctx: JSContextPointer, @@ -291,12 +295,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ - "number", - "number", - "number", - "number", - ]) + ) => void | Promise = this.module.cwrap( + "QTS_SetProp", + null, + ["number", "number", "number", "number"], + { async: true }, + ) QTS_DefineProp: ( ctx: JSContextPointer, @@ -346,6 +350,7 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], + { async: true }, ) QTS_Call: ( @@ -364,13 +369,12 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ - "number", - "number", - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Call", "number", - ]) + ["number", "number", "number", "number", "number"], + { async: true }, + ) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -389,6 +393,7 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], + { async: true }, ) QTS_Eval: ( @@ -416,14 +421,12 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ - "number", - "number", - "number", - "string", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Eval", "number", - ]) + ["number", "number", "number", "string", "number", "number"], + { async: true }, + ) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts b/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts index e89c9b41..0c2cced8 100644 --- a/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts @@ -210,6 +210,7 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], + { async: true }, ) QTS_IsGlobalSymbol: ( @@ -239,6 +240,7 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], + { async: true }, ) QTS_GetProp: ( @@ -253,11 +255,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetProp", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -271,11 +274,12 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_GetPropNumber", "number", - ]) + ["number", "number", "number"], + { async: true }, + ) QTS_SetProp: ( ctx: JSContextPointer, @@ -291,12 +295,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ - "number", - "number", - "number", - "number", - ]) + ) => void | Promise = this.module.cwrap( + "QTS_SetProp", + null, + ["number", "number", "number", "number"], + { async: true }, + ) QTS_DefineProp: ( ctx: JSContextPointer, @@ -346,6 +350,7 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], + { async: true }, ) QTS_Call: ( @@ -364,13 +369,12 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ - "number", - "number", - "number", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Call", "number", - ]) + ["number", "number", "number", "number", "number"], + { async: true }, + ) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -389,6 +393,7 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], + { async: true }, ) QTS_Eval: ( @@ -416,14 +421,12 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ - "number", - "number", - "number", - "string", - "number", + ) => JSValuePointer | Promise = this.module.cwrap( + "QTS_Eval", "number", - ]) + ["number", "number", "number", "string", "number", "number"], + { async: true }, + ) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/scripts/generate.ts b/scripts/generate.ts index aac0ddaf..b2ccc4b3 100755 --- a/scripts/generate.ts +++ b/scripts/generate.ts @@ -223,21 +223,15 @@ function renderFunction(args: { const ffiParams = JSON.stringify(params.map((param) => param.type.ffi)) const cwrapArgs = [JSON.stringify(functionName), JSON.stringify(returnType.ffi), ffiParams] - if (context.DEBUG && async) { + if (async) { // https://emscripten.org/docs/porting/asyncify.html#usage-with-ccall // Passing {async:true} to cwrap/ccall will wrap all return values in // Promise.resolve(...), even if the c code doesn't suspend and returns a // primitive value. // - // When compiled with -s ASSERTIONS=1, Emscripten will throw if the - // function suspends and {async: true} wasn't passed. - // - // However, we'd like to avoid Promise/async overhead if the call can - // return a primitive value directly. So, we compile in {async:true} - // only in DEBUG mode, where assertions are enabled. - // - // Then we rely on our type system to ensure our code supports both - // primitive and promise-wrapped return values in production mode. + // Asyncify requires this option whenever the function may suspend. Debug + // builds assert when it is missing; release builds otherwise continue in + // a corrupted state. cwrapArgs.push("{ async: true }") } From 4a5b8ae6620815e2d021184dc09faf738060be00 Mon Sep 17 00:00:00 2001 From: Ronnie Ayesigwa <98583453+TheBoyWhoLivedd@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:21:32 +0300 Subject: [PATCH 2/2] Keep generated wrappers out of the source diff The build regenerates and formats variant FFI files from scripts/generate.ts. Exclude those generated outputs so the pull request contains only canonical source changes. --- .../src/ffi.ts | 57 +++++++++---------- .../src/ffi.ts | 57 +++++++++---------- .../src/ffi.ts | 57 +++++++++---------- .../src/ffi.ts | 57 +++++++++---------- .../src/ffi.ts | 57 +++++++++---------- 5 files changed, 135 insertions(+), 150 deletions(-) diff --git a/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts b/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts index 0c2cced8..e89c9b41 100644 --- a/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-ng-wasmfile-release-asyncify/src/ffi.ts @@ -210,7 +210,6 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], - { async: true }, ) QTS_IsGlobalSymbol: ( @@ -240,7 +239,6 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], - { async: true }, ) QTS_GetProp: ( @@ -255,12 +253,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetProp", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -274,12 +271,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetPropNumber", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_SetProp: ( ctx: JSContextPointer, @@ -295,12 +291,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap( - "QTS_SetProp", - null, - ["number", "number", "number", "number"], - { async: true }, - ) + ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ + "number", + "number", + "number", + "number", + ]) QTS_DefineProp: ( ctx: JSContextPointer, @@ -350,7 +346,6 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], - { async: true }, ) QTS_Call: ( @@ -369,12 +364,13 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Call", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ "number", - ["number", "number", "number", "number", "number"], - { async: true }, - ) + "number", + "number", + "number", + "number", + ]) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -393,7 +389,6 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], - { async: true }, ) QTS_Eval: ( @@ -421,12 +416,14 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Eval", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ "number", - ["number", "number", "number", "string", "number", "number"], - { async: true }, - ) + "number", + "number", + "string", + "number", + "number", + ]) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts b/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts index 0c2cced8..e89c9b41 100644 --- a/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-singlefile-browser-release-asyncify/src/ffi.ts @@ -210,7 +210,6 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], - { async: true }, ) QTS_IsGlobalSymbol: ( @@ -240,7 +239,6 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], - { async: true }, ) QTS_GetProp: ( @@ -255,12 +253,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetProp", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -274,12 +271,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetPropNumber", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_SetProp: ( ctx: JSContextPointer, @@ -295,12 +291,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap( - "QTS_SetProp", - null, - ["number", "number", "number", "number"], - { async: true }, - ) + ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ + "number", + "number", + "number", + "number", + ]) QTS_DefineProp: ( ctx: JSContextPointer, @@ -350,7 +346,6 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], - { async: true }, ) QTS_Call: ( @@ -369,12 +364,13 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Call", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ "number", - ["number", "number", "number", "number", "number"], - { async: true }, - ) + "number", + "number", + "number", + "number", + ]) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -393,7 +389,6 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], - { async: true }, ) QTS_Eval: ( @@ -421,12 +416,14 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Eval", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ "number", - ["number", "number", "number", "string", "number", "number"], - { async: true }, - ) + "number", + "number", + "string", + "number", + "number", + ]) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts b/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts index 0c2cced8..e89c9b41 100644 --- a/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-singlefile-cjs-release-asyncify/src/ffi.ts @@ -210,7 +210,6 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], - { async: true }, ) QTS_IsGlobalSymbol: ( @@ -240,7 +239,6 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], - { async: true }, ) QTS_GetProp: ( @@ -255,12 +253,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetProp", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -274,12 +271,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetPropNumber", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_SetProp: ( ctx: JSContextPointer, @@ -295,12 +291,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap( - "QTS_SetProp", - null, - ["number", "number", "number", "number"], - { async: true }, - ) + ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ + "number", + "number", + "number", + "number", + ]) QTS_DefineProp: ( ctx: JSContextPointer, @@ -350,7 +346,6 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], - { async: true }, ) QTS_Call: ( @@ -369,12 +364,13 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Call", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ "number", - ["number", "number", "number", "number", "number"], - { async: true }, - ) + "number", + "number", + "number", + "number", + ]) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -393,7 +389,6 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], - { async: true }, ) QTS_Eval: ( @@ -421,12 +416,14 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Eval", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ "number", - ["number", "number", "number", "string", "number", "number"], - { async: true }, - ) + "number", + "number", + "string", + "number", + "number", + ]) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts b/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts index 0c2cced8..e89c9b41 100644 --- a/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-singlefile-mjs-release-asyncify/src/ffi.ts @@ -210,7 +210,6 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], - { async: true }, ) QTS_IsGlobalSymbol: ( @@ -240,7 +239,6 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], - { async: true }, ) QTS_GetProp: ( @@ -255,12 +253,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetProp", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -274,12 +271,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetPropNumber", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_SetProp: ( ctx: JSContextPointer, @@ -295,12 +291,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap( - "QTS_SetProp", - null, - ["number", "number", "number", "number"], - { async: true }, - ) + ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ + "number", + "number", + "number", + "number", + ]) QTS_DefineProp: ( ctx: JSContextPointer, @@ -350,7 +346,6 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], - { async: true }, ) QTS_Call: ( @@ -369,12 +364,13 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Call", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ "number", - ["number", "number", "number", "number", "number"], - { async: true }, - ) + "number", + "number", + "number", + "number", + ]) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -393,7 +389,6 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], - { async: true }, ) QTS_Eval: ( @@ -421,12 +416,14 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Eval", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ "number", - ["number", "number", "number", "string", "number", "number"], - { async: true }, - ) + "number", + "number", + "string", + "number", + "number", + ]) QTS_GetModuleNamespace: ( ctx: JSContextPointer, diff --git a/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts b/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts index 0c2cced8..e89c9b41 100644 --- a/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts +++ b/packages/variant-quickjs-wasmfile-release-asyncify/src/ffi.ts @@ -210,7 +210,6 @@ export class QuickJSAsyncFFI { "QTS_GetSymbolDescriptionOrKey", "number", ["number", "number"], - { async: true }, ) QTS_IsGlobalSymbol: ( @@ -240,7 +239,6 @@ export class QuickJSAsyncFFI { "QTS_ExecutePendingJob", "number", ["number", "number", "number"], - { async: true }, ) QTS_GetProp: ( @@ -255,12 +253,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetProp", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetProp", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_GetPropNumber: ( ctx: JSContextPointer, @@ -274,12 +271,11 @@ export class QuickJSAsyncFFI { ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: number, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_GetPropNumber", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_GetPropNumber", "number", [ "number", - ["number", "number", "number"], - { async: true }, - ) + "number", + "number", + ]) QTS_SetProp: ( ctx: JSContextPointer, @@ -295,12 +291,12 @@ export class QuickJSAsyncFFI { this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer, prop_value: JSValuePointer | JSValueConstPointer, - ) => void | Promise = this.module.cwrap( - "QTS_SetProp", - null, - ["number", "number", "number", "number"], - { async: true }, - ) + ) => void | Promise = this.module.cwrap("QTS_SetProp", null, [ + "number", + "number", + "number", + "number", + ]) QTS_DefineProp: ( ctx: JSContextPointer, @@ -350,7 +346,6 @@ export class QuickJSAsyncFFI { "QTS_GetOwnPropertyNames", "number", ["number", "number", "number", "number", "number"], - { async: true }, ) QTS_Call: ( @@ -369,12 +364,13 @@ export class QuickJSAsyncFFI { this_obj: JSValuePointer | JSValueConstPointer, argc: number, argv_ptrs: JSValueConstPointerPointer, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Call", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Call", "number", [ "number", - ["number", "number", "number", "number", "number"], - { async: true }, - ) + "number", + "number", + "number", + "number", + ]) QTS_ResolveException: (ctx: JSContextPointer, maybe_exception: JSValuePointer) => JSValuePointer = this.module.cwrap("QTS_ResolveException", "number", ["number", "number"]) @@ -393,7 +389,6 @@ export class QuickJSAsyncFFI { "QTS_Dump", "number", ["number", "number"], - { async: true }, ) QTS_Eval: ( @@ -421,12 +416,14 @@ export class QuickJSAsyncFFI { filename: string, detectModule: EvalDetectModule, evalFlags: EvalFlags, - ) => JSValuePointer | Promise = this.module.cwrap( - "QTS_Eval", + ) => JSValuePointer | Promise = this.module.cwrap("QTS_Eval", "number", [ "number", - ["number", "number", "number", "string", "number", "number"], - { async: true }, - ) + "number", + "number", + "string", + "number", + "number", + ]) QTS_GetModuleNamespace: ( ctx: JSContextPointer,