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