mirror of
https://github.com/Z3Prover/z3
synced 2026-01-22 01:54:44 +00:00
Add TypeScript API parity: Solver introspection, congruence closure, and Model sort universe methods (#8129)
* Initial plan
* Add TypeScript API parity: Solver and Model introspection methods
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Format code and add API parity demo example
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Add comprehensive API parity documentation
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Fix Context usage in tests and demo - use api.Context('main')
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Delete src/api/js/API_PARITY.md
* Delete src/api/js/examples/high-level/api-parity-demo.ts
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
bac004047b
commit
c324f41eb0
5 changed files with 516 additions and 43 deletions
|
|
@ -12,26 +12,28 @@ export function makeCCWrapper() {
|
|||
if (fn == null) {
|
||||
throw new Error(`could not find definition for ${fnName}`);
|
||||
}
|
||||
|
||||
|
||||
// Check if function has array parameters
|
||||
const arrayParams = fn.params.filter(p => p.isArray && p.kind === 'in_array');
|
||||
const hasArrayParams = arrayParams.length > 0;
|
||||
|
||||
|
||||
if (hasArrayParams) {
|
||||
// Generate custom wrapper for functions with array parameters
|
||||
const paramList = fn.params.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`).join(', ');
|
||||
|
||||
const paramList = fn.params
|
||||
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
|
||||
.join(', ');
|
||||
|
||||
// Find the size parameter for each array and build copy/free code
|
||||
const arrayCopies: string[] = [];
|
||||
const arrayFrees: string[] = [];
|
||||
const arrayCopyNames: string[] = [];
|
||||
|
||||
|
||||
for (let p of arrayParams) {
|
||||
const sizeParam = fn.params[p.sizeIndex!];
|
||||
const ptrType = p.cType.endsWith('*') ? p.cType : `${p.cType}*`;
|
||||
const copyName = `${p.name}_copy`;
|
||||
arrayCopyNames.push(copyName);
|
||||
|
||||
|
||||
// Allocate and copy with null check
|
||||
arrayCopies.push(`${ptrType} ${copyName} = (${ptrType})malloc(sizeof(${p.cType}) * ${sizeParam.name});`);
|
||||
arrayCopies.push(`if (!${copyName}) {`);
|
||||
|
|
@ -39,25 +41,27 @@ export function makeCCWrapper() {
|
|||
arrayCopies.push(` return;`);
|
||||
arrayCopies.push(`}`);
|
||||
arrayCopies.push(`memcpy(${copyName}, ${p.name}, sizeof(${p.cType}) * ${sizeParam.name});`);
|
||||
|
||||
|
||||
arrayFrees.push(`free(${copyName});`);
|
||||
}
|
||||
|
||||
|
||||
// Build lambda capture list
|
||||
const nonArrayParams = fn.params.filter(p => !p.isArray || p.kind !== 'in_array');
|
||||
const captureList = [...arrayCopyNames, ...nonArrayParams.map(p => p.name)].join(', ');
|
||||
|
||||
|
||||
// Build argument list for the actual function call, using copied arrays
|
||||
const callArgs = fn.params.map(p => {
|
||||
if (p.isArray && p.kind === 'in_array') {
|
||||
return `${p.name}_copy`;
|
||||
}
|
||||
return p.name;
|
||||
}).join(', ');
|
||||
|
||||
const callArgs = fn.params
|
||||
.map(p => {
|
||||
if (p.isArray && p.kind === 'in_array') {
|
||||
return `${p.name}_copy`;
|
||||
}
|
||||
return p.name;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
const isString = fn.cRet === 'Z3_string';
|
||||
const returnType = isString ? 'auto' : fn.cRet;
|
||||
|
||||
|
||||
wrappers.push(
|
||||
`
|
||||
extern "C" void async_${fn.name}(${paramList}) {
|
||||
|
|
@ -65,15 +69,19 @@ extern "C" void async_${fn.name}(${paramList}) {
|
|||
std::thread t([${captureList}] {
|
||||
try {
|
||||
${returnType} result = ${fn.name}(${callArgs});
|
||||
${isString ? `
|
||||
${
|
||||
isString
|
||||
? `
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
resolve_async(UTF8ToString($0));
|
||||
}, result);
|
||||
` : `
|
||||
`
|
||||
: `
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
resolve_async($0);
|
||||
}, result);
|
||||
`}
|
||||
`
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
reject_async(new Error(UTF8ToString($0)));
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ async function makeTsWrapper() {
|
|||
|
||||
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
|
||||
|
|
@ -181,7 +181,7 @@ 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') {
|
||||
|
|
@ -197,13 +197,14 @@ async function makeTsWrapper() {
|
|||
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 = `
|
||||
suffix =
|
||||
`
|
||||
} finally {
|
||||
${allocatedArrays.map(arr => `Mod._free(${arr});`).join('\n ')}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue