summaryrefslogtreecommitdiffstats
path: root/ext/wasm/api/sqlite3-api-glue.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm/api/sqlite3-api-glue.js')
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js720
1 files changed, 720 insertions, 0 deletions
diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js
new file mode 100644
index 0000000..86aa1d1
--- /dev/null
+++ b/ext/wasm/api/sqlite3-api-glue.js
@@ -0,0 +1,720 @@
+/*
+ 2022-07-22
+
+ The author disclaims copyright to this source code. In place of a
+ legal notice, here is a blessing:
+
+ * May you do good and not evil.
+ * May you find forgiveness for yourself and forgive others.
+ * May you share freely, never taking more than you give.
+
+ ***********************************************************************
+
+ This file glues together disparate pieces of JS which are loaded in
+ previous steps of the sqlite3-api.js bootstrapping process:
+ sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It
+ initializes the main API pieces so that the downstream components
+ (e.g. sqlite3-api-oo1.js) have all that they need.
+*/
+self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
+ 'use strict';
+ const toss = (...args)=>{throw new Error(args.join(' '))};
+ const toss3 = sqlite3.SQLite3Error.toss;
+ const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
+ self.WhWasmUtilInstaller(wasm);
+ delete self.WhWasmUtilInstaller;
+
+ /**
+ Install JS<->C struct bindings for the non-opaque struct types we
+ need... */
+ sqlite3.StructBinder = self.Jaccwabyt({
+ heap: 0 ? wasm.memory : wasm.heap8u,
+ alloc: wasm.alloc,
+ dealloc: wasm.dealloc,
+ functionTable: wasm.functionTable,
+ bigIntEnabled: wasm.bigIntEnabled,
+ memberPrefix: '$'
+ });
+ delete self.Jaccwabyt;
+
+ if(0){
+ /* "The problem" is that the following isn't even remotely
+ type-safe. OTOH, nothing about WASM pointers is. */
+ const argPointer = wasm.xWrap.argAdapter('*');
+ wasm.xWrap.argAdapter('StructType', (v)=>{
+ if(v && v.constructor && v instanceof StructBinder.StructType){
+ v = v.pointer;
+ }
+ return wasm.isPtr(v)
+ ? argPointer(v)
+ : toss("Invalid (object) type for StructType-type argument.");
+ });
+ }
+
+ {/* Convert Arrays and certain TypedArrays to strings for
+ 'flexible-string'-type arguments */
+ const xString = wasm.xWrap.argAdapter('string');
+ wasm.xWrap.argAdapter(
+ 'flexible-string', (v)=>xString(util.flexibleString(v))
+ );
+ }
+
+ 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 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)
+ ('sqlite3_context*', aPtr)
+ ('sqlite3_value*', aPtr)
+ ('sqlite3_vfs*', aPtr)
+ ('void*', aPtr);
+ wasm.xWrap.resultAdapter('sqlite3*', aPtr)
+ ('sqlite3_context*', aPtr)
+ ('sqlite3_stmt*', aPtr)
+ ('sqlite3_vfs*', aPtr)
+ ('void*', aPtr);
+
+ /**
+ Populate api object with sqlite3_...() by binding the "raw" wasm
+ exports into type-converting proxies using wasm.xWrap().
+ */
+ for(const e of wasm.bindingSignatures){
+ capi[e[0]] = wasm.xWrap.apply(null, e);
+ }
+ for(const e of wasm.bindingSignatures.wasm){
+ wasm[e[0]] = wasm.xWrap.apply(null, e);
+ }
+
+ /* For C API functions which cannot work properly unless
+ wasm.bigIntEnabled is true, install a bogus impl which
+ throws if called when bigIntEnabled is false. */
+ const fI64Disabled = function(fname){
+ return ()=>toss(fname+"() disabled due to lack",
+ "of BigInt support in this build.");
+ };
+ for(const e of wasm.bindingSignatures.int64){
+ capi[e[0]] = wasm.bigIntEnabled
+ ? wasm.xWrap.apply(null, e)
+ : fI64Disabled(e[0]);
+ }
+
+ /* There's no(?) need to expose bindingSignatures to clients,
+ implicitly making it part of the public interface. */
+ delete wasm.bindingSignatures;
+
+ if(wasm.exports.sqlite3_wasm_db_error){
+ util.sqlite3_wasm_db_error = wasm.xWrap(
+ 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
+ );
+ }else{
+ util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
+ console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
+ return errCode;
+ };
+ }
+
+ }/*xWrap() bindings*/;
+
+ /**
+ When registering a VFS and its related components it may be
+ necessary to ensure that JS keeps a reference to them to keep
+ them from getting garbage collected. Simply pass each such value
+ to this function and a reference will be held to it for the life
+ of the app.
+ */
+ capi.sqlite3_vfs_register.addReference = function f(...args){
+ if(!f._) f._ = [];
+ f._.push(...args);
+ };
+
+ /**
+ Internal helper to assist in validating call argument counts in
+ the hand-written sqlite3_xyz() wrappers. We do this only for
+ consistency with non-special-case wrappings.
+ */
+ const __dbArgcMismatch = (pDb,f,n)=>{
+ return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
+ f+"() requires "+n+" argument"+
+ (1===n?"":'s')+".");
+ };
+
+ /**
+ Helper for flexible-string conversions which require a
+ byte-length counterpart argument. Passed a value and its
+ ostensible length, this function returns [V,N], where V
+ is either v or a transformed copy of v and N is either n,
+ -1, or the byte length of v (if it's a byte array).
+ */
+ const __flexiString = function(v,n){
+ if('string'===typeof v){
+ n = -1;
+ }else if(util.isSQLableTypedArray(v)){
+ n = v.byteLength;
+ v = util.typedArrayToString(v);
+ }else if(Array.isArray(v)){
+ v = v.join("");
+ n = -1;
+ }
+ return [v, n];
+ };
+
+ if(1){/* Special-case handling of sqlite3_exec() */
+ const __exec = wasm.xWrap("sqlite3_exec", "int",
+ ["sqlite3*", "flexible-string", "*", "*", "**"]);
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
+ if(f.length!==arguments.length){
+ return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
+ }else if('function' !== typeof callback){
+ return __exec(pDb, sql, callback, pVoid, pErrMsg);
+ }
+ /* Wrap the callback in a WASM-bound function and convert the callback's
+ `(char**)` arguments to arrays of strings... */
+ const cbwrap = function(pVoid, nCols, pColVals, pColNames){
+ let rc = capi.SQLITE_ERROR;
+ try {
+ let aVals = [], aNames = [], i = 0, offset = 0;
+ for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
+ aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
+ aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
+ }
+ rc = callback(pVoid, nCols, aVals, aNames) | 0;
+ /* The first 2 args of the callback are useless for JS but
+ we want the JS mapping of the C API to be as close to the
+ C API as possible. */
+ }catch(e){
+ /* If we set the db error state here, the higher-level exec() call
+ replaces it with its own, so we have no way of reporting the
+ exception message except the console. We must not propagate
+ exceptions through the C API. */
+ }
+ return rc;
+ };
+ let pFunc, rc;
+ try{
+ pFunc = wasm.installFunction("ipipp", cbwrap);
+ rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
+ }catch(e){
+ rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
+ "Error running exec(): "+e.message);
+ }finally{
+ if(pFunc) wasm.uninstallFunction(pFunc);
+ }
+ return rc;
+ };
+ }/*sqlite3_exec() proxy*/;
+
+ if(1){/* Special-case handling of sqlite3_create_function_v2()
+ and sqlite3_create_window_function() */
+ const sqlite3CreateFunction = wasm.xWrap(
+ "sqlite3_create_function_v2", "int",
+ ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
+ "int"/*eTextRep*/, "*"/*pApp*/,
+ "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/]
+ );
+ const sqlite3CreateWindowFunction = wasm.xWrap(
+ "sqlite3_create_window_function", "int",
+ ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
+ "int"/*eTextRep*/, "*"/*pApp*/,
+ "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/,
+ "*"/*xInverse*/, "*"/*xDestroy*/]
+ );
+
+ const __udfSetResult = function(pCtx, val){
+ //console.warn("udfSetResult",typeof val, val);
+ switch(typeof val) {
+ case 'undefined':
+ /* Assume that the client already called sqlite3_result_xxx(). */
+ break;
+ case 'boolean':
+ capi.sqlite3_result_int(pCtx, val ? 1 : 0);
+ break;
+ case 'bigint':
+ if(wasm.bigIntEnabled){
+ if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
+ else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
+ }else if(util.bigIntFits32(val)){
+ capi.sqlite3_result_int(pCtx, Number(val));
+ }else if(util.bigIntFitsDouble(val)){
+ capi.sqlite3_result_double(pCtx, Number(val));
+ }else{
+ toss3("BigInt value",val.toString(),"is too BigInt.");
+ }
+ break;
+ case 'number': {
+ (util.isInt32(val)
+ ? capi.sqlite3_result_int
+ : capi.sqlite3_result_double)(pCtx, val);
+ break;
+ }
+ case 'string':
+ capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
+ break;
+ case 'object':
+ if(null===val/*yes, typeof null === 'object'*/) {
+ capi.sqlite3_result_null(pCtx);
+ break;
+ }else if(util.isBindableTypedArray(val)){
+ const pBlob = wasm.allocFromTypedArray(val);
+ capi.sqlite3_result_blob(
+ pCtx, pBlob, val.byteLength,
+ wasm.exports[sqlite3.config.deallocExportName]
+ );
+ break;
+ }
+ // else fall through
+ default:
+ toss3("Don't not how to handle this UDF result value:",(typeof val), val);
+ };
+ }/*__udfSetResult()*/;
+
+ const __udfConvertArgs = function(argc, pArgv){
+ let i, pVal, valType, arg;
+ const tgt = [];
+ for(i = 0; i < argc; ++i){
+ pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
+ /**
+ Curiously: despite ostensibly requiring 8-byte
+ alignment, the pArgv array is parcelled into chunks of
+ 4 bytes (1 pointer each). The values those point to
+ have 8-byte alignment but the individual argv entries
+ do not.
+ */
+ valType = capi.sqlite3_value_type(pVal);
+ switch(valType){
+ case capi.SQLITE_INTEGER:
+ if(wasm.bigIntEnabled){
+ arg = capi.sqlite3_value_int64(pVal);
+ if(util.bigIntFitsDouble(arg)) arg = Number(arg);
+ }
+ else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
+ break;
+ case capi.SQLITE_FLOAT:
+ arg = capi.sqlite3_value_double(pVal);
+ break;
+ case capi.SQLITE_TEXT:
+ arg = capi.sqlite3_value_text(pVal);
+ break;
+ case capi.SQLITE_BLOB:{
+ const n = capi.sqlite3_value_bytes(pVal);
+ const pBlob = capi.sqlite3_value_blob(pVal);
+ if(n && !pBlob) sqlite3.WasmAllocError.toss(
+ "Cannot allocate memory for blob argument of",n,"byte(s)"
+ );
+ arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
+ break;
+ }
+ case capi.SQLITE_NULL:
+ arg = null; break;
+ default:
+ toss3("Unhandled sqlite3_value_type()",valType,
+ "is possibly indicative of incorrect",
+ "pointer size assumption.");
+ }
+ tgt.push(arg);
+ }
+ return tgt;
+ }/*__udfConvertArgs()*/;
+
+ const __udfSetError = (pCtx, e)=>{
+ if(e instanceof sqlite3.WasmAllocError){
+ capi.sqlite3_result_error_nomem(pCtx);
+ }else{
+ const msg = ('string'===typeof e) ? e : e.message;
+ capi.sqlite3_result_error(pCtx, msg, -1);
+ }
+ };
+
+ const __xFunc = function(callback){
+ return function(pCtx, argc, pArgv){
+ try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
+ catch(e){
+ //console.error('xFunc() caught:',e);
+ __udfSetError(pCtx, e);
+ }
+ };
+ };
+
+ const __xInverseAndStep = function(callback){
+ return function(pCtx, argc, pArgv){
+ try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
+ catch(e){ __udfSetError(pCtx, e) }
+ };
+ };
+
+ const __xFinalAndValue = function(callback){
+ return function(pCtx){
+ try{ __udfSetResult(pCtx, callback(pCtx)) }
+ catch(e){ __udfSetError(pCtx, e) }
+ };
+ };
+
+ const __xDestroy = function(callback){
+ return function(pVoid){
+ try{ callback(pVoid) }
+ catch(e){ console.error("UDF xDestroy method threw:",e) }
+ };
+ };
+
+ const __xMap = Object.assign(Object.create(null), {
+ xFunc: {sig:'v(pip)', f:__xFunc},
+ xStep: {sig:'v(pip)', f:__xInverseAndStep},
+ xInverse: {sig:'v(pip)', f:__xInverseAndStep},
+ xFinal: {sig:'v(p)', f:__xFinalAndValue},
+ xValue: {sig:'v(p)', f:__xFinalAndValue},
+ xDestroy: {sig:'v(p)', f:__xDestroy}
+ });
+
+ const __xWrapFuncs = function(theFuncs, tgtUninst){
+ const rc = []
+ let k;
+ for(k in theFuncs){
+ let fArg = theFuncs[k];
+ if('function'===typeof fArg){
+ const w = __xMap[k];
+ fArg = wasm.installFunction(w.sig, w.f(fArg));
+ tgtUninst.push(fArg);
+ }
+ rc.push(fArg);
+ }
+ return rc;
+ };
+
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_create_function_v2 = function f(
+ pDb, funcName, nArg, eTextRep, pApp,
+ xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
+ xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
+ xFinal, //void (*xFinal)(sqlite3_context*)
+ xDestroy //void (*xDestroy)(void*)
+ ){
+ if(f.length!==arguments.length){
+ return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length);
+ }
+ /* Wrap the callbacks in a WASM-bound functions... */
+ const uninstall = [/*funcs to uninstall on error*/];
+ let rc;
+ try{
+ const funcArgs = __xWrapFuncs({xFunc, xStep, xFinal, xDestroy},
+ uninstall);
+ rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
+ pApp, ...funcArgs);
+ }catch(e){
+ console.error("sqlite3_create_function_v2() setup threw:",e);
+ for(let v of uninstall){
+ wasm.uninstallFunction(v);
+ }
+ rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
+ "Creation of UDF threw: "+e.message);
+ }
+ return rc;
+ };
+
+ capi.sqlite3_create_function = function f(
+ pDb, funcName, nArg, eTextRep, pApp,
+ xFunc, xStep, xFinal
+ ){
+ return (f.length===arguments.length)
+ ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
+ pApp, xFunc, xStep, xFinal, 0)
+ : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length);
+ };
+
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_create_window_function = function f(
+ pDb, funcName, nArg, eTextRep, pApp,
+ xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
+ xFinal, //void (*xFinal)(sqlite3_context*)
+ xValue, //void (*xFinal)(sqlite3_context*)
+ xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**)
+ xDestroy //void (*xDestroy)(void*)
+ ){
+ if(f.length!==arguments.length){
+ return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length);
+ }
+ /* Wrap the callbacks in a WASM-bound functions... */
+ const uninstall = [/*funcs to uninstall on error*/];
+ let rc;
+ try{
+ const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy},
+ uninstall);
+ rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
+ pApp, ...funcArgs);
+ }catch(e){
+ console.error("sqlite3_create_window_function() setup threw:",e);
+ for(let v of uninstall){
+ wasm.uninstallFunction(v);
+ }
+ rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
+ "Creation of UDF threw: "+e.message);
+ }
+ return rc;
+ };
+ /**
+ A helper for UDFs implemented in JS and bound to WASM by the
+ client. Given a JS value, udfSetResult(pCtx,X) calls one of the
+ sqlite3_result_xyz(pCtx,...) routines, depending on X's data
+ type:
+
+ - `null`: sqlite3_result_null()
+ - `boolean`: sqlite3_result_int()
+ - `number`: sqlite3_result_int() or sqlite3_result_double()
+ - `string`: sqlite3_result_text()
+ - Uint8Array or Int8Array: sqlite3_result_blob()
+ - `undefined`: indicates that the UDF called one of the
+ `sqlite3_result_xyz()` routines on its own, making this
+ function a no-op. Results are _undefined_ if this function is
+ passed the `undefined` value but did _not_ call one of the
+ `sqlite3_result_xyz()` routines.
+
+ Anything else triggers sqlite3_result_error().
+ */
+ capi.sqlite3_create_function_v2.udfSetResult =
+ capi.sqlite3_create_function.udfSetResult =
+ capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
+
+ /**
+ A helper for UDFs implemented in JS and bound to WASM by the
+ client. When passed the
+ (argc,argv) values from the UDF-related functions which receive
+ them (xFunc, xStep, xInverse), it creates a JS array
+ representing those arguments, converting each to JS in a manner
+ appropriate to its data type: numeric, text, blob
+ (Uint8Array), or null.
+
+ Results are undefined if it's passed anything other than those
+ two arguments from those specific contexts.
+
+ Thus an argc of 4 will result in a length-4 array containing
+ the converted values from the corresponding argv.
+
+ The conversion will throw only on allocation error or an internal
+ error.
+ */
+ capi.sqlite3_create_function_v2.udfConvertArgs =
+ capi.sqlite3_create_function.udfConvertArgs =
+ capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
+
+ /**
+ A helper for UDFs implemented in JS and bound to WASM by the
+ client. It expects to be a passed `(sqlite3_context*, Error)`
+ (an exception object or message string). And it sets the
+ current UDF's result to sqlite3_result_error_nomem() or
+ sqlite3_result_error(), depending on whether the 2nd argument
+ is a sqlite3.WasmAllocError object or not.
+ */
+ capi.sqlite3_create_function_v2.udfSetError =
+ capi.sqlite3_create_function.udfSetError =
+ capi.sqlite3_create_window_function.udfSetError = __udfSetError;
+
+ }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
+
+ if(1){/* Special-case handling of sqlite3_prepare_v2() and
+ sqlite3_prepare_v3() */
+ /**
+ Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
+ */
+ const __prepare = Object.create(null);
+ /**
+ This binding expects a JS string as its 2nd argument and
+ null as its final argument. In order to compile multiple
+ statements from a single string, the "full" impl (see
+ below) must be used.
+ */
+ __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
+ "int", ["sqlite3*", "string",
+ "int"/*ignored for this impl!*/,
+ "int", "**",
+ "**"/*MUST be 0 or null or undefined!*/]);
+ /**
+ Impl which requires that the 2nd argument be a pointer
+ to the SQL string, instead of being converted to a
+ string. This variant is necessary for cases where we
+ require a non-NULL value for the final argument
+ (exec()'ing multiple statements from one input
+ string). For simpler cases, where only the first
+ statement in the SQL string is required, the wrapper
+ named sqlite3_prepare_v2() is sufficient and easier to
+ use because it doesn't require dealing with pointers.
+ */
+ __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
+ "int", ["sqlite3*", "*", "int", "int",
+ "**", "**"]);
+
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
+ if(f.length!==arguments.length){
+ return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
+ }
+ const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
+ switch(typeof xSql){
+ case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
+ case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
+ default:
+ return util.sqlite3_wasm_db_error(
+ pDb, capi.SQLITE_MISUSE,
+ "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
+ );
+ }
+ };
+
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
+ return (f.length===arguments.length)
+ ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
+ : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length);
+ };
+ }/*sqlite3_prepare_v2/v3()*/;
+
+ {/* Import C-level constants and structs... */
+ const cJson = wasm.xCall('sqlite3_wasm_enum_json');
+ if(!cJson){
+ toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
+ "static buffer size!");
+ }
+ wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
+ //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
+ for(const t of ['access', 'blobFinalizers', 'dataTypes',
+ 'encodings', 'fcntl', 'flock', 'ioCap',
+ 'openFlags', 'prepareFlags', 'resultCodes',
+ 'serialize', 'syncFlags', 'trace', 'udfFlags',
+ 'version'
+ ]){
+ for(const e of Object.entries(wasm.ctype[t])){
+ // ^^^ [k,v] there triggers a buggy code transormation via one
+ // of the Emscripten-driven optimizers.
+ capi[e[0]] = e[1];
+ }
+ }
+ const __rcMap = Object.create(null);
+ for(const t of ['resultCodes']){
+ for(const e of Object.entries(wasm.ctype[t])){
+ __rcMap[e[1]] = e[0];
+ }
+ }
+ /**
+ For the given integer, returns the SQLITE_xxx result code as a
+ string, or undefined if no such mapping is found.
+ */
+ capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc];
+ /* Bind all registered C-side structs... */
+ const notThese = Object.assign(Object.create(null),{
+ // Structs NOT to register
+ WasmTestStruct: true
+ });
+ if(!util.isUIThread()){
+ /* We remove the kvvfs VFS from Worker threads below. */
+ notThese.sqlite3_kvvfs_methods = true;
+ }
+ for(const s of wasm.ctype.structs){
+ if(!notThese[s.name]){
+ capi[s.name] = sqlite3.StructBinder(s);
+ }
+ }
+ }/*end C constant imports*/
+
+ const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
+ if( pKvvfs ){/* kvvfs-specific glue */
+ if(util.isUIThread()){
+ const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
+ wasm.exports.sqlite3_wasm_kvvfs_methods()
+ );
+ delete capi.sqlite3_kvvfs_methods;
+
+ const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
+ pstack = wasm.pstack,
+ pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
+
+ const kvvfsStorage = (zClass)=>
+ ((115/*=='s'*/===wasm.getMemValue(zClass))
+ ? sessionStorage : localStorage);
+
+ /**
+ Implementations for members of the object referred to by
+ sqlite3_wasm_kvvfs_methods(). We swap out the native
+ implementations with these, which use localStorage or
+ sessionStorage for their backing store.
+ */
+ const kvvfsImpls = {
+ xRead: (zClass, zKey, zBuf, nBuf)=>{
+ const stack = pstack.pointer,
+ astack = wasm.scopedAllocPush();
+ try {
+ const zXKey = kvvfsMakeKey(zClass,zKey);
+ if(!zXKey) return -3/*OOM*/;
+ const jKey = wasm.cstringToJs(zXKey);
+ const jV = kvvfsStorage(zClass).getItem(jKey);
+ if(!jV) return -1;
+ const nV = jV.length /* Note that we are relying 100% on v being
+ ASCII so that jV.length is equal to the
+ C-string's byte length. */;
+ if(nBuf<=0) return nV;
+ else if(1===nBuf){
+ wasm.setMemValue(zBuf, 0);
+ return nV;
+ }
+ const zV = wasm.scopedAllocCString(jV);
+ if(nBuf > nV + 1) nBuf = nV + 1;
+ wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
+ wasm.setMemValue(zBuf + nBuf - 1, 0);
+ return nBuf - 1;
+ }catch(e){
+ console.error("kvstorageRead()",e);
+ return -2;
+ }finally{
+ pstack.restore(stack);
+ wasm.scopedAllocPop(astack);
+ }
+ },
+ xWrite: (zClass, zKey, zData)=>{
+ const stack = pstack.pointer;
+ try {
+ const zXKey = kvvfsMakeKey(zClass,zKey);
+ if(!zXKey) return 1/*OOM*/;
+ const jKey = wasm.cstringToJs(zXKey);
+ kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData));
+ return 0;
+ }catch(e){
+ console.error("kvstorageWrite()",e);
+ return capi.SQLITE_IOERR;
+ }finally{
+ pstack.restore(stack);
+ }
+ },
+ xDelete: (zClass, zKey)=>{
+ const stack = pstack.pointer;
+ try {
+ const zXKey = kvvfsMakeKey(zClass,zKey);
+ if(!zXKey) return 1/*OOM*/;
+ kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey));
+ return 0;
+ }catch(e){
+ console.error("kvstorageDelete()",e);
+ return capi.SQLITE_IOERR;
+ }finally{
+ pstack.restore(stack);
+ }
+ }
+ }/*kvvfsImpls*/;
+ for(const k of Object.keys(kvvfsImpls)){
+ kvvfsMethods[kvvfsMethods.memberKey(k)] =
+ wasm.installFunction(
+ kvvfsMethods.memberSignature(k),
+ kvvfsImpls[k]
+ );
+ }
+ }else{
+ /* Worker thread: unregister kvvfs to avoid it being used
+ for anything other than local/sessionStorage. It "can"
+ be used that way but it's not really intended to be. */
+ capi.sqlite3_vfs_unregister(pKvvfs);
+ }
+ }/*pKvvfs*/
+
+});