3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-19 10:52:02 +00:00

Update emscripten (#7473)

* fixes for newer emscripten thread handling behavior

* fix return type for async wrapper functions

* update prettier

* update typescript and fix errors

* update emscripten version in CI

* update js readme about tests
This commit is contained in:
Kevin Gibbons 2024-12-06 18:11:14 -08:00 committed by GitHub
parent 4fbf54afd0
commit e5f8327483
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 275 additions and 108 deletions

View file

@ -40,7 +40,13 @@ function spawnSync(command: string, opts: SpawnOptions = {}) {
}
function exportedFuncs(): string[] {
const extras = ['_malloc', '_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)];
const extras = [
'_malloc',
'_free',
'_set_throwy_error_handler',
'_set_noop_error_handler',
...asyncFuncs.map(f => '_async_' + f),
];
// TODO(ritave): This variable is unused in original script, find out if it's important
const fns: any[] = (functions as any[]).filter(f => !asyncFuncs.includes(f.name));
@ -66,10 +72,10 @@ fs.mkdirSync(path.dirname(ccWrapperPath), { recursive: true });
fs.writeFileSync(ccWrapperPath, makeCCWrapper());
const fns = JSON.stringify(exportedFuncs());
const methods = '["ccall","FS","allocate","UTF8ToString","intArrayFromString","ALLOC_NORMAL"]';
const methods = '["PThread","ccall","FS","UTF8ToString","intArrayFromString"]';
const libz3a = path.normalize('../../../build/libz3.a');
spawnSync(
`emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s DEMANGLE_SUPPORT=1 -s TOTAL_MEMORY=2GB -s TOTAL_STACK=20MB -I z3/src/api/ -o build/z3-built.js`,
`emcc build/async-fns.cc ${libz3a} --std=c++20 --pre-js src/low-level/async-wrapper.js -g2 -pthread -fexceptions -s WASM_BIGINT -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=0 -s PTHREAD_POOL_SIZE_STRICT=0 -s MODULARIZE=1 -s 'EXPORT_NAME="initZ3"' -s EXPORTED_RUNTIME_METHODS=${methods} -s EXPORTED_FUNCTIONS=${fns} -s DISABLE_EXCEPTION_CATCHING=0 -s SAFE_HEAP=0 -s TOTAL_MEMORY=2GB -s TOTAL_STACK=20MB -I z3/src/api/ -o build/z3-built.js`,
);
fs.rmSync(ccWrapperPath);

View file

@ -58,8 +58,24 @@ void wrapper(Args&&... args) {
reject_async('failed with unknown exception');
});
}
MAIN_THREAD_ASYNC_EM_ASM({
// this clears the earliest timeout
// not necessarily the one corresponding to this thread
// but that's ok
clearTimeout(threadTimeouts.shift());
});
});
t.detach();
EM_ASM({
// https://github.com/emscripten-core/emscripten/issues/23092
// in Node.js, the process will die if there is no active work
// which can happen while the thread is spawning
// or while it is running
// so we set a timeout here so it stays alive
// this needs to be longer than it could conceivably take to call the one function
// it gets cleared as soon as the thread actually finishes
threadTimeouts.push(setTimeout(() => {}, 600000));
});
}
template<typename Fn, Fn fn, typename... Args>
@ -79,8 +95,24 @@ void wrapper_str(Args&&... args) {
reject_async(new Error('failed with unknown exception'));
});
}
MAIN_THREAD_ASYNC_EM_ASM({
// this clears the earliest timeout
// not necessarily the one corresponding to this thread
// but that's ok
clearTimeout(threadTimeouts.shift());
});
});
t.detach();
EM_ASM({
// https://github.com/emscripten-core/emscripten/issues/23092
// in Node.js, the process will die if there is no active work
// which can happen while the thread is spawning
// or while it is running
// so we set a timeout here so it stays alive
// this needs to be longer than it could conceivably take to call the one function
// it gets cleared as soon as the thread actually finishes
threadTimeouts.push(setTimeout(() => {}, 600000));
});
}

View file

@ -10,7 +10,7 @@ assert(process.argv.length === 4, `Usage: ${process.argv[0]} ${process.argv[1]}
const wrapperFilePath = process.argv[2];
const typesFilePath = process.argv[3];
function makeTsWrapper() {
async function makeTsWrapper() {
const subtypes = {
__proto__: null,
Z3_sort: 'Z3_ast',
@ -339,8 +339,10 @@ function makeTsWrapper() {
`.trim();
}
// async functions are invocations of the wrapper from make-ts-wrapper.ts
// the wrapper spawns a thread and returns void, so we need to use void as the return type here
// prettier-ignore
let invocation = `Mod.ccall('${isAsync ? "async_" : ""}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(", ")}])`;
let invocation = `Mod.ccall('${isAsync ? "async_" : ""}${fn.name}', '${isAsync ? 'void' : cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(", ")}])`;
if (isAsync) {
invocation = `await Mod.async_call(() => ${invocation})`;
@ -462,13 +464,18 @@ export async function init(initModule: any) {
`;
return {
wrapperDocument: prettier.format(wrapperDocument, { singleQuote: true, parser: 'typescript' }),
typesDocument: prettier.format(typesDocument, { singleQuote: true, parser: 'typescript' }),
wrapperDocument: await prettier.format(wrapperDocument, { singleQuote: true, parser: 'typescript' }),
typesDocument: await prettier.format(typesDocument, { singleQuote: true, parser: 'typescript' }),
};
}
const { wrapperDocument, typesDocument } = makeTsWrapper();
fs.mkdirSync(path.dirname(wrapperFilePath), { recursive: true });
fs.writeFileSync(wrapperFilePath, wrapperDocument);
fs.mkdirSync(path.dirname(typesFilePath), { recursive: true });
fs.writeFileSync(typesFilePath, typesDocument);
(async () => {
const { wrapperDocument, typesDocument } = await makeTsWrapper();
fs.mkdirSync(path.dirname(wrapperFilePath), { recursive: true });
fs.writeFileSync(wrapperFilePath, wrapperDocument);
fs.mkdirSync(path.dirname(typesFilePath), { recursive: true });
fs.writeFileSync(typesFilePath, typesDocument);
})().catch(e => {
console.error(e);
process.exit(1);
});