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