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