1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add a more complete test for [76c8435a] and add some commentary about (A) the inability to automatically clean up automatically-generated WASM proxy functions for sqlite3_set_auxdata() destructors and (B) how to deal with (A) to avoid leaking WASM proxy functions.

FossilOrigin-Name: d693c2dddbd10a2e0b77893b04b11502e30b768f1b06814105f7f35172845fb9
This commit is contained in:
stephan
2025-02-03 14:55:56 +00:00
parent cf9f841c5e
commit d98689f4d3
4 changed files with 105 additions and 10 deletions

View File

@ -228,6 +228,31 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}),
'*'
]],
/**
2025-02-03: We do not have a way to automatically clean up
destructors which are automatically converted from JS functions
via the final argument to sqlite3_set_auxdata(). Because of
that, it is strongly recommended that clients use
wasm.installFunction() to create such callbacks, then pass that
pointer to sqlite3_set_auxdata(). Relying on automated
conversions here will lead to leaks of JS/WASM proxy functions
because sqlite3_set_auxdata() is frequently called in UDFs.
The sqlite3.oo1.DB class's onclose handlers can be used for this
purpose. For example:
const pAuxDtor = wasm.installFunction('v(p)', function(ptr){
//free ptr
});
myDb.onclose = {
after: ()=>{
wasm.uninstallFunction(pAuxDtor);
}
};
Then pass pAuxDtor as the final argument to appropriate
sqlite3_set_auxdata() calls.
*/
["sqlite3_set_auxdata", undefined, [
"sqlite3_context*", "int", "*",
new wasm.xWrap.FuncPtrAdapter({
@ -1047,6 +1072,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'sqlite3_set_authorizer',
'sqlite3_trace_v2',
'sqlite3_update_hook'
/*
We do not yet have a way to clean up automatically-converted
sqlite3_set_auxdata() finalizers.
*/
]) {
const x = wasm.exports[name];
if( !x ){

View File

@ -3437,6 +3437,73 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
}
})
.t({
/* https://github.com/sqlite/sqlite-wasm/issues/92 */
name: 'sqlite3_set_auxdata() binding signature',
test: function(sqlite3){
const db = new sqlite3.oo1.DB();
const stack = wasm.pstack.pointer;
const pAux = wasm.pstack.alloc(4);
let pAuxDestructed = 0;
const args = [];
const pAuxDtor = wasm.installFunction('v(p)', function(ptr){
//log("freeing auxdata");
++pAuxDestructed;
});
let pAuxDtorDestructed = false;
db.onclose = {
after: ()=>{
pAuxDtorDestructed = true;
wasm.uninstallFunction(pAuxDtor);
}
};
try{
db.createFunction("auxtest",{
xFunc: function(pCx, x, y){
args.push(x);
T.assert(wasm.isPtr(pCx));
const localAux = capi.sqlite3_get_auxdata(pCx, 0);
if( !localAux ){
//log("setting auxdata");
/**
We do not currently an automated way to clean up
auxdata finalizer functions (the 4th argument to
sqlite3_set_auxdata()) which get automatically
converted from JS to WASM. Because of that, relying
on automated conversions for those is not
recommended. Instead, follow the pattern show in
this function: use wasm.installFunction() to create
the function, then pass the resulting function
pointer this function, and cleanup (at some point)
using wasm.uninstallFunction().
*/
capi.sqlite3_set_auxdata(pCx, 0, pAux, pAuxDtor);
}else{
/* This is never actually hit in this example and it's
not entirely clear how to cause it to. The point of
this test, however, is to demonstrate that the
finalizer impl gets triggered, so we're not going to
fret over this at the moment. */
//log("seen auxdata",localAux);
T.assert(pAux===localAux);
}
return x;
}
});
db.exec([
"create table t(a);",
"insert into t(a) values(1),(2),(3);",
"select auxtest(a,a), auxtest(a,a) from t order by a"
]);
}finally{
db.close();
wasm.pstack.restore(stack);
}
T.assert(6===args.length);
T.assert(pAuxDestructed>0);
T.assert(pAuxDtorDestructed);
}
})
;/*end of Bug Reports group*/;
////////////////////////////////////////////////////////////////////////