1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-10-24 09:53:10 +03:00

Diverse internal cleanups in the JS/WASM pieces. A potential fix for a hypothetical db-close-time resource leak of a subset of automated JS-to-WASM function conversions in Safari. That browser exposes WASM-exported functions via nullary wrappers, which causes a handful of them to misbehave (not clean up) at sqlite3_close_v2()-time.

FossilOrigin-Name: fabbc8b6d184d52a513e80fabd900f578424fc8a8055e3d64fac54b9e28ea18a
This commit is contained in:
stephan
2025-09-15 14:11:55 +00:00
parent 36595d0630
commit c35f07ec2f
4 changed files with 412 additions and 401 deletions

View File

@@ -542,8 +542,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
4th-argument value, taking care not to pass a value which
truncates a multi-byte UTF-8 character. When passing
WASM-format strings, it is important that the final argument be
valid or unexpected content can result can result, or even a
crash if the application reads past the WASM heap bounds.
valid or unexpected content can result, or WASM may crash if
the application reads past the WASM heap bounds.
*/
sqlite3_bind_text: undefined/*installed later*/,
@@ -614,10 +614,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
functions passed in to this routine, and thus wrapped by this
routine, get automatic conversions of arguments and result
values. The routines which perform those conversions are
exposed for client-side use as
sqlite3_create_function_v2.convertUdfArgs() and
sqlite3_create_function_v2.setUdfResult(). sqlite3_create_function()
and sqlite3_create_window_function() have those same methods.
exposed for client-side use as sqlite3_values_to_js(),
sqlite3_result_js(), and sqlite3_result_error_js().
For xFunc(), xStep(), and xFinal():
@@ -635,18 +633,14 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
possibly generating a console.error() message. Destructors
must not throw.
Once installed, there is currently no way to uninstall the
automatically-converted WASM-bound JS functions from WASM. They
can be uninstalled from the database as documented in the C
API, but this wrapper currently has no infrastructure in place
to also free the WASM-bound JS wrappers, effectively resulting
in a memory leak if the client uninstalls the UDF. Improving that
is a potential TODO, but removing client-installed UDFs is rare
in practice. If this factor is relevant for a given client,
they can create WASM-bound JS functions themselves, hold on to their
pointers, and pass the pointers in to here. Later on, they can
free those pointers (using `wasm.uninstallFunction()` or
equivalent).
Automatically-converted JS-to-WASM functions will be cleaned up
either when (A) this function is called again with the same
name, arity, and encoding, but null/0 values for the functions,
or (B) when pDb is passed to sqlite3_close_v2(). If this factor
is relevant for a given client, they can create WASM-bound JS
functions themselves, hold on to their pointers, and pass the
pointers in to here. Later on, they can free those pointers
(using `wasm.uninstallFunction()` or equivalent).
C reference: https://sqlite.org/c3ref/create_function.html
@@ -686,10 +680,10 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
2) sqlite3_prepare_v3(pDb, sqlPointer, sqlByteLen, prepFlags, ppStmt, sqlPointerToPointer)
Note that the SQL length argument (the 3rd argument) must, for
usage (1), always be negative because it must be a byte length
and that value is expensive to calculate from JS (where only
the character length of strings is readily available). It is
The SQL length argument (the 3rd argument) must, for usage (1),
always be negative because it must be a byte length and that
value is expensive to calculate from JS (where only the
character length of strings is readily available). It is
retained in this API's interface for code/documentation
compatibility reasons but is currently _always_ ignored. With
usage (2), the 3rd argument is used as-is but is is still
@@ -715,8 +709,10 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
(e.g. using capi.wasm.alloc() or equivalent). In that case,
the final argument may be 0/null/undefined or must be a pointer
to which the "tail" of the compiled SQL is written, as
documented for the C-side sqlite3_prepare_v3(). In case (2),
the underlying C function is called with the equivalent of:
documented for the C-side sqlite3_prepare_v3().
In case (2), the underlying C function is called with the
equivalent of:
(pDb, sqlAsPointer, sqlByteLen, prepFlags, ppStmt, pzTail)
@@ -1553,27 +1549,29 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
config.warn("sqlite3_js_vfs_create_file() is deprecated and",
"should be avoided because it can lead to C-level crashes.",
"See its documentation for alternative options.");
"See its documentation for alternatives.");
let pData;
if(data){
if(wasm.isPtr(data)){
if( wasm.isPtr(data) ){
pData = data;
}else if(data instanceof ArrayBuffer){
data = new Uint8Array(data);
}
if(data instanceof Uint8Array){
pData = wasm.allocFromTypedArray(data);
if(arguments.length<4 || !util.isInt32(dataLen) || dataLen<0){
dataLen = data.byteLength;
}
}else{
SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file().");
if( data instanceof ArrayBuffer ){
data = new Uint8Array(data);
}
if( data instanceof Uint8Array ){
pData = wasm.allocFromTypedArray(data);
if(arguments.length<4 || !util.isInt32(dataLen) || dataLen<0){
dataLen = data.byteLength;
}
}else{
SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file().");
}
}
}else{
pData = 0;
}
if(!util.isInt32(dataLen) || dataLen<0){
wasm.dealloc(pData);
if( pData && pData!==data ) wasm.dealloc(pData);
SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file().");
}
try{
@@ -1581,7 +1579,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
capi.sqlite3_js_rc_str(rc));
}finally{
wasm.dealloc(pData);
if( pData && pData!==data ) wasm.dealloc(pData);
}
};