diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/wasm/api/extern-post-js.c-pp.js | 2 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 38 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-vfs-opfs-sahpool.js | 82 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-vfs-opfs.c-pp.js | 2 | ||||
-rw-r--r-- | ext/wasm/tester1.c-pp.js | 81 |
5 files changed, 137 insertions, 68 deletions
diff --git a/ext/wasm/api/extern-post-js.c-pp.js b/ext/wasm/api/extern-post-js.c-pp.js index fac00370d..bd5225d11 100644 --- a/ext/wasm/api/extern-post-js.c-pp.js +++ b/ext/wasm/api/extern-post-js.c-pp.js @@ -59,7 +59,7 @@ const toExportForESM = initModuleState.sqlite3Dir = li.join('/') + '/'; } - globalThis.sqlite3InitModule = function ff(...args){ + globalThis.sqlite3InitModule = async function ff(...args){ //console.warn("Using replaced sqlite3InitModule()",globalThis.location); return originalInit(...args).then((EmscriptenModule)=>{ if('undefined'!==typeof WorkerGlobalScope && diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index ac3253670..22ec87ff3 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -1876,26 +1876,28 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( then it must be called by client-level code, which must not use the library until the returned promise resolves. - Bug: if called while a prior call is still resolving, the 2nd - call will resolve prematurely, before the 1st call has finished - resolving. The current build setup precludes that possibility, - so it's only a hypothetical problem if/when this function - ever needs to be invoked by clients. + If called multiple times it will return the same promise on + subsequent calls. The current build setup precludes that + possibility, so it's only a hypothetical problem if/when this + function ever needs to be invoked by clients. In Emscripten-based builds, this function is called automatically and deleted from this object. */ - asyncPostInit: async function(){ + asyncPostInit: function ff(){ + if(ff.ready instanceof Promise) return ff.ready; let lip = sqlite3ApiBootstrap.initializersAsync; delete sqlite3ApiBootstrap.initializersAsync; - if(!lip || !lip.length) return Promise.resolve(sqlite3); + if(!lip || !lip.length){ + return ff.ready = Promise.resolve(sqlite3); + } lip = lip.map((f)=>{ - const p = (f instanceof Promise) ? f : f(sqlite3); - return p.catch((e)=>{ - console.error("an async sqlite3 initializer failed:",e); - throw e; - }); + return (f instanceof Promise) ? f : f(sqlite3); }); + const catcher = (e)=>{ + config.error("an async sqlite3 initializer failed:",e); + throw e; + }; const postInit = ()=>{ if(!sqlite3.__isUnderTest){ /* Delete references to internal-only APIs which are used by @@ -1911,16 +1913,16 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return sqlite3; }; if(1){ - /* Run all initializers in sequence. The advantage is that it - allows us to have post-init cleanup defined outside of this - routine at the end of the list and have it run at a - well-defined time. */ + /* Run all initializers in the sequence they were added. The + advantage is that it allows us to have post-init cleanup + defined outside of this routine at the end of the list and + have it run at a well-defined time. */ let p = lip.shift(); while(lip.length) p = p.then(lip.shift()); - return p.then(postInit); + return ff.ready = p.then(postInit).catch(catcher); }else{ /* Run them in an arbitrary order. */ - return Promise.all(lip).then(postInit); + return ff.ready = Promise.all(lip).then(postInit).catch(catcher); } }, /** diff --git a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js index 5ab58bef5..1f026c7cc 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js +++ b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js @@ -52,7 +52,7 @@ is not detected, the VFS is not registered. */ 'use strict'; -globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){ +globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const toss = sqlite3.util.toss; const toss3 = sqlite3.util.toss3; const initPromises = Object.create(null); @@ -400,46 +400,47 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){ /** - A SAHPoolUtil instance is exposed to clients in order to - manipulate an OpfsSAHPool object without directly exposing that + A SAHPoolUtil instance is exposed to clients in order to manipulate an OpfsSAHPool object without directly exposing that object and allowing for some semantic changes compared to that class. + + Class docs are in the client-level docs for installOpfsSAHPoolVfs(). */ class SAHPoolUtil { + /* This object's associated OpfsSAHPool. */ + #p; constructor(sahPool){ - /* TODO: move the this-to-sahPool mapping into an external - WeakMap so as to not expose it to downstream clients. */ - this.$p = sahPool; + this.#p = sahPool; this.vfsName = sahPool.vfsName; } - addCapacity = async function(n){ - return this.$p.addCapacity(n); + async addCapacity(n){ + return this.#p.addCapacity(n); } - reduceCapacity = async function(n){ - return this.$p.reduceCapacity(n); + async reduceCapacity(n){ + return this.#p.reduceCapacity(n); } - getCapacity = function(){ - return this.$p.getCapacity(this.$p); + getCapacity(){ + return this.#p.getCapacity(this.#p); } - getActiveFileCount = function(){ - return this.$p.getFileCount(); + getActiveFileCount(){ + return this.#p.getFileCount(); } - reserveMinimumCapacity = async function(min){ - const c = this.$p.getCapacity(); - return (c < min) ? this.$p.addCapacity(min - c) : c; + async reserveMinimumCapacity(min){ + const c = this.#p.getCapacity(); + return (c < min) ? this.#p.addCapacity(min - c) : c; } - exportFile = function(name){ - const sah = this.$p.mapFilenameToSAH.get(name) || toss("File not found:",name); + exportFile(name){ + const sah = this.#p.mapFilenameToSAH.get(name) || toss("File not found:",name); const n = sah.getSize() - HEADER_OFFSET_DATA; const b = new Uint8Array(n>=0 ? n : 0); if(n>0) sah.read(b, {at: HEADER_OFFSET_DATA}); return b; } - importDb = function(name, bytes){ + importDb(name, bytes){ const n = bytes.byteLength; if(n<512 || n%512!=0){ toss("Byte array size is invalid for an SQLite db."); @@ -450,35 +451,33 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){ toss("Input does not contain an SQLite database header."); } } - const sah = this.$p.mapFilenameToSAH.get(name) - || this.$p.nextAvailableSAH() + const sah = this.#p.mapFilenameToSAH.get(name) + || this.#p.nextAvailableSAH() || toss("No available handles to import to."); sah.write(bytes, {at: HEADER_OFFSET_DATA}); - this.$p.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); + this.#p.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); } - wipeFiles = async function(){ - return this.$p.reset(true); - } + async wipeFiles(){return this.#p.reset(true)} - unlink = function(filename){ - return this.$p.deletePath(filename); + unlink(filename){ + return this.#p.deletePath(filename); } - removeVfs = async function(){ - if(!this.$p.cVfs.pointer) return false; - capi.sqlite3_vfs_unregister(this.$p.cVfs.pointer); - this.$p.cVfs.dispose(); + async removeVfs(){ + if(!this.#p.cVfs.pointer) return false; + capi.sqlite3_vfs_unregister(this.#p.cVfs.pointer); + this.#p.cVfs.dispose(); try{ - this.$p.releaseAccessHandles(); - if(this.$p.parentDirHandle){ - await this.$p.parentDirHandle.removeEntry( - this.$p.dirHandle.name, {recursive: true} + this.#p.releaseAccessHandles(); + if(this.#p.parentDirHandle){ + await this.#p.parentDirHandle.removeEntry( + this.#p.dirHandle.name, {recursive: true} ); - this.$p.dirHandle = this.$p.parentDirHandle = undefined; + this.#p.dirHandle = this.#p.parentDirHandle = undefined; } }catch(e){ - console.error(this.$p.vfsName,"removeVfs() failed:",e); + sqlite3.config.error(this.#p.vfsName,"removeVfs() failed:",e); /*otherwise ignored - there is no recovery strategy*/ } return true; @@ -679,7 +678,7 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){ throw new Error("Just testing rejection."); } if(initPromises[vfsName]){ - //console.warn("Returning same OpfsSAHPool result",vfsName,initPromises[vfsName]); + console.warn("Returning same OpfsSAHPool result",options,vfsName,initPromises[vfsName]); return initPromises[vfsName]; } if(!globalThis.FileSystemHandle || @@ -743,6 +742,9 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){ ensues. */ return initPromises[vfsName] = apiVersionCheck().then(async function(){ + if(options.$testThrowInInit){ + throw options.$testThrowInInit; + } const thePool = new OpfsSAHPool(opfsVfs, options); return thePool.isReady.then(async()=>{ /** @@ -1025,4 +1027,4 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){ }); }).catch(promiseReject); }/*installOpfsSAHPoolVfs()*/; -}/*sqlite3ApiBootstrap.initializersAsync*/); +}/*sqlite3ApiBootstrap.initializers*/); diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js index 35b7b8865..7d313a263 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -23,7 +23,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ installOpfsVfs() returns a Promise which, on success, installs an sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs which accept a VFS. It is intended to be called via - sqlite3ApiBootstrap.initializersAsync or an equivalent mechanism. + sqlite3ApiBootstrap.initializers or an equivalent mechanism. The installed VFS uses the Origin-Private FileSystem API for all file storage. On error it is rejected with an exception diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 8b7119866..ed0e5c3c7 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -65,6 +65,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule; const haveWasmCTests = ()=>{ return !!wasm.exports.sqlite3_wasm_test_intptr; }; + const hasOpfs = ()=>{ + return globalThis.FileSystemHandle + && globalThis.FileSystemDirectoryHandle + && globalThis.FileSystemFileHandle + && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle + && navigator?.storage?.getDirectory; + }; + { const mapToString = (v)=>{ switch(typeof v){ @@ -277,7 +285,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule; } } const tc = TestUtil.counter, now = performance.now(); - await t.test.call(groupState, sqlite3); + let rc = t.test.call(groupState, sqlite3); + /*if(rc instanceof Promise){ + rc = rc.catch((e)=>{ + error("Test failure:",e); + throw e; + }); + }*/ + await rc; const then = performance.now(); runtime += then - now; logClass('faded',indent, indent, @@ -339,6 +354,11 @@ globalThis.sqlite3InitModule = sqlite3InitModule; T.g = T.addGroup; T.t = T.addTest; let capi, wasm/*assigned after module init*/; + const sahPoolConfig = { + name: 'opfs-sahpool-tester1', + clearOnInit: true, + initialCapacity: 3 + }; //////////////////////////////////////////////////////////////////////// // End of infrastructure setup. Now define the tests... //////////////////////////////////////////////////////////////////////// @@ -1288,7 +1308,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule; if(1){ const vfsList = capi.sqlite3_js_vfs_list(); T.assert(vfsList.length>1); - //log("vfsList =",vfsList); wasm.scopedAllocCall(()=>{ const vfsArg = (v)=>wasm.xWrap.testConvertArg('sqlite3_vfs*',v); for(const v of vfsList){ @@ -2617,8 +2636,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule; //////////////////////////////////////////////////////////////////////// T.g('OPFS: Origin-Private File System', - (sqlite3)=>(sqlite3.opfs - ? true : "requires Worker thread in a compatible browser")) + (sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs") + || 'requires "opfs" VFS')) .t({ name: 'OPFS db sanity checks', test: async function(sqlite3){ @@ -2738,6 +2757,48 @@ globalThis.sqlite3InitModule = sqlite3InitModule; ;/* end OPFS tests */ //////////////////////////////////////////////////////////////////////// + T.g('OPFS SyncAccessHandle Pool VFS', + (sqlite3)=>(hasOpfs() || "requires OPFS APIs")) + .t({ + name: 'SAH sanity checks', + test: async function(sqlite3){ + T.assert(!sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name)) + .assert(sqlite3.capi.sqlite3_js_vfs_list().indexOf(sahPoolConfig.name) < 0) + const inst = sqlite3.installOpfsSAHPoolVfs, + catcher = (e)=>{ + error("Cannot load SAH pool VFS.", + "This might not be a problem,", + "depending on the environment."); + return false; + }; + let u1, u2; + const P1 = inst(sahPoolConfig).then(u=>u1 = u).catch(catcher), + P2 = inst(sahPoolConfig).then(u=>u2 = u).catch(catcher); + await Promise.all([P1, P2]); + if(!P1) return; + T.assert(u1 === u2) + .assert(sahPoolConfig.name === u1.vfsName) + .assert(sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name)) + .assert(u1.getCapacity() === sahPoolConfig.initialCapacity) + .assert(5 === (await u2.addCapacity(2))) + .assert(sqlite3.capi.sqlite3_js_vfs_list().indexOf(sahPoolConfig.name) >= 0) + .assert(true === await u2.removeVfs()) + .assert(false === await u1.removeVfs()) + .assert(!sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name)); + + let cErr, u3; + const conf2 = JSON.parse(JSON.stringify(sahPoolConfig)); + conf2.$testThrowInInit = new Error("Testing throwing during init."); + conf2.name = sahPoolConfig.name+'-err'; + const P3 = await inst(conf2).then(u=>u3 = u).catch((e)=>cErr=e); + T.assert(P3 === conf2.$testThrowInInit) + .assert(cErr === P3) + .assert(undefined === u3) + .assert(!sqlite3.capi.sqlite3_vfs_find(conf2.name)); + } + }/*OPFS SAH Pool sanity checks*/) + + //////////////////////////////////////////////////////////////////////// T.g('Hook APIs') .t({ name: "sqlite3_commit/rollback/update_hook()", @@ -2942,8 +3003,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; .assert( capi.sqlite3session_enable(pSession, -1) > 0 ) .assert(undefined === db1.selectValue('select a from t where rowid=2')); }else{ - warn("sqlite3session_enable() tests disabled due to unexpected results.", - "(Possibly a tester misunderstanding, as opposed to a bug.)"); + warn("sqlite3session_enable() tests are currently disabled."); } let db1Count = db1.selectValue("select count(*) from t"); T.assert( db1Count === (testSessionEnable ? 2 : 3) ); @@ -3088,11 +3148,15 @@ globalThis.sqlite3InitModule = sqlite3InitModule; globalThis.sqlite3InitModule({ print: log, printErr: error - }).then(function(sqlite3){ - //console.log('sqlite3 =',sqlite3); + }).then(async function(sqlite3){ log("Done initializing WASM/JS bits. Running tests..."); sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes."); globalThis.S = sqlite3; + /*await sqlite3.installOpfsSAHPoolVfs(sahPoolConfig) + .then((u)=>log("Loaded",u.vfsName,"VFS")) + .catch(e=>{ + log("Cannot install OpfsSAHPool.",e); + });*/ capi = sqlite3.capi; wasm = sqlite3.wasm; log("sqlite3 version:",capi.sqlite3_libversion(), @@ -3107,6 +3171,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; }else{ logClass('warning',"sqlite3_wasm_test_...() APIs unavailable."); } + log("registered vfs list =",capi.sqlite3_js_vfs_list()); TestUtil.runTests(sqlite3); }); })(self); |