diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 3 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-glue.js | 8 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-oo1.js | 3 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 3 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-wasm.c | 25 | ||||
-rw-r--r-- | ext/wasm/common/whwasmutil.js | 109 | ||||
-rw-r--r-- | ext/wasm/tester1.c-pp.js | 48 |
7 files changed, 147 insertions, 52 deletions
diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 11fdfc32e..8bf26bc6f 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -87,7 +87,10 @@ _sqlite3_set_auxdata _sqlite3_shutdown _sqlite3_sourceid _sqlite3_sql +_sqlite3_status +_sqlite3_status64 _sqlite3_step +_sqlite3_stmt_status _sqlite3_strglob _sqlite3_stricmp _sqlite3_strlike diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 587d72c67..b95f15e71 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -87,10 +87,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const aPtr = wasm.xWrap.argAdapter('*'); const nilType = function(){}; wasm.xWrap.argAdapter('sqlite3_filename', aPtr) - ('sqlite3_stmt*', aPtr) ('sqlite3_context*', aPtr) ('sqlite3_value*', aPtr) ('void*', aPtr) + ('sqlite3_stmt*', (v)=> + aPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType)) + ? v.pointer : v)) ('sqlite3*', (v)=> aPtr((v instanceof (sqlite3?.oo1?.DB || nilType)) ? v.pointer : v)) @@ -620,7 +622,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'encodings', 'fcntl', 'flock', 'ioCap', 'limits', 'openFlags', 'prepareFlags', 'resultCodes', - 'serialize', 'syncFlags', 'trace', 'udfFlags', + 'serialize', 'sqlite3Status', + 'stmtStatus', 'syncFlags', + 'trace', 'udfFlags', 'version' ]; if(wasm.bigIntEnabled){ defineGroups.push('vtab'); diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index caae43275..a73983e8c 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -825,8 +825,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ around the NUL terminator, and get stuck in and endless loop at the end of the SQL, endlessly re-preparing an empty statement. */ ){ - wasm.setPtrValue(ppStmt, 0); - wasm.setPtrValue(pzTail, 0); + wasm.setPtrValue([ppStmt, pzTail], 0); DB.checkRc(this, capi.sqlite3_prepare_v3( this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail )); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index bf2f6610d..dafe3c523 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -999,7 +999,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_shutdown", undefined], ["sqlite3_sourceid", "string"], ["sqlite3_sql", "string", "sqlite3_stmt*"], + ["sqlite3_status", "int", "int", "*", "*", "int"], ["sqlite3_step", "int", "sqlite3_stmt*"], + ["sqlite3_stmt_status", "int", "sqlite3_stmt*", "int", "int"], ["sqlite3_strglob", "int", "string","string"], ["sqlite3_stricmp", "int", "string", "string"], ["sqlite3_strlike", "int", "string", "string","int"], @@ -1060,6 +1062,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_realloc64", "*","*", "i64"], ["sqlite3_result_int64", undefined, "*", "i64"], ["sqlite3_result_zeroblob64", "int", "*", "i64"], + ["sqlite3_status64", "int", "int", "*", "*", "int"], ["sqlite3_total_changes64", "i64", ["sqlite3*"]], ["sqlite3_uri_int64", "i64", ["sqlite3_filename", "string", "i64"]], ["sqlite3_value_int64","i64", "sqlite3_value*"], diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 7312ef6ab..ec0f72f73 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -702,6 +702,31 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_DESERIALIZE_RESIZEABLE); } _DefGroup; + DefGroup(sqlite3Status){ + DefInt(SQLITE_STATUS_MEMORY_USED); + DefInt(SQLITE_STATUS_PAGECACHE_USED); + DefInt(SQLITE_STATUS_PAGECACHE_OVERFLOW); + DefInt(SQLITE_STATUS_SCRATCH_USED) /* NOT USED */; + DefInt(SQLITE_STATUS_SCRATCH_OVERFLOW) /* NOT USED */; + DefInt(SQLITE_STATUS_MALLOC_SIZE); + DefInt(SQLITE_STATUS_PARSER_STACK); + DefInt(SQLITE_STATUS_PAGECACHE_SIZE); + DefInt(SQLITE_STATUS_SCRATCH_SIZE) /* NOT USED */; + DefInt(SQLITE_STATUS_MALLOC_COUNT); + } _DefGroup; + + DefGroup(stmtStatus){ + DefInt(SQLITE_STMTSTATUS_FULLSCAN_STEP); + DefInt(SQLITE_STMTSTATUS_SORT); + DefInt(SQLITE_STMTSTATUS_AUTOINDEX); + DefInt(SQLITE_STMTSTATUS_VM_STEP); + DefInt(SQLITE_STMTSTATUS_REPREPARE); + DefInt(SQLITE_STMTSTATUS_RUN); + DefInt(SQLITE_STMTSTATUS_FILTER_MISS); + DefInt(SQLITE_STMTSTATUS_FILTER_HIT); + DefInt(SQLITE_STMTSTATUS_MEMUSED); + } _DefGroup; + DefGroup(syncFlags) { DefInt(SQLITE_SYNC_NORMAL); DefInt(SQLITE_SYNC_FULL); diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 0da1841b6..3f3281876 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -599,6 +599,11 @@ self.WhWasmUtilInstaller = function(target){ type triggers an exception if this.bigIntEnabled is falsy). Throws if given an invalid type. + If the first argument is an array, it is treated as an array of + addresses and the result is an array of the values from each of + those address, using the same 2nd argument for determining the + value type to fetch. + As a special case, if type ends with a `*`, it is considered to be a pointer type and is treated as the WASM numeric type appropriate for the pointer size (`i32`). @@ -641,23 +646,33 @@ self.WhWasmUtilInstaller = function(target){ See: setMemValue() */ - target.getMemValue = function(ptr, type='i8'){ + target.getMemValue = function f(ptr, type='i8'){ if(type.endsWith('*')) type = ptrIR; const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength) ? cache : heapWrappers(); - switch(type){ - case 'i1': - case 'i8': return c.HEAP8[ptr>>0]; - case 'i16': return c.HEAP16[ptr>>1]; - case 'i32': return c.HEAP32[ptr>>2]; - case 'i64': - if(target.bigIntEnabled) return BigInt(c.HEAP64[ptr>>3]); - break; - case 'float': case 'f32': return c.HEAP32F[ptr>>2]; - case 'double': case 'f64': return Number(c.HEAP64F[ptr>>3]); - default: break; - } - toss('Invalid type for getMemValue():',type); + const list = Array.isArray(ptr) ? [] : undefined; + let rc; + do{ + if(list) ptr = arguments[0].shift(); + switch(type){ + case 'i1': + case 'i8': rc = c.HEAP8[ptr>>0]; break; + case 'i16': rc = c.HEAP16[ptr>>1]; break; + case 'i32': rc = c.HEAP32[ptr>>2]; break; + case 'float': case 'f32': rc = c.HEAP32F[ptr>>2]; break; + case 'double': case 'f64': rc = Number(c.HEAP64F[ptr>>3]); break; + case 'i64': + if(target.bigIntEnabled){ + rc = BigInt(c.HEAP64[ptr>>3]); + break; + } + /* fallthru */ + default: + toss('Invalid type for getMemValue():',type); + } + if(list) list.push(rc); + }while(list && arguments[0].length); + return list || rc; }; /** @@ -668,41 +683,59 @@ self.WhWasmUtilInstaller = function(target){ argument ends with `*` then it is treated as a pointer type and this function behaves as if the 3rd argument were `i32`. - This function returns itself. + If the first argument is an array, it is treated like a list + of pointers and the given value is written to each one. + + Returns `this`. (Prior to 2022-12-09 it returns this function.) ACHTUNG: calling this often, e.g. in a loop, can have a noticably painful impact on performance. Rather than doing so, use - heapForSize() to fetch the heap object and assign directly to it. + heapForSize() to fetch the heap object and assign directly to it + or use the heap's set() method. */ - target.setMemValue = function f(ptr, value, type='i8'){ + target.setMemValue = function(ptr, value, type='i8'){ if (type.endsWith('*')) type = ptrIR; const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength) ? cache : heapWrappers(); - switch (type) { - case 'i1': - case 'i8': c.HEAP8[ptr>>0] = value; return f; - case 'i16': c.HEAP16[ptr>>1] = value; return f; - case 'i32': c.HEAP32[ptr>>2] = value; return f; - case 'i64': - if(c.HEAP64){ - c.HEAP64[ptr>>3] = BigInt(value); - return f; - } - break; - case 'float': case 'f32': c.HEAP32F[ptr>>2] = value; return f; - case 'double': case 'f64': c.HEAP64F[ptr>>3] = value; return f; + for(const p of (Array.isArray(ptr) ? ptr : [ptr])){ + switch (type) { + case 'i1': + case 'i8': c.HEAP8[p>>0] = value; continue; + case 'i16': c.HEAP16[p>>1] = value; continue; + case 'i32': c.HEAP32[p>>2] = value; continue; + case 'float': case 'f32': c.HEAP32F[p>>2] = value; continue; + case 'double': case 'f64': c.HEAP64F[p>>3] = value; continue; + case 'i64': + if(c.HEAP64){ + c.HEAP64[p>>3] = BigInt(value); + continue; + } + /* fallthru */ + default: + toss('Invalid type for setMemValue(): ' + type); + } } - toss('Invalid type for setMemValue(): ' + type); + return this; }; + /** + Convenience form of getMemValue() intended for fetching + pointer-to-pointer values. If passed a single non-array argument + it returns the value of that one pointer address. If passed + multiple arguments, or a single array of arguments, it returns an + array of their values. + */ + target.getPtrValue = function(...ptr){ + return target.getMemValue( (1===ptr.length ? ptr[0] : ptr), ptrIR ); + }; - /** Convenience form of getMemValue() intended for fetching - pointer-to-pointer values. */ - target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR); - - /** Convenience form of setMemValue() intended for setting - pointer-to-pointer values. */ - target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR); + /** + A variant of setMemValue() intended for setting + pointer-to-pointer values. Its differences from setMemValue() are + that (1) it defaults to a value of 0, (2) it always writes + to the pointer-sized heap view, and (3) it returns `this`. + */ + target.setPtrValue = (ptr, value=0)=>target.setMemValue(ptr, value, ptrIR); /** Returns true if the given value appears to be legal for use as diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index b1c4243d3..ddf766427 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -707,10 +707,10 @@ self.sqlite3InitModule = sqlite3InitModule; T.assert(12n===rc); w.scopedAllocCall(function(){ - let pI1 = w.scopedAlloc(8), pI2 = pI1+4; - w.setMemValue(pI1, 0,'*')(pI2, 0, '*'); - let f = w.xWrap('sqlite3_wasm_test_int64_minmax',undefined,['i64*','i64*']); - let r1 = w.getMemValue(pI1, 'i64'), r2 = w.getMemValue(pI2, 'i64'); + const pI1 = w.scopedAlloc(8), pI2 = pI1+4; + w.setPtrValue([pI1, pI2], 0); + const f = w.xWrap('sqlite3_wasm_test_int64_minmax',undefined,['i64*','i64*']); + const [r1, r2] = w.getMemValue([pI1, pI2], 'i64'); T.assert(!Number.isSafeInteger(r1)).assert(!Number.isSafeInteger(r2)); }); } @@ -1033,7 +1033,7 @@ self.sqlite3InitModule = sqlite3InitModule; } }; - T.assert(Number.isInteger(db.pointer)) + T.assert(wasm.isPtr(db.pointer)) .mustThrowMatching(()=>db.pointer=1, /read-only/) .assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)) .assert('main'===db.dbName(0)) @@ -1078,7 +1078,28 @@ self.sqlite3InitModule = sqlite3InitModule; 0, 4096, 12); T.assert(0 === rc); } - }finally{ + wasm.setPtrValue([pCur, pHi], 0); + let [vCur, vHi] = wasm.getPtrValue(pCur, pHi); + T.assert(0===vCur).assert(0===vHi); + rc = capi.sqlite3_status(capi.SQLITE_STATUS_MEMORY_USED, + pCur, pHi, 0); + [vCur, vHi] = wasm.getPtrValue([pCur, pHi]); + T.assert(vCur!==0).assert(vHi!==0); + [vCur, vHi] = wasm.getMemValue([vCur, vHi], 'i32'); + T.assert(0 === rc).assert(vCur > 0).assert(vHi >= vCur); + if(wasm.bigIntEnabled){ + // Again in 64-bit. Recall that pCur and pHi are allocated + // large enough to account for this re-use. + wasm.setPtrValue([pCur, pHi], 0) + .setMemValue([vCur, vHi], 0, 'i64'); + rc = capi.sqlite3_status64(capi.SQLITE_STATUS_MEMORY_USED, + pCur, pHi, 0); + [vCur, vHi] = wasm.getPtrValue([pCur, pHi]); + T.assert(vCur!==0).assert(vHi!==0); + [vCur, vHi] = wasm.getMemValue([vCur, vHi], 'i64'); + T.assert(0 === rc).assert(vCur > 0).assert(vHi >= vCur); + } + }finally{ wasm.pstack.restore(stack); } }) @@ -1090,9 +1111,13 @@ self.sqlite3InitModule = sqlite3InitModule; ); //debug("statement =",st); try { - T.assert(Number.isInteger(st.pointer)) + T.assert(wasm.isPtr(st.pointer)) .mustThrowMatching(()=>st.pointer=1, /read-only/) .assert(1===this.db.openStatementCount()) + .assert( + capi.sqlite3_stmt_status( + st, capi.SQLITE_STMTSTATUS_RUN, 0 + ) === 0) .assert(!st._mayGet) .assert('a' === st.getColumnName(0)) .assert(1===st.columnCount) @@ -1118,7 +1143,11 @@ self.sqlite3InitModule = sqlite3InitModule; .assert(st._mayGet) .assert(false===st.step()) .assert(!st._mayGet) - ; + .assert( + capi.sqlite3_stmt_status( + st, capi.SQLITE_STMTSTATUS_RUN, 0 + ) > 0); + T.assert(0===capi.sqlite3_strglob("*.txt", "foo.txt")). assert(0!==capi.sqlite3_strglob("*.txt", "foo.xtx")). assert(0===capi.sqlite3_strlike("%.txt", "foo.txt", 0)). @@ -1681,8 +1710,7 @@ self.sqlite3InitModule = sqlite3InitModule; const pMin = w.scopedAlloc(16); const pMax = pMin + 8; const g64 = (p)=>w.getMemValue(p,ptrType64); - w.setMemValue(pMin, 0, ptrType64); - w.setMemValue(pMax, 0, ptrType64); + w.setMemValue([pMin, pMax], 0, ptrType64); const minMaxI64 = [ w.xCall('sqlite3_wasm_test_int64_min'), w.xCall('sqlite3_wasm_test_int64_max') |