aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm')
-rw-r--r--ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api3
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js8
-rw-r--r--ext/wasm/api/sqlite3-api-oo1.js3
-rw-r--r--ext/wasm/api/sqlite3-api-prologue.js3
-rw-r--r--ext/wasm/api/sqlite3-wasm.c25
-rw-r--r--ext/wasm/common/whwasmutil.js109
-rw-r--r--ext/wasm/tester1.c-pp.js48
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')