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