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