From: stephan Date: Sun, 21 Sep 2025 21:17:41 +0000 (+0000) Subject: Fix a Number/BigInt descrepancy in oo1.DB() when passed a WASM-string db filename... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e6db55f0e24872618043696c4472369da6f66734;p=thirdparty%2Fsqlite.git Fix a Number/BigInt descrepancy in oo1.DB() when passed a WASM-string db filename. Consolidate some duplicated internal code and adjacent minor cleanups. FossilOrigin-Name: d078aff7817ccd4f891024e55703519307a53815d472086bf3d42b2be28698b3 --- diff --git a/ext/wasm/api/sqlite3-api-glue.c-pp.js b/ext/wasm/api/sqlite3-api-glue.c-pp.js index 903b8b4171..5564ce881e 100644 --- a/ext/wasm/api/sqlite3-api-glue.c-pp.js +++ b/ext/wasm/api/sqlite3-api-glue.c-pp.js @@ -1470,17 +1470,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Helper for string:flexible conversions which requires a byte-length counterpart argument. Passed a value and its ostensible length, this function returns [V,N], where V is - either v or a transformed copy of v and N is either (if v is a - WASM pointer, in which case n might be a BigInt), -1 (if v is a - string or Array), or the byte length of v (if it's a byte array - or ArrayBuffer). + either v or a to-string transformed copy of v and N is either n + (if v is a WASM pointer, in which case n might be a BigInt), -1 + (if v is a string or Array), or the byte length of v (if it's a + byte array or ArrayBuffer). */ const __flexiString = (v,n)=>{ if('string'===typeof v){ n = -1; }else if(util.isSQLableTypedArray(v)){ n = v.byteLength; - v = util.typedArrayToString( + v = wasm.typedArrayToString( (v instanceof ArrayBuffer) ? new Uint8Array(v) : v ); }else if(Array.isArray(v)){ diff --git a/ext/wasm/api/sqlite3-api-oo1.c-pp.js b/ext/wasm/api/sqlite3-api-oo1.c-pp.js index 51d7e20f99..84a388302f 100644 --- a/ext/wasm/api/sqlite3-api-oo1.c-pp.js +++ b/ext/wasm/api/sqlite3-api-oo1.c-pp.js @@ -269,7 +269,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ sqlite3.config.error("Invalid DB ctor args",opt,arguments); toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt); } - let fnJs = ('number'===typeof fn) ? wasm.cstrToJs(fn) : fn; + let fnJs = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn; const vfsCheck = ctor._name2vfs[fnJs]; if(vfsCheck){ vfsName = vfsCheck.vfs; @@ -1283,9 +1283,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ toss3("xValue must be provided if xInverse is."); } const pApp = opt.pApp; - if(undefined!==pApp && - null!==pApp && - (('number'!==typeof pApp) || !util.isInt32(pApp))){ + if( undefined!==pApp + && null!==pApp + && !wasm.isPtr(pApp) ){ toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); } const xDestroy = opt.xDestroy || 0; @@ -1586,18 +1586,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ If key is a number and within range of stmt's bound parameter count, key is returned. - If key is not a number then it is checked against named - parameters. If a match is found, its index is returned. + If key is not a number then it must be a JS string (not a WASM + string) and it is checked against named parameters. If a match is + found, its index is returned. Else it throws. */ const affirmParamIndex = function(stmt,key){ const n = ('number'===typeof key) ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key); - if(0===n || !util.isInt32(n)){ - toss3("Invalid bind() parameter name: "+key); - } - else if(n<1 || n>stmt.parameterCount) toss3("Bind index",key,"is out of range."); + if( 0===n || !util.isInt32(n) ) toss3("Invalid bind() parameter name: "+key); + else if( n<1 || n>stmt.parameterCount ) toss3("Bind index",key,"is out of range."); return n; }; diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 42d232cce8..32ecb1ed0f 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -214,7 +214,10 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; /** Internal helper for SQLite3Error ctor. */ - const __isInt = (n)=>'number'===typeof n && n===(n | 0); + const isInt32 = (n)=> + 'number'===typeof n + && n===(n | 0) + && n<=2147483647 && n>=-2147483648; /** An Error subclass specifically for reporting DB-level errors and @@ -248,7 +251,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( constructor(...args){ let rc; if(args.length){ - if(__isInt(args[0])){ + if(isInt32(args[0])){ rc = args[0]; if(1===args.length){ super(__rcStr(args[0])); @@ -291,16 +294,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'."); } - /** - Returns true if n is a 32-bit (signed) integer, else - false. This is used for determining when we need to switch to - double-type DB operations for integer values in order to keep - more precision. - */ - const isInt32 = (n)=>{ - return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) - && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); - }; /** Returns true if the given BigInt value is small enough to fit into an int64 value, else false. @@ -336,16 +329,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false; }; - - /** Internal helper to use in operations which need to distinguish - between TypedArrays which are backed by a SharedArrayBuffer - from those which are not. */ - const __SAB = ('undefined'===typeof SharedArrayBuffer) - ? function(){} : SharedArrayBuffer; - /** Returns true if the given TypedArray object is backed by a - SharedArrayBuffer, else false. */ - const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB); - /** Returns true if v appears to be one of our bind()-able TypedArray types: Uint8Array or Int8Array or ArrayBuffer. Support for @@ -354,11 +337,10 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( 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 instanceof ArrayBuffer); - }; + const isBindableTypedArray = (v)=> + v && (v instanceof Uint8Array + || v instanceof Int8Array + || v instanceof ArrayBuffer); /** Returns true if v appears to be one of the TypedArray types @@ -369,69 +351,29 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( and friends to the isBindableTypedArray() list but not to the isSQLableTypedArray() list. */ - const isSQLableTypedArray = (v)=>{ - return v && (v instanceof Uint8Array - || v instanceof Int8Array - || v instanceof ArrayBuffer); - }; + const isSQLableTypedArray = (v)=> + v && (v instanceof Uint8Array + || v instanceof Int8Array + || v instanceof ArrayBuffer); /** Returns true if isBindableTypedArray(v) does, else throws with a message that v is not a supported TypedArray value. */ - const affirmBindableTypedArray = (v)=>{ - return isBindableTypedArray(v) - || toss3("Value is not of a supported TypedArray type."); - }; - - /** - Returns either aTypedArray.slice(begin,end) (if - aTypedArray.buffer is a SharedArrayBuffer) or - aTypedArray.subarray(begin,end) (if it's not). - - This distinction is important for APIs which don't like to - work on SABs, e.g. TextDecoder, and possibly for our - own APIs which work on memory ranges which "might" be - modified by other threads while they're working. - */ - const typedArrayPart = (aTypedArray, begin, end)=>{ - //if( 8===wasm..ptr.size ){ - // slice() and subarray() do not like BigInt args. - if( 'bigint'===typeof begin ) begin = Number(begin); - if( 'bigint'===typeof end ) end = Number(end); - begin = Number(begin); - end = Number(end); - //} - return isSharedTypedArray(aTypedArray) - ? aTypedArray.slice(begin, end) - : aTypedArray.subarray(begin, end); - }; - - const utf8Decoder = new TextDecoder('utf-8'); - - /** - Uses TextDecoder to decode the given half-open range of the given - TypedArray to a string. This differs from a simple call to - TextDecoder in that it accounts for whether the first argument is - backed by a SharedArrayBuffer or not, and can work more - efficiently if it's not (TextDecoder refuses to act upon an SAB). - - If begin/end are not provided or are falsy then each defaults to - the start/end of the string. - */ - const typedArrayToString = function(typedArray, begin, end){ - return utf8Decoder.decode( - typedArrayPart(typedArray, begin || 0, end || typedArray.length) - ); - }; + const affirmBindableTypedArray = (v)=> + isBindableTypedArray(v) + || toss3("Value is not of a supported TypedArray type."); /** If v is-a Array, its join("") result is returned. If - isSQLableTypedArray(v) is true then typedArrayToString(v) is + isSQLableTypedArray(v) is true then wasm.typedArrayToString(v) is returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is returned. Else v is returned as-is. + + Reminder to self: the "return as-is" instead of returning ''+v is + arguably a design mistake but changing it is risky at this point. */ const flexibleString = function(v){ if(isSQLableTypedArray(v)){ - return typedArrayToString( + return wasm.typedArrayToString( (v instanceof ArrayBuffer) ? new Uint8Array(v) : v, 0, v.length ); @@ -806,13 +748,11 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( bigIntFits32, bigIntFits64, bigIntFitsDouble, isBindableTypedArray, isInt32, isSQLableTypedArray, isTypedArray, - typedArrayToString, isUIThread: ()=>(globalThis.window===globalThis && !!globalThis.document), // is this true for ESM?: 'undefined'===typeof WorkerGlobalScope - isSharedTypedArray, toss: function(...args){throw new Error(args.join(' '))}, toss3, - typedArrayPart, + typedArrayPart: wasm.typedArrayPart, /** Given a byte array or ArrayBuffer, this function throws if the lead bytes of that buffer do not hold a SQLite3 database header, @@ -1270,7 +1210,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( do{ const j = (n>nAlloc ? nAlloc : n); r(j, ptr); - ta.set(typedArrayPart(heap, ptr, wasm.ptr.add(ptr,j)), offset); + ta.set(wasm.typedArrayPart(heap, ptr, wasm.ptr.add(ptr,j)), offset); n -= j; offset += j; } while(n > 0); diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 1cc4f7bc0c..31ab522fa5 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -996,24 +996,61 @@ globalThis.WhWasmUtilInstaller = function(target){ return Number(pos - ptr); }; + /** Internal helper to use in operations which need to distinguish - between SharedArrayBuffer heap memory and non-shared heap. */ + between TypedArrays which are backed by a SharedArrayBuffer + from those which are not. */ const __SAB = ('undefined'===typeof SharedArrayBuffer) - ? function(){} : SharedArrayBuffer; - const __utf8Decode = function(arrayBuffer, begin, end){ - //if( 'bigint'===typeof begin ) begin = Number(begin); - //if( 'bigint'===typeof end ) end = Number(end); - /*if( 8===__ptrSize ){ - begin = Number(begin); - end = Number(end); - }*/ - return cache.utf8Decoder.decode( - (arrayBuffer.buffer instanceof __SAB) - ? arrayBuffer.slice(begin, end) - : arrayBuffer.subarray(begin, end) - ); + ? function(){/*dummy class*/} : SharedArrayBuffer; + /** Returns true if the given TypedArray object is backed by a + SharedArrayBuffer, else false. */ + const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB); + + target.isSharedTypedArray = isSharedTypedArray; + + /** + Returns either aTypedArray.slice(begin,end) (if + aTypedArray.buffer is a SharedArrayBuffer) or + aTypedArray.subarray(begin,end) (if it's not). + + This distinction is important for APIs which don't like to + work on SABs, e.g. TextDecoder, and possibly for our + own APIs which work on memory ranges which "might" be + modified by other threads while they're working. + + begin and end may be of type Number or (in 64-bit builds) BigInt + (which get coerced to Numbers). + */ + const typedArrayPart = (aTypedArray, begin, end)=>{ + if( 8===__ptrSize ){ + // slice() and subarray() do not like BigInt args. + if( 'bigint'===typeof begin ) begin = Number(begin); + if( 'bigint'===typeof end ) end = Number(end); + } + return isSharedTypedArray(aTypedArray) + ? aTypedArray.slice(begin, end) + : aTypedArray.subarray(begin, end); }; + target.typedArrayPart = typedArrayPart; + + /** + Uses TextDecoder to decode the given half-open range of the given + TypedArray to a string. This differs from a simple call to + TextDecoder in that it accounts for whether the first argument is + backed by a SharedArrayBuffer or not, and can work more + efficiently if it's not (TextDecoder refuses to act upon an SAB). + + If begin/end are not provided or are falsy then each defaults to + the start/end of the array. + */ + const typedArrayToString = (typedArray, begin, end)=> + cache.utf8Decoder.decode( + typedArrayPart(typedArray, begin, end) + ); + + target.typedArrayToString = typedArrayToString; + /** Expects ptr to be a pointer into the WASM heap memory which refers to a NUL-terminated C-style string encoded as UTF-8. This @@ -1024,7 +1061,7 @@ globalThis.WhWasmUtilInstaller = function(target){ target.cstrToJs = function(ptr){ const n = target.cstrlen(ptr); return n - ? __utf8Decode(heapWrappers().HEAP8U, Number(ptr), Number(ptr)+n) + ? typedArrayToString(heapWrappers().HEAP8U, Number(ptr), Number(ptr)+n) : (null===n ? n : ""); }; diff --git a/manifest b/manifest index 7120657501..c4dc7a5894 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sStructType.dispose()/ondispose()\smemleak\sin\sJaccwabyt\sbindings\sin\s64-bit\sbuilds\sand\supdate\sits\sdocs\sfor\s64-bit\sadditions.\sRemove\sthe\sparts\sof\stester1.js\srelated\sto\sskipping\sspecific\stests\sin\s64-bit\sbuilds. -D 2025-09-21T20:26:47.094 +C Fix\sa\sNumber/BigInt\sdescrepancy\sin\soo1.DB()\swhen\spassed\sa\sWASM-string\sdb\sfilename.\sConsolidate\ssome\sduplicated\sinternal\scode\sand\sadjacent\sminor\scleanups. +D 2025-09-21T21:17:41.884 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -597,9 +597,9 @@ F ext/wasm/api/post-js-footer.js 365405929f41ca0e6d389ed8a8da3f3c93e11d3ef43a90a F ext/wasm/api/post-js-header.js 53740d824e5d9027eb1e6fd59e216abbd2136740ce260ea5f0699ff2acb0a701 F ext/wasm/api/pre-js.c-pp.js 58f823de197e2c10d76179aa05410a593b7ae03e1ece983bb42ffd818e8857e1 F ext/wasm/api/sqlite3-api-cleanup.js d4f1a5e665afaf84015f6ef0ddd766f638cb28501c4569b1d4b527c4b5a2b9a4 -F ext/wasm/api/sqlite3-api-glue.c-pp.js 814bdccd7e7c28520cd9a2480785fda027529061e9187460d7155775034afd0f -F ext/wasm/api/sqlite3-api-oo1.c-pp.js 831ce373495f6a5d9230f31a1e09e8995e317828926e736d58c9e7091c6b1d07 -F ext/wasm/api/sqlite3-api-prologue.js bdf8e553c2142916fd7a2382e1becfed7a5755da95f585f632336634e5728448 +F ext/wasm/api/sqlite3-api-glue.c-pp.js 775a4971821aa523c35589b8bfb630672979b6f93d40e31fac67e625278b1b49 +F ext/wasm/api/sqlite3-api-oo1.c-pp.js db4c8ebb03bac60db32ce03f8c615b00f4e4ad53e7d5de5e63d2780cba052caa +F ext/wasm/api/sqlite3-api-prologue.js 859556cbccac7585d532d34d88ffd94d5f3899f205ecd86aebde7ca233800b9c F ext/wasm/api/sqlite3-api-worker1.c-pp.js 760191cd13416e6f5adfd9fcc8a97fed5645c9e0a5fbac213a2d4ce2d79a4334 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966 @@ -618,7 +618,7 @@ F ext/wasm/c-pp.c cca55c5b55ebd8d29916adbedb0e40baa12caa9a2e8429f812683c308f9b0e F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f -F ext/wasm/common/whwasmutil.js aff84dc5b7bf1f06a567d4ab43d28422447f5806487210f805d4d85b400b82b8 +F ext/wasm/common/whwasmutil.js 01ff73506497bc72fb4befa728180fadbfce233cbc6d334349de20ac599f8680 F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 @@ -2175,8 +2175,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 0336fa95e15c53ac6ab8152a840163a5aac64725874ffb848ce1d95e3af90586 -R b1fcd52f2c860acb4ea0ae4f12feba2f +P 6baea1d719b0c345fee5f32a3917ce9c507a2d8cb029eaca675daca77f297eba +R d30caea5f4cf59306605198c4b94e5e3 U stephan -Z bf93d953cde2ac0d4ff0dc420f01f479 +Z 4273dba133b18477f15537f7ebd93229 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1a1b11754c..2d24b06026 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6baea1d719b0c345fee5f32a3917ce9c507a2d8cb029eaca675daca77f297eba +d078aff7817ccd4f891024e55703519307a53815d472086bf3d42b2be28698b3