From 027afccdcd6e9077b7043545a3f8bd32cfb5a397 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 23 Dec 2022 23:46:33 +0000 Subject: [PATCH 1/9] Reimplement JS's sqlite3_bind_text/blob() with hand-written bindings to permit more flexible inputs. Add automated JS-to-C function conversion to sqlite3_busy_handler(). sqlite3.wasm.xWrap()'s '*' argument conversion no longer treats JS strings as C-strings: those conversions require explicit opt-in via the 'string' converter (or equivalent). FossilOrigin-Name: 96ba44946b3e88b6aa305c4363cbbfeab0d9120b3d8c4d2587d68b9293ea7cc6 --- ext/wasm/api/sqlite3-api-glue.js | 153 ++++++++++++++++++++------- ext/wasm/api/sqlite3-api-oo1.js | 11 +- ext/wasm/api/sqlite3-api-prologue.js | 100 +++++++++++++++-- ext/wasm/common/whwasmutil.js | 44 ++++---- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 240 insertions(+), 88 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index a760e921fe..dd80963f3e 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -42,10 +42,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ wasm.bindingSignatures = [ // Please keep these sorted by function name! ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], - ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*" - /* TODO: we should arguably write a custom wrapper which knows - how to handle Blob, TypedArrays, and JS strings. */ - ], + /* sqlite3_bind_blob() and sqlite3_bind_text() have hand-written + bindings to permit more flexible inputs. */ ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"], ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"], @@ -53,16 +51,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"], ["sqlite3_bind_pointer", "int", "sqlite3_stmt*", "int", "*", "string:static", "*"], - ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "*" - /* We should arguably create a hand-written binding of - bind_text() which does more flexible text conversion, along - the lines of sqlite3_prepare_v3(). The slightly problematic - part is the final argument (text destructor). */ - ], - //["sqlite3_busy_handler","int", "sqlite3*", "*", "*"], - // ^^^^ TODO: custom binding which auto-converts JS function arg - // to a WASM function, noting that calling it multiple times - // would introduce a leak. + ["sqlite3_busy_handler","int", [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + signature: 'i(pi)', + contextKey: (argIndex,argv)=>'sqlite3@'+argv[0] + }), + "*" + ]], ["sqlite3_busy_timeout","int", "sqlite3*", "int"], ["sqlite3_close_v2", "int", "sqlite3*"], ["sqlite3_changes", "int", "sqlite3*"], @@ -779,6 +775,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(1){/* Special-case handling of sqlite3_prepare_v2() and sqlite3_prepare_v3() */ + /** Helper for string:flexible conversions which require a byte-length counterpart argument. Passed a value and its @@ -802,32 +799,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /** Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). */ - const __prepare = Object.create(null); - /** - This binding expects a JS string as its 2nd argument and - null as its final argument. In order to compile multiple - statements from a single string, the "full" impl (see - below) must be used. - */ - __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', - "int", ["sqlite3*", "string", - "int"/*ignored for this impl!*/, - "int", "**", - "**"/*MUST be 0 or null or undefined!*/]); - /** - Impl which requires that the 2nd argument be a pointer - to the SQL string, instead of being converted to a - string. This variant is necessary for cases where we - require a non-NULL value for the final argument - (exec()'ing multiple statements from one input - string). For simpler cases, where only the first - statement in the SQL string is required, the wrapper - named sqlite3_prepare_v2() is sufficient and easier to - use because it doesn't require dealing with pointers. - */ - __prepare.full = wasm.xWrap('sqlite3_prepare_v3', - "int", ["sqlite3*", "*", "int", "int", - "**", "**"]); + const __prepare = { + /** + This binding expects a JS string as its 2nd argument and + null as its final argument. In order to compile multiple + statements from a single string, the "full" impl (see + below) must be used. + */ + basic: wasm.xWrap('sqlite3_prepare_v3', + "int", ["sqlite3*", "string", + "int"/*ignored for this impl!*/, + "int", "**", + "**"/*MUST be 0 or null or undefined!*/]), + /** + Impl which requires that the 2nd argument be a pointer + to the SQL string, instead of being converted to a + string. This variant is necessary for cases where we + require a non-NULL value for the final argument + (exec()'ing multiple statements from one input + string). For simpler cases, where only the first + statement in the SQL string is required, the wrapper + named sqlite3_prepare_v2() is sufficient and easier to + use because it doesn't require dealing with pointers. + */ + full: wasm.xWrap('sqlite3_prepare_v3', + "int", ["sqlite3*", "*", "int", "int", + "**", "**"]) + }; /* Documented in the capi object's initializer. */ capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ @@ -852,7 +850,80 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length); }; - }/*sqlite3_prepare_v2/v3()*/; + + }/*sqlite3_prepare_v2/v3()*/ + + {/*sqlite3_bind_text/blob()*/ + const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [ + "sqlite3_stmt*", "int", "string", "int", "*" + ]); + const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [ + "sqlite3_stmt*", "int", "*", "int", "*" + ]); + + /** Documented in the capi object's initializer. */ + capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy){ + if(f.length!==arguments.length){ + return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), + "sqlite3_bind_text", f.length); + }else if(wasm.isPtr(text) || null===text){ + return __bindText(pStmt, iCol, text, nText, xDestroy); + }else if(text instanceof ArrayBuffer){ + text = new Uint8Array(text); + }else if(Array.isArray(pMem)){ + text = pMem.join(''); + } + let p, n; + try { + if(util.isSQLableTypedArray(text)){ + p = wasm.allocFromTypedArray(text); + n = text.byteLength; + }else if('string'===typeof text){ + [p, n] = wasm.allocCString(text); + }else{ + return util.sqlite3_wasm_db_error( + capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, + "Invalid 3rd argument type for sqlite3_bind_text()." + ); + } + return __bindText(pStmt, iCol, p, n, capi.SQLITE_TRANSIENT); + }finally{ + wasm.dealloc(p); + } + }/*sqlite3_bind_text()*/; + + /** Documented in the capi object's initializer. */ + capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy){ + if(f.length!==arguments.length){ + return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), + "sqlite3_bind_blob", f.length); + }else if(wasm.isPtr(pMem) || null===pMem){ + return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy); + }else if(pMem instanceof ArrayBuffer){ + pMem = new Uint8Array(pMem); + }else if(Array.isArray(pMem)){ + pMem = pMem.join(''); + } + let p, n; + try{ + if(util.isBindableTypedArray(pMem)){ + p = wasm.allocFromTypedArray(pMem); + n = nMem>=0 ? nMem : pMem.byteLength; + }else if('string'===typeof pMem){ + [p, n] = wasm.allocCString(pMem); + }else{ + return util.sqlite3_wasm_db_error( + capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, + "Invalid 3rd argument type for sqlite3_bind_blob()." + ); + } + return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_TRANSIENT); + }finally{ + wasm.dealloc(p); + } + }/*sqlite3_bind_blob()*/; + + }/*sqlite3_bind_text/blob()*/ {/* sqlite3_set_authorizer() */ const __ssa = wasm.xWrap("sqlite3_set_authorizer", 'int', [ diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 1776cb327e..ddb346e545 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -1332,8 +1332,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }else if(!util.isBindableTypedArray(val)){ toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, or Int8Array."); - }else if(1){ - /* _Hypothetically_ more efficient than the impl in the 'else' block. */ + }else{ const stack = wasm.scopedAllocPush(); try{ const pBlob = wasm.scopedAlloc(val.byteLength || 1); @@ -1343,14 +1342,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }finally{ wasm.scopedAllocPop(stack); } - }else{ - const pBlob = wasm.allocFromTypedArray(val); - try{ - rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, - capi.SQLITE_TRANSIENT); - }finally{ - wasm.dealloc(pBlob); - } } break; } diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 6c50e99b0a..c3533a4767 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -417,6 +417,92 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; Object.assign(capi, { + /** + sqlite3_bind_blob() works exactly like its C counterpart unless + its 3rd argument is one of: + + - JS string: the 3rd argument is converted to a C string, the + 4th argument is ignored, and the C-string's length is used + in its place. + + - Array: converted to a string as defined for "flexible + strings" and then it's treated as a JS string. + + - Int8Array or Uint8Array: wasm.allocFromTypedArray() is used to + conver the memory to the WASM heap. If the 4th argument is + 0 or greater, it is used as-is, otherwise the array's byteLength + value is used. This is an exception to the C API's undefined + behavior for a negative 4th argument, but results are undefined + if the given 4th argument value is greater than the byteLength + of the input array. + + - If it's an ArrayBuffer, it gets wrapped in a Uint8Array and + treated as that type. + + In all of those cases, the final argument (text destructor) is + ignored and capi.SQLITE_TRANSIENT is assumed. + + A 3rd argument of `null` is treated as if it were a WASM pointer + of 0. + + If the 3rd argument is neither a WASM pointer nor one of the + above-described types, capi.SQLITE_MISUSE is returned. + + The first argument may be either an `sqlite3_stmt*` WASM + pointer or an sqlite3.oo1.Stmt instance. + + For consistency with the C API, it requires the same number of + arguments. It returns capi.SQLITE_MISUSE if passed any other + argument count. + */ + sqlite3_bind_blob: undefined/*installed later*/, + + /** + sqlite3_bind_text() works exactly like its C counterpart unless + its 3rd argument is one of: + + - JS string: the 3rd argument is converted to a C string, the + 4th argument is ignored, and the C-string's length is used + in its place. + + - Array: converted to a string as defined for "flexible + strings". The 4th argument is ignored and a value of -1 + is assumed. + + - Int8Array or Uint8Array: is assumed to contain UTF-8 text, is + converted to a string. The 4th argument is ignored, replaced + by the array's byteLength value. + + - If it's an ArrayBuffer, it gets wrapped in a Uint8Array and + treated as that type. + + In each of those cases, the final argument (text destructor) is + ignored and capi.SQLITE_TRANSIENT is assumed. + + A 3rd argument of `null` is treated as if it were a WASM pointer + of 0. + + If the 3rd argument is neither a WASM pointer nor one of the + above-described types, capi.SQLITE_MISUSE is returned. + + The first argument may be either an `sqlite3_stmt*` WASM + pointer or an sqlite3.oo1.Stmt instance. + + For consistency with the C API, it requires the same number of + arguments. It returns capi.SQLITE_MISUSE if passed any other + argument count. + + If client code needs to bind partial strings, it needs to + either parcel the string up before passing it in here or it + must pass in a WASM pointer for the 3rd argument and a valid + 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. + */ + sqlite3_bind_text: undefined/*installed later*/, + /** sqlite3_create_function_v2() differs from its native counterpart only in the following ways: @@ -525,18 +611,18 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` flag. */ - sqlite3_create_function_v2: function( + sqlite3_create_function_v2: ( pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy - ){/*installed later*/}, + )=>{/*installed later*/}, /** Equivalent to passing the same arguments to sqlite3_create_function_v2(), with 0 as the final argument. */ - sqlite3_create_function:function( + sqlite3_create_function: ( pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal - ){/*installed later*/}, + )=>{/*installed later*/}, /** The sqlite3_create_window_function() JS wrapper differs from its native implementation in the exact same way that @@ -544,10 +630,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( xInverse(), is treated identically to xStep() by the wrapping layer. */ - sqlite3_create_window_function: function( + sqlite3_create_window_function: ( pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy - ){/*installed later*/}, + )=>{/*installed later*/}, /** The sqlite3_prepare_v3() binding handles two different uses with differing JS/WASM semantics: @@ -669,7 +755,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( toss3, typedArrayPart }; - + Object.assign(wasm, { /** Emscripten APIs have a deep-seated assumption that all pointers diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 880ef4ab4e..de7500a020 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -1406,7 +1406,8 @@ self.WhWasmUtilInstaller = function(target){ .set('int', xArg.get('i32')) .set('null', (i)=>i) .set(null, xArg.get('null')) - .set('**', __xArgPtr); + .set('**', __xArgPtr) + .set('*', __xArgPtr); xResult.set('*', __xArgPtr) .set('pointer', __xArgPtr) .set('number', (v)=>Number(v)) @@ -1448,23 +1449,23 @@ self.WhWasmUtilInstaller = function(target){ if('string'===typeof v) return target.scopedAllocCString(v); return v ? __xArgPtr(v) : null; }; - xArg.set('string', __xArgString); - xArg.set('utf8', __xArgString); - xArg.set('pointer', __xArgString); - xArg.set('*', __xArgString); + xArg.set('string', __xArgString) + .set('utf8', __xArgString) + .set('pointer', __xArgString); + //xArg.set('*', __xArgString); - xResult.set('string', (i)=>target.cstrToJs(i)); - xResult.set('utf8', xResult.get('string')); - xResult.set('string:dealloc', (i)=>{ - try { return i ? target.cstrToJs(i) : null } - finally{ target.dealloc(i) } - }); - xResult.set('utf8:dealloc', xResult.get('string:dealloc')); - xResult.set('json', (i)=>JSON.parse(target.cstrToJs(i))); - xResult.set('json:dealloc', (i)=>{ - try{ return i ? JSON.parse(target.cstrToJs(i)) : null } - finally{ target.dealloc(i) } - }); + xResult.set('string', (i)=>target.cstrToJs(i)) + .set('utf8', xResult.get('string')) + .set('string:dealloc', (i)=>{ + try { return i ? target.cstrToJs(i) : null } + finally{ target.dealloc(i) } + }) + .set('utf8:dealloc', xResult.get('string:dealloc')) + .set('json', (i)=>JSON.parse(target.cstrToJs(i))) + .set('json:dealloc', (i)=>{ + try{ return i ? JSON.parse(target.cstrToJs(i)) : null } + finally{ target.dealloc(i) } + }); /** Internal-use-only base class for FuncPtrAdapter and potentially @@ -1748,10 +1749,13 @@ self.WhWasmUtilInstaller = function(target){ - `N*` (args): a type name in the form `N*`, where N is a numeric type name, is treated the same as WASM pointer. - - `*` and `pointer` (args): have multple semantics. They - behave exactly as described below for `string` args. + - `*` and `pointer` (args): are assumed to be WASM pointer values + and are returned coerced to an appropriately-sized pointer + value (i32 or i64). Non-numeric values will coerce to 0 and + out-of-range values will have undefined results (just as with + any pointer misuse). - - `*` and `pointer` (results): are aliases for the current + - `*` and `pointer` (results): aliases for the current WASM pointer numeric type. - `**` (args): is simply a descriptive alias for the WASM pointer diff --git a/manifest b/manifest index 0476b9eced..9fa7ef3ea9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Internal\sJS\scleanups.\sCorrect\spart\sof\s[ac136925a645]\sto\saccount\sfor\sthe\seTextRep\sflag\sbeing\sable\sto\shold\sflags\sother\sthan\sthe\sencoding. -D 2022-12-23T21:10:49.493 +C Reimplement\sJS's\ssqlite3_bind_text/blob()\swith\shand-written\sbindings\sto\spermit\smore\sflexible\sinputs.\sAdd\sautomated\sJS-to-C\sfunction\sconversion\sto\ssqlite3_busy_handler().\ssqlite3.wasm.xWrap()'s\s'*'\sargument\sconversion\sno\slonger\streats\sJS\sstrings\sas\sC-strings:\sthose\sconversions\srequire\sexplicit\sopt-in\svia\sthe\s'string'\sconverter\s(or\sequivalent). +D 2022-12-23T23:46:33.608 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -503,9 +503,9 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 -F ext/wasm/api/sqlite3-api-glue.js f50211dc11f1debf972cdaea87cb4df9772022828072e7100cf5025d1dca2a78 -F ext/wasm/api/sqlite3-api-oo1.js 4cce9671e8a31ac9b76bd8559e7827ccc2b121734e460fa9c7d243735a771ec8 -F ext/wasm/api/sqlite3-api-prologue.js 789639d256e3134563c5c9be25ade28b40e06e783885d26cccc01cd1ed4b820d +F ext/wasm/api/sqlite3-api-glue.js f0651048a2601bf79f7f39c2c855f6417e65548417f5019ac9ac2ffb2463f2b9 +F ext/wasm/api/sqlite3-api-oo1.js c2a3e310f993a632b6c5da0c49b0635863a73df60c4f9fc0a30648f25e4ec32a +F ext/wasm/api/sqlite3-api-prologue.js 683956ea6ab5e0132db48bb693a6bb9dd92f36c8c0902af36572e9b29006ac6d F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f @@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07 F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f -F ext/wasm/common/whwasmutil.js 97807770ec452fdcaa48509c5decd3a2c1e3001164a62a3223a347f66967533e +F ext/wasm/common/whwasmutil.js 700fb1b702986522d2177fe8247bfbab3040e82cb4f6c35e929c4c85fbd7ffc5 F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P deffe6fb211410fa1a1fbca824a52b4e09b54d4b4f4a4e12d71c9e4b7e8606fb -R 537077e8e7a576993086a957d5c52d05 +P 1dfc03ab1e0269807beef27bf884ab9ead7553d4a5f6ed213f812d7fa052045f +R 0557f08bf576b7be78ec588d2742348d U stephan -Z 379d99233453fc912eb71dcbaf16b8ce +Z 4e12d5aedea06f895991428eb871a6d7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a091b64a43..83020f5c61 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1dfc03ab1e0269807beef27bf884ab9ead7553d4a5f6ed213f812d7fa052045f \ No newline at end of file +96ba44946b3e88b6aa305c4363cbbfeab0d9120b3d8c4d2587d68b9293ea7cc6 \ No newline at end of file From 0cdc01d01a288a83327aaa3ed9d29966ffe7b81f Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 24 Dec 2022 01:59:42 +0000 Subject: [PATCH 2/9] If sqlite3.oo1.DB.exec()'s callback returns a literal false, stop step()ing over results as if the end of the result set had been reached. Unrelated minor code-adjacent cleanups. FossilOrigin-Name: 33a58c8ece3b37a8edc3434af36643e2d489a53a672d778c5b77e66666045c30 --- ext/wasm/api/sqlite3-api-oo1.js | 45 ++++++++++++++++++++++----------- ext/wasm/tester1.c-pp.js | 8 ++++++ manifest | 14 +++++----- manifest.uuid | 2 +- 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index ddb346e545..10f0ebc181 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -447,7 +447,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if('$'===opt.rowMode[0]){ out.cbArg = function(stmt){ const rc = stmt.get(this.obj)[this.colName]; - return (undefined===rc) ? toss3("exec(): unknown result column:",this.colName) : rc; + return (undefined===rc) + ? toss3("exec(): unknown result column:",this.colName) + : rc; }.bind({ obj:Object.create(null), colName: opt.rowMode.substr(1) @@ -608,7 +610,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ try{ rc = wasm.cstrToJs(v.$zName) } finally { v.dispose() } } - return rc; + return rc; }, /** Compiles the given SQL and returns a prepared Stmt. This is @@ -697,21 +699,27 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ unchanged. Achtung: an SQL result may have multiple columns with identical names. - - `callback` = a function which gets called for each row of - the result set, but only if that statement has any result + - `callback` = a function which gets called for each row of the + result set, but only if that statement has any result _rows_. The callback's "this" is the options object, noting that this function synthesizes one if the caller does not pass one to exec(). The second argument passed to the callback is always the current Stmt object, as it's needed if the caller wants to fetch the column names or some such (noting that they could also be fetched via `this.columnNames`, if the client - provides the `columnNames` option). + provides the `columnNames` option). If the callback returns a + literal `false` (as opposed to any other falsy value, e.g. an + implicit `undefined` return), any ongoing statement-`step()` + iteration stops without an error. The return value of the + callback is otherwise ignored. ACHTUNG: The callback MUST NOT modify the Stmt object. Calling any of the Stmt.get() variants, Stmt.getColumnName(), or similar, is legal, but calling step() or finalize() is not. Member methods which are illegal in this context will - trigger an exception. + trigger an exception, but clients must also refrain from using + any lower-level (C-style) APIs which might modify the + statement. The first argument passed to the callback defaults to an array of values from the current result row but may be changed with ... @@ -799,7 +807,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Array.isArray(opt.resultRows) ? opt.resultRows : undefined; let stmt; let bind = opt.bind; - let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */; + let evalFirstResult = !!( + arg.cbArg || opt.columnNames || resultRows + ) /* true to step through the first result-returning statement */; const stack = wasm.scopedAllocPush(); const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : undefined; try{ @@ -810,9 +820,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ space for the SQL (pSql). When prepare_v2() returns, pzTail will point to somewhere in pSql. */ let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql); - const ppStmt = wasm.scopedAlloc(/* output (sqlite3_stmt**) arg and pzTail */ - (2 * wasm.ptrSizeof) - + (sqlByteLen + 1/* SQL + NUL */)); + const ppStmt = wasm.scopedAlloc( + /* output (sqlite3_stmt**) arg and pzTail */ + (2 * wasm.ptrSizeof) + (sqlByteLen + 1/* SQL + NUL */) + ); const pzTail = ppStmt + wasm.ptrSizeof /* final arg to sqlite3_prepare_v2() */; let pSql = pzTail + wasm.ptrSizeof; const pSqlEnd = pSql + sqlByteLen; @@ -848,11 +859,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(Array.isArray(opt.columnNames)){ stmt.getColumnNames(opt.columnNames); } - while(!!arg.cbArg && stmt.step()){ - stmt._isLocked = true; - const row = arg.cbArg(stmt); - if(resultRows) resultRows.push(row); - if(callback) callback.call(opt, row, stmt); + if(arg.cbArg || resultRows){ + for(; stmt.step(); stmt._isLocked = false){ + stmt._isLocked = true; + const row = arg.cbArg(stmt); + if(resultRows) resultRows.push(row); + if(callback && false === callback.call(opt, row, stmt)){ + break; + } + } stmt._isLocked = false; } }else{ diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 5411c064ce..82dd73e23b 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1391,6 +1391,14 @@ self.sqlite3InitModule = sqlite3InitModule; db.selectValue("SELECT "+Number.MIN_SAFE_INTEGER)). assert(Number.MAX_SAFE_INTEGER === db.selectValue("SELECT "+Number.MAX_SAFE_INTEGER)); + + counter = 0; + db.exec({ + sql: "SELECT a FROM t", + callback: ()=>(1===++counter), + }); + T.assert(2===counter, + "Expecting exec step() loop to stop if callback returns false."); if(wasm.bigIntEnabled && haveWasmCTests()){ const mI = wasm.xCall('sqlite3_wasm_test_int64_max'); const b = BigInt(Number.MAX_SAFE_INTEGER * 2); diff --git a/manifest b/manifest index 9fa7ef3ea9..63b0ff62c9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reimplement\sJS's\ssqlite3_bind_text/blob()\swith\shand-written\sbindings\sto\spermit\smore\sflexible\sinputs.\sAdd\sautomated\sJS-to-C\sfunction\sconversion\sto\ssqlite3_busy_handler().\ssqlite3.wasm.xWrap()'s\s'*'\sargument\sconversion\sno\slonger\streats\sJS\sstrings\sas\sC-strings:\sthose\sconversions\srequire\sexplicit\sopt-in\svia\sthe\s'string'\sconverter\s(or\sequivalent). -D 2022-12-23T23:46:33.608 +C If\ssqlite3.oo1.DB.exec()'s\scallback\sreturns\sa\sliteral\sfalse,\sstop\sstep()ing\sover\sresults\sas\sif\sthe\send\sof\sthe\sresult\sset\shad\sbeen\sreached.\sUnrelated\sminor\scode-adjacent\scleanups. +D 2022-12-24T01:59:42.190 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -504,7 +504,7 @@ F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 F ext/wasm/api/sqlite3-api-glue.js f0651048a2601bf79f7f39c2c855f6417e65548417f5019ac9ac2ffb2463f2b9 -F ext/wasm/api/sqlite3-api-oo1.js c2a3e310f993a632b6c5da0c49b0635863a73df60c4f9fc0a30648f25e4ec32a +F ext/wasm/api/sqlite3-api-oo1.js 06fcaf4007b68b39db20c83b2620d5c04322bcbe2abb9bde7941b191257b6d43 F ext/wasm/api/sqlite3-api-prologue.js 683956ea6ab5e0132db48bb693a6bb9dd92f36c8c0902af36572e9b29006ac6d F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js 50b51af2b5466de0cba8ebc97b86bc886c32f937d8f4b36d3b3936ef9748e534 +F ext/wasm/tester1.c-pp.js 99d6dc920d0c379c78053edb89a870666cfb9d4d4548b31c94427f9135a1c0a2 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1dfc03ab1e0269807beef27bf884ab9ead7553d4a5f6ed213f812d7fa052045f -R 0557f08bf576b7be78ec588d2742348d +P 96ba44946b3e88b6aa305c4363cbbfeab0d9120b3d8c4d2587d68b9293ea7cc6 +R 7ef916e9c75ca3398704313fbddc267b U stephan -Z 4e12d5aedea06f895991428eb871a6d7 +Z c6e787d4dc5ffd60ed702bf77e978ab3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 83020f5c61..4cbd4b8876 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -96ba44946b3e88b6aa305c4363cbbfeab0d9120b3d8c4d2587d68b9293ea7cc6 \ No newline at end of file +33a58c8ece3b37a8edc3434af36643e2d489a53a672d778c5b77e66666045c30 \ No newline at end of file From 4b4ae86445c621a3eb6b67b96c67a3eb29d9751a Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 24 Dec 2022 11:16:49 +0000 Subject: [PATCH 3/9] oo1.DB.exec() rowMode="$columnName": a minor optimization and a preemtive fix for a hypothetical corner-case bug. FossilOrigin-Name: 0b2df22bd92914708ad0851d0401ad2cf3edb1968b88b2c07fe40792a731c5ba --- ext/wasm/api/sqlite3-api-oo1.js | 25 ++++++++++++------------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 10f0ebc181..5404fc52d8 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -439,23 +439,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(util.isInt32(opt.rowMode)){ out.cbArg = (stmt)=>stmt.get(opt.rowMode); break; - }else if('string'===typeof opt.rowMode && opt.rowMode.length>1){ + }else if('string'===typeof opt.rowMode + && opt.rowMode.length>1 + && '$'===opt.rowMode[0]){ /* "$X": fetch column named "X" (case-sensitive!). Prior to 2022-12-14 ":X" and "@X" were also permitted, but having so many options is unnecessary and likely to cause confusion. */ - if('$'===opt.rowMode[0]){ - out.cbArg = function(stmt){ - const rc = stmt.get(this.obj)[this.colName]; - return (undefined===rc) - ? toss3("exec(): unknown result column:",this.colName) - : rc; - }.bind({ - obj:Object.create(null), - colName: opt.rowMode.substr(1) - }); - break; - } + const $colName = opt.rowMode.substr(1); + out.cbArg = (stmt)=>{ + const rc = stmt.get(Object.create(null))[$colName]; + return (undefined===rc) + ? toss3(capi.SQLITE_NOTFOUND, + "exec(): unknown result column:",$colName) + : rc; + }; + break; } toss3("Invalid rowMode:",opt.rowMode); } diff --git a/manifest b/manifest index 63b0ff62c9..da21b211c7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C If\ssqlite3.oo1.DB.exec()'s\scallback\sreturns\sa\sliteral\sfalse,\sstop\sstep()ing\sover\sresults\sas\sif\sthe\send\sof\sthe\sresult\sset\shad\sbeen\sreached.\sUnrelated\sminor\scode-adjacent\scleanups. -D 2022-12-24T01:59:42.190 +C oo1.DB.exec()\srowMode="$columnName":\sa\sminor\soptimization\sand\sa\spreemtive\sfix\sfor\sa\shypothetical\scorner-case\sbug. +D 2022-12-24T11:16:49.554 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -504,7 +504,7 @@ F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 F ext/wasm/api/sqlite3-api-glue.js f0651048a2601bf79f7f39c2c855f6417e65548417f5019ac9ac2ffb2463f2b9 -F ext/wasm/api/sqlite3-api-oo1.js 06fcaf4007b68b39db20c83b2620d5c04322bcbe2abb9bde7941b191257b6d43 +F ext/wasm/api/sqlite3-api-oo1.js 7ae49f6d9dcbc268d6fc9bce6bd070c72884d544bd0c6f12fa682d51027bf4d3 F ext/wasm/api/sqlite3-api-prologue.js 683956ea6ab5e0132db48bb693a6bb9dd92f36c8c0902af36572e9b29006ac6d F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 96ba44946b3e88b6aa305c4363cbbfeab0d9120b3d8c4d2587d68b9293ea7cc6 -R 7ef916e9c75ca3398704313fbddc267b +P 33a58c8ece3b37a8edc3434af36643e2d489a53a672d778c5b77e66666045c30 +R d15205afc8348e399a5e2c3665229d17 U stephan -Z c6e787d4dc5ffd60ed702bf77e978ab3 +Z 3c00734fbb2a03c4e427540c406cdc55 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4cbd4b8876..41002f02ab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -33a58c8ece3b37a8edc3434af36643e2d489a53a672d778c5b77e66666045c30 \ No newline at end of file +0b2df22bd92914708ad0851d0401ad2cf3edb1968b88b2c07fe40792a731c5ba \ No newline at end of file From 0db1c901378e29fd576e93d16a1305c6269d411d Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 24 Dec 2022 13:46:27 +0000 Subject: [PATCH 4/9] Add a test for the (failure) case of client-level code calling the oo1.Stmt constructor directly. FossilOrigin-Name: 6a37874db04f3b4842994ad17fc74cb6222f8ea0fa1315a23aff1ffa69bcd12a --- ext/wasm/api/sqlite3-api-oo1.js | 19 +++++++++++-------- ext/wasm/tester1.c-pp.js | 8 +++++++- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 5404fc52d8..87509b59a3 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -337,7 +337,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ */ const Stmt = function(){ if(BindTypes!==arguments[2]){ - toss3("Do not call the Stmt constructor directly. Use DB.prepare()."); + toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare()."); } this.db = arguments[0]; __ptrMap.set(this, arguments[1]); @@ -887,10 +887,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } return arg.returnVal(); }/*exec()*/, + /** - Creates a new scalar UDF (User-Defined Function) which is - accessible via SQL code. This function may be called in any - of the following forms: + Creates a new UDF (User-Defined Function) which is accessible + via SQL code. This function may be called in any of the + following forms: - (name, function) - (name, function, optionsObject) @@ -906,10 +907,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ functions. Creating an aggregate or window function requires the options-object form (see below for details). - UDFs cannot currently be removed from a DB handle after they're - added. More correctly, they can be removed as documented for - sqlite3_create_function_v2(), but doing so will "leak" the - JS-created WASM binding of those functions. + UDFs can be removed as documented for + sqlite3_create_function_v2() and + sqlite3_create_window_function(), but doing so will "leak" the + JS-created WASM binding of those functions (meaning that their + entries in the WASM indirect function table still + exist). Eliminating that potential leak is a pending TODO. On success, returns this object. Throws on error. diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 82dd73e23b..8b80d4d88d 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1219,7 +1219,7 @@ self.sqlite3InitModule = sqlite3InitModule; }) //////////////////////////////////////////////////////////////////// - .t('DB.Stmt', function(S){ + .t('DB.Stmt', function(sqlite3){ let st = this.db.prepare( new TextEncoder('utf-8').encode("select 3 as a") ); @@ -1274,6 +1274,12 @@ self.sqlite3InitModule = sqlite3InitModule; } T.assert(!st.pointer) .assert(0===this.db.openStatementCount()); + + T.mustThrowMatching(()=>new sqlite3.oo1.Stmt("hi"), function(err){ + return (err instanceof sqlite3.SQLite3Error) + && capi.SQLITE_MISUSE === err.resultCode + && 0 < err.message.indexOf("Do not call the Stmt constructor directly.") + }); }) //////////////////////////////////////////////////////////////////////// diff --git a/manifest b/manifest index da21b211c7..f7f283894f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C oo1.DB.exec()\srowMode="$columnName":\sa\sminor\soptimization\sand\sa\spreemtive\sfix\sfor\sa\shypothetical\scorner-case\sbug. -D 2022-12-24T11:16:49.554 +C Add\sa\stest\sfor\sthe\s(failure)\scase\sof\sclient-level\scode\scalling\sthe\soo1.Stmt\sconstructor\sdirectly. +D 2022-12-24T13:46:27.601 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -504,7 +504,7 @@ F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 F ext/wasm/api/sqlite3-api-glue.js f0651048a2601bf79f7f39c2c855f6417e65548417f5019ac9ac2ffb2463f2b9 -F ext/wasm/api/sqlite3-api-oo1.js 7ae49f6d9dcbc268d6fc9bce6bd070c72884d544bd0c6f12fa682d51027bf4d3 +F ext/wasm/api/sqlite3-api-oo1.js 485f43129b25b939209effcec635e801bad75bf0a534c7ca327a7cd934744163 F ext/wasm/api/sqlite3-api-prologue.js 683956ea6ab5e0132db48bb693a6bb9dd92f36c8c0902af36572e9b29006ac6d F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js 99d6dc920d0c379c78053edb89a870666cfb9d4d4548b31c94427f9135a1c0a2 +F ext/wasm/tester1.c-pp.js d395eb6aaf4de79bf2c027f7c4b5e4ae007ee1822d6e064e946d0305672a9761 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 33a58c8ece3b37a8edc3434af36643e2d489a53a672d778c5b77e66666045c30 -R d15205afc8348e399a5e2c3665229d17 +P 0b2df22bd92914708ad0851d0401ad2cf3edb1968b88b2c07fe40792a731c5ba +R 56f356985466274ad72950f0668d6b94 U stephan -Z 3c00734fbb2a03c4e427540c406cdc55 +Z bfd3bc98ed13225718cba2333de93b83 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 41002f02ab..696b648cc0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0b2df22bd92914708ad0851d0401ad2cf3edb1968b88b2c07fe40792a731c5ba \ No newline at end of file +6a37874db04f3b4842994ad17fc74cb6222f8ea0fa1315a23aff1ffa69bcd12a \ No newline at end of file From cede6384fde313cd346c341e6088cb4c8a34f5c5 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 24 Dec 2022 14:16:02 +0000 Subject: [PATCH 5/9] Extend oo1.Stmt.bind() to accept ArrayBuffer instances to bind as blobs. FossilOrigin-Name: f76bd30137fbff981625ffcb28cddd5e8651803dfc3f2d8d7801ead33496311d --- ext/wasm/api/sqlite3-api-oo1.js | 42 ++++++++++++++++++--------------- ext/wasm/tester1.c-pp.js | 5 +++- manifest | 14 +++++------ manifest.uuid | 2 +- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 87509b59a3..e3c90271b1 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -1230,7 +1230,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /* else fall through */ default: //console.log("isSupportedBindType",t,v); - return util.isBindableTypedArray(v) ? BindTypes.blob : undefined; + return (util.isBindableTypedArray(v) || (v instanceof ArrayBuffer)) + ? BindTypes.blob : undefined; } }; @@ -1346,19 +1347,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ case BindTypes.blob: { if('string'===typeof val){ rc = f._.string(stmt, ndx, val, true); + break; + }else if(val instanceof ArrayBuffer){ + val = new Uint8Array(val); }else if(!util.isBindableTypedArray(val)){ toss3("Binding a value as a blob requires", - "that it be a string, Uint8Array, or Int8Array."); - }else{ - const stack = wasm.scopedAllocPush(); - try{ - const pBlob = wasm.scopedAlloc(val.byteLength || 1); - wasm.heap8().set(val.byteLength ? val : [0], pBlob) - rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, - capi.SQLITE_TRANSIENT); - }finally{ - wasm.scopedAllocPop(stack); - } + "that it be a string, Uint8Array, Int8Array, or ArrayBuffer."); + } + const stack = wasm.scopedAllocPush(); + try{ + const pBlob = wasm.scopedAlloc(val.byteLength || 1); + wasm.heap8().set(val.byteLength ? val : [0], pBlob) + rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, + capi.SQLITE_TRANSIENT); + }finally{ + wasm.scopedAllocPop(stack); } break; } @@ -1367,6 +1370,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ toss3("Unsupported bind() argument type: "+(typeof val)); } if(rc) DB.checkRc(stmt.db.pointer, rc); + stmt._mayGet = false; return stmt; }; @@ -1454,8 +1458,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - Strings are bound as strings (use bindAsBlob() to force blob binding). - - Uint8Array and Int8Array instances are bound as blobs. - (TODO: binding the other TypedArray types.) + - Uint8Array, Int8Array, and ArrayBuffer instances are bound as + blobs. (TODO? binding the other TypedArray types.) If passed an array, each element of the array is bound at the parameter index equal to the array index plus 1 @@ -1510,8 +1514,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v)); return this; + }else if(arg instanceof ArrayBuffer){ + arg = new Uint8Array(arg); } - else if('object'===typeof arg/*null was checked above*/ + if('object'===typeof arg/*null was checked above*/ && !util.isBindableTypedArray(arg)){ /* Treat each property of arg as a named bound parameter. */ if(1!==arguments.length){ @@ -1533,7 +1539,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ the value. The ndx may be a numbered or named bind index. The value must be of type string, null/undefined (both get treated as null), or a TypedArray of a type supported by the bind() - API. + API. This API cannot bind numbers as blobs. If passed a single argument, a bind index of 1 is assumed and the first argument is the value. @@ -1549,9 +1555,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ && BindTypes.null !== t){ toss3("Invalid value type for bindAsBlob()"); } - bindOne(this, ndx, BindTypes.blob, arg); - this._mayGet = false; - return this; + return bindOne(this, ndx, BindTypes.blob, arg); }, /** Steps the statement one time. If the result indicates that a diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 8b80d4d88d..a87532e18b 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1648,7 +1648,10 @@ self.sqlite3InitModule = sqlite3InitModule; assert(T.eqApprox(1.3,db.selectValue("select asis(1 + 0.3)"))); let blobArg = new Uint8Array([0x68, 0x69]); - let blobRc = db.selectValue("select asis(?1)", blobArg); + let blobRc = db.selectValue( + "select asis(?1)", + blobArg.buffer/*confirm that ArrayBuffer is handled as a Uint8Array*/ + ); T.assert(blobRc instanceof Uint8Array). assert(2 === blobRc.length). assert(0x68==blobRc[0] && 0x69==blobRc[1]); diff --git a/manifest b/manifest index f7f283894f..713880bc5c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\stest\sfor\sthe\s(failure)\scase\sof\sclient-level\scode\scalling\sthe\soo1.Stmt\sconstructor\sdirectly. -D 2022-12-24T13:46:27.601 +C Extend\soo1.Stmt.bind()\sto\saccept\sArrayBuffer\sinstances\sto\sbind\sas\sblobs. +D 2022-12-24T14:16:02.217 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -504,7 +504,7 @@ F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 F ext/wasm/api/sqlite3-api-glue.js f0651048a2601bf79f7f39c2c855f6417e65548417f5019ac9ac2ffb2463f2b9 -F ext/wasm/api/sqlite3-api-oo1.js 485f43129b25b939209effcec635e801bad75bf0a534c7ca327a7cd934744163 +F ext/wasm/api/sqlite3-api-oo1.js 1312eaf2776c957e41a6fd63c31e7487502bf71745805c41f72429e0925802a5 F ext/wasm/api/sqlite3-api-prologue.js 683956ea6ab5e0132db48bb693a6bb9dd92f36c8c0902af36572e9b29006ac6d F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js d395eb6aaf4de79bf2c027f7c4b5e4ae007ee1822d6e064e946d0305672a9761 +F ext/wasm/tester1.c-pp.js 145c493221727eb40194280bb2852da49f857e850d8394c31b7bd4caeb5d7bed F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 0b2df22bd92914708ad0851d0401ad2cf3edb1968b88b2c07fe40792a731c5ba -R 56f356985466274ad72950f0668d6b94 +P 6a37874db04f3b4842994ad17fc74cb6222f8ea0fa1315a23aff1ffa69bcd12a +R b07f6a4d9e30a3f649f5decc82bdf7ef U stephan -Z bfd3bc98ed13225718cba2333de93b83 +Z 934cef85bd1b053ac09da3261d128afb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 696b648cc0..bc46d13f05 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6a37874db04f3b4842994ad17fc74cb6222f8ea0fa1315a23aff1ffa69bcd12a \ No newline at end of file +f76bd30137fbff981625ffcb28cddd5e8651803dfc3f2d8d7801ead33496311d \ No newline at end of file From 4099b3cab3c3451a2d9643738308be0b2d9e44b1 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 24 Dec 2022 15:28:45 +0000 Subject: [PATCH 6/9] Replace JS-side use of SQLITE_TRANSIENT with the new SQLITE_WASM_DEALLOC, reducing the amount allocation/copying required by sqlite3_bind_blob/text() and sqlite3_result_blob/text(). Remove the 'experimental' log message from the virtual table tests. FossilOrigin-Name: ffe2999a91a7dec129a38afb675fe9e539d7c347886bfea85cba55f6367d54d1 --- ext/wasm/api/sqlite3-api-glue.js | 43 ++++++++++++++++++++++++---- ext/wasm/api/sqlite3-api-oo1.js | 26 ++++++----------- ext/wasm/api/sqlite3-api-prologue.js | 38 +++++++++++++----------- ext/wasm/api/sqlite3-wasm.c | 5 ++++ ext/wasm/tester1.c-pp.js | 2 -- manifest | 20 ++++++------- manifest.uuid | 2 +- 7 files changed, 84 insertions(+), 52 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index dd80963f3e..7b5fa11816 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -24,6 +24,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ self.WhWasmUtilInstaller(wasm); delete self.WhWasmUtilInstaller; + { + /** + Find a mapping for SQLITE_WASM_DEALLOC, which the API + guarantees is a WASM pointer to the same underlying function as + wasm.dealloc() (noting that wasm.dealloc() is permitted to be a + JS wrapper around the WASM function). There is unfortunately no + O(1) algorithm for finding this pointer: we have to walk the + WASM indirect function table to find it. However, experience + indicates that that particular function is always very close to + the front of the table (it's been entry #3 in all relevant + tests). + */ + const dealloc = wasm.exports[sqlite3.config.deallocExportName]; + const nFunc = wasm.functionTable().length; + let i; + for(i = 0; i < nFunc; ++i){ + const e = wasm.functionEntry(i); + if(dealloc === e){ + capi.SQLITE_WASM_DEALLOC = i; + break; + } + } + if(dealloc !== wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)){ + toss("Internal error: cannot find function pointer for SQLITE_WASM_DEALLOC."); + } + } + /** Signatures for the WASM-exported C-side functions. Each entry is an array with 2+ elements: @@ -874,7 +901,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ text = pMem.join(''); } let p, n; - try { + try{ if(util.isSQLableTypedArray(text)){ p = wasm.allocFromTypedArray(text); n = text.byteLength; @@ -886,9 +913,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "Invalid 3rd argument type for sqlite3_bind_text()." ); } - return __bindText(pStmt, iCol, p, n, capi.SQLITE_TRANSIENT); - }finally{ + return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); + }catch(e){ wasm.dealloc(p); + return util.sqlite3_wasm_db_error( + capi.sqlite3_db_handle(pStmt), e + ); } }/*sqlite3_bind_text()*/; @@ -917,9 +947,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "Invalid 3rd argument type for sqlite3_bind_blob()." ); } - return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_TRANSIENT); - }finally{ + return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); + }catch(e){ wasm.dealloc(p); + return util.sqlite3_wasm_db_error( + capi.sqlite3_db_handle(pStmt), e + ); } }/*sqlite3_bind_blob()*/; diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index e3c90271b1..16f5f00b15 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -1285,7 +1285,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ success. */ const bindOne = function f(stmt,ndx,bindType,val){ - affirmUnlocked(stmt, 'bind()'); + affirmUnlocked(affirmStmtOpen(stmt), 'bind()'); if(!f._){ f._tooBigInt = (v)=>toss3( "BigInt value is too big to store without precision loss:", v @@ -1295,14 +1295,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ so we have no range checking. */ f._ = { string: function(stmt, ndx, val, asBlob){ - const stack = wasm.scopedAllocPush(); - try{ - const [pStr, n] = wasm.scopedAllocCString(val, true); - const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; - return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); - }finally{ - wasm.scopedAllocPop(stack); - } + const [pStr, n] = wasm.allocCString(val, true); + const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; + return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_WASM_DEALLOC); } }; }/* static init */ @@ -1354,15 +1349,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, Int8Array, or ArrayBuffer."); } - const stack = wasm.scopedAllocPush(); - try{ - const pBlob = wasm.scopedAlloc(val.byteLength || 1); - wasm.heap8().set(val.byteLength ? val : [0], pBlob) - rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, - capi.SQLITE_TRANSIENT); - }finally{ - wasm.scopedAllocPop(stack); - } + const pBlob = wasm.alloc(val.byteLength || 1); + wasm.heap8().set(val.byteLength ? val : [0], pBlob) + rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, + capi.SQLITE_WASM_DEALLOC); break; } default: diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index c3533a4767..faa2dbbbf4 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -321,14 +321,17 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; /** - Returns true if v appears to be one of our bind()-able TypedArray - types: Uint8Array or Int8Array. Support for TypedArrays with - element sizes >1 is a potential TODO just waiting on a use case - to justify them. + Returns v if v appears to be one of our bind()-able TypedArray + types: Uint8Array or Int8Array or ArrayBuffer. Support for + TypedArrays with element sizes >1 is a potential TODO just + waiting on a use case to justify them. Until then, their `buffer` + property can be used to pass them as an ArrayBuffer. If it's not + a bindable array type, a falsy value is returned. */ const isBindableTypedArray = (v)=>{ - return v && (v instanceof Uint8Array || v instanceof Int8Array); - //v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); + return v && (v instanceof Uint8Array + || v instanceof Int8Array + || v instanceof ArrayBuffer); }; /** @@ -341,8 +344,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( isSQLableTypedArray() list. */ const isSQLableTypedArray = (v)=>{ - return v && (v instanceof Uint8Array || v instanceof Int8Array); - //v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); + return v && (v instanceof Uint8Array + || v instanceof Int8Array + || v instanceof ArrayBuffer); }; /** Returns true if isBindableTypedArray(v) does, else throws with a message @@ -439,8 +443,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( - If it's an ArrayBuffer, it gets wrapped in a Uint8Array and treated as that type. - In all of those cases, the final argument (text destructor) is - ignored and capi.SQLITE_TRANSIENT is assumed. + In all of those cases, the final argument (destructor) is + ignored and capi.SQLITE_WASM_DEALLOC is assumed. A 3rd argument of `null` is treated as if it were a WASM pointer of 0. @@ -477,7 +481,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( treated as that type. In each of those cases, the final argument (text destructor) is - ignored and capi.SQLITE_TRANSIENT is assumed. + ignored and capi.SQLITE_WASM_DEALLOC is assumed. A 3rd argument of `null` is treated as if it were a WASM pointer of 0. @@ -1645,7 +1649,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( capi.sqlite3_result_error(pCtx, ''+e, -1); } }; - + /** This function passes its 2nd argument to one of the sqlite3_result_xyz() routines, depending on the type of that @@ -1661,7 +1665,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( - `bigint`: similar to `number` but will trigger an error if the value is too big to store in an int64. - `string`: `sqlite3_result_text()` - - Uint8Array or Int8Array: `sqlite3_result_blob()` + - Uint8Array or Int8Array or ArrayBuffer: `sqlite3_result_blob()` - `undefined`: is a no-op provided to simplify certain use cases. Anything else triggers `sqlite3_result_error()` with a @@ -1712,9 +1716,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( f(pCtx, val); break; } - case 'string': - capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT); + case 'string': { + const [p, n] = wasm.allocCString(val,true); + capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC); break; + } case 'object': if(null===val/*yes, typeof null === 'object'*/) { capi.sqlite3_result_null(pCtx); @@ -1723,7 +1729,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( const pBlob = wasm.allocFromTypedArray(val); capi.sqlite3_result_blob( pCtx, pBlob, val.byteLength, - wasm.exports[sqlite3.config.deallocExportName] + capi.SQLITE_WASM_DEALLOC ); break; } diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index ed3b96dea1..f2bf5fef1e 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -1621,6 +1621,11 @@ int sqlite3_wasm_test_intptr(int * p){ return *p = *p * 2; } +SQLITE_WASM_KEEP +void * sqlite3_wasm_test_voidptr(void * p){ + return p; +} + SQLITE_WASM_KEEP int64_t sqlite3_wasm_test_int64_max(void){ return (int64_t)0x7fffffffffffffff; diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index a87532e18b..ae745a28de 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1987,7 +1987,6 @@ self.sqlite3InitModule = sqlite3InitModule; name: 'virtual table #1: eponymous w/ manual exception handling', predicate: ()=>!!capi.sqlite3_index_info, test: function(sqlite3){ - warn("The vtab/module JS bindings are experimental and subject to change."); const VT = sqlite3.vtab; const tmplCols = Object.assign(Object.create(null),{ A: 0, B: 1 @@ -2185,7 +2184,6 @@ self.sqlite3InitModule = sqlite3InitModule; name: 'virtual table #2: non-eponymous w/ automated exception wrapping', predicate: ()=>!!capi.sqlite3_index_info, test: function(sqlite3){ - warn("The vtab/module JS bindings are experimental and subject to change."); const VT = sqlite3.vtab; const tmplCols = Object.assign(Object.create(null),{ A: 0, B: 1 diff --git a/manifest b/manifest index 713880bc5c..85eda685fc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Extend\soo1.Stmt.bind()\sto\saccept\sArrayBuffer\sinstances\sto\sbind\sas\sblobs. -D 2022-12-24T14:16:02.217 +C Replace\sJS-side\suse\sof\sSQLITE_TRANSIENT\swith\sthe\snew\sSQLITE_WASM_DEALLOC,\sreducing\sthe\samount\sallocation/copying\srequired\sby\ssqlite3_bind_blob/text()\sand\ssqlite3_result_blob/text().\sRemove\sthe\s'experimental'\slog\smessage\sfrom\sthe\svirtual\stable\stests. +D 2022-12-24T15:28:45.647 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -503,16 +503,16 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 -F ext/wasm/api/sqlite3-api-glue.js f0651048a2601bf79f7f39c2c855f6417e65548417f5019ac9ac2ffb2463f2b9 -F ext/wasm/api/sqlite3-api-oo1.js 1312eaf2776c957e41a6fd63c31e7487502bf71745805c41f72429e0925802a5 -F ext/wasm/api/sqlite3-api-prologue.js 683956ea6ab5e0132db48bb693a6bb9dd92f36c8c0902af36572e9b29006ac6d +F ext/wasm/api/sqlite3-api-glue.js ba532798e577497da9221bf9ac7d286619eec4d16736c927f1d10f3c8d21ada3 +F ext/wasm/api/sqlite3-api-oo1.js 5393fb0b325d2fdafada7fdbfb9219af9a865631acb351d5c5196a982b632c8b +F ext/wasm/api/sqlite3-api-prologue.js b0302c61abf21966c8cf9788453fea29c790633f7a14a92e05e6db994b590d11 F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f F ext/wasm/api/sqlite3-v-helper.js 6f6c3e390a72e08b0a5b16a0d567d7af3c04d172831853a29d72a6f1dd40ff24 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 66daf6fb6843bea615fe193109e1542efbeca24f560ee9da63375a910bb48115 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c b114bb85cdf8be7b2939ddcc2bbc2f30c190c44b993c34a77b017c978345efb1 +F ext/wasm/api/sqlite3-wasm.c 313489816e1733a10ece74a92cbea65d3ee241eb07d98088e96258cc211c9719 F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js 145c493221727eb40194280bb2852da49f857e850d8394c31b7bd4caeb5d7bed +F ext/wasm/tester1.c-pp.js dec750b65ec33ff267b35370ab746899859beb0a7c695cb9b087663d5b144512 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6a37874db04f3b4842994ad17fc74cb6222f8ea0fa1315a23aff1ffa69bcd12a -R b07f6a4d9e30a3f649f5decc82bdf7ef +P f76bd30137fbff981625ffcb28cddd5e8651803dfc3f2d8d7801ead33496311d +R 244f04d134cc6151e3968d82d6c44161 U stephan -Z 934cef85bd1b053ac09da3261d128afb +Z 5d490510463cd36c1f126d30ea32761d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bc46d13f05..cf1529c72f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f76bd30137fbff981625ffcb28cddd5e8651803dfc3f2d8d7801ead33496311d \ No newline at end of file +ffe2999a91a7dec129a38afb675fe9e539d7c347886bfea85cba55f6367d54d1 \ No newline at end of file From 75c04ba89cee1bb6b7b2b599eeacfbd063c84703 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 25 Dec 2022 10:22:27 +0000 Subject: [PATCH 7/9] Simplify the signature for JS functions, as opposed to function pointers, passed to sqlite3_exec(), eliminating the superfluous initial two arguments. Update related tests to demonstrate both function-passing approaches. FossilOrigin-Name: e7cc70cdda426863f82ebe1305f4c3053824c5a605b1516b0b7f205f1203178b --- ext/wasm/api/sqlite3-api-glue.js | 39 +++++++++--------- ext/wasm/api/sqlite3-api-prologue.js | 6 +-- ext/wasm/tester1.c-pp.js | 59 ++++++++++++++++++++++------ manifest | 16 ++++---- manifest.uuid | 2 +- 5 files changed, 80 insertions(+), 42 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 7b5fa11816..0ef796664a 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -234,7 +234,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ optional features into account. */ wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); } - + /** Functions which require BigInt (int64) support are separated from the others because we need to conditionally bind them or apply @@ -569,7 +569,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }/*sqlite3_create_collation() and friends*/ - if(1){/* Special-case handling of sqlite3_exec() */ + {/* Special-case handling of sqlite3_exec() */ const __exec = wasm.xWrap("sqlite3_exec", "int", ["sqlite3*", "string:flexible", new wasm.xWrap.FuncPtrAdapter({ @@ -585,23 +585,23 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } /* Wrap the callback in a WASM-bound function and convert the callback's `(char**)` arguments to arrays of strings... */ + let aNames; const cbwrap = function(pVoid, nCols, pColVals, pColNames){ let rc = capi.SQLITE_ERROR; try { - let aVals = [], aNames = [], i = 0, offset = 0; - for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){ - aVals.push( wasm.cstrToJs(wasm.peekPtr(pColVals + offset)) ); - aNames.push( wasm.cstrToJs(wasm.peekPtr(pColNames + offset)) ); - } - rc = callback(pVoid, nCols, aVals, aNames) | 0; - /* The first 2 args of the callback are useless for JS but - we want the JS mapping of the C API to be as close to the - C API as possible. */ + const aVals = wasm.cArgvToJs(nCols, pColVals); + if(!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); + rc = callback(aVals, aNames) | 0; }catch(e){ - /* If we set the db error state here, the higher-level exec() call - replaces it with its own, so we have no way of reporting the - exception message except the console. We must not propagate - exceptions through the C API. */ + /* If we set the db error state here, the higher-level + exec() call replaces it with its own, so we have no way + of reporting the exception message except the console. We + must not propagate exceptions through the C API. Though + we make an effort to report OOM here, sqlite3_exec() + translates that into SQLITE_ABORT as well. */ + rc = (e instanceof sqlite3.WasmAllocError) + ? capi.SQLITE_NOMEM + : (e.resultCode || capi.SQLITE_ERROR); } return rc; }; @@ -616,7 +616,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; }/*sqlite3_exec() proxy*/; - if(1){/* Special-case handling of sqlite3_create_function_v2() + {/* Special-case handling of sqlite3_create_function_v2() and sqlite3_create_window_function() */ /* Maintenance reminder: FuncPtrAdapter is not expressive enough to be able to perform these mappings. */ @@ -1041,20 +1041,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", "static buffer size!"); } - wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); + wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); + // Groups of SQLITE_xyz macros... const defineGroups = ['access', 'authorizer', 'blobFinalizers', 'config', 'dataTypes', 'dbConfig', 'dbStatus', 'encodings', 'fcntl', 'flock', 'ioCap', 'limits', 'openFlags', 'prepareFlags', 'resultCodes', - 'serialize', 'sqlite3Status', + 'sqlite3Status', 'stmtStatus', 'syncFlags', 'trace', 'txnState', 'udfFlags', 'version' ]; if(wasm.bigIntEnabled){ - defineGroups.push('vtab'); + defineGroups.push('serialize', 'vtab'); } for(const t of defineGroups){ for(const e of Object.entries(wasm.ctype[t])){ diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index faa2dbbbf4..e207fc194d 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -948,7 +948,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( const m = f._rx.exec(opt); rv[0] = (m ? m[1] : opt); rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; - }; + }; } const rc = {}, ov = [0,0]; let i = 0, k; @@ -1749,8 +1749,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( argument of sqlite3_value_to_js(). If the sqlite3_column_value() returns NULL (e.g. because the column index is out of range), this function returns `undefined`, regardless of the 3rd - argument. 3rd argument is falsy and conversion fails, `undefined` - will be returned. + argument. If the 3rd argument is falsy and conversion fails, + `undefined` will be returned. Note that sqlite3_column_value() returns an "unprotected" value object, but in a single-threaded environment (like this one) diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index ae745a28de..2314dae2d3 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1882,18 +1882,18 @@ self.sqlite3InitModule = sqlite3InitModule; T.assert(3===resultRows.length) .assert(2===resultRows[1]); T.assert(2===db.selectValue('select a from foo.bar where a>1 order by a')); + + /** Demonstrate the JS-simplified form of the sqlite3_exec() callback... */ let colCount = 0, rowCount = 0; - const execCallback = function(pVoid, nCols, aVals, aNames){ - //console.warn("execCallback(",arguments,")"); - colCount = nCols; - ++rowCount; - T.assert(2===aVals.length) - .assert(2===aNames.length) - .assert(+(aVals[1]) === 2 * +(aVals[0])); - }; let rc = capi.sqlite3_exec( - db.pointer, "select a, a*2 from foo.bar", execCallback, - 0, 0 + db, "select a, a*2 from foo.bar", function(aVals, aNames){ + //console.warn("execCallback(",arguments,")"); + colCount = aVals.length; + ++rowCount; + T.assert(2===aVals.length) + .assert(2===aNames.length) + .assert(+(aVals[1]) === 2 * +(aVals[0])); + }, 0, 0 ); T.assert(0===rc).assert(3===rowCount).assert(2===colCount); rc = capi.sqlite3_exec( @@ -1902,8 +1902,45 @@ self.sqlite3InitModule = sqlite3InitModule; }, 0, 0 ); T.assert(capi.SQLITE_ABORT === rc); + + /* Demonstrate how to get access to the "full" callback + signature, as opposed to the simplified JS-specific one... */ + rowCount = colCount = 0; + const pCb = wasm.installFunction('i(pipp)', function(pVoid,nCols,aVals,aCols){ + /* Tip: wasm.cArgvToJs() can be used to convert aVals and + aCols to arrays: const vals = wasm.cArgvToJs(nCols, + aVals); */ + ++rowCount; + colCount = nCols; + T.assert(2 === nCols) + .assert(wasm.isPtr(pVoid)) + .assert(wasm.isPtr(aVals)) + .assert(wasm.isPtr(aCols)) + .assert(+wasm.cstrToJs(wasm.peekPtr(aVals + wasm.ptrSizeof)) + === 2 * +wasm.cstrToJs(wasm.peekPtr(aVals))); + return 0; + }); + try { + T.assert(wasm.isPtr(pCb)); + rc = capi.sqlite3_exec(db, "select a, a*2 from foo.bar", pCb, 0, 0); + T.assert(0===rc) + .assert(3===rowCount) + .assert(2===colCount); + }finally{ + wasm.uninstallFunction(pCb); + } + + // Demonstrate that an OOM result does not propagate through sqlite3_exec()... + rc = capi.sqlite3_exec( + db, "select a, a*2 from foo.bar", function(aVals, aNames){ + sqlite3.WasmAllocError.toss("just testing"); + }, 0, 0 + ); + T.assert(capi.SQLITE_ABORT === rc); + db.exec("detach foo"); - T.mustThrow(()=>db.exec("select * from foo.bar")); + T.mustThrow(()=>db.exec("select * from foo.bar"), + "Because foo is no longer attached."); }) //////////////////////////////////////////////////////////////////// diff --git a/manifest b/manifest index 85eda685fc..f514b0cc1e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Replace\sJS-side\suse\sof\sSQLITE_TRANSIENT\swith\sthe\snew\sSQLITE_WASM_DEALLOC,\sreducing\sthe\samount\sallocation/copying\srequired\sby\ssqlite3_bind_blob/text()\sand\ssqlite3_result_blob/text().\sRemove\sthe\s'experimental'\slog\smessage\sfrom\sthe\svirtual\stable\stests. -D 2022-12-24T15:28:45.647 +C Simplify\sthe\ssignature\sfor\sJS\sfunctions,\sas\sopposed\sto\sfunction\spointers,\spassed\sto\ssqlite3_exec(),\seliminating\sthe\ssuperfluous\sinitial\stwo\sarguments.\sUpdate\srelated\stests\sto\sdemonstrate\sboth\sfunction-passing\sapproaches. +D 2022-12-25T10:22:27.506 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -503,9 +503,9 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 -F ext/wasm/api/sqlite3-api-glue.js ba532798e577497da9221bf9ac7d286619eec4d16736c927f1d10f3c8d21ada3 +F ext/wasm/api/sqlite3-api-glue.js 72f1ed1d60db62e18cf752c5871bc7c21cfef9258d9c81e61c830914c3f7da91 F ext/wasm/api/sqlite3-api-oo1.js 5393fb0b325d2fdafada7fdbfb9219af9a865631acb351d5c5196a982b632c8b -F ext/wasm/api/sqlite3-api-prologue.js b0302c61abf21966c8cf9788453fea29c790633f7a14a92e05e6db994b590d11 +F ext/wasm/api/sqlite3-api-prologue.js a27762fd1ed2576897026f28a748a69edcdcc53ef79f46cead5e4446dc949763 F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js dec750b65ec33ff267b35370ab746899859beb0a7c695cb9b087663d5b144512 +F ext/wasm/tester1.c-pp.js e75daf4e82ce9c9506d165544b6052403802dd1bcf3f4f06fa6084b8520fb549 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f76bd30137fbff981625ffcb28cddd5e8651803dfc3f2d8d7801ead33496311d -R 244f04d134cc6151e3968d82d6c44161 +P ffe2999a91a7dec129a38afb675fe9e539d7c347886bfea85cba55f6367d54d1 +R 57b44198d665feb79b35a3b3cc62df00 U stephan -Z 5d490510463cd36c1f126d30ea32761d +Z fa52e396693ed4ebb322eeecdf80849b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cf1529c72f..aca2994565 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ffe2999a91a7dec129a38afb675fe9e539d7c347886bfea85cba55f6367d54d1 \ No newline at end of file +e7cc70cdda426863f82ebe1305f4c3053824c5a605b1516b0b7f205f1203178b \ No newline at end of file From 485229e14774ef12d31a815b05aee047739086dd Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 25 Dec 2022 12:51:53 +0000 Subject: [PATCH 8/9] Enhance sqlite3.wasm.xWrap.FuncPtrAdapter to be able to handle sqlite3_create_function() and friends and reimplement those bindings to use this feature (this will also simplify certain session API bindings). Interal API changes only with no client-side breakage. FossilOrigin-Name: 7f9ace1b11a6703031790af9cf08ab25df25850a86e6ca2a7aeaefd8aa395e6d --- ext/wasm/api/sqlite3-api-glue.js | 241 ++++++++++++++----------------- ext/wasm/common/whwasmutil.js | 77 ++++++---- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 169 insertions(+), 165 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 0ef796664a..9066b3431c 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -82,7 +82,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "sqlite3*", new wasm.xWrap.FuncPtrAdapter({ signature: 'i(pi)', - contextKey: (argIndex,argv)=>'sqlite3@'+argv[0] + contextKey: (argv,argIndex)=>'sqlite3@'+argv[0] }), "*" ]], @@ -160,7 +160,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ name: 'xProgressHandler', signature: 'i(p)', bindScope: 'context', - contextKey: (argIndex,argv)=>'sqlite3@'+argv[0] + contextKey: (argv,argIndex)=>'sqlite3@'+argv[0] }), "*" ] ], @@ -200,7 +200,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ new wasm.xWrap.FuncPtrAdapter({ name: 'sqlite3_trace_v2::callback', signature: 'i(ippp)', - contextKey: (argIndex, argv)=>'sqlite3@'+argv[0] + contextKey: (argv,argIndex)=>'sqlite3@'+argv[0] }), "*"], ["sqlite3_txn_state", "int", ["sqlite3*","string"]], /* Note that sqlite3_uri_...() have very specific requirements for @@ -497,28 +497,24 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ); }; - if(1){/* Bindings for sqlite3_create_collation[_v2]() */ - const __collationContextKey = (argIndex,argv)=>{ + {/* Bindings for sqlite3_create_collation[_v2]() */ + // contextKey() impl for wasm.xWrap.FuncPtrAdapter + const contextKey = (argv,argIndex)=>{ return 'argv['+argIndex+']:sqlite3@'+argv[0]+ ':'+wasm.cstrToJs(argv[1]).toLowerCase() }; - const __ccv2 = wasm.xWrap( - 'sqlite3_create_collation_v2', 'int', - 'sqlite3*','string','int','*', - new wasm.xWrap.FuncPtrAdapter({ - /* int(*xCompare)(void*,int,const void*,int,const void*) */ - name: 'sqlite3_create_collation_v2::xCompare', - signature: 'i(pipip)', - bindScope: 'context', - contextKey: __collationContextKey - }), - new wasm.xWrap.FuncPtrAdapter({ - /* void(*xDestroy(void*) */ - name: 'sqlite3_create_collation_v2::xDestroy', - signature: 'v(p)', - bindScope: 'context', - contextKey: __collationContextKey - }) + const __sqlite3CreateCollationV2 = wasm.xWrap( + 'sqlite3_create_collation_v2', 'int', [ + 'sqlite3*', 'string', 'int', '*', + new wasm.xWrap.FuncPtrAdapter({ + /* int(*xCompare)(void*,int,const void*,int,const void*) */ + name: 'xCompare', signature: 'i(pipip)', contextKey + }), + new wasm.xWrap.FuncPtrAdapter({ + /* void(*xDestroy(void*) */ + name: 'xDestroy', signature: 'v(p)', contextKey + }) + ] ); /** @@ -552,13 +548,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){ return __errEncoding(pDb); } - let rc, pfCompare, pfDestroy; - try{ - rc = __ccv2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); + try{ + return __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); }catch(e){ - rc = util.sqlite3_wasm_db_error(pDb, e); + return util.sqlite3_wasm_db_error(pDb, e); } - return rc; }; capi.sqlite3_create_collation = (pDb,zName,eTextRep,pArg,xCompare)=>{ @@ -617,82 +611,91 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }/*sqlite3_exec() proxy*/; {/* Special-case handling of sqlite3_create_function_v2() - and sqlite3_create_window_function() */ - /* Maintenance reminder: FuncPtrAdapter is not expressive enough - to be able to perform these mappings. */ - const sqlite3CreateFunction = wasm.xWrap( - "sqlite3_create_function_v2", "int", - ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, - "int"/*eTextRep*/, "*"/*pApp*/, - "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/] - ); + and sqlite3_create_window_function(). */ + /** + FuncPtrAdapter for contextKey() for sqlite3_create_function(). + */ + const contextKey = function(argv,argIndex){ + return ( + 'sqlite3@'+argv[0] + +':'+argIndex + +':'+wasm.cstrToJs(argv[1]).toLowerCase() + ) + }; - const sqlite3CreateWindowFunction = wasm.xWrap( - "sqlite3_create_window_function", "int", - ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, - "int"/*eTextRep*/, "*"/*pApp*/, - "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, - "*"/*xInverse*/, "*"/*xDestroy*/] - ); - - const __xFunc = function(callback){ - return function(pCtx, argc, pArgv){ - try{ - capi.sqlite3_result_js( - pCtx, - callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) - ); - }catch(e){ - //console.error('xFunc() caught:',e); - capi.sqlite3_result_error_js(pCtx, e); + /** + JS proxies for the various sqlite3_create[_window]_function() + callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter. + */ + const __cfProxy = Object.assign(Object.create(null), { + xInverseAndStep: { + signature:'v(pip)', contextKey, + callProxy: (callback)=>{ + return (pCtx, argc, pArgv)=>{ + try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) } + catch(e){ capi.sqlite3_result_error_js(pCtx, e) } + }; } - }; - }; - - const __xInverseAndStep = function(callback){ - return function(pCtx, argc, pArgv){ - try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) } - catch(e){ capi.sqlite3_result_error_js(pCtx, e) } - }; - }; - - const __xFinalAndValue = function(callback){ - return function(pCtx){ - try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) } - catch(e){ capi.sqlite3_result_error_js(pCtx, e) } - }; - }; - - const __xDestroy = function(callback){ - return function(pVoid){ - try{ callback(pVoid) } - catch(e){ console.error("UDF xDestroy method threw:",e) } - }; - }; - - const __xMap = Object.assign(Object.create(null), { - xFunc: {sig:'v(pip)', f:__xFunc}, - xStep: {sig:'v(pip)', f:__xInverseAndStep}, - xInverse: {sig:'v(pip)', f:__xInverseAndStep}, - xFinal: {sig:'v(p)', f:__xFinalAndValue}, - xValue: {sig:'v(p)', f:__xFinalAndValue}, - xDestroy: {sig:'v(p)', f:__xDestroy} - }); - - /* Internal helper for sqlite3_create_function() and friends. */ - const __xWrapFuncs = function(theKeys, theFuncs, tgtUninst){ - const rc = [] - for(const k of theKeys){ - let fArg = theFuncs[k]; - if('function'===typeof fArg){ - const w = __xMap[k] || toss3("Internal error in __xWrapFuncs: invalid key:",k); - fArg = wasm.installFunction(w.sig, w.f(fArg)); - tgtUninst.push(fArg); + }, + xFinalAndValue: { + signature:'v(p)', contextKey, + callProxy: (callback)=>{ + return (pCtx)=>{ + try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) } + catch(e){ capi.sqlite3_result_error_js(pCtx, e) } + }; + } + }, + xFunc: { + signature:'v(pip)', contextKey, + callProxy: (callback)=>{ + return (pCtx, argc, pArgv)=>{ + try{ + capi.sqlite3_result_js( + pCtx, + callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) + ); + }catch(e){ + //console.error('xFunc() caught:',e); + capi.sqlite3_result_error_js(pCtx, e); + } + }; + } + }, + xDestroy: { + signature:'v(p)', contextKey, + //Arguable: a well-behaved destructor doesn't require a proxy. + callProxy: (callback)=>{ + return (pVoid)=>{ + try{ callback(pVoid) } + catch(e){ console.error("UDF xDestroy method threw:",e) } + }; } - rc.push(fArg); } - return rc; - }; + })/*__cfProxy*/; + + const __sqlite3CreateFunction = wasm.xWrap( + "sqlite3_create_function_v2", "int", [ + "sqlite3*", "string"/*funcName*/, "int"/*nArg*/, + "int"/*eTextRep*/, "*"/*pApp*/, + new wasm.xWrap.FuncPtrAdapter({name: 'xFunc', ...__cfProxy.xFunc}), + new wasm.xWrap.FuncPtrAdapter({name: 'xStep', ...__cfProxy.xInverseAndStep}), + new wasm.xWrap.FuncPtrAdapter({name: 'xFinal', ...__cfProxy.xFinalAndValue}), + new wasm.xWrap.FuncPtrAdapter({name: 'xDestroy', ...__cfProxy.xDestroy}) + ] + ); + + const __sqlite3CreateWindowFunction = wasm.xWrap( + "sqlite3_create_window_function", "int", [ + "sqlite3*", "string"/*funcName*/, "int"/*nArg*/, + "int"/*eTextRep*/, "*"/*pApp*/, + new wasm.xWrap.FuncPtrAdapter({name: 'xStep', ...__cfProxy.xInverseAndStep}), + new wasm.xWrap.FuncPtrAdapter({name: 'xFinal', ...__cfProxy.xFinalAndValue}), + new wasm.xWrap.FuncPtrAdapter({name: 'xValue', ...__cfProxy.xFinalAndValue}), + new wasm.xWrap.FuncPtrAdapter({name: 'xInverse', ...__cfProxy.xInverseAndStep}), + new wasm.xWrap.FuncPtrAdapter({name: 'xDestroy', ...__cfProxy.xDestroy}) + ] + ); /* Documented in the api object's initializer. */ capi.sqlite3_create_function_v2 = function f( @@ -709,26 +712,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){ return __errEncoding(pDb); } - /* Wrap the callbacks in a WASM-bound functions... */ - const uninstall = [/*funcs to uninstall on error*/]; - let rc; try{ - const funcArgs = __xWrapFuncs(['xFunc','xStep','xFinal','xDestroy'], - {xFunc, xStep, xFinal, xDestroy}, - uninstall); - rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, - pApp, ...funcArgs); + return __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, + pApp, xFunc, xStep, xFinal, xDestroy); }catch(e){ console.error("sqlite3_create_function_v2() setup threw:",e); - for(let v of uninstall){ - wasm.uninstallFunction(v); - } - rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, - "Creation of UDF threw: "+e.message); + return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e); } - return rc; }; + /* Documented in the api object's initializer. */ capi.sqlite3_create_function = function f( pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal @@ -744,8 +737,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ pDb, funcName, nArg, eTextRep, pApp, xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) xFinal, //void (*xFinal)(sqlite3_context*) - xValue, //void (*xFinal)(sqlite3_context*) - xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**) + xValue, //void (*xValue)(sqlite3_context*) + xInverse,//void (*xInverse)(sqlite3_context*,int,sqlite3_value**) xDestroy //void (*xDestroy)(void*) ){ if( f.length!==arguments.length ){ @@ -755,24 +748,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){ return __errEncoding(pDb); } - /* Wrap the callbacks in a WASM-bound functions... */ - const uninstall = [/*funcs to uninstall on error*/]; - let rc; try{ - const funcArgs = __xWrapFuncs(['xStep','xFinal','xValue','xInverse','xDestroy'], - {xStep, xFinal, xValue, xInverse, xDestroy}, - uninstall); - rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, - pApp, ...funcArgs); + return __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, + pApp, xStep, xFinal, xValue, + xInverse, xDestroy); }catch(e){ console.error("sqlite3_create_window_function() setup threw:",e); - for(let v of uninstall){ - wasm.uninstallFunction(v); - } - rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, - "Creation of UDF threw: "+e.message); + return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e); } - return rc; }; /** A _deprecated_ alias for capi.sqlite3_result_js() which diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index de7500a020..26c44cf1a6 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -1496,7 +1496,7 @@ self.WhWasmUtilInstaller = function(target){ types are indeterminate, whereas the LHS values will be WASM-compatible values by the time this is called. */ - convertArg(v,argIndex,argv){ + convertArg(v,argv,argIndex){ toss("AbstractArgAdapter must be subclassed."); } }; @@ -1541,24 +1541,38 @@ self.WhWasmUtilInstaller = function(target){ context. This mode is the default if bindScope is _not_ set but a property named contextKey (described below) is. + - callProxy (function): if set, this must be a function which + will act as a proxy for any "converted" JS function. It is + passed the being-converted function value and must return + either that function or a function which acts on its + behalf. The returned function will be the one which gets + installed into the WASM function table. The proxy must perform + any required argument conversion (noting that it will be called + from C code, so will receive C-format arguments) before passing + them on to the being-converted function. Whether or not the + proxy itself must return a value depends on the context. If it + does, it must be a WASM-friendly value, as it will be returning + from a call made from native code. + - contextKey (function): is only used if bindScope is 'context' or if bindScope is not set and this function is, in which case - 'context' is assumed. This function gets passed - (argIndex,argv), where argIndex is the index of _this_ function - pointer in its _wrapping_ function's arguments and argv is the - _current_ still-being-xWrap()-processed args array. All - arguments to the left of argIndex will have been processed by - xWrap() by the time this is called. argv[argIndex] will be the - value the user passed in to the xWrap()'d function for the - argument this FuncPtrAdapter is mapped to. Arguments to the - right of argv[argIndex] will not yet have been converted before - this is called. The function must return a key which uniquely + 'context' is assumed. This function gets bound to this object, + so its "this" is this object. It gets passed (argv,argIndex), + where argIndex is the index of _this_ function pointer in its + _wrapping_ function's arguments and argv is the _current_ + still-being-xWrap()-processed args array. All arguments to the + left of argIndex will have been processed by xWrap() by the + time this is called. argv[argIndex] will be the value the user + passed in to the xWrap()'d function for the argument this + FuncPtrAdapter is mapped to. Arguments to the right of + argv[argIndex] will not yet have been converted before this is + called. The function must return a key which uniquely identifies this function mapping context for _this_ FuncPtrAdapter instance (other instances are not considered), taking into account that C functions often take some sort of state object as one or more of their arguments. As an example, if the xWrap()'d function takes `(int,T*,functionPtr,X*)` and - this FuncPtrAdapter is the argv[2]nd arg, contextKey(2,argv) + this FuncPtrAdapter is the argv[2]nd arg, contextKey(argv,2) might return 'T@'+argv[1], or even just argv[1]. Note, however, that the (X*) argument will not yet have been processed by the time this is called and should not be used as @@ -1570,7 +1584,7 @@ self.WhWasmUtilInstaller = function(target){ use their pointers in the key because most C-strings in this constellation are transient. - Yes, that ^^^ is a bit awkward, but it's what we have. + Yes, that ^^^ is quite awkward, but it's what we have. The constructor only saves the above state for later, and does not actually bind any functions. Its convertArg() method is @@ -1598,6 +1612,8 @@ self.WhWasmUtilInstaller = function(target){ if( ('singleton'===this.bindScope) ) this.singleton = []; else this.singleton = undefined; //console.warn("FuncPtrAdapter()",opt,this); + this.callProxy = (opt.callProxy instanceof Function) + ? opt.callProxy : undefined; } static bindScopes = [ @@ -1605,7 +1621,7 @@ self.WhWasmUtilInstaller = function(target){ ]; /* Dummy impl. Overwritten per-instance as needed. */ - contextKey(argIndex,argv){ + contextKey(argv,argIndex){ return this; } @@ -1638,14 +1654,16 @@ self.WhWasmUtilInstaller = function(target){ See the parent class's convertArg() docs for details on what exactly the 2nd and 3rd arguments are. */ - convertArg(v,argIndex,argv){ + convertArg(v,argv,argIndex){ //console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v); let pair = this.singleton; if(!pair && this.isContext){ - pair = this.contextMap(this.contextKey(argIndex, argv)); + pair = this.contextMap(this.contextKey(argv,argIndex)); } if(pair && pair[0]===v) return pair[1]; if(v instanceof Function){ + /* Install a WASM binding and return its pointer. */ + if(this.callProxy) v = this.callProxy(v); const fp = __installFunction(v, this.signature, this.isTransient); if(pair){ /* Replace existing stashed mapping */ @@ -1660,10 +1678,10 @@ self.WhWasmUtilInstaller = function(target){ }else if(target.isPtr(v) || null===v || undefined===v){ if(pair && pair[1] && pair[1]!==v){ /* uninstall stashed mapping and replace stashed mapping with v. */ - //console.warn("FuncPtrAdapter is uninstalling function", this.contextKey(argIndex,argv),v); + //console.warn("FuncPtrAdapter is uninstalling function", this.contextKey(argv,argIndex),v); try{target.uninstallFunction(pair[1])} catch(e){/*ignored*/} - pair[0] = pair[1] = (v || 0); + pair[0] = pair[1] = (v | 0); } return v || 0; }else{ @@ -1897,17 +1915,20 @@ self.WhWasmUtilInstaller = function(target){ The public interface of argument adapters is that they take ONE argument and return a (possibly) converted result for it. The passing-on of arguments after the first is an - internal impl. detail for the sake of AbstractArgAdapter, and - not to be relied on or documented for other cases. The fact - that this is how AbstractArgAdapter.convertArgs() gets its 2nd+ - arguments, and how FuncPtrAdapter.contextKey() gets its - args, is also an implementation detail and subject to - change. i.e. the public interface of 1 argument is stable. - The fact that any arguments may be passed in after that one, - and what those arguments are, is _not_ part of the public - interface and is _not_ stable. + internal implementation detail for the sake of + AbstractArgAdapter, and not to be relied on or documented + for other cases. The fact that this is how + AbstractArgAdapter.convertArgs() gets its 2nd+ arguments, + and how FuncPtrAdapter.contextKey() gets its args, is also + an implementation detail and subject to change. i.e. the + public interface of 1 argument is stable. The fact that any + arguments may be passed in after that one, and what those + arguments are, is _not_ part of the public interface and is + _not_ stable. */ - for(const i in args) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], i, args); + for(const i in args) args[i] = cxw.convertArgNoCheck( + argTypes[i], args[i], args, i + ); return cxw.convertResultNoCheck(resultType, xf.apply(null,args)); }finally{ target.scopedAllocPop(scope); diff --git a/manifest b/manifest index f514b0cc1e..2c24cd9701 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\ssignature\sfor\sJS\sfunctions,\sas\sopposed\sto\sfunction\spointers,\spassed\sto\ssqlite3_exec(),\seliminating\sthe\ssuperfluous\sinitial\stwo\sarguments.\sUpdate\srelated\stests\sto\sdemonstrate\sboth\sfunction-passing\sapproaches. -D 2022-12-25T10:22:27.506 +C Enhance\ssqlite3.wasm.xWrap.FuncPtrAdapter\sto\sbe\sable\sto\shandle\ssqlite3_create_function()\sand\sfriends\sand\sreimplement\sthose\sbindings\sto\suse\sthis\sfeature\s(this\swill\salso\ssimplify\scertain\ssession\sAPI\sbindings).\sInteral\sAPI\schanges\sonly\swith\sno\sclient-side\sbreakage. +D 2022-12-25T12:51:53.541 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -503,7 +503,7 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 -F ext/wasm/api/sqlite3-api-glue.js 72f1ed1d60db62e18cf752c5871bc7c21cfef9258d9c81e61c830914c3f7da91 +F ext/wasm/api/sqlite3-api-glue.js 4114cbd92818eda90bffb9cf3f181a4c1eb50521c95a3ea6d4e0bbc552901ffb F ext/wasm/api/sqlite3-api-oo1.js 5393fb0b325d2fdafada7fdbfb9219af9a865631acb351d5c5196a982b632c8b F ext/wasm/api/sqlite3-api-prologue.js a27762fd1ed2576897026f28a748a69edcdcc53ef79f46cead5e4446dc949763 F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f @@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07 F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f -F ext/wasm/common/whwasmutil.js 700fb1b702986522d2177fe8247bfbab3040e82cb4f6c35e929c4c85fbd7ffc5 +F ext/wasm/common/whwasmutil.js 46e1ae432eda14ade6c84318c257917dd86755cb0bfa329d989522e057adff6e F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ffe2999a91a7dec129a38afb675fe9e539d7c347886bfea85cba55f6367d54d1 -R 57b44198d665feb79b35a3b3cc62df00 +P e7cc70cdda426863f82ebe1305f4c3053824c5a605b1516b0b7f205f1203178b +R 942325294903c7f082d03ed59c06bba6 U stephan -Z fa52e396693ed4ebb322eeecdf80849b +Z 9f06406b1eaa13fb27bb0e5cf658bbb6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index aca2994565..59d0c24513 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e7cc70cdda426863f82ebe1305f4c3053824c5a605b1516b0b7f205f1203178b \ No newline at end of file +7f9ace1b11a6703031790af9cf08ab25df25850a86e6ca2a7aeaefd8aa395e6d \ No newline at end of file From 7015aa9f4957c3131c8bc814c81c4c5d2dea8f89 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 25 Dec 2022 14:04:06 +0000 Subject: [PATCH 9/9] Add missing sqlite3_context_db_handle() JS binding. Reimplement sqlite3_set_authorizer() and sqlite3_set_auxdata() JS bindings to take advantage of [7f9ace1b11a67]. Teach FuncPtrAdapter to emit a console.warn() message if it is invoked after the library is bootstrapped, the goal being to inform users that it's an internal API and should not be invoked from client-side code. FossilOrigin-Name: 8e3d4f6294037396e388ec21abb18bf0201a6bec6ff004730cc5d11b705a6d2b --- ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 1 + ext/wasm/api/sqlite3-api-glue.js | 114 ++++++++++---------- ext/wasm/api/sqlite3-api-prologue.js | 1 + ext/wasm/api/sqlite3-wasm.c | 12 ++- ext/wasm/common/whwasmutil.js | 7 ++ ext/wasm/tester1.c-pp.js | 1 + manifest | 22 ++-- manifest.uuid | 2 +- 8 files changed, 83 insertions(+), 77 deletions(-) diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 75e5ea3da1..47a7e4a062 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -29,6 +29,7 @@ _sqlite3_column_value _sqlite3_compileoption_get _sqlite3_compileoption_used _sqlite3_complete +_sqlite3_context_db_handle _sqlite3_create_collation _sqlite3_create_collation_v2 _sqlite3_create_function diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 9066b3431c..85c549ae3e 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -82,7 +82,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "sqlite3*", new wasm.xWrap.FuncPtrAdapter({ signature: 'i(pi)', - contextKey: (argv,argIndex)=>'sqlite3@'+argv[0] + contextKey: (argv,argIndex)=>argv[0/* sqlite3* */] }), "*" ]], @@ -103,6 +103,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_compileoption_get", "string", "int"], ["sqlite3_compileoption_used", "int", "string"], ["sqlite3_complete", "int", "string:flexible"], + ["sqlite3_context_db_handle", "sqlite3*", "sqlite3_context*"], + /* sqlite3_create_function(), sqlite3_create_function_v2(), and sqlite3_create_window_function() use hand-written bindings to simplify handling of their function-type arguments. */ @@ -154,16 +156,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ for those, depending on how their SQL argument is provided. */ /* sqlite3_randomness() uses a hand-written wrapper to extend the range of supported argument types. */ - [ - "sqlite3_progress_handler", undefined, [ - "sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({ - name: 'xProgressHandler', - signature: 'i(p)', - bindScope: 'context', - contextKey: (argv,argIndex)=>'sqlite3@'+argv[0] - }), "*" - ] - ], + ["sqlite3_progress_handler", undefined, [ + "sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({ + name: 'xProgressHandler', + signature: 'i(p)', + bindScope: 'context', + contextKey: (argv,argIndex)=>argv[0/* sqlite3* */] + }), "*" + ]], ["sqlite3_realloc", "*","*","int"], ["sqlite3_reset", "int", "sqlite3_stmt*"], ["sqlite3_result_blob", undefined, "sqlite3_context*", "*", "int", "*"], @@ -179,7 +179,34 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_result_subtype", undefined, "sqlite3_value*", "int"], ["sqlite3_result_text", undefined, "sqlite3_context*", "string", "int", "*"], ["sqlite3_result_zeroblob", undefined, "sqlite3_context*", "int"], - ["sqlite3_set_auxdata", undefined, "sqlite3_context*", "int", "*", "*"/* => v(*) */], + ["sqlite3_set_authorizer", "int", [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: "sqlite3_set_authorizer::xAuth", + signature: "i(pi"+"ssss)", + contextKey: (argv, argIndex)=>argv[0/*(sqlite3*)*/], + callProxy: (callback)=>{ + return (pV, iCode, s0, s1, s2, s3)=>{ + try{ + s0 = s0 && wasm.cstrToJs(s0); s1 = s1 && wasm.cstrToJs(s1); + s2 = s2 && wasm.cstrToJs(s2); s3 = s3 && wasm.cstrToJs(s3); + return callback(pV, iCode, s0, s1, s2, s3) || 0; + }catch(e){ + return e.resultCode || capi.SQLITE_ERROR; + } + } + } + }), + "*"/*pUserData*/ + ]], + ["sqlite3_set_auxdata", undefined, [ + "sqlite3_context*", "int", "*", + new wasm.xWrap.FuncPtrAdapter({ + name: 'xDestroyAuxData', + signature: 'v(*)', + contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */] + }) + ]], ["sqlite3_shutdown", undefined], ["sqlite3_sourceid", "string"], ["sqlite3_sql", "string", "sqlite3_stmt*"], @@ -196,12 +223,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "sqlite3*", "string", "string", "string", "**", "**", "*", "*", "*"], ["sqlite3_total_changes", "int", "sqlite3*"], - ["sqlite3_trace_v2", "int", "sqlite3*", "int", - new wasm.xWrap.FuncPtrAdapter({ - name: 'sqlite3_trace_v2::callback', - signature: 'i(ippp)', - contextKey: (argv,argIndex)=>'sqlite3@'+argv[0] - }), "*"], + ["sqlite3_trace_v2", "int", [ + "sqlite3*", "int", + new wasm.xWrap.FuncPtrAdapter({ + name: 'sqlite3_trace_v2::callback', + signature: 'i(ippp)', + contextKey: (argv,argIndex)=>argv[0/* sqlite3* */] + }), + "*" + ]], ["sqlite3_txn_state", "int", ["sqlite3*","string"]], /* Note that sqlite3_uri_...() have very specific requirements for their first C-string arguments, so we cannot perform any value @@ -267,8 +297,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_result_int64", undefined, "*", "i64"], ["sqlite3_result_zeroblob64", "int", "*", "i64"], ["sqlite3_serialize","*", "sqlite3*", "string", "*", "int"], - /* sqlite3_set_authorizer() requires a hand-written binding for - string conversions, so is defined elsewhere. */ ["sqlite3_set_last_insert_rowid", undefined, ["sqlite3*", "i64"]], ["sqlite3_status64", "int", "int", "*", "*", "int"], ["sqlite3_total_changes64", "i64", ["sqlite3*"]], @@ -500,8 +528,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ {/* Bindings for sqlite3_create_collation[_v2]() */ // contextKey() impl for wasm.xWrap.FuncPtrAdapter const contextKey = (argv,argIndex)=>{ - return 'argv['+argIndex+']:sqlite3@'+argv[0]+ - ':'+wasm.cstrToJs(argv[1]).toLowerCase() + return 'argv['+argIndex+']:'+argv[0/* sqlite3* */]+ + ':'+wasm.cstrToJs(argv[1/* collation name */]).toLowerCase() }; const __sqlite3CreateCollationV2 = wasm.xWrap( 'sqlite3_create_collation_v2', 'int', [ @@ -581,11 +609,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ `(char**)` arguments to arrays of strings... */ let aNames; const cbwrap = function(pVoid, nCols, pColVals, pColNames){ - let rc = capi.SQLITE_ERROR; try { const aVals = wasm.cArgvToJs(nCols, pColVals); if(!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); - rc = callback(aVals, aNames) | 0; + return callback(aVals, aNames) | 0; }catch(e){ /* If we set the db error state here, the higher-level exec() call replaces it with its own, so we have no way @@ -593,11 +620,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ must not propagate exceptions through the C API. Though we make an effort to report OOM here, sqlite3_exec() translates that into SQLITE_ABORT as well. */ - rc = (e instanceof sqlite3.WasmAllocError) - ? capi.SQLITE_NOMEM - : (e.resultCode || capi.SQLITE_ERROR); + return e.resultCode || capi.SQLITE_ERROR; } - return rc; }; let rc; try{ @@ -617,7 +641,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ */ const contextKey = function(argv,argIndex){ return ( - 'sqlite3@'+argv[0] + argv[0/* sqlite3* */] +':'+argIndex +':'+wasm.cstrToJs(argv[1]).toLowerCase() ) @@ -941,37 +965,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }/*sqlite3_bind_text/blob()*/ - {/* sqlite3_set_authorizer() */ - const __ssa = wasm.xWrap("sqlite3_set_authorizer", 'int', [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_set_authorizer::xAuth", - signature: "i(pi"+"ssss)", - contextKey: (argIndex, argv)=>argv[0/*(sqlite3*)*/] - }), - "*" - ]); - capi.sqlite3_set_authorizer = function(pDb, xAuth, pUserData){ - if(3!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_set_authorizer', 3); - if(xAuth instanceof Function){ - const xProxy = xAuth; - /* Create a proxy which will receive the C-strings from WASM - and convert them to JS strings for the client-supplied - function. */ - xAuth = function(pV, iCode, s0, s1, s2, s3){ - try{ - s0 = s0 && wasm.cstrToJs(s0); s1 = s1 && wasm.cstrToJs(s1); - s2 = s2 && wasm.cstrToJs(s2); s3 = s3 && wasm.cstrToJs(s3); - return xProxy(pV, iCode, s0, s1, s2, s3) || 0; - }catch(e){ - return util.sqlite3_wasm_db_error(pDb, e); - } - }; - } - return __ssa(pDb, xAuth, pUserData); - }; - }/* sqlite3_set_authorizer() */ - {/* sqlite3_config() */ /** Wraps a small subset of the C API's sqlite3_config() options. @@ -1190,4 +1183,5 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }/*pKvvfs*/ + wasm.xWrap.FuncPtrAdapter.warnOnUse = true; }); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index e207fc194d..7804c04589 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -405,6 +405,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }else{ super("Allocation failed."); } + this.resultCode = capi.SQLITE_NOMEM; this.name = 'WasmAllocError'; } }; diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index f2bf5fef1e..5c9b533184 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -323,11 +323,13 @@ SQLITE_WASM_KEEP int sqlite3_wasm_pstack_quota(void){ */ SQLITE_WASM_KEEP int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){ - if( 0!=zMsg ){ - const int nMsg = sqlite3Strlen30(zMsg); - sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg); - }else{ - sqlite3ErrorWithMsg(db, err_code, NULL); + if( db!=0 ){ + if( 0!=zMsg ){ + const int nMsg = sqlite3Strlen30(zMsg); + sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg); + }else{ + sqlite3ErrorWithMsg(db, err_code, NULL); + } } return err_code; } diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 26c44cf1a6..8e18586120 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -1598,6 +1598,11 @@ self.WhWasmUtilInstaller = function(target){ xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter { constructor(opt) { super(opt); + if(xArg.FuncPtrAdapter.warnOnUse){ + console.warn('xArg.FuncPtrAdapter is an internal-only API', + 'and is not intended to be invoked from', + 'client-level code. Invoked with:',opt); + } this.signature = opt.signature; if(!opt.bindScope && (opt.contextKey instanceof Function)){ opt.bindScope = 'context'; @@ -1616,6 +1621,8 @@ self.WhWasmUtilInstaller = function(target){ ? opt.callProxy : undefined; } + static warnOnUse = false; + static bindScopes = [ 'transient', 'context', 'singleton' ]; diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 2314dae2d3..a9b29729b8 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1625,6 +1625,7 @@ self.sqlite3InitModule = sqlite3InitModule; db.createFunction("bar", { arity: -1, xFunc: (pCx,...args)=>{ + T.assert(db.pointer === capi.sqlite3_context_db_handle(pCx)); let rc = 0; for(const v of args) rc += v; return rc; diff --git a/manifest b/manifest index 2c24cd9701..ce22b98bdd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\ssqlite3.wasm.xWrap.FuncPtrAdapter\sto\sbe\sable\sto\shandle\ssqlite3_create_function()\sand\sfriends\sand\sreimplement\sthose\sbindings\sto\suse\sthis\sfeature\s(this\swill\salso\ssimplify\scertain\ssession\sAPI\sbindings).\sInteral\sAPI\schanges\sonly\swith\sno\sclient-side\sbreakage. -D 2022-12-25T12:51:53.541 +C Add\smissing\ssqlite3_context_db_handle()\sJS\sbinding.\sReimplement\ssqlite3_set_authorizer()\sand\ssqlite3_set_auxdata()\sJS\sbindings\sto\stake\sadvantage\sof\s[7f9ace1b11a67].\sTeach\sFuncPtrAdapter\sto\semit\sa\sconsole.warn()\smessage\sif\sit\sis\sinvoked\safter\sthe\slibrary\sis\sbootstrapped,\sthe\sgoal\sbeing\sto\sinform\susers\sthat\sit's\san\sinternal\sAPI\sand\sshould\snot\sbe\sinvoked\sfrom\sclient-side\scode. +D 2022-12-25T14:04:06.021 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -494,7 +494,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile ffe0e9818a3b6b36c85a1d10dab76b220a8f5cd83439c62e50223a7970b3d68a F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20 F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 4c7788042196cecab32f87d8e4965c183fea59037603888059f244b1752babcc +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 7e0468ad0294786da90b2062043288f734a65243a458a541f2f880a785be0bb9 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md 77a2f1f2fc60a35def7455dffc8d3f2c56385d6ac5c6cecc60fa938252ea2c54 F ext/wasm/api/extern-post-js.c-pp.js 8923f76c3d2213159e12d641dc750523ead5c848185dc4996fae5cc12397f88d @@ -503,16 +503,16 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 -F ext/wasm/api/sqlite3-api-glue.js 4114cbd92818eda90bffb9cf3f181a4c1eb50521c95a3ea6d4e0bbc552901ffb +F ext/wasm/api/sqlite3-api-glue.js ef838f43483e8bfd4ccb5a01feb227198b0f69d40120dc33c805b6c39eb2c36d F ext/wasm/api/sqlite3-api-oo1.js 5393fb0b325d2fdafada7fdbfb9219af9a865631acb351d5c5196a982b632c8b -F ext/wasm/api/sqlite3-api-prologue.js a27762fd1ed2576897026f28a748a69edcdcc53ef79f46cead5e4446dc949763 +F ext/wasm/api/sqlite3-api-prologue.js 4ffe2b80742e2fcf44de6174bfb2981ff26ea0d1fe505bb511ffe0d9ac8fe6d0 F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f F ext/wasm/api/sqlite3-v-helper.js 6f6c3e390a72e08b0a5b16a0d567d7af3c04d172831853a29d72a6f1dd40ff24 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 66daf6fb6843bea615fe193109e1542efbeca24f560ee9da63375a910bb48115 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 313489816e1733a10ece74a92cbea65d3ee241eb07d98088e96258cc211c9719 +F ext/wasm/api/sqlite3-wasm.c 325ca43b982ef608243493c42249ba4e6b2e549f7cbd9b332e2f7ee8227d2ae3 F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07 F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f -F ext/wasm/common/whwasmutil.js 46e1ae432eda14ade6c84318c257917dd86755cb0bfa329d989522e057adff6e +F ext/wasm/common/whwasmutil.js de2871d66b786a5272a345ccf48e56ec74b12b1e228cc218160109aac8bbfced F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6 @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js e75daf4e82ce9c9506d165544b6052403802dd1bcf3f4f06fa6084b8520fb549 +F ext/wasm/tester1.c-pp.js 47190277040a6163716c02d11930dc4f028d44a6fcdacb10303010f239ce0a6c F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e7cc70cdda426863f82ebe1305f4c3053824c5a605b1516b0b7f205f1203178b -R 942325294903c7f082d03ed59c06bba6 +P 7f9ace1b11a6703031790af9cf08ab25df25850a86e6ca2a7aeaefd8aa395e6d +R f078a5b2f216a35fd658583197af5c35 U stephan -Z 9f06406b1eaa13fb27bb0e5cf658bbb6 +Z 83e66bae6539242de70262c0ffddff2b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 59d0c24513..9f257ff602 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7f9ace1b11a6703031790af9cf08ab25df25850a86e6ca2a7aeaefd8aa395e6d \ No newline at end of file +8e3d4f6294037396e388ec21abb18bf0201a6bec6ff004730cc5d11b705a6d2b \ No newline at end of file