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 'boolean': 230 capi.sqlite3_result_int(pCtx, val ? 1 : 0); 231 break; 232 case 'bigint': 233 if(wasm.bigIntEnabled){ 234 if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); 235 else toss3("BigInt value",val.toString(),"is too BigInt for int64."); 236 }else if(util.bigIntFits32(val)){ 237 capi.sqlite3_result_int(pCtx, Number(val)); 238 }else if(util.bigIntFitsDouble(val)){ 239 capi.sqlite3_result_double(pCtx, Number(val)); 240 }else{ 241 toss3("BigInt value",val.toString(),"is too BigInt."); 242 } 243 break; 244 case 'number': { 245 (util.isInt32(val) 246 ? capi.sqlite3_result_int 247 : capi.sqlite3_result_double)(pCtx, val); 248 break; 249 } 250 case 'string': 251 capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT); 252 break; 253 case 'object': 254 if(null===val/*yes, typeof null === 'object'*/) { 255 capi.sqlite3_result_null(pCtx); 256 break; 257 }else if(util.isBindableTypedArray(val)){ 258 const pBlob = wasm.allocFromTypedArray(val); 259 capi.sqlite3_result_blob( 260 pCtx, pBlob, val.byteLength, 261 wasm.exports[sqlite3.config.deallocExportName] 262 ); 263 break; 264 } 265 // else fall through 266 default: 267 toss3("Don't not how to handle this UDF result value:",(typeof val), val); 268 }; 269 }/*__udfSetResult()*/; 270 271 const __udfConvertArgs = function(argc, pArgv){ 272 let i, pVal, valType, arg; 273 const tgt = []; 274 for(i = 0; i < argc; ++i){ 275 pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i)); 276 /** 277 Curiously: despite ostensibly requiring 8-byte 278 alignment, the pArgv array is parcelled into chunks of 279 4 bytes (1 pointer each). The values those point to 280 have 8-byte alignment but the individual argv entries 281 do not. 282 */ 283 valType = capi.sqlite3_value_type(pVal); 284 switch(valType){ 285 case capi.SQLITE_INTEGER: 286 if(wasm.bigIntEnabled){ 287 arg = capi.sqlite3_value_int64(pVal); 288 if(util.bigIntFitsDouble(arg)) arg = Number(arg); 289 } 290 else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/; 291 break; 292 case capi.SQLITE_FLOAT: 293 arg = capi.sqlite3_value_double(pVal); 294 break; 295 case capi.SQLITE_TEXT: 296 arg = capi.sqlite3_value_text(pVal); 297 break; 298 case capi.SQLITE_BLOB:{ 299 const n = capi.sqlite3_value_bytes(pVal); 300 const pBlob = capi.sqlite3_value_blob(pVal); 301 if(n && !pBlob) sqlite3.WasmAllocError.toss( 302 "Cannot allocate memory for blob argument of",n,"byte(s)" 303 ); 304 arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null; 305 break; 306 } 307 case capi.SQLITE_NULL: 308 arg = null; break; 309 default: 310 toss3("Unhandled sqlite3_value_type()",valType, 311 "is possibly indicative of incorrect", 312 "pointer size assumption."); 313 } 314 tgt.push(arg); 315 } 316 return tgt; 317 }/*__udfConvertArgs()*/; 318 319 const __udfSetError = (pCtx, e)=>{ 320 if(e instanceof sqlite3.WasmAllocError){ 321 capi.sqlite3_result_error_nomem(pCtx); 322 }else{ 323 capi.sqlite3_result_error(pCtx, e.message, -1); 324 } 325 }; 326 327 const __xFunc = function(callback){ 328 return function(pCtx, argc, pArgv){ 329 try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) } 330 catch(e){ 331 //console.error('xFunc() caught:',e); 332 __udfSetError(pCtx, e); 333 } 334 }; 335 }; 336 337 const __xInverseAndStep = function(callback){ 338 return function(pCtx, argc, pArgv){ 339 try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) } 340 catch(e){ __udfSetError(pCtx, e) } 341 }; 342 }; 343 344 const __xFinalAndValue = function(callback){ 345 return function(pCtx){ 346 try{ __udfSetResult(pCtx, callback(pCtx)) } 347 catch(e){ __udfSetError(pCtx, e) } 348 }; 349 }; 350 351 const __xDestroy = function(callback){ 352 return function(pVoid){ 353 try{ callback(pVoid) } 354 catch(e){ console.error("UDF xDestroy method threw:",e) } 355 }; 356 }; 357 358 const __xMap = Object.assign(Object.create(null), { 359 xFunc: {sig:'v(pip)', f:__xFunc}, 360 xStep: {sig:'v(pip)', f:__xInverseAndStep}, 361 xInverse: {sig:'v(pip)', f:__xInverseAndStep}, 362 xFinal: {sig:'v(p)', f:__xFinalAndValue}, 363 xValue: {sig:'v(p)', f:__xFinalAndValue}, 364 xDestroy: {sig:'v(p)', f:__xDestroy} 365 }); 366 367 const __xWrapFuncs = function(theFuncs, tgtUninst){ 368 const rc = [] 369 let k; 370 for(k in theFuncs){ 371 let fArg = theFuncs[k]; 372 if('function'===typeof fArg){ 373 const w = __xMap[k]; 374 fArg = wasm.installFunction(w.sig, w.f(fArg)); 375 tgtUninst.push(fArg); 376 } 377 rc.push(fArg); 378 } 379 return rc; 380 }; 381 382 /* Documented in the api object's initializer. */ 383 capi.sqlite3_create_function_v2 = function f( 384 pDb, funcName, nArg, eTextRep, pApp, 385 xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**) 386 xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) 387 xFinal, //void (*xFinal)(sqlite3_context*) 388 xDestroy //void (*xDestroy)(void*) 389 ){ 390 if(f.length!==arguments.length){ 391 return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length); 392 } 393 /* Wrap the callbacks in a WASM-bound functions... */ 394 const wasm = capi.wasm; 395 const uninstall = [/*funcs to uninstall on error*/]; 396 let rc; 397 try{ 398 const funcArgs = __xWrapFuncs({xFunc, xStep, xFinal, xDestroy}, 399 uninstall); 400 rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, 401 pApp, ...funcArgs); 402 }catch(e){ 403 console.error("sqlite3_create_function_v2() setup threw:",e); 404 for(let v of uninstall){ 405 wasm.uninstallFunction(v); 406 } 407 rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 408 "Creation of UDF threw: "+e.message); 409 } 410 return rc; 411 }; 412 413 capi.sqlite3_create_function = function f( 414 pDb, funcName, nArg, eTextRep, pApp, 415 xFunc, xStep, xFinal 416 ){ 417 return (f.length===arguments.length) 418 ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, 419 pApp, xFunc, xStep, xFinal, 0) 420 : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length); 421 }; 422 423 /* Documented in the api object's initializer. */ 424 capi.sqlite3_create_window_function = function f( 425 pDb, funcName, nArg, eTextRep, pApp, 426 xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) 427 xFinal, //void (*xFinal)(sqlite3_context*) 428 xValue, //void (*xFinal)(sqlite3_context*) 429 xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**) 430 xDestroy //void (*xDestroy)(void*) 431 ){ 432 if(f.length!==arguments.length){ 433 return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length); 434 } 435 /* Wrap the callbacks in a WASM-bound functions... */ 436 const wasm = capi.wasm; 437 const uninstall = [/*funcs to uninstall on error*/]; 438 let rc; 439 try{ 440 const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, 441 uninstall); 442 rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, 443 pApp, ...funcArgs); 444 }catch(e){ 445 console.error("sqlite3_create_function_v2() setup threw:",e); 446 for(let v of uninstall){ 447 wasm.uninstallFunction(v); 448 } 449 rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 450 "Creation of UDF threw: "+e.message); 451 } 452 return rc; 453 }; 454 /** 455 A helper for UDFs implemented in JS and bound to WASM by the 456 client. Given a JS value, udfSetResult(pCtx,X) calls one of the 457 sqlite3_result_xyz(pCtx,...) routines, depending on X's data 458 type: 459 460 - `null`: sqlite3_result_null() 461 - `boolean`: sqlite3_result_int() 462 - `number`: sqlite3_result_int() or sqlite3_result_double() 463 - `string`: sqlite3_result_text() 464 - Uint8Array or Int8Array: sqlite3_result_blob() 465 466 Anything else triggers sqlite3_result_error(). 467 */ 468 capi.sqlite3_create_function_v2.udfSetResult = 469 capi.sqlite3_create_function.udfSetResult = 470 capi.sqlite3_create_window_function.udfSetResult = __udfSetResult; 471 472 /** 473 A helper for UDFs implemented in JS and bound to WASM by the 474 client. When passed the 475 (argc,argv) values from the UDF-related functions which receive 476 them (xFunc, xStep, xInverse), it creates a JS array 477 representing those arguments, converting each to JS in a manner 478 appropriate to its data type: numeric, text, blob 479 (Uint8Array), or null. 480 481 Results are undefined if it's passed anything other than those 482 two arguments from those specific contexts. 483 484 Thus an argc of 4 will result in a length-4 array containing 485 the converted values from the corresponding argv. 486 487 The conversion will throw only on allocation error or an internal 488 error. 489 */ 490 capi.sqlite3_create_function_v2.udfConvertArgs = 491 capi.sqlite3_create_function.udfConvertArgs = 492 capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs; 493 494 /** 495 A helper for UDFs implemented in JS and bound to WASM by the 496 client. It expects to be a passed `(sqlite3_context*, Error)` 497 (i.e. an exception object). And it sets the current UDF's 498 result to sqlite3_result_error_nomem() or sqlite3_result_error(), 499 depending on whether the 2nd argument is a 500 sqlite3.WasmAllocError object or not. 501 */ 502 capi.sqlite3_create_function_v2.udfSetError = 503 capi.sqlite3_create_function.udfSetError = 504 capi.sqlite3_create_window_function.udfSetError = __udfSetError; 505 506 }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; 507 508 if(1){/* Special-case handling of sqlite3_prepare_v2() and 509 sqlite3_prepare_v3() */ 510 /** 511 Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). 512 */ 513 const __prepare = Object.create(null); 514 /** 515 This binding expects a JS string as its 2nd argument and 516 null as its final argument. In order to compile multiple 517 statements from a single string, the "full" impl (see 518 below) must be used. 519 */ 520 __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', 521 "int", ["sqlite3*", "string", 522 "int"/*ignored for this impl!*/, 523 "int", "**", 524 "**"/*MUST be 0 or null or undefined!*/]); 525 /** 526 Impl which requires that the 2nd argument be a pointer 527 to the SQL string, instead of being converted to a 528 string. This variant is necessary for cases where we 529 require a non-NULL value for the final argument 530 (exec()'ing multiple statements from one input 531 string). For simpler cases, where only the first 532 statement in the SQL string is required, the wrapper 533 named sqlite3_prepare_v2() is sufficient and easier to 534 use because it doesn't require dealing with pointers. 535 */ 536 __prepare.full = wasm.xWrap('sqlite3_prepare_v3', 537 "int", ["sqlite3*", "*", "int", "int", 538 "**", "**"]); 539 540 /* Documented in the api object's initializer. */ 541 capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ 542 if(f.length!==arguments.length){ 543 return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); 544 } 545 const [xSql, xSqlLen] = __flexiString(sql, sqlLen); 546 switch(typeof xSql){ 547 case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); 548 case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); 549 default: 550 return util.sqlite3_wasm_db_error( 551 pDb, capi.SQLITE_MISUSE, 552 "Invalid SQL argument type for sqlite3_prepare_v2/v3()." 553 ); 554 } 555 }; 556 557 /* Documented in the api object's initializer. */ 558 capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){ 559 return (f.length===arguments.length) 560 ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) 561 : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length); 562 }; 563 }/*sqlite3_prepare_v2/v3()*/; 564 565 {/* Import C-level constants and structs... */ 566 const cJson = wasm.xCall('sqlite3_wasm_enum_json'); 567 if(!cJson){ 568 toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", 569 "static buffer size!"); 570 } 571 wasm.ctype = JSON.parse(wasm.cstringToJs(cJson)); 572 //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); 573 for(const t of ['access', 'blobFinalizers', 'dataTypes', 574 'encodings', 'fcntl', 'flock', 'ioCap', 575 'openFlags', 'prepareFlags', 'resultCodes', 576 'serialize', 'syncFlags', 'trace', 'udfFlags', 577 'version' 578 ]){ 579 for(const e of Object.entries(wasm.ctype[t])){ 580 // ^^^ [k,v] there triggers a buggy code transormation via one 581 // of the Emscripten-driven optimizers. 582 capi[e[0]] = e[1]; 583 } 584 } 585 const __rcMap = Object.create(null); 586 for(const t of ['resultCodes']){ 587 for(const e of Object.entries(wasm.ctype[t])){ 588 __rcMap[e[1]] = e[0]; 589 } 590 } 591 /** 592 For the given integer, returns the SQLITE_xxx result code as a 593 string, or undefined if no such mapping is found. 594 */ 595 capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc]; 596 /* Bind all registered C-side structs... */ 597 const notThese = Object.assign(Object.create(null),{ 598 // Structs NOT to register 599 WasmTestStruct: true 600 }); 601 for(const s of wasm.ctype.structs){ 602 if(!notThese[s.name]){ 603 capi[s.name] = sqlite3.StructBinder(s); 604 } 605 } 606 }/*end C constant imports*/ 607 608 if( util.isMainWindow() 609 && 0!==capi.sqlite3_vfs_find("kvvfs") ){/* kvvfs-specific glue */ 610 const kvvfsMethods = new capi.sqlite3_kvvfs_methods( 611 wasm.exports.sqlite3_wasm_kvvfs_methods() 612 ); 613 delete capi.sqlite3_kvvfs_methods; 614 615 const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack, 616 pstack = wasm.pstack, 617 pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc; 618 619 const kvvfsStorage = (zClass)=> 620 ((115/*=='s'*/===wasm.getMemValue(zClass)) 621 ? sessionStorage : localStorage); 622 623 const kvvfsImpls = { 624 xRead: (zClass, zKey, zBuf, nBuf)=>{ 625 const stack = pstack.pointer, 626 astack = wasm.scopedAllocPush(); 627 try { 628 const zXKey = kvvfsMakeKey(zClass,zKey); 629 if(!zXKey) return -3/*OOM*/; 630 const jKey = wasm.cstringToJs(zXKey); 631 const jV = kvvfsStorage(zClass).getItem(jKey); 632 if(!jV) return -1; 633 const nV = jV.length /* Note that we are relying 100% on v being 634 ASCII so that jV.length is equal to the 635 C-string's byte length. */; 636 if(nBuf<=0) return nV; 637 else if(1===nBuf){ 638 wasm.setMemValue(zBuf, 0); 639 return nV; 640 } 641 const zV = wasm.scopedAllocCString(jV); 642 if(nBuf > nV + 1) nBuf = nV + 1; 643 wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1); 644 wasm.setMemValue(zBuf + nBuf - 1, 0); 645 return nBuf - 1; 646 }catch(e){ 647 console.error("kvstorageRead()",e); 648 return -2; 649 }finally{ 650 pstack.restore(stack); 651 wasm.scopedAllocPop(astack); 652 } 653 }, 654 xWrite: (zClass, zKey, zData)=>{ 655 const stack = pstack.pointer; 656 try { 657 const zXKey = kvvfsMakeKey(zClass,zKey); 658 if(!zXKey) return 1/*OOM*/; 659 const jKey = wasm.cstringToJs(zXKey); 660 kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData)); 661 return 0; 662 }catch(e){ 663 console.error("kvstorageWrite()",e); 664 return capi.SQLITE_IOERR; 665 }finally{ 666 pstack.restore(stack); 667 } 668 }, 669 xDelete: (zClass, zKey)=>{ 670 const stack = pstack.pointer; 671 try { 672 const zXKey = kvvfsMakeKey(zClass,zKey); 673 if(!zXKey) return 1/*OOM*/; 674 kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey)); 675 return 0; 676 }catch(e){ 677 console.error("kvstorageDelete()",e); 678 return capi.SQLITE_IOERR; 679 }finally{ 680 pstack.restore(stack); 681 } 682 } 683 }/*kvvfsImpls*/; 684 for(let k of Object.keys(kvvfsImpls)){ 685 kvvfsMethods[kvvfsMethods.memberKey(k)] = 686 wasm.installFunction( 687 kvvfsMethods.memberSignature(k), 688 kvvfsImpls[k] 689 ); 690 } 691 }/*kvvfs*/ 692 693}); 694