diff options
author | stephan <stephan@noemail.net> | 2022-10-02 03:11:13 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2022-10-02 03:11:13 +0000 |
commit | 63e9ec2f9c7042fc8fb3f858144ee9ebe5408f69 (patch) | |
tree | 1e85e4eef656f867b1a220388cf0563f715223ac | |
parent | 6479c5a359e932a76225a903f1a6655cda8c277d (diff) | |
download | sqlite-63e9ec2f9c7042fc8fb3f858144ee9ebe5408f69.tar.gz sqlite-63e9ec2f9c7042fc8fb3f858144ee9ebe5408f69.zip |
More fleshing out of sqlite3.capi.wasm.pstack.
FossilOrigin-Name: eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3
-rw-r--r-- | ext/wasm/api/sqlite3-api-glue.js | 59 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-oo1.js | 88 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 22 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-wasm.c | 12 | ||||
-rw-r--r-- | ext/wasm/common/whwasmutil.js | 45 | ||||
-rw-r--r-- | ext/wasm/testing1.js | 41 | ||||
-rw-r--r-- | manifest | 22 | ||||
-rw-r--r-- | manifest.uuid | 2 |
8 files changed, 205 insertions, 86 deletions
diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index ab9424aca..b6ec1695e 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -55,10 +55,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(1){// WhWasmUtil.xWrap() bindings... /** - Add some descriptive xWrap() aliases for '*' intended to - (A) initially improve readability/correctness of capi.signatures - and (B) eventually perhaps provide some sort of type-safety - in their conversions. + Add some descriptive xWrap() aliases for '*' intended to (A) + initially improve readability/correctness of capi.signatures + and (B) eventually perhaps provide automatic conversion from + higher-level representations, e.g. capi.sqlite3_vfs to + `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. */ const aPtr = wasm.xWrap.argAdapter('*'); wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); @@ -248,6 +249,56 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; }/*sqlite3_prepare_v2/v3()*/; + if(1){// Extend wasm.pstack, now that the wasm utils are installed + /** + Allocates n chunks, each sz bytes, as a single memory block and + returns the addresses as an array of n element, each holding + the address of one chunk. + + Throws a WasmAllocError if allocation fails. + + Example: + + ``` + const [p1, p2, p3] = wasm.pstack.allocChunks(3,4); + ``` + */ + wasm.pstack.allocChunks = (n,sz)=>{ + const mem = wasm.pstack.alloc(n * sz); + const rc = []; + let i = 0, offset = 0; + for(; i < n; offset = (sz * ++i)){ + rc.push(mem + offset); + } + return rc; + }; + + /** + A convenience wrapper for allocChunks() which sizes each chunks + as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if + safePtrSize is truthy). + + How it returns its result differs depending on its first + argument: if it's 1, it returns a single pointer value. If it's + more than 1, it returns the same as allocChunks(). + + When one of the pointers refers to a 64-bit value, e.g. a + double or int64, and that value must be written or fetch, + e.g. using wasm.setMemValue() or wasm.getMemValue(), it is + important that the pointer in question be aligned to an 8-byte + boundary or else it will not be fetched or written properly and + will corrupt or read neighboring memory. + + However, when all pointers involved are "small", it is safe to + pass a falsy value to save to memory. + */ + wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{ + return 1===n + ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) + : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); + }; + }/*wasm.pstack filler*/ + /** Install JS<->C struct bindings for the non-opaque struct types we need... */ diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 627af2e8a..07f0657fa 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -18,7 +18,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const toss = (...args)=>{throw new Error(args.join(' '))}; const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)}; - const capi = sqlite3.capi, util = capi.util; + const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util; /* What follows is colloquially known as "OO API #1". It is a binding of the sqlite3 API which is designed to be run within the same thread (main or worker) as the one in which the @@ -33,7 +33,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ accessor and store their real values in this map. Keys = DB/Stmt objects, values = pointer values. This also unifies how those are accessed, for potential use downstream via custom - capi.wasm.xWrap() function signatures which know how to extract + wasm.xWrap() function signatures which know how to extract it. */ const __ptrMap = new WeakMap(); @@ -72,7 +72,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ being-construct DB object as its "this". See the DB constructor for the argument docs. This is split into a separate function in order to enable simple creation of special-case DB constructors, - e.g. a hypothetical LocalStorageDB or OpfsDB. + e.g. JsStorageDB and OpfsDB. Expects to be passed a configuration object with the following properties: @@ -123,7 +123,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ console.error("Invalid DB ctor args",opt,arguments); toss3("Invalid arguments for DB constructor."); } - let fnJs = ('number'===typeof fn) ? capi.wasm.cstringToJs(fn) : fn; + let fnJs = ('number'===typeof fn) ? wasm.cstringToJs(fn) : fn; const vfsCheck = ctor._name2vfs[fnJs]; if(vfsCheck){ vfsName = vfsCheck.vfs; @@ -136,20 +136,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; oflags |= capi.SQLITE_OPEN_EXRESCODE; - const stack = capi.wasm.scopedAllocPush(); + const scope = wasm.scopedAllocPush(); try { - const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; + const ppDb = wasm.allocPtr() /* output (sqlite3**) arg */; const pVfsName = vfsName ? ( - ('number'===typeof vfsName ? vfsName : capi.wasm.scopedAllocCString(vfsName)) + ('number'===typeof vfsName ? vfsName : wasm.scopedAllocCString(vfsName)) ): 0; const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName); - ptr = capi.wasm.getPtrValue(ppDb); + ptr = wasm.getPtrValue(ppDb); checkSqlite3Rc(ptr, rc); }catch( e ){ if( ptr ) capi.sqlite3_close_v2(ptr); throw e; }finally{ - capi.wasm.scopedAllocPop(stack); + wasm.scopedAllocPop(scope); } this.filename = fnJs; __ptrMap.set(this, ptr); @@ -265,7 +265,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ blob: 5 }; BindTypes['undefined'] == BindTypes.null; - if(capi.wasm.bigIntEnabled){ + if(wasm.bigIntEnabled){ BindTypes.bigint = BindTypes.number; } @@ -454,7 +454,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(s && s.pointer) s.finalize(); }); Object.values(__udfMap.get(this)).forEach( - capi.wasm.uninstallFunction.bind(capi.wasm) + wasm.uninstallFunction.bind(capi.wasm) ); __ptrMap.delete(this); __stmtMap.delete(this); @@ -539,15 +539,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ */ prepare: function(sql){ affirmDbOpen(this); - const stack = capi.wasm.pstack.pointer; + const stack = wasm.pstack.pointer; let ppStmt, pStmt; try{ - ppStmt = capi.wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */; + ppStmt = wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */; DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); - pStmt = capi.wasm.getPtrValue(ppStmt); + pStmt = wasm.getPtrValue(ppStmt); } finally { - capi.wasm.pstack.restore(stack); + wasm.pstack.restore(stack); } if(!pStmt) toss3("Cannot prepare empty SQL."); const stmt = new Stmt(this, pStmt, BindTypes); @@ -846,7 +846,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ let i, pVal, valType, arg; const tgt = []; for(i = 0; i < argc; ++i){ - pVal = capi.wasm.getPtrValue(pArgv + (capi.wasm.ptrSizeof * i)); + pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i)); /** Curiously: despite ostensibly requiring 8-byte alignment, the pArgv array is parcelled into chunks of @@ -868,7 +868,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const pBlob = capi.sqlite3_value_blob(pVal); arg = new Uint8Array(n); let i; - const heap = n ? capi.wasm.heap8() : false; + const heap = n ? wasm.heap8() : false; for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i]; break; } @@ -902,10 +902,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ capi.sqlite3_result_null(pCx); break; }else if(util.isBindableTypedArray(val)){ - const pBlob = capi.wasm.allocFromTypedArray(val); + const pBlob = wasm.allocFromTypedArray(val); capi.sqlite3_result_blob(pCx, pBlob, val.byteLength, capi.SQLITE_TRANSIENT); - capi.wasm.dealloc(pBlob); + wasm.dealloc(pBlob); break; } // else fall through @@ -925,7 +925,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } } }; - const pUdf = capi.wasm.installFunction(wrapper, "v(iii)"); + const pUdf = wasm.installFunction(wrapper, "v(iii)"); let fFlags = 0 /*flags for sqlite3_create_function_v2()*/; if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; @@ -938,12 +938,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); }catch(e){ - capi.wasm.uninstallFunction(pUdf); + wasm.uninstallFunction(pUdf); throw e; } const udfMap = __udfMap.get(this); if(udfMap[name]){ - try{capi.wasm.uninstallFunction(udfMap[name])} + try{wasm.uninstallFunction(udfMap[name])} catch(e){/*ignore*/} } udfMap[name] = pUdf; @@ -1049,7 +1049,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ case BindTypes.string: return t; case BindTypes.bigint: - if(capi.wasm.bigIntEnabled) return t; + if(wasm.bigIntEnabled) return t; /* else fall through */ default: //console.log("isSupportedBindType",t,v); @@ -1109,7 +1109,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const bindOne = function f(stmt,ndx,bindType,val){ affirmUnlocked(stmt, 'bind()'); if(!f._){ - if(capi.wasm.bigIntEnabled){ + if(wasm.bigIntEnabled){ f._maxInt = BigInt("0x7fffffffffffffff"); f._minInt = ~f._maxInt; } @@ -1120,25 +1120,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ string: function(stmt, ndx, val, asBlob){ if(1){ /* _Hypothetically_ more efficient than the impl in the 'else' block. */ - const stack = capi.wasm.scopedAllocPush(); + const stack = wasm.scopedAllocPush(); try{ - const n = capi.wasm.jstrlen(val); - const pStr = capi.wasm.scopedAlloc(n); - capi.wasm.jstrcpy(val, capi.wasm.heap8u(), pStr, n, false); + const n = wasm.jstrlen(val); + const pStr = wasm.scopedAlloc(n); + wasm.jstrcpy(val, wasm.heap8u(), pStr, n, false); const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); }finally{ - capi.wasm.scopedAllocPop(stack); + wasm.scopedAllocPop(stack); } }else{ - const bytes = capi.wasm.jstrToUintArray(val,false); - const pStr = capi.wasm.alloc(bytes.length || 1); - capi.wasm.heap8u().set(bytes.length ? bytes : [0], pStr); + const bytes = wasm.jstrToUintArray(val,false); + const pStr = wasm.alloc(bytes.length || 1); + wasm.heap8u().set(bytes.length ? bytes : [0], pStr); try{ const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT); }finally{ - capi.wasm.dealloc(pStr); + wasm.dealloc(pStr); } } } @@ -1160,7 +1160,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ else if('bigint'===typeof val){ if(val<f._minInt || val>f._maxInt){ toss3("BigInt value is out of range for storing as int64: "+val); - }else if(capi.wasm.bigIntEnabled){ + }else if(wasm.bigIntEnabled){ m = capi.sqlite3_bind_int64; }else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){ val = Number(val); @@ -1170,7 +1170,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }else{ // !int32, !bigint val = Number(val); - if(capi.wasm.bigIntEnabled && Number.isInteger(val)){ + if(wasm.bigIntEnabled && Number.isInteger(val)){ m = capi.sqlite3_bind_int64; }else{ m = capi.sqlite3_bind_double; @@ -1190,22 +1190,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "that it be a string, Uint8Array, or Int8Array."); }else if(1){ /* _Hypothetically_ more efficient than the impl in the 'else' block. */ - const stack = capi.wasm.scopedAllocPush(); + const stack = wasm.scopedAllocPush(); try{ - const pBlob = capi.wasm.scopedAlloc(val.byteLength || 1); - capi.wasm.heap8().set(val.byteLength ? val : [0], pBlob) + 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{ - capi.wasm.scopedAllocPop(stack); + wasm.scopedAllocPop(stack); } }else{ - const pBlob = capi.wasm.allocFromTypedArray(val); + const pBlob = wasm.allocFromTypedArray(val); try{ rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_TRANSIENT); }finally{ - capi.wasm.dealloc(pBlob); + wasm.dealloc(pBlob); } } break; @@ -1518,7 +1518,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ : asType){ case capi.SQLITE_NULL: return null; case capi.SQLITE_INTEGER:{ - if(capi.wasm.bigIntEnabled){ + if(wasm.bigIntEnabled){ const rc = capi.sqlite3_column_int64(this.pointer, ndx); if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){ /* Coerce "normal" number ranges to normal number values, @@ -1549,8 +1549,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const n = capi.sqlite3_column_bytes(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx), rc = new Uint8Array(n); - //heap = n ? capi.wasm.heap8() : false; - if(n) rc.set(capi.wasm.heap8u().slice(ptr, ptr+n), 0); + //heap = n ? wasm.heap8() : false; + if(n) rc.set(wasm.heap8u().slice(ptr, ptr+n), 0); //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i]; if(n && this.db._blobXfer instanceof Array){ /* This is an optimization soley for the diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 97376be6c..59533815f 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -265,6 +265,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( this.name = 'WasmAllocError'; } }; + WasmAllocError.toss = (...args)=>{ + throw new WasmAllocError(args.join(' ')); + }; /** The main sqlite3 binding API gets installed into this object, @@ -733,6 +736,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into capi.wasm. + + TODO: get rid of sqlite3_wasm_vfs_unlink(). It is ill-conceived + and only rarely actually useful. */ capi.wasm.bindingSignatures.wasm = [ ["sqlite3_wasm_vfs_unlink", "int", "string"] @@ -781,15 +787,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( Attempts to allocate the given number of bytes from the pstack. On success, it zeroes out a block of memory of the given size, adjusts the pstack pointer, and returns a pointer - to the memory. On error, returns 0. The memory must eventually - be released using restore(). + to the memory. On error, returns throws a WasmAllocError. The + memory must eventually be released using restore(). This method always adjusts the given value to be a multiple of 8 bytes because failing to do so can lead to incorrect results when reading and writing 64-bit values from/to the WASM heap. */ - alloc: capi.wasm.exports.sqlite3_wasm_pstack_alloc + alloc: (n)=>{ + return capi.wasm.exports.sqlite3_wasm_pstack_alloc(n) + || WasmAllocError.toss("Could not allocate",n, + "bytes from the pstack."); + } + // More methods get added after the capi.wasm object is populated + // by WhWasmUtilInstaller. }); /** sqlite3.capi.wasm.pstack.pointer resolves to the current pstack @@ -828,7 +840,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( this.name = 'SQLite3Error'; } }; - + SQLite3Error.toss = (...args)=>{ + throw new SQLite3Error(args.join(' ')); + }; /** State for sqlite3_wasmfs_opfs_dir(). */ let __persistentDir = undefined; diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index a2de8ca71..b9454155d 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -59,6 +59,10 @@ #include <assert.h> #include "sqlite3.c" /* yes, .c instead of .h. */ +#if defined(__EMSCRIPTEN__) +# include <emscripten/console.h> +#endif + /* ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not ** Emscripten-specific. It explicitly marks functions for export into @@ -667,6 +671,9 @@ WASM_KEEP int sqlite3_wasm_vfs_unlink(const char * zName){ int rc = SQLITE_MISUSE /* ??? */; sqlite3_vfs * const pVfs = sqlite3_vfs_find(0); +#if defined(__EMSCRIPTEN__) + emscripten_console_warn("sqlite3_wasm_vfs_unlink() will be removed."); +#endif if( zName && pVfs && pVfs->xDelete ){ rc = pVfs->xDelete(pVfs, zName, 1); } @@ -750,9 +757,7 @@ int sqlite3_wasm_db_serialize( sqlite3* pDb, unsigned char **pOut, sqlite3_int64 } -#if defined(__EMSCRIPTEN__) -#include <emscripten/console.h> -#if defined(SQLITE_WASM_WASMFS) +#if defined(__EMSCRIPTEN__) && defined(SQLITE_WASM_WASMFS) #include <emscripten/wasmfs.h> /* @@ -809,6 +814,5 @@ int sqlite3_wasm_init_wasmfs(const char *zUnused){ return SQLITE_NOTFOUND; } #endif /* __EMSCRIPTEN__ && SQLITE_WASM_WASMFS */ -#endif #undef WASM_KEEP diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 39a214be0..6b08d4f78 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -1091,28 +1091,36 @@ self.WhWasmUtilInstaller = function(target){ }; /** Internal impl for allocPtr() and scopedAllocPtr(). */ - const __allocPtr = function(howMany, method){ + const __allocPtr = function(howMany, safePtrSize, method){ __affirmAlloc(target, method); - let m = target[method](howMany * ptrSizeof); - target.setMemValue(m, 0, ptrIR) + const pIr = safePtrSize ? 'i64' : ptrIR; + let m = target[method](howMany * (safePtrSize ? 8 : ptrSizeof)); + target.setMemValue(m, 0, pIr) if(1===howMany){ return m; } const a = [m]; for(let i = 1; i < howMany; ++i){ - m += ptrSizeof; + m += (safePtrSize ? 8 : ptrSizeof); a[i] = m; - target.setMemValue(m, 0, ptrIR); + target.setMemValue(m, 0, pIr); } return a; }; /** - Allocates a single chunk of memory capable of holding `howMany` - pointers and zeroes them out. If `howMany` is 1 then the memory - chunk is returned directly, else an array of pointer addresses is - returned, which can optionally be used with "destructuring - assignment" like this: + Allocates one or more pointers as a single chunk of memory and + zeroes them out. + + The first argument is the number of pointers to allocate. The + second specifies whether they should use a "safe" pointer size (8 + bytes) or whether they may use the default pointer size + (typically 4 but also possibly 8). + + How the result is returned depends on its first argument: if + passed 1, it returns the allocated memory address. If passed more + than one then an array of pointer addresses is returned, which + can optionally be used with "destructuring assignment" like this: ``` const [p1, p2, p3] = allocPtr(3); @@ -1121,14 +1129,27 @@ self.WhWasmUtilInstaller = function(target){ ACHTUNG: when freeing the memory, pass only the _first_ result value to dealloc(). The others are part of the same memory chunk and must not be freed separately. + + The reason for the 2nd argument is.. + + When one of the pointers will refer to a 64-bit value, e.g. a + double or int64, an that value must be written or fetch, + e.g. using setMemValue() or getMemValue(), it is important that + the pointer in question be aligned to an 8-byte boundary or else + it will not be fetched or written properly and will corrupt or + read neighboring memory. It i only safe to pass false when the + client code is certain that it will only get/fetch 4-byte values + (or smaller). */ - target.allocPtr = (howMany=1)=>__allocPtr(howMany, 'alloc'); + target.allocPtr = + (howMany=1, safePtrSize=true)=>__allocPtr(howMany, safePtrSize, 'alloc'); /** Identical to allocPtr() except that it allocates using scopedAlloc() instead of alloc(). */ - target.scopedAllocPtr = (howMany=1)=>__allocPtr(howMany, 'scopedAlloc'); + target.scopedAllocPtr = + (howMany=1, safePtrSize=true)=>__allocPtr(howMany, safePtrSize, 'scopedAlloc'); /** If target.exports[name] exists, it is returned, else an diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index dd61ab281..053fb6f27 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -1030,31 +1030,60 @@ */ const testPstack = function(db,sqlite3){ const w = sqlite3.capi.wasm, P = w.pstack; + const isAllocErr = (e)=>e instanceof sqlite3.WasmAllocError; const stack = P.pointer; T.assert(0===stack % 8 /* must be 8-byte aligned */); try{ const quota = P.remaining; log("pstack quota",quota); T.assert(quota >= 4096) - .assert(0 === P.alloc(0)) - .assert(0 === P.alloc(-1)); + .mustThrowMatching(()=>P.alloc(0), isAllocErr) + .mustThrowMatching(()=>P.alloc(-1), isAllocErr); let p1 = P.alloc(12); T.assert(p1 === stack - 16/*8-byte aligned*/) .assert(P.pointer === p1); let p2 = P.alloc(7); T.assert(p2 === p1-8/*8-byte aligned, stack grows downwards*/) - .assert(0 === P.alloc(quota)) + .mustThrowMatching(()=>P.alloc(quota), isAllocErr) .assert(24 === stack - p2) .assert(P.pointer === p2); let n = quota - (stack - p2); let p3 = P.alloc(n); T.assert(p3 === stack-quota) - .assert(0 === P.alloc(1)); + .mustThrowMatching(()=>P.alloc(1), isAllocErr); }finally{ P.restore(stack); - T.assert(P.pointer === stack); } - }/*testPstack()*/; + + T.assert(P.pointer === stack); + try { + const [p1, p2, p3] = P.allocChunks(3,4); + T.assert(P.pointer === stack-16/*always rounded to multiple of 8*/) + .assert(p2 === p1 + 4) + .assert(p3 === p2 + 4); + T.mustThrowMatching(()=>P.allocChunks(1024, 1024 * 16), + (e)=>e instanceof sqlite3.WasmAllocError) + }finally{ + P.restore(stack); + } + + T.assert(P.pointer === stack); + try { + let [p1, p2, p3] = P.allocPtr(3,false); + let sPos = stack-16/*always rounded to multiple of 8*/; + T.assert(P.pointer === sPos) + .assert(p2 === p1 + 4) + .assert(p3 === p2 + 4); + [p1, p2, p3] = P.allocPtr(3); + T.assert(P.pointer === sPos-24/*3 x 8 bytes*/) + .assert(p2 === p1 + 8) + .assert(p3 === p2 + 8); + p1 = P.allocPtr(); + T.assert('number'===typeof p1); + }finally{ + P.restore(stack); + } +}/*testPstack()*/; const clearKvvfs = function(){ const sz = sqlite3.capi.sqlite3_web_kvvfs_size(); @@ -1,5 +1,5 @@ -C Document\sthe\sroles\sof\sthe\snew\s(this\spast\sweek)\sJS\sfiles\sadded\sto\sthe\sbuild\sprocess. -D 2022-10-02T01:48:14.286 +C More\sfleshing\sout\sof\ssqlite3.capi.wasm.pstack. +D 2022-10-02T03:11:13.806 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -485,19 +485,19 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99 -F ext/wasm/api/sqlite3-api-glue.js b15a51b88aaa472d36bf82d5123dbfdafe8ddf6ca75fba934510e4a20bbe4adb -F ext/wasm/api/sqlite3-api-oo1.js 7667d320f6b9fb5252050a2f9c0b1769e11b84dbc0763b999baf65b451b14369 +F ext/wasm/api/sqlite3-api-glue.js 474a5e0bf8016e22aefee44ca45408f08f4159c0b782295ac1d86c4556e91d9a +F ext/wasm/api/sqlite3-api-oo1.js 066e67f3033e1b300140d431557c468f5cd0a4c17253f156e05b8a2e2c802da7 F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541 -F ext/wasm/api/sqlite3-api-prologue.js a93bd69969eb8b8f9c4cb34e5d86dcbbe5adbeeea39c1cce57194256c5f28434 +F ext/wasm/api/sqlite3-api-prologue.js 9b0c5150f0129b3dc558fec0bfc9c8bf7ebda402b58965bcf98b2ed117b55239 F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c d72aecf0e50a4403402095ef4e8d6a814fdc2256589944c1dc974c70d2f65b7e +F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6 F ext/wasm/batch-runner.html c363032aba7a525920f61f8be112a29459f73f07e46f0ba3b7730081a617826e F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62e1a00ff5015 F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 -F ext/wasm/common/whwasmutil.js d2557d6ef1ebaaf3c9a0cea2231fd398b0d8ca8129b51580af1c92f8d04335e0 +F ext/wasm/common/whwasmutil.js cdb33775fdc55c9b1cbb617d22d24b4a29dc9c1389b827a5b14886a291480d70 F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8 @@ -530,7 +530,7 @@ F ext/wasm/test-opfs-vfs.js a59ff9210b17d46b0c6fbf6a0ba60143c033327865f2e556e14f F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae -F ext/wasm/testing1.js 51ef1ced0669f804787ce96f19dcf64367550a7923a998514be56076326988d7 +F ext/wasm/testing1.js bdea170b16189028c1f63023c620df52ddf31ed416bad56d729c60031b1e27ae F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494 F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f @@ -2029,8 +2029,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 f9db664f756f3707afcb5dce87f6d946625848f27ea84337af68de72d4ad6c6b -R 8201c0f212d8f73dc23b363b8bdcd606 +P 8b3bc7313aff551e5ee0b7aeb927095cf19b9b96abbdd922066c130656b8aa7d +R a0d2cb1d1b18288fcfda68347c547fdd U stephan -Z 7616e552f06f17aa339add7ed573ed79 +Z 5fc153c99e29258d724616b67e70d32b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 29067e24b..fa1c36d77 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8b3bc7313aff551e5ee0b7aeb927095cf19b9b96abbdd922066c130656b8aa7d
\ No newline at end of file +eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3
\ No newline at end of file |