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