diff options
Diffstat (limited to 'ext/wasm/api/sqlite3-api-prologue.js')
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 163 |
1 files changed, 113 insertions, 50 deletions
diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index adf56b3a0..59537a499 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -610,12 +610,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }/*wasm*/ }/*capi*/; + const wasm = capi.wasm; + /** - capi.wasm.alloc()'s srcTypedArray.byteLength bytes, + wasm.alloc()'s srcTypedArray.byteLength bytes, populates them with the values from the source TypedArray, and returns the pointer to that memory. The returned pointer must eventually be passed to - capi.wasm.dealloc() to clean it up. + wasm.dealloc() to clean it up. As a special case, to avoid further special cases where this is used, if srcTypedArray.byteLength is 0, it @@ -628,27 +630,27 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( Int8Array types and will throw if srcTypedArray is of any other type. */ - capi.wasm.allocFromTypedArray = function(srcTypedArray){ + wasm.allocFromTypedArray = function(srcTypedArray){ affirmBindableTypedArray(srcTypedArray); - const pRet = capi.wasm.alloc(srcTypedArray.byteLength || 1); - capi.wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); + const pRet = wasm.alloc(srcTypedArray.byteLength || 1); + wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); return pRet; }; const keyAlloc = config.allocExportName || 'malloc', keyDealloc = config.deallocExportName || 'free'; for(const key of [keyAlloc, keyDealloc]){ - const f = capi.wasm.exports[key]; + const f = wasm.exports[key]; if(!(f instanceof Function)) toss("Missing required exports[",key,"] function."); } - capi.wasm.alloc = function(n){ - const m = capi.wasm.exports[keyAlloc](n); + wasm.alloc = function(n){ + const m = wasm.exports[keyAlloc](n); if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes."); return m; }; - capi.wasm.dealloc = (m)=>capi.wasm.exports[keyDealloc](m); + wasm.dealloc = (m)=>wasm.exports[keyDealloc](m); /** Reports info about compile-time options using @@ -679,7 +681,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( "SQLITE_" prefix. When it returns an object of all options, the prefix is elided. */ - capi.wasm.compileOptionUsed = function f(optName){ + wasm.compileOptionUsed = function f(optName){ if(!arguments.length){ if(f._result) return f._result; else if(!f._opt){ @@ -720,7 +722,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( is an array with 2+ elements: [ "c-side name", - "result type" (capi.wasm.xWrap() syntax), + "result type" (wasm.xWrap() syntax), [arg types in xWrap() syntax] // ^^^ this needn't strictly be an array: it can be subsequent // elements instead: [x,y,z] is equivalent to x,y,z @@ -730,7 +732,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( result/argument type strings gets plugged in at a later phase in the API initialization process. */ - capi.wasm.bindingSignatures = [ + wasm.bindingSignatures = [ // Please keep these sorted by function name! ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*" @@ -829,13 +831,13 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_value_type", "int", "sqlite3_value*"], ["sqlite3_vfs_find", "*", "string"], ["sqlite3_vfs_register", "int", "*", "int"] - ]/*capi.wasm.bindingSignatures*/; + ]/*wasm.bindingSignatures*/; - if(false && capi.wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ + if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ /* ^^^ "the problem" is that this is an option feature and the build-time function-export list does not currently take optional features into account. */ - capi.wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); + wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); } /** @@ -843,7 +845,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( the others because we need to conditionally bind them or apply dummy impls, depending on the capabilities of the environment. */ - capi.wasm.bindingSignatures.int64 = [ + wasm.bindingSignatures.int64 = [ ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], ["sqlite3_changes64","i64", ["sqlite3*"]], ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], @@ -859,18 +861,18 @@ 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. + wasm. TODO: get rid of sqlite3_wasm_vfs_unlink(). It is ill-conceived and only rarely actually useful. */ - capi.wasm.bindingSignatures.wasm = [ + wasm.bindingSignatures.wasm = [ ["sqlite3_wasm_vfs_unlink", "int", "string"] ]; /** - sqlite3.capi.wasm.pstack (pseudo-stack) holds a special-case + sqlite3.wasm.pstack (pseudo-stack) holds a special-case stack-style allocator intended only for use with _small_ data of not more than (in total) a few kb in size, managed as if it were stack-based. @@ -900,13 +902,13 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( memory lives in the WASM heap and can be used with routines such as wasm.setMemValue() and any wasm.heap8u().slice(). */ - capi.wasm.pstack = Object.assign(Object.create(null),{ + wasm.pstack = Object.assign(Object.create(null),{ /** Sets the current ppstack position to the given pointer. Results are undefined if the passed-in value did not come from this.pointer. */ - restore: capi.wasm.exports.sqlite3_wasm_pstack_restore, + restore: wasm.exports.sqlite3_wasm_pstack_restore, /** Attempts to allocate the given number of bytes from the pstack. On success, it zeroes out a block of memory of the @@ -920,31 +922,89 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( heap. */ alloc: (n)=>{ - return capi.wasm.exports.sqlite3_wasm_pstack_alloc(n) + return wasm.exports.sqlite3_wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate",n, "bytes from the pstack."); + }, + /** + 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); + ``` + */ + allocChunks: (n,sz)=>{ + const mem = wasm.pstack.alloc(n * sz); + const r += []; + 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 falsy). + + 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 a returned pointers will refer to a 64-bit value, e.g. a + double or int64, and that value must be written or fetched, + 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 point to "small" data, it + is safe to pass a falsy value to save to memory. + */ + allocPtr: (n=1,safePtrSize=true)=>{ + return 1===n + ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) + : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); } - // More methods get added after the capi.wasm object is populated - // by WhWasmUtilInstaller. - }); - /** - sqlite3.capi.wasm.pstack.pointer resolves to the current pstack - position pointer. This value is intended _only_ to be passed to restore(). - */ - Object.defineProperty(capi.wasm.pstack, 'pointer', { - configurable: false, iterable: true, writeable: false, - get: capi.wasm.exports.sqlite3_wasm_pstack_ptr - //Whether or not a setter as an alternative to restore() is - //clearer or would just lead to confusion is unclear. - //set: capi.wasm.exports.sqlite3_wasm_pstack_restore - }); + })/*wasm.pstack*/; + Object.defineProperties(wasm.pstack, { + /** + sqlite3.wasm.pstack.pointer resolves to the current pstack + position pointer. This value is intended _only_ to be passed to + restore(). + */ + pointer: { + configurable: false, iterable: true, writeable: false, + get: wasm.exports.sqlite3_wasm_pstack_ptr + //Whether or not a setter as an alternative to restore() is + //clearer or would just lead to confusion is unclear. + //set: wasm.exports.sqlite3_wasm_pstack_restore + }, + /** + Resolves to the total number of bytes available in the pstack, + including any space which is currently allocated. This value is + a compile-time constant. + */ + quota: { + configurable: false, iterable: true, writeable: false, + get: wasm.exports.sqlite3_wasm_pstack_quota + } + })/*wasm.pstack properties*/; + /** - sqlite3.capi.wasm.pstack.remaining resolves to the amount of + sqlite3.wasm.pstack.remaining resolves to the amount of space remaining in the pstack. */ - Object.defineProperty(capi.wasm.pstack, 'remaining', { + Object.defineProperty(wasm.pstack, 'remaining', { configurable: false, iterable: true, writeable: false, - get: capi.wasm.exports.sqlite3_wasm_pstack_remaining + get: wasm.exports.sqlite3_wasm_pstack_remaining }); /** @@ -994,7 +1054,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return __persistentDir = ""; } try{ - if(pdir && 0===capi.wasm.xCallWrapped( + if(pdir && 0===wasm.xCallWrapped( 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir )){ return __persistentDir = pdir; @@ -1024,10 +1084,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; // This bit is highly arguable and is incompatible with the fiddle shell. - if(false && 0===capi.wasm.exports.sqlite3_vfs_find(0)){ + if(false && 0===wasm.exports.sqlite3_vfs_find(0)){ /* Assume that sqlite3_initialize() has not yet been called. This will be the case in an SQLITE_OS_KV build. */ - capi.wasm.exports.sqlite3_initialize(); + wasm.exports.sqlite3_initialize(); } /** @@ -1058,15 +1118,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( else if(!pDb){ return capi.sqlite3_vfs_find(0)===pK ? pK : false; } - const ppVfs = capi.wasm.allocPtr(); + const ppVfs = wasm.allocPtr(); try{ return ( (0===capi.sqlite3_file_control( pDb, dbName, capi.SQLITE_FCNTL_VFS_POINTER, ppVfs - )) && (capi.wasm.getPtrValue(ppVfs) === pK) + )) && (wasm.getPtrValue(ppVfs) === pK) ) ? pK : false; }finally{ - capi.wasm.dealloc(ppVfs); + wasm.dealloc(ppVfs); } }catch(e){ /* Ignore - probably bad args to a wasm-bound function. */ @@ -1083,7 +1143,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( let pVfs = capi.sqlite3_vfs_find(0); while(pVfs){ const oVfs = new capi.sqlite3_vfs(pVfs); - rc.push(capi.wasm.cstringToJs(oVfs.$zName)); + rc.push(wasm.cstringToJs(oVfs.$zName)); pVfs = oVfs.$pNext; oVfs.dispose(); } @@ -1097,7 +1157,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ capi.sqlite3_web_db_export = function(pDb){ if(!pDb) toss('Invalid sqlite3* argument.'); - const wasm = capi.wasm; + const wasm = wasm; if(!wasm.bigIntEnabled) toss('BigInt64 support is not enabled.'); const stack = wasm.pstack.pointer; let pOut; @@ -1278,7 +1338,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( Note that the order of insertion into this array is significant for some pieces. e.g. sqlite3.capi.wasm cannot be fully utilized until - the whwasmutil.js part is plugged in. + the whwasmutil.js part is plugged in via sqlite3-api-glue.js. */ self.sqlite3ApiBootstrap.initializers = []; /** @@ -1290,12 +1350,15 @@ self.sqlite3ApiBootstrap.initializers = []; Counterpart of self.sqlite3ApiBootstrap.initializers, specifically for initializers which are asynchronous. All functions in this list take the sqlite3 object as their argument and MUST return a - Promise. Both the resolved value and rejection cases are ignored. + Promise. The resolved value and ignored and rejection will kill the + asyncPostInit() process but will be otherwise ignored because the + post-synchronous-init async initialization parts are (as of this + writing) all optional. This list is not processed until the client calls sqlite3.asyncPostInit(). This means, for example, that intializers added to self.sqlite3ApiBootstrap.initializers may push entries to - this list. + this list. */ self.sqlite3ApiBootstrap.initializersAsync = []; /** |