3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-17 15:39:27 +00:00

Fix memory lifetime bug in async array parameter handling for JS API (#8125)

* Initial plan

* Fix async array parameter handling in JS API wrappers

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Add test for solver.check() with assumptions

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Address code review feedback: add null checks and improve readability

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

* Add unsatCore() method to Solver class

Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
Copilot 2026-01-08 18:43:58 -08:00 committed by GitHub
parent dc2d2e2edf
commit 7a35caa60a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 159 additions and 13 deletions

View file

@ -137,7 +137,7 @@ async function makeTsWrapper() {
// otherwise fall back to ccall
const ctypes = fn.params.map(p =>
let ctypes = fn.params.map(p =>
p.kind === 'in_array' ? 'array' : p.kind === 'out_array' ? 'number' : p.isPtr ? 'number' : toEmType(p.type),
);
@ -149,6 +149,8 @@ async function makeTsWrapper() {
const args: (string | FuncParam)[] = fn.params;
let arrayLengthParams = new Map();
let allocatedArrays: string[] = []; // Track allocated arrays for cleanup
for (let p of inParams) {
if (p.nullable && !p.isArray) {
// this would be easy to implement - just map null to 0 - but nothing actually uses nullable non-array input parameters, so we can't ensure we've done it right
@ -179,6 +181,33 @@ async function makeTsWrapper() {
}
args[sizeIndex] = `${p.name}.length`;
params[sizeIndex] = null;
// For async functions, we need to manually manage array memory
// because ccall frees it before the async thread uses it
if (isAsync && p.kind === 'in_array') {
const paramIdx = fn.params.indexOf(p);
const ptrName = `${p.name}_ptr`;
allocatedArrays.push(ptrName);
// Allocate memory for array of pointers (4 bytes per pointer on wasm32)
prefix += `
const ${ptrName} = Mod._malloc(${p.name}.length * 4);
Mod.HEAPU32.set(${p.name} as unknown as number[], ${ptrName} / 4);
`.trim();
args[paramIdx] = ptrName;
ctypes[paramIdx] = 'number'; // Pass as pointer, not array
}
}
// Add try-finally for async functions with allocated arrays
if (isAsync && allocatedArrays.length > 0) {
prefix += `
try {
`.trim();
suffix = `
} finally {
${allocatedArrays.map(arr => `Mod._free(${arr});`).join('\n ')}
}
`.trim() + suffix;
}
let returnType = fn.ret;