1/* 2 2022-07-22 3 4 The author disclaims copyright to this source code. In place of a 5 legal notice, here is a blessing: 6 7 * May you do good and not evil. 8 * May you find forgiveness for yourself and forgive others. 9 * May you share freely, never taking more than you give. 10 11 *********************************************************************** 12 13 This file glues together disparate pieces of JS which are loaded in 14 previous steps of the sqlite3-api.js bootstrapping process: 15 sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It 16 initializes the main API pieces so that the downstream components 17 (e.g. sqlite3-api-oo1.js) have all that they need. 18*/ 19self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 20 'use strict'; 21 const toss = (...args)=>{throw new Error(args.join(' '))}; 22 const toss3 = sqlite3.SQLite3Error.toss; 23 const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; 24 self.WhWasmUtilInstaller(wasm); 25 delete self.WhWasmUtilInstaller; 26 27 /** 28 Install JS<->C struct bindings for the non-opaque struct types we 29 need... */ 30 sqlite3.StructBinder = self.Jaccwabyt({ 31 heap: 0 ? wasm.memory : wasm.heap8u, 32 alloc: wasm.alloc, 33 dealloc: wasm.dealloc, 34 functionTable: wasm.functionTable, 35 bigIntEnabled: wasm.bigIntEnabled, 36 memberPrefix: '$' 37 }); 38 delete self.Jaccwabyt; 39 40 if(0){ 41 /* "The problem" is that the following isn't even remotely 42 type-safe. OTOH, nothing about WASM pointers is. */ 43 const argPointer = wasm.xWrap.argAdapter('*'); 44 wasm.xWrap.argAdapter('StructType', (v)=>{ 45 if(v && v.constructor && v instanceof StructBinder.StructType){ 46 v = v.pointer; 47 } 48 return wasm.isPtr(v) 49 ? argPointer(v) 50 : toss("Invalid (object) type for StructType-type argument."); 51 }); 52 } 53 54 if(1){/* Convert Arrays and certain TypedArrays to strings for 55 'flexible-string'-type arguments */ 56 const xString = wasm.xWrap.argAdapter('string'); 57 wasm.xWrap.argAdapter( 58 'flexible-string', (v)=>xString(util.flexibleString(v)) 59 ); 60 } 61 62 if(1){// WhWasmUtil.xWrap() bindings... 63 /** 64 Add some descriptive xWrap() aliases for '*' intended to (A) 65 initially improve readability/correctness of capi.signatures 66 and (B) eventually perhaps provide automatic conversion from 67 higher-level representations, e.g. capi.sqlite3_vfs to 68 `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. 69 */ 70 const aPtr = wasm.xWrap.argAdapter('*'); 71 wasm.xWrap.argAdapter('sqlite3*', aPtr) 72 ('sqlite3_stmt*', aPtr) 73 ('sqlite3_context*', aPtr) 74 ('sqlite3_value*', aPtr) 75 ('sqlite3_vfs*', aPtr) 76 ('void*', aPtr); 77 wasm.xWrap.resultAdapter('sqlite3*', aPtr) 78 ('sqlite3_context*', aPtr) 79 ('sqlite3_stmt*', aPtr) 80 ('sqlite3_vfs*', aPtr) 81 ('void*', aPtr); 82 83 /** 84 Populate api object with sqlite3_...() by binding the "raw" wasm 85 exports into type-converting proxies using wasm.xWrap(). 86 */ 87 for(const e of wasm.bindingSignatures){ 88 capi[e[0]] = wasm.xWrap.apply(null, e); 89 } 90 for(const e of wasm.bindingSignatures.wasm){ 91 wasm[e[0]] = wasm.xWrap.apply(null, e); 92 } 93 94 /* For C API functions which cannot work properly unless 95 wasm.bigIntEnabled is true, install a bogus impl which 96 throws if called when bigIntEnabled is false. */ 97 const fI64Disabled = function(fname){ 98 return ()=>toss(fname+"() disabled due to lack", 99 "of BigInt support in this build."); 100 }; 101 for(const e of wasm.bindingSignatures.int64){ 102 capi[e[0]] = wasm.bigIntEnabled 103 ? wasm.xWrap.apply(null, e) 104 : fI64Disabled(e[0]); 105 } 106 107 /* There's no(?) need to expose bindingSignatures to clients, 108 implicitly making it part of the public interface. */ 109 delete wasm.bindingSignatures; 110 111 if(wasm.exports.sqlite3_wasm_db_error){ 112 util.sqlite3_wasm_db_error = wasm.xWrap( 113 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string' 114 ); 115 }else{ 116 util.sqlite3_wasm_db_error = function(pDb,errCode,msg){ 117 console.warn("sqlite3_wasm_db_error() is not exported.",arguments); 118 return errCode; 119 }; 120 } 121 122 }/*xWrap() bindings*/; 123 124 /** 125 When registering a VFS and its related components it may be 126 necessary to ensure that JS keeps a reference to them to keep 127 them from getting garbage collected. Simply pass each such value 128 to this function and a reference will be held to it for the life 129 of the app. 130 */ 131 capi.sqlite3_vfs_register.addReference = function f(...args){ 132 if(!f._) f._ = []; 133 f._.push(...args); 134 }; 135 136 /** 137 Internal helper to assist in validating call argument counts in 138 the hand-written sqlite3_xyz() wrappers. We do this only for 139 consistency with non-special-case wrappings. 140 */ 141 const __dbArgcMismatch = (pDb,f,n)=>{ 142 return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE, 143 f+"() requires "+n+" argument"+ 144 (1===n?'':'s')+"."); 145 }; 146 147 /** 148 Helper for flexible-string conversions which require a 149 byte-length counterpart argument. Passed a value and its 150 ostensible length, this function returns [V,N], where V 151 is either v or a transformed copy of v and N is either n, 152 -1, or the byte length of v (if it's a byte array). 153 */ 154 const __flexiString = function(v,n){ 155 if('string'===typeof v){ 156 n = -1; 157 }else if(util.isSQLableTypedArray(v)){ 158 n = v.byteLength; 159 v = util.typedArrayToString(v); 160 }else if(Array.isArray(v)){ 161 v = v.join(''); 162 n = -1; 163 } 164 return [v, n]; 165 }; 166 167 if(1){/* Special-case handling of sqlite3_exec() */ 168 const __exec = wasm.xWrap("sqlite3_exec", "int", 169 ["sqlite3*", "flexible-string", "*", "*", "**"]); 170 /* Documented in the api object's initializer. */ 171 capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){ 172 if(f.length!==arguments.length){ 173 return __dbArgcMismatch(pDb,"sqlite3_exec",f.length); 174 }else if('function' !== typeof callback){ 175 return __exec(pDb, sql, callback, pVoid, pErrMsg); 176 } 177 /* Wrap the callback in a WASM-bound function and convert the callback's 178 `(char**)` arguments to arrays of strings... */ 179 const cbwrap = function(pVoid, nCols, pColVals, pColNames){ 180 let rc = capi.SQLITE_ERROR; 181 try { 182 let aVals = [], aNames = [], i = 0, offset = 0; 183 for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){ 184 aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) ); 185 aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) ); 186 } 187 rc = callback(pVoid, nCols, aVals, aNames) | 0; 188 /* The first 2 args of the callback are useless for JS but 189 we want the JS mapping of the C API to be as close to the 190 C API as possible. */ 191 }catch(e){ 192 /* If we set the db error state here, the higher-level exec() call 193 replaces it with its own, so we have no way of reporting the 194 exception message except the console. We must not propagate 195 exceptions through the C API. */ 196 } 197 return rc; 198 }; 199 let pFunc, rc; 200 try{ 201 pFunc = wasm.installFunction("ipipp", cbwrap); 202 rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg); 203 }catch(e){ 204 rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 205 "Error running exec(): "+e.message); 206 }finally{ 207 if(pFunc) wasm.uninstallFunction(pFunc); 208 } 209 return rc; 210 }; 211 }/*sqlite3_exec() proxy*/; 212 213 if(1){/* Special-case handling of sqlite3_create_function_v2() 214 and sqlite3_create_window_function() */ 215 const sqlite3CreateFunction = wasm.xWrap( 216 "sqlite3_create_function_v2", "int", 217 ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, 218 "int"/*eTextRep*/, "*"/*pApp*/, 219 "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/] 220 ); 221 const sqlite3CreateWindowFunction = wasm.xWrap( 222 "sqlite3_create_window_function", "int", 223 ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, 224 "int"/*eTextRep*/, "*"/*pApp*/, 225 "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, 226 "*"/*xInverse*/, "*"/*xDestroy*/] 227 ); 228 229 const __udfSetResult = function(pCtx, val){ 230 //console.warn("udfSetResult",typeof val, val); 231 switch(typeof val) { 232 case 'undefined': 233 /* Assume that the client already called sqlite3_result_xxx(). */ 234 break; 235 case 'boolean': 236 capi.sqlite3_result_int(pCtx, val ? 1 : 0); 237 break; 238 case 'bigint': 239 if(wasm.bigIntEnabled){ 240 if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); 241 else toss3("BigInt value",val.toString(),"is too BigInt for int64."); 242 }else if(util.bigIntFits32(val)){ 243 capi.sqlite3_result_int(pCtx, Number(val)); 244 }else if(util.bigIntFitsDouble(val)){ 245 capi.sqlite3_result_double(pCtx, Number(val)); 246 }else{ 247 toss3("BigInt value",val.toString(),"is too BigInt."); 248 } 249 break; 250 case 'number': { 251 (util.isInt32(val) 252 ? capi.sqlite3_result_int 253 : capi.sqlite3_result_double)(pCtx, val); 254 break; 255 } 256 case 'string': 257 capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT); 258 break; 259 case 'object': 260 if(null===val/*yes, typeof null === 'object'*/) { 261 capi.sqlite3_result_null(pCtx); 262 break; 263 }else if(util.isBindableTypedArray(val)){ 264 const pBlob = wasm.allocFromTypedArray(val); 265 capi.sqlite3_result_blob( 266 pCtx, pBlob, val.byteLength, 267 wasm.exports[sqlite3.config.deallocExportName] 268 ); 269 break; 270 } 271 // else fall through 272 default: 273 toss3("Don't not how to handle this UDF result value:",(typeof val), val); 274 }; 275 }/*__udfSetResult()*/; 276 277 const __udfConvertArgs = function(argc, pArgv){ 278 let i, pVal, valType, arg; 279 const tgt = []; 280 for(i = 0; i < argc; ++i){ 281 pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i)); 282 /** 283 Curiously: despite ostensibly requiring 8-byte 284 alignment, the pArgv array is parcelled into chunks of 285 4 bytes (1 pointer each). The values those point to 286 have 8-byte alignment but the individual argv entries 287 do not. 288 */ 289 valType = capi.sqlite3_value_type(pVal); 290 switch(valType){ 291 case capi.SQLITE_INTEGER: 292 if(wasm.bigIntEnabled){ 293 arg = capi.sqlite3_value_int64(pVal); 294 if(util.bigIntFitsDouble(arg)) arg = Number(arg); 295 } 296 else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/; 297 break; 298 case capi.SQLITE_FLOAT: 299 arg = capi.sqlite3_value_double(pVal); 300 break; 301 case capi.SQLITE_TEXT: 302 arg = capi.sqlite3_value_text(pVal); 303 break; 304 case capi.SQLITE_BLOB:{ 305 const n = capi.sqlite3_value_bytes(pVal); 306 const pBlob = capi.sqlite3_value_blob(pVal); 307 if(n && !pBlob) sqlite3.WasmAllocError.toss( 308 "Cannot allocate memory for blob argument of",n,"byte(s)" 309 ); 310 arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null; 311 break; 312 } 313 case capi.SQLITE_NULL: 314 arg = null; break; 315 default: 316 toss3("Unhandled sqlite3_value_type()",valType, 317 "is possibly indicative of incorrect", 318 "pointer size assumption."); 319 } 320 tgt.push(arg); 321 } 322 return tgt; 323 }/*__udfConvertArgs()*/; 324 325 const __udfSetError = (pCtx, e)=>{ 326 if(e instanceof sqlite3.WasmAllocError){ 327 capi.sqlite3_result_error_nomem(pCtx); 328 }else{ 329 const msg = ('string'===typeof e) ? e : e.message; 330 capi.sqlite3_result_error(pCtx, msg, -1); 331 } 332 }; 333 334 const __xFunc = function(callback){ 335 return function(pCtx, argc, pArgv){ 336 try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) } 337 catch(e){ 338 //console.error('xFunc() caught:',e); 339 __udfSetError(pCtx, e); 340 } 341 }; 342 }; 343 344 const __xInverseAndStep = function(callback){ 345 return function(pCtx, argc, pArgv){ 346 try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) } 347 catch(e){ __udfSetError(pCtx, e) } 348 }; 349 }; 350 351 const __xFinalAndValue = function(callback){ 352 return function(pCtx){ 353 try{ __udfSetResult(pCtx, callback(pCtx)) } 354 catch(e){ __udfSetError(pCtx, e) } 355 }; 356 }; 357 358 const __xDestroy = function(callback){ 359 return function(pVoid){ 360 try{ callback(pVoid) } 361 catch(e){ console.error("UDF xDestroy method threw:",e) } 362 }; 363 }; 364 365 const __xMap = Object.assign(Object.create(null), { 366 xFunc: {sig:'v(pip)', f:__xFunc}, 367 xStep: {sig:'v(pip)', f:__xInverseAndStep}, 368 xInverse: {sig:'v(pip)', f:__xInverseAndStep}, 369 xFinal: {sig:'v(p)', f:__xFinalAndValue}, 370 xValue: {sig:'v(p)', f:__xFinalAndValue}, 371 xDestroy: {sig:'v(p)', f:__xDestroy} 372 }); 373 374 const __xWrapFuncs = function(theFuncs, tgtUninst){ 375 const rc = [] 376 let k; 377 for(k in theFuncs){ 378 let fArg = theFuncs[k]; 379 if('function'===typeof fArg){ 380 const w = __xMap[k]; 381 fArg = wasm.installFunction(w.sig, w.f(fArg)); 382 tgtUninst.push(fArg); 383 } 384 rc.push(fArg); 385 } 386 return rc; 387 }; 388 389 /* Documented in the api object's initializer. */ 390 capi.sqlite3_create_function_v2 = function f( 391 pDb, funcName, nArg, eTextRep, pApp, 392 xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**) 393 xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) 394 xFinal, //void (*xFinal)(sqlite3_context*) 395 xDestroy //void (*xDestroy)(void*) 396 ){ 397 if(f.length!==arguments.length){ 398 return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length); 399 } 400 /* Wrap the callbacks in a WASM-bound functions... */ 401 const uninstall = [/*funcs to uninstall on error*/]; 402 let rc; 403 try{ 404 const funcArgs = __xWrapFuncs({xFunc, xStep, xFinal, xDestroy}, 405 uninstall); 406 rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, 407 pApp, ...funcArgs); 408 }catch(e){ 409 console.error("sqlite3_create_function_v2() setup threw:",e); 410 for(let v of uninstall){ 411 wasm.uninstallFunction(v); 412 } 413 rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 414 "Creation of UDF threw: "+e.message); 415 } 416 return rc; 417 }; 418 419 capi.sqlite3_create_function = function f( 420 pDb, funcName, nArg, eTextRep, pApp, 421 xFunc, xStep, xFinal 422 ){ 423 return (f.length===arguments.length) 424 ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, 425 pApp, xFunc, xStep, xFinal, 0) 426 : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length); 427 }; 428 429 /* Documented in the api object's initializer. */ 430 capi.sqlite3_create_window_function = function f( 431 pDb, funcName, nArg, eTextRep, pApp, 432 xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) 433 xFinal, //void (*xFinal)(sqlite3_context*) 434 xValue, //void (*xFinal)(sqlite3_context*) 435 xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**) 436 xDestroy //void (*xDestroy)(void*) 437 ){ 438 if(f.length!==arguments.length){ 439 return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length); 440 } 441 /* Wrap the callbacks in a WASM-bound functions... */ 442 const uninstall = [/*funcs to uninstall on error*/]; 443 let rc; 444 try{ 445 const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, 446 uninstall); 447 rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, 448 pApp, ...funcArgs); 449 }catch(e){ 450 console.error("sqlite3_create_window_function() setup threw:",e); 451 for(let v of uninstall){ 452 wasm.uninstallFunction(v); 453 } 454 rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 455 "Creation of UDF threw: "+e.message); 456 } 457 return rc; 458 }; 459 /** 460 A helper for UDFs implemented in JS and bound to WASM by the 461 client. Given a JS value, udfSetResult(pCtx,X) calls one of the 462 sqlite3_result_xyz(pCtx,...) routines, depending on X's data 463 type: 464 465 - `null`: sqlite3_result_null() 466 - `boolean`: sqlite3_result_int() 467 - `number`: sqlite3_result_int() or sqlite3_result_double() 468 - `string`: sqlite3_result_text() 469 - Uint8Array or Int8Array: sqlite3_result_blob() 470 - `undefined`: indicates that the UDF called one of the 471 `sqlite3_result_xyz()` routines on its own, making this 472 function a no-op. Results are _undefined_ if this function is 473 passed the `undefined` value but did _not_ call one of the 474 `sqlite3_result_xyz()` routines. 475 476 Anything else triggers sqlite3_result_error(). 477 */ 478 capi.sqlite3_create_function_v2.udfSetResult = 479 capi.sqlite3_create_function.udfSetResult = 480 capi.sqlite3_create_window_function.udfSetResult = __udfSetResult; 481 482 /** 483 A helper for UDFs implemented in JS and bound to WASM by the 484 client. When passed the 485 (argc,argv) values from the UDF-related functions which receive 486 them (xFunc, xStep, xInverse), it creates a JS array 487 representing those arguments, converting each to JS in a manner 488 appropriate to its data type: numeric, text, blob 489 (Uint8Array), or null. 490 491 Results are undefined if it's passed anything other than those 492 two arguments from those specific contexts. 493 494 Thus an argc of 4 will result in a length-4 array containing 495 the converted values from the corresponding argv. 496 497 The conversion will throw only on allocation error or an internal 498 error. 499 */ 500 capi.sqlite3_create_function_v2.udfConvertArgs = 501 capi.sqlite3_create_function.udfConvertArgs = 502 capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs; 503 504 /** 505 A helper for UDFs implemented in JS and bound to WASM by the 506 client. It expects to be a passed `(sqlite3_context*, Error)` 507 (an exception object or message string). And it sets the 508 current UDF's result to sqlite3_result_error_nomem() or 509 sqlite3_result_error(), depending on whether the 2nd argument 510 is a sqlite3.WasmAllocError object or not. 511 */ 512 capi.sqlite3_create_function_v2.udfSetError = 513 capi.sqlite3_create_function.udfSetError = 514 capi.sqlite3_create_window_function.udfSetError = __udfSetError; 515 516 }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; 517 518 if(1){/* Special-case handling of sqlite3_prepare_v2() and 519 sqlite3_prepare_v3() */ 520 /** 521 Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). 522 */ 523 const __prepare = Object.create(null); 524 /** 525 This binding expects a JS string as its 2nd argument and 526 null as its final argument. In order to compile multiple 527 statements from a single string, the "full" impl (see 528 below) must be used. 529 */ 530 __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', 531 "int", ["sqlite3*", "string", 532 "int"/*ignored for this impl!*/, 533 "int", "**", 534 "**"/*MUST be 0 or null or undefined!*/]); 535 /** 536 Impl which requires that the 2nd argument be a pointer 537 to the SQL string, instead of being converted to a 538 string. This variant is necessary for cases where we 539 require a non-NULL value for the final argument 540 (exec()'ing multiple statements from one input 541 string). For simpler cases, where only the first 542 statement in the SQL string is required, the wrapper 543 named sqlite3_prepare_v2() is sufficient and easier to 544 use because it doesn't require dealing with pointers. 545 */ 546 __prepare.full = wasm.xWrap('sqlite3_prepare_v3', 547 "int", ["sqlite3*", "*", "int", "int", 548 "**", "**"]); 549 550 /* Documented in the api object's initializer. */ 551 capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ 552 if(f.length!==arguments.length){ 553 return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); 554 } 555 const [xSql, xSqlLen] = __flexiString(sql, sqlLen); 556 switch(typeof xSql){ 557 case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); 558 case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); 559 default: 560 return util.sqlite3_wasm_db_error( 561 pDb, capi.SQLITE_MISUSE, 562 "Invalid SQL argument type for sqlite3_prepare_v2/v3()." 563 ); 564 } 565 }; 566 567 /* Documented in the api object's initializer. */ 568 capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){ 569 return (f.length===arguments.length) 570 ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) 571 : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length); 572 }; 573 }/*sqlite3_prepare_v2/v3()*/; 574 575 {/* Import C-level constants and structs... */ 576 const cJson = wasm.xCall('sqlite3_wasm_enum_json'); 577 if(!cJson){ 578 toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", 579 "static buffer size!"); 580 } 581 wasm.ctype = JSON.parse(wasm.cstringToJs(cJson)); 582 //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); 583 for(const t of ['access', 'blobFinalizers', 'dataTypes', 584 'encodings', 'fcntl', 'flock', 'ioCap', 585 'openFlags', 'prepareFlags', 'resultCodes', 586 'serialize', 'syncFlags', 'trace', 'udfFlags', 587 'version' 588 ]){ 589 for(const e of Object.entries(wasm.ctype[t])){ 590 // ^^^ [k,v] there triggers a buggy code transormation via one 591 // of the Emscripten-driven optimizers. 592 capi[e[0]] = e[1]; 593 } 594 } 595 const __rcMap = Object.create(null); 596 for(const t of ['resultCodes']){ 597 for(const e of Object.entries(wasm.ctype[t])){ 598 __rcMap[e[1]] = e[0]; 599 } 600 } 601 /** 602 For the given integer, returns the SQLITE_xxx result code as a 603 string, or undefined if no such mapping is found. 604 */ 605 capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc]; 606 /* Bind all registered C-side structs... */ 607 const notThese = Object.assign(Object.create(null),{ 608 // Structs NOT to register 609 WasmTestStruct: true 610 }); 611 if(!util.isUIThread()){ 612 /* We remove the kvvfs VFS from Worker threads below. */ 613 notThese.sqlite3_kvvfs_methods = true; 614 } 615 for(const s of wasm.ctype.structs){ 616 if(!notThese[s.name]){ 617 capi[s.name] = sqlite3.StructBinder(s); 618 } 619 } 620 }/*end C constant imports*/ 621 622 const pKvvfs = capi.sqlite3_vfs_find("kvvfs"); 623 if( pKvvfs ){/* kvvfs-specific glue */ 624 if(util.isUIThread()){ 625 const kvvfsMethods = new capi.sqlite3_kvvfs_methods( 626 wasm.exports.sqlite3_wasm_kvvfs_methods() 627 ); 628 delete capi.sqlite3_kvvfs_methods; 629 630 const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack, 631 pstack = wasm.pstack, 632 pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc; 633 634 const kvvfsStorage = (zClass)=> 635 ((115/*=='s'*/===wasm.getMemValue(zClass)) 636 ? sessionStorage : localStorage); 637 638 const kvvfsImpls = { 639 xRead: (zClass, zKey, zBuf, nBuf)=>{ 640 const stack = pstack.pointer, 641 astack = wasm.scopedAllocPush(); 642 try { 643 const zXKey = kvvfsMakeKey(zClass,zKey); 644 if(!zXKey) return -3/*OOM*/; 645 const jKey = wasm.cstringToJs(zXKey); 646 const jV = kvvfsStorage(zClass).getItem(jKey); 647 if(!jV) return -1; 648 const nV = jV.length /* Note that we are relying 100% on v being 649 ASCII so that jV.length is equal to the 650 C-string's byte length. */; 651 if(nBuf<=0) return nV; 652 else if(1===nBuf){ 653 wasm.setMemValue(zBuf, 0); 654 return nV; 655 } 656 const zV = wasm.scopedAllocCString(jV); 657 if(nBuf > nV + 1) nBuf = nV + 1; 658 wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1); 659 wasm.setMemValue(zBuf + nBuf - 1, 0); 660 return nBuf - 1; 661 }catch(e){ 662 console.error("kvstorageRead()",e); 663 return -2; 664 }finally{ 665 pstack.restore(stack); 666 wasm.scopedAllocPop(astack); 667 } 668 }, 669 xWrite: (zClass, zKey, zData)=>{ 670 const stack = pstack.pointer; 671 try { 672 const zXKey = kvvfsMakeKey(zClass,zKey); 673 if(!zXKey) return 1/*OOM*/; 674 const jKey = wasm.cstringToJs(zXKey); 675 kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData)); 676 return 0; 677 }catch(e){ 678 console.error("kvstorageWrite()",e); 679 return capi.SQLITE_IOERR; 680 }finally{ 681 pstack.restore(stack); 682 } 683 }, 684 xDelete: (zClass, zKey)=>{ 685 const stack = pstack.pointer; 686 try { 687 const zXKey = kvvfsMakeKey(zClass,zKey); 688 if(!zXKey) return 1/*OOM*/; 689 kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey)); 690 return 0; 691 }catch(e){ 692 console.error("kvstorageDelete()",e); 693 return capi.SQLITE_IOERR; 694 }finally{ 695 pstack.restore(stack); 696 } 697 } 698 }/*kvvfsImpls*/; 699 for(let k of Object.keys(kvvfsImpls)){ 700 kvvfsMethods[kvvfsMethods.memberKey(k)] = 701 wasm.installFunction( 702 kvvfsMethods.memberSignature(k), 703 kvvfsImpls[k] 704 ); 705 } 706 }else{ 707 /* Worker thread: unregister kvvfs to avoid it being used 708 for anything other than local/sessionStorage. It "can" 709 be used that way but it's not really intended to be. */ 710 capi.sqlite3_vfs_unregister(pKvvfs); 711 } 712 }/*pKvvfs*/ 713 714}); 715