1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

More fleshing out of sqlite3.capi.wasm.pstack.

FossilOrigin-Name: eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3
This commit is contained in:
stephan
2022-10-02 03:11:13 +00:00
parent 6479c5a359
commit 63e9ec2f9c
8 changed files with 205 additions and 86 deletions

View File

@ -55,10 +55,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
if(1){// WhWasmUtil.xWrap() bindings... if(1){// WhWasmUtil.xWrap() bindings...
/** /**
Add some descriptive xWrap() aliases for '*' intended to Add some descriptive xWrap() aliases for '*' intended to (A)
(A) initially improve readability/correctness of capi.signatures initially improve readability/correctness of capi.signatures
and (B) eventually perhaps provide some sort of type-safety and (B) eventually perhaps provide automatic conversion from
in their conversions. higher-level representations, e.g. capi.sqlite3_vfs to
`sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
*/ */
const aPtr = wasm.xWrap.argAdapter('*'); const aPtr = wasm.xWrap.argAdapter('*');
wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
@ -248,6 +249,56 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}; };
}/*sqlite3_prepare_v2/v3()*/; }/*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 Install JS<->C struct bindings for the non-opaque struct types we
need... */ need... */

View File

@ -18,7 +18,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const toss = (...args)=>{throw new Error(args.join(' '))}; const toss = (...args)=>{throw new Error(args.join(' '))};
const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)}; 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 /* What follows is colloquially known as "OO API #1". It is a
binding of the sqlite3 API which is designed to be run within binding of the sqlite3 API which is designed to be run within
the same thread (main or worker) as the one in which the 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 accessor and store their real values in this map. Keys = DB/Stmt
objects, values = pointer values. This also unifies how those are objects, values = pointer values. This also unifies how those are
accessed, for potential use downstream via custom 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. it.
*/ */
const __ptrMap = new WeakMap(); 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 being-construct DB object as its "this". See the DB constructor
for the argument docs. This is split into a separate function for the argument docs. This is split into a separate function
in order to enable simple creation of special-case DB constructors, 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 Expects to be passed a configuration object with the following
properties: properties:
@ -123,7 +123,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
console.error("Invalid DB ctor args",opt,arguments); console.error("Invalid DB ctor args",opt,arguments);
toss3("Invalid arguments for DB constructor."); 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]; const vfsCheck = ctor._name2vfs[fnJs];
if(vfsCheck){ if(vfsCheck){
vfsName = vfsCheck.vfs; vfsName = vfsCheck.vfs;
@ -136,20 +136,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
oflags |= capi.SQLITE_OPEN_EXRESCODE; oflags |= capi.SQLITE_OPEN_EXRESCODE;
const stack = capi.wasm.scopedAllocPush(); const scope = wasm.scopedAllocPush();
try { try {
const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; const ppDb = wasm.allocPtr() /* output (sqlite3**) arg */;
const pVfsName = vfsName ? ( const pVfsName = vfsName ? (
('number'===typeof vfsName ? vfsName : capi.wasm.scopedAllocCString(vfsName)) ('number'===typeof vfsName ? vfsName : wasm.scopedAllocCString(vfsName))
): 0; ): 0;
const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName); const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName);
ptr = capi.wasm.getPtrValue(ppDb); ptr = wasm.getPtrValue(ppDb);
checkSqlite3Rc(ptr, rc); checkSqlite3Rc(ptr, rc);
}catch( e ){ }catch( e ){
if( ptr ) capi.sqlite3_close_v2(ptr); if( ptr ) capi.sqlite3_close_v2(ptr);
throw e; throw e;
}finally{ }finally{
capi.wasm.scopedAllocPop(stack); wasm.scopedAllocPop(scope);
} }
this.filename = fnJs; this.filename = fnJs;
__ptrMap.set(this, ptr); __ptrMap.set(this, ptr);
@ -265,7 +265,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
blob: 5 blob: 5
}; };
BindTypes['undefined'] == BindTypes.null; BindTypes['undefined'] == BindTypes.null;
if(capi.wasm.bigIntEnabled){ if(wasm.bigIntEnabled){
BindTypes.bigint = BindTypes.number; BindTypes.bigint = BindTypes.number;
} }
@ -454,7 +454,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
if(s && s.pointer) s.finalize(); if(s && s.pointer) s.finalize();
}); });
Object.values(__udfMap.get(this)).forEach( Object.values(__udfMap.get(this)).forEach(
capi.wasm.uninstallFunction.bind(capi.wasm) wasm.uninstallFunction.bind(capi.wasm)
); );
__ptrMap.delete(this); __ptrMap.delete(this);
__stmtMap.delete(this); __stmtMap.delete(this);
@ -539,15 +539,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/ */
prepare: function(sql){ prepare: function(sql){
affirmDbOpen(this); affirmDbOpen(this);
const stack = capi.wasm.pstack.pointer; const stack = wasm.pstack.pointer;
let ppStmt, pStmt; let ppStmt, pStmt;
try{ 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)); DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null));
pStmt = capi.wasm.getPtrValue(ppStmt); pStmt = wasm.getPtrValue(ppStmt);
} }
finally { finally {
capi.wasm.pstack.restore(stack); wasm.pstack.restore(stack);
} }
if(!pStmt) toss3("Cannot prepare empty SQL."); if(!pStmt) toss3("Cannot prepare empty SQL.");
const stmt = new Stmt(this, pStmt, BindTypes); const stmt = new Stmt(this, pStmt, BindTypes);
@ -846,7 +846,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
let i, pVal, valType, arg; let i, pVal, valType, arg;
const tgt = []; const tgt = [];
for(i = 0; i < argc; ++i){ 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 Curiously: despite ostensibly requiring 8-byte
alignment, the pArgv array is parcelled into chunks of 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); const pBlob = capi.sqlite3_value_blob(pVal);
arg = new Uint8Array(n); arg = new Uint8Array(n);
let i; 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]; for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
break; break;
} }
@ -902,10 +902,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
capi.sqlite3_result_null(pCx); capi.sqlite3_result_null(pCx);
break; break;
}else if(util.isBindableTypedArray(val)){ }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.sqlite3_result_blob(pCx, pBlob, val.byteLength,
capi.SQLITE_TRANSIENT); capi.SQLITE_TRANSIENT);
capi.wasm.dealloc(pBlob); wasm.dealloc(pBlob);
break; break;
} }
// else fall through // 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()*/; let fFlags = 0 /*flags for sqlite3_create_function_v2()*/;
if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC;
if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; 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, capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
}catch(e){ }catch(e){
capi.wasm.uninstallFunction(pUdf); wasm.uninstallFunction(pUdf);
throw e; throw e;
} }
const udfMap = __udfMap.get(this); const udfMap = __udfMap.get(this);
if(udfMap[name]){ if(udfMap[name]){
try{capi.wasm.uninstallFunction(udfMap[name])} try{wasm.uninstallFunction(udfMap[name])}
catch(e){/*ignore*/} catch(e){/*ignore*/}
} }
udfMap[name] = pUdf; udfMap[name] = pUdf;
@ -1049,7 +1049,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
case BindTypes.string: case BindTypes.string:
return t; return t;
case BindTypes.bigint: case BindTypes.bigint:
if(capi.wasm.bigIntEnabled) return t; if(wasm.bigIntEnabled) return t;
/* else fall through */ /* else fall through */
default: default:
//console.log("isSupportedBindType",t,v); //console.log("isSupportedBindType",t,v);
@ -1109,7 +1109,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const bindOne = function f(stmt,ndx,bindType,val){ const bindOne = function f(stmt,ndx,bindType,val){
affirmUnlocked(stmt, 'bind()'); affirmUnlocked(stmt, 'bind()');
if(!f._){ if(!f._){
if(capi.wasm.bigIntEnabled){ if(wasm.bigIntEnabled){
f._maxInt = BigInt("0x7fffffffffffffff"); f._maxInt = BigInt("0x7fffffffffffffff");
f._minInt = ~f._maxInt; f._minInt = ~f._maxInt;
} }
@ -1120,25 +1120,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
string: function(stmt, ndx, val, asBlob){ string: function(stmt, ndx, val, asBlob){
if(1){ if(1){
/* _Hypothetically_ more efficient than the impl in the 'else' block. */ /* _Hypothetically_ more efficient than the impl in the 'else' block. */
const stack = capi.wasm.scopedAllocPush(); const stack = wasm.scopedAllocPush();
try{ try{
const n = capi.wasm.jstrlen(val); const n = wasm.jstrlen(val);
const pStr = capi.wasm.scopedAlloc(n); const pStr = wasm.scopedAlloc(n);
capi.wasm.jstrcpy(val, capi.wasm.heap8u(), pStr, n, false); wasm.jstrcpy(val, wasm.heap8u(), pStr, n, false);
const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text;
return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT); return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT);
}finally{ }finally{
capi.wasm.scopedAllocPop(stack); wasm.scopedAllocPop(stack);
} }
}else{ }else{
const bytes = capi.wasm.jstrToUintArray(val,false); const bytes = wasm.jstrToUintArray(val,false);
const pStr = capi.wasm.alloc(bytes.length || 1); const pStr = wasm.alloc(bytes.length || 1);
capi.wasm.heap8u().set(bytes.length ? bytes : [0], pStr); wasm.heap8u().set(bytes.length ? bytes : [0], pStr);
try{ try{
const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text; const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text;
return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT); return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT);
}finally{ }finally{
capi.wasm.dealloc(pStr); wasm.dealloc(pStr);
} }
} }
} }
@ -1160,7 +1160,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
else if('bigint'===typeof val){ else if('bigint'===typeof val){
if(val<f._minInt || val>f._maxInt){ if(val<f._minInt || val>f._maxInt){
toss3("BigInt value is out of range for storing as int64: "+val); 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; m = capi.sqlite3_bind_int64;
}else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){ }else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){
val = Number(val); val = Number(val);
@ -1170,7 +1170,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
} }
}else{ // !int32, !bigint }else{ // !int32, !bigint
val = Number(val); val = Number(val);
if(capi.wasm.bigIntEnabled && Number.isInteger(val)){ if(wasm.bigIntEnabled && Number.isInteger(val)){
m = capi.sqlite3_bind_int64; m = capi.sqlite3_bind_int64;
}else{ }else{
m = capi.sqlite3_bind_double; m = capi.sqlite3_bind_double;
@ -1190,22 +1190,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
"that it be a string, Uint8Array, or Int8Array."); "that it be a string, Uint8Array, or Int8Array.");
}else if(1){ }else if(1){
/* _Hypothetically_ more efficient than the impl in the 'else' block. */ /* _Hypothetically_ more efficient than the impl in the 'else' block. */
const stack = capi.wasm.scopedAllocPush(); const stack = wasm.scopedAllocPush();
try{ try{
const pBlob = capi.wasm.scopedAlloc(val.byteLength || 1); const pBlob = wasm.scopedAlloc(val.byteLength || 1);
capi.wasm.heap8().set(val.byteLength ? val : [0], pBlob) wasm.heap8().set(val.byteLength ? val : [0], pBlob)
rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength,
capi.SQLITE_TRANSIENT); capi.SQLITE_TRANSIENT);
}finally{ }finally{
capi.wasm.scopedAllocPop(stack); wasm.scopedAllocPop(stack);
} }
}else{ }else{
const pBlob = capi.wasm.allocFromTypedArray(val); const pBlob = wasm.allocFromTypedArray(val);
try{ try{
rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength,
capi.SQLITE_TRANSIENT); capi.SQLITE_TRANSIENT);
}finally{ }finally{
capi.wasm.dealloc(pBlob); wasm.dealloc(pBlob);
} }
} }
break; break;
@ -1518,7 +1518,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
: asType){ : asType){
case capi.SQLITE_NULL: return null; case capi.SQLITE_NULL: return null;
case capi.SQLITE_INTEGER:{ case capi.SQLITE_INTEGER:{
if(capi.wasm.bigIntEnabled){ if(wasm.bigIntEnabled){
const rc = capi.sqlite3_column_int64(this.pointer, ndx); const rc = capi.sqlite3_column_int64(this.pointer, ndx);
if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){ if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){
/* Coerce "normal" number ranges to normal number values, /* 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), const n = capi.sqlite3_column_bytes(this.pointer, ndx),
ptr = capi.sqlite3_column_blob(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx),
rc = new Uint8Array(n); rc = new Uint8Array(n);
//heap = n ? capi.wasm.heap8() : false; //heap = n ? wasm.heap8() : false;
if(n) rc.set(capi.wasm.heap8u().slice(ptr, ptr+n), 0); if(n) rc.set(wasm.heap8u().slice(ptr, ptr+n), 0);
//for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i]; //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i];
if(n && this.db._blobXfer instanceof Array){ if(n && this.db._blobXfer instanceof Array){
/* This is an optimization soley for the /* This is an optimization soley for the

View File

@ -265,6 +265,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
this.name = 'WasmAllocError'; this.name = 'WasmAllocError';
} }
}; };
WasmAllocError.toss = (...args)=>{
throw new WasmAllocError(args.join(' '));
};
/** /**
The main sqlite3 binding API gets installed into this object, 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 Functions which are intended solely for API-internal use by the
WASM components, not client code. These get installed into WASM components, not client code. These get installed into
capi.wasm. capi.wasm.
TODO: get rid of sqlite3_wasm_vfs_unlink(). It is ill-conceived
and only rarely actually useful.
*/ */
capi.wasm.bindingSignatures.wasm = [ capi.wasm.bindingSignatures.wasm = [
["sqlite3_wasm_vfs_unlink", "int", "string"] ["sqlite3_wasm_vfs_unlink", "int", "string"]
@ -781,15 +787,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
Attempts to allocate the given number of bytes from the Attempts to allocate the given number of bytes from the
pstack. On success, it zeroes out a block of memory of the pstack. On success, it zeroes out a block of memory of the
given size, adjusts the pstack pointer, and returns a pointer given size, adjusts the pstack pointer, and returns a pointer
to the memory. On error, returns 0. The memory must eventually to the memory. On error, returns throws a WasmAllocError. The
be released using restore(). memory must eventually be released using restore().
This method always adjusts the given value to be a multiple This method always adjusts the given value to be a multiple
of 8 bytes because failing to do so can lead to incorrect of 8 bytes because failing to do so can lead to incorrect
results when reading and writing 64-bit values from/to the WASM results when reading and writing 64-bit values from/to the WASM
heap. 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 sqlite3.capi.wasm.pstack.pointer resolves to the current pstack
@ -828,7 +840,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
this.name = 'SQLite3Error'; this.name = 'SQLite3Error';
} }
}; };
SQLite3Error.toss = (...args)=>{
throw new SQLite3Error(args.join(' '));
};
/** State for sqlite3_wasmfs_opfs_dir(). */ /** State for sqlite3_wasmfs_opfs_dir(). */
let __persistentDir = undefined; let __persistentDir = undefined;

View File

@ -59,6 +59,10 @@
#include <assert.h> #include <assert.h>
#include "sqlite3.c" /* yes, .c instead of .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 ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not
** Emscripten-specific. It explicitly marks functions for export into ** Emscripten-specific. It explicitly marks functions for export into
@ -667,6 +671,9 @@ WASM_KEEP
int sqlite3_wasm_vfs_unlink(const char * zName){ int sqlite3_wasm_vfs_unlink(const char * zName){
int rc = SQLITE_MISUSE /* ??? */; int rc = SQLITE_MISUSE /* ??? */;
sqlite3_vfs * const pVfs = sqlite3_vfs_find(0); 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 ){ if( zName && pVfs && pVfs->xDelete ){
rc = pVfs->xDelete(pVfs, zName, 1); 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__) #if defined(__EMSCRIPTEN__) && defined(SQLITE_WASM_WASMFS)
#include <emscripten/console.h>
#if defined(SQLITE_WASM_WASMFS)
#include <emscripten/wasmfs.h> #include <emscripten/wasmfs.h>
/* /*
@ -809,6 +814,5 @@ int sqlite3_wasm_init_wasmfs(const char *zUnused){
return SQLITE_NOTFOUND; return SQLITE_NOTFOUND;
} }
#endif /* __EMSCRIPTEN__ && SQLITE_WASM_WASMFS */ #endif /* __EMSCRIPTEN__ && SQLITE_WASM_WASMFS */
#endif
#undef WASM_KEEP #undef WASM_KEEP

View File

@ -1091,28 +1091,36 @@ self.WhWasmUtilInstaller = function(target){
}; };
/** Internal impl for allocPtr() and scopedAllocPtr(). */ /** Internal impl for allocPtr() and scopedAllocPtr(). */
const __allocPtr = function(howMany, method){ const __allocPtr = function(howMany, safePtrSize, method){
__affirmAlloc(target, method); __affirmAlloc(target, method);
let m = target[method](howMany * ptrSizeof); const pIr = safePtrSize ? 'i64' : ptrIR;
target.setMemValue(m, 0, ptrIR) let m = target[method](howMany * (safePtrSize ? 8 : ptrSizeof));
target.setMemValue(m, 0, pIr)
if(1===howMany){ if(1===howMany){
return m; return m;
} }
const a = [m]; const a = [m];
for(let i = 1; i < howMany; ++i){ for(let i = 1; i < howMany; ++i){
m += ptrSizeof; m += (safePtrSize ? 8 : ptrSizeof);
a[i] = m; a[i] = m;
target.setMemValue(m, 0, ptrIR); target.setMemValue(m, 0, pIr);
} }
return a; return a;
}; };
/** /**
Allocates a single chunk of memory capable of holding `howMany` Allocates one or more pointers as a single chunk of memory and
pointers and zeroes them out. If `howMany` is 1 then the memory zeroes them out.
chunk is returned directly, else an array of pointer addresses is
returned, which can optionally be used with "destructuring The first argument is the number of pointers to allocate. The
assignment" like this: 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); const [p1, p2, p3] = allocPtr(3);
@ -1121,14 +1129,27 @@ self.WhWasmUtilInstaller = function(target){
ACHTUNG: when freeing the memory, pass only the _first_ result ACHTUNG: when freeing the memory, pass only the _first_ result
value to dealloc(). The others are part of the same memory chunk value to dealloc(). The others are part of the same memory chunk
and must not be freed separately. 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() Identical to allocPtr() except that it allocates using scopedAlloc()
instead of alloc(). 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 If target.exports[name] exists, it is returned, else an

View File

@ -1030,31 +1030,60 @@
*/ */
const testPstack = function(db,sqlite3){ const testPstack = function(db,sqlite3){
const w = sqlite3.capi.wasm, P = w.pstack; const w = sqlite3.capi.wasm, P = w.pstack;
const isAllocErr = (e)=>e instanceof sqlite3.WasmAllocError;
const stack = P.pointer; const stack = P.pointer;
T.assert(0===stack % 8 /* must be 8-byte aligned */); T.assert(0===stack % 8 /* must be 8-byte aligned */);
try{ try{
const quota = P.remaining; const quota = P.remaining;
log("pstack quota",quota); log("pstack quota",quota);
T.assert(quota >= 4096) T.assert(quota >= 4096)
.assert(0 === P.alloc(0)) .mustThrowMatching(()=>P.alloc(0), isAllocErr)
.assert(0 === P.alloc(-1)); .mustThrowMatching(()=>P.alloc(-1), isAllocErr);
let p1 = P.alloc(12); let p1 = P.alloc(12);
T.assert(p1 === stack - 16/*8-byte aligned*/) T.assert(p1 === stack - 16/*8-byte aligned*/)
.assert(P.pointer === p1); .assert(P.pointer === p1);
let p2 = P.alloc(7); let p2 = P.alloc(7);
T.assert(p2 === p1-8/*8-byte aligned, stack grows downwards*/) 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(24 === stack - p2)
.assert(P.pointer === p2); .assert(P.pointer === p2);
let n = quota - (stack - p2); let n = quota - (stack - p2);
let p3 = P.alloc(n); let p3 = P.alloc(n);
T.assert(p3 === stack-quota) T.assert(p3 === stack-quota)
.assert(0 === P.alloc(1)); .mustThrowMatching(()=>P.alloc(1), isAllocErr);
}finally{ }finally{
P.restore(stack); 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 clearKvvfs = function(){
const sz = sqlite3.capi.sqlite3_web_kvvfs_size(); const sz = sqlite3.capi.sqlite3_web_kvvfs_size();

View File

@ -1,5 +1,5 @@
C Document\sthe\sroles\sof\sthe\snew\s(this\spast\sweek)\sJS\sfiles\sadded\sto\sthe\sbuild\sprocess. C More\sfleshing\sout\sof\ssqlite3.capi.wasm.pstack.
D 2022-10-02T01:48:14.286 D 2022-10-02T03:11:13.806
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 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/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b
F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99 F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99
F ext/wasm/api/sqlite3-api-glue.js b15a51b88aaa472d36bf82d5123dbfdafe8ddf6ca75fba934510e4a20bbe4adb F ext/wasm/api/sqlite3-api-glue.js 474a5e0bf8016e22aefee44ca45408f08f4159c0b782295ac1d86c4556e91d9a
F ext/wasm/api/sqlite3-api-oo1.js 7667d320f6b9fb5252050a2f9c0b1769e11b84dbc0763b999baf65b451b14369 F ext/wasm/api/sqlite3-api-oo1.js 066e67f3033e1b300140d431557c468f5cd0a4c17253f156e05b8a2e2c802da7
F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541 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-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 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.html c363032aba7a525920f61f8be112a29459f73f07e46f0ba3b7730081a617826e
F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62e1a00ff5015 F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62e1a00ff5015
F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05 F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 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-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f
F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4
F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8 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.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e
F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae 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.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494 F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494
F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f 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.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P f9db664f756f3707afcb5dce87f6d946625848f27ea84337af68de72d4ad6c6b P 8b3bc7313aff551e5ee0b7aeb927095cf19b9b96abbdd922066c130656b8aa7d
R 8201c0f212d8f73dc23b363b8bdcd606 R a0d2cb1d1b18288fcfda68347c547fdd
U stephan U stephan
Z 7616e552f06f17aa339add7ed573ed79 Z 5fc153c99e29258d724616b67e70d32b
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
8b3bc7313aff551e5ee0b7aeb927095cf19b9b96abbdd922066c130656b8aa7d eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3