1/*
2  2022-07-22
3
4  The author disclaims copyright to this source code.  In place of a
5  legal notice, here is a blessing:
6
7  *   May you do good and not evil.
8  *   May you find forgiveness for yourself and forgive others.
9  *   May you share freely, never taking more than you give.
10
11  ***********************************************************************
12
13  This file glues together disparate pieces of JS which are loaded in
14  previous steps of the sqlite3-api.js bootstrapping process:
15  sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It
16  initializes the main API pieces so that the downstream components
17  (e.g. sqlite3-api-oo1.js) have all that they need.
18*/
19self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
20  'use strict';
21  const toss = (...args)=>{throw new Error(args.join(' '))};
22  const toss3 = sqlite3.SQLite3Error.toss;
23  const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
24  self.WhWasmUtilInstaller(capi.wasm);
25  delete self.WhWasmUtilInstaller;
26
27  /**
28     Install JS<->C struct bindings for the non-opaque struct types we
29     need... */
30  sqlite3.StructBinder = self.Jaccwabyt({
31    heap: 0 ? wasm.memory : wasm.heap8u,
32    alloc: wasm.alloc,
33    dealloc: wasm.dealloc,
34    functionTable: wasm.functionTable,
35    bigIntEnabled: wasm.bigIntEnabled,
36    memberPrefix: '$'
37  });
38  delete self.Jaccwabyt;
39
40  if(0){
41    /*  "The problem" is that the following isn't even remotely
42        type-safe. OTOH, nothing about WASM pointers is. */
43    const argPointer = wasm.xWrap.argAdapter('*');
44    wasm.xWrap.argAdapter('StructType', (v)=>{
45      if(v && v.constructor && v instanceof StructBinder.StructType){
46        v = v.pointer;
47      }
48      return (v === (v | 0) /* v is a 32-bit integer */)
49        ? argPointer(v)
50        : toss("Invalid (object) type for StructType-type argument.");
51    });
52  }
53
54  if(1){/* Convert Arrays and certain TypedArrays to strings for
55           'flexible-string'-type arguments */
56    const xString = wasm.xWrap.argAdapter('string');
57    wasm.xWrap.argAdapter(
58      'flexible-string', (v)=>xString(util.flexibleString(v))
59    );
60  }
61
62  if(1){// WhWasmUtil.xWrap() bindings...
63    /**
64       Add some descriptive xWrap() aliases for '*' intended to (A)
65       initially improve readability/correctness of capi.signatures
66       and (B) eventually perhaps provide automatic conversion from
67       higher-level representations, e.g. capi.sqlite3_vfs to
68       `sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
69    */
70    const aPtr = wasm.xWrap.argAdapter('*');
71    wasm.xWrap.argAdapter('sqlite3*', aPtr)
72    ('sqlite3_stmt*', aPtr)
73    ('sqlite3_context*', aPtr)
74    ('sqlite3_value*', aPtr)
75    ('sqlite3_vfs*', aPtr)
76    ('void*', aPtr);
77    wasm.xWrap.resultAdapter('sqlite3*', aPtr)
78    ('sqlite3_context*', aPtr)
79    ('sqlite3_stmt*', aPtr)
80    ('sqlite3_vfs*', aPtr)
81    ('void*', aPtr);
82
83    /**
84       Populate api object with sqlite3_...() by binding the "raw" wasm
85       exports into type-converting proxies using wasm.xWrap().
86    */
87    for(const e of wasm.bindingSignatures){
88      capi[e[0]] = wasm.xWrap.apply(null, e);
89    }
90    for(const e of wasm.bindingSignatures.wasm){
91      capi.wasm[e[0]] = wasm.xWrap.apply(null, e);
92    }
93
94    /* For C API functions which cannot work properly unless
95       wasm.bigIntEnabled is true, install a bogus impl which
96       throws if called when bigIntEnabled is false. */
97    const fI64Disabled = function(fname){
98      return ()=>toss(fname+"() disabled due to lack",
99                      "of BigInt support in this build.");
100    };
101    for(const e of wasm.bindingSignatures.int64){
102      capi[e[0]] = wasm.bigIntEnabled
103        ? wasm.xWrap.apply(null, e)
104        : fI64Disabled(e[0]);
105    }
106
107    if(wasm.exports.sqlite3_wasm_db_error){
108      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
109        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
110      );
111    }else{
112      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
113        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
114        return errCode;
115      };
116    }
117
118    /**
119       When registering a VFS and its related components it may be
120       necessary to ensure that JS keeps a reference to them to keep
121       them from getting garbage collected. Simply pass each such value
122       to this function and a reference will be held to it for the life
123       of the app.
124    */
125    capi.sqlite3_vfs_register.addReference = function f(...args){
126      if(!f._) f._ = [];
127      f._.push(...args);
128    };
129
130  }/*xWrap() bindings*/;
131
132  /**
133     Internal helper to assist in validating call argument counts in
134     the hand-written sqlite3_xyz() wrappers. We do this only for
135     consistency with non-special-case wrappings.
136  */
137  const __dbArgcMismatch = (pDb,f,n)=>{
138    return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
139                                              f+"() requires "+n+" argument"+
140                                              (1===n?'':'s')+".");
141  };
142
143  /**
144     Helper for flexible-string conversions which require a
145     byte-length counterpart argument. Passed a value and its
146     ostensible length, this function returns [V,N], where V
147     is either v or a transformed copy of v and N is either n,
148     -1, or the byte length of v (if it's a byte array).
149  */
150  const __flexiString = function(v,n){
151    if('string'===typeof v){
152      n = -1;
153    }else if(util.isSQLableTypedArray(v)){
154      n = v.byteLength;
155      v = util.typedArrayToString(v);
156    }else if(Array.isArray(v)){
157      v = v.join('');
158      n = -1;
159    }
160    return [v, n];
161  };
162
163  if(1){/* Special-case handling of sqlite3_exec() */
164    const __exec = wasm.xWrap("sqlite3_exec", "int",
165                              ["sqlite3*", "flexible-string", "*", "*", "**"]);
166    /* Documented in the api object's initializer. */
167    capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
168      if(f.length!==arguments.length){
169        return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
170      }else if('function' !== typeof callback){
171        return __exec(pDb, sql, callback, pVoid, pErrMsg);
172      }
173      /* Wrap the callback in a WASM-bound function and convert the callback's
174         `(char**)` arguments to arrays of strings... */
175      const wasm = capi.wasm;
176      const cbwrap = function(pVoid, nCols, pColVals, pColNames){
177        let rc = capi.SQLITE_ERROR;
178        try {
179          let aVals = [], aNames = [], i = 0, offset = 0;
180          for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
181            aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
182            aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
183          }
184          rc = callback(pVoid, nCols, aVals, aNames) | 0;
185          /* The first 2 args of the callback are useless for JS but
186             we want the JS mapping of the C API to be as close to the
187             C API as possible. */
188        }catch(e){
189          /* If we set the db error state here, the higher-level exec() call
190             replaces it with its own, so we have no way of reporting the
191             exception message except the console. We must not propagate
192             exceptions through the C API. */
193        }
194        return rc;
195      };
196      let pFunc, rc;
197      try{
198        pFunc = wasm.installFunction("ipipp", cbwrap);
199        rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
200      }catch(e){
201        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
202                                        "Error running exec(): "+e.message);
203      }finally{
204        if(pFunc) wasm.uninstallFunction(pFunc);
205      }
206      return rc;
207    };
208  }/*sqlite3_exec() proxy*/;
209
210  if(1){/* Special-case handling of sqlite3_create_function_v2()
211           and sqlite3_create_window_function() */
212    const sqlite3CreateFunction = wasm.xWrap(
213      "sqlite3_create_function_v2", "int",
214      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
215       "int"/*eTextRep*/, "*"/*pApp*/,
216       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/]
217    );
218    const sqlite3CreateWindowFunction = wasm.xWrap(
219      "sqlite3_create_window_function", "int",
220      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
221       "int"/*eTextRep*/, "*"/*pApp*/,
222       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/,
223       "*"/*xInverse*/, "*"/*xDestroy*/]
224    );
225
226    const __udfSetResult = function(pCtx, val){
227      //console.warn("udfSetResult",typeof val, val);
228      switch(typeof val) {
229          case 'undefined':
230            /* Assume that the client already called sqlite3_result_xxx(). */
231            break;
232          case 'boolean':
233            capi.sqlite3_result_int(pCtx, val ? 1 : 0);
234            break;
235          case 'bigint':
236            if(wasm.bigIntEnabled){
237              if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
238              else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
239            }else if(util.bigIntFits32(val)){
240              capi.sqlite3_result_int(pCtx, Number(val));
241            }else if(util.bigIntFitsDouble(val)){
242              capi.sqlite3_result_double(pCtx, Number(val));
243            }else{
244              toss3("BigInt value",val.toString(),"is too BigInt.");
245            }
246            break;
247          case 'number': {
248            (util.isInt32(val)
249             ? capi.sqlite3_result_int
250             : capi.sqlite3_result_double)(pCtx, val);
251            break;
252          }
253          case 'string':
254            capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
255            break;
256          case 'object':
257            if(null===val/*yes, typeof null === 'object'*/) {
258              capi.sqlite3_result_null(pCtx);
259              break;
260            }else if(util.isBindableTypedArray(val)){
261              const pBlob = wasm.allocFromTypedArray(val);
262              capi.sqlite3_result_blob(
263                pCtx, pBlob, val.byteLength,
264                wasm.exports[sqlite3.config.deallocExportName]
265              );
266              break;
267            }
268            // else fall through
269          default:
270          toss3("Don't not how to handle this UDF result value:",(typeof val), val);
271      };
272    }/*__udfSetResult()*/;
273
274    const __udfConvertArgs = function(argc, pArgv){
275      let i, pVal, valType, arg;
276      const tgt = [];
277      for(i = 0; i < argc; ++i){
278        pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
279        /**
280           Curiously: despite ostensibly requiring 8-byte
281           alignment, the pArgv array is parcelled into chunks of
282           4 bytes (1 pointer each). The values those point to
283           have 8-byte alignment but the individual argv entries
284           do not.
285        */
286        valType = capi.sqlite3_value_type(pVal);
287        switch(valType){
288            case capi.SQLITE_INTEGER:
289              if(wasm.bigIntEnabled){
290                arg = capi.sqlite3_value_int64(pVal);
291                if(util.bigIntFitsDouble(arg)) arg = Number(arg);
292              }
293              else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
294              break;
295            case capi.SQLITE_FLOAT:
296              arg = capi.sqlite3_value_double(pVal);
297              break;
298            case capi.SQLITE_TEXT:
299              arg = capi.sqlite3_value_text(pVal);
300              break;
301            case capi.SQLITE_BLOB:{
302              const n = capi.sqlite3_value_bytes(pVal);
303              const pBlob = capi.sqlite3_value_blob(pVal);
304              if(n && !pBlob) sqlite3.WasmAllocError.toss(
305                "Cannot allocate memory for blob argument of",n,"byte(s)"
306              );
307              arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
308              break;
309            }
310            case capi.SQLITE_NULL:
311              arg = null; break;
312            default:
313              toss3("Unhandled sqlite3_value_type()",valType,
314                    "is possibly indicative of incorrect",
315                    "pointer size assumption.");
316        }
317        tgt.push(arg);
318      }
319      return tgt;
320    }/*__udfConvertArgs()*/;
321
322    const __udfSetError = (pCtx, e)=>{
323      if(e instanceof sqlite3.WasmAllocError){
324        capi.sqlite3_result_error_nomem(pCtx);
325      }else{
326        const msg = ('string'===typeof e) ? e : e.message;
327        capi.sqlite3_result_error(pCtx, msg, -1);
328      }
329    };
330
331    const __xFunc = function(callback){
332      return function(pCtx, argc, pArgv){
333        try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
334        catch(e){
335          //console.error('xFunc() caught:',e);
336          __udfSetError(pCtx, e);
337        }
338      };
339    };
340
341    const __xInverseAndStep = function(callback){
342      return function(pCtx, argc, pArgv){
343        try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
344        catch(e){ __udfSetError(pCtx, e) }
345      };
346    };
347
348    const __xFinalAndValue = function(callback){
349      return function(pCtx){
350        try{ __udfSetResult(pCtx, callback(pCtx)) }
351        catch(e){ __udfSetError(pCtx, e) }
352      };
353    };
354
355    const __xDestroy = function(callback){
356      return function(pVoid){
357        try{ callback(pVoid) }
358        catch(e){ console.error("UDF xDestroy method threw:",e) }
359      };
360    };
361
362    const __xMap = Object.assign(Object.create(null), {
363      xFunc:    {sig:'v(pip)', f:__xFunc},
364      xStep:    {sig:'v(pip)', f:__xInverseAndStep},
365      xInverse: {sig:'v(pip)', f:__xInverseAndStep},
366      xFinal:   {sig:'v(p)',   f:__xFinalAndValue},
367      xValue:   {sig:'v(p)',   f:__xFinalAndValue},
368      xDestroy: {sig:'v(p)',   f:__xDestroy}
369    });
370
371    const __xWrapFuncs = function(theFuncs, tgtUninst){
372      const rc = []
373      let k;
374      for(k in theFuncs){
375        let fArg = theFuncs[k];
376        if('function'===typeof fArg){
377          const w = __xMap[k];
378          fArg = wasm.installFunction(w.sig, w.f(fArg));
379          tgtUninst.push(fArg);
380        }
381        rc.push(fArg);
382      }
383      return rc;
384    };
385
386    /* Documented in the api object's initializer. */
387    capi.sqlite3_create_function_v2 = function f(
388      pDb, funcName, nArg, eTextRep, pApp,
389      xFunc,   //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
390      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
391      xFinal,  //void (*xFinal)(sqlite3_context*)
392      xDestroy //void (*xDestroy)(void*)
393    ){
394      if(f.length!==arguments.length){
395        return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length);
396      }
397      /* Wrap the callbacks in a WASM-bound functions... */
398      const wasm = capi.wasm;
399      const uninstall = [/*funcs to uninstall on error*/];
400      let rc;
401      try{
402        const funcArgs =  __xWrapFuncs({xFunc, xStep, xFinal, xDestroy},
403                                       uninstall);
404        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
405                                   pApp, ...funcArgs);
406      }catch(e){
407        console.error("sqlite3_create_function_v2() setup threw:",e);
408        for(let v of uninstall){
409          wasm.uninstallFunction(v);
410        }
411        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
412                                        "Creation of UDF threw: "+e.message);
413      }
414      return rc;
415    };
416
417    capi.sqlite3_create_function = function f(
418      pDb, funcName, nArg, eTextRep, pApp,
419      xFunc, xStep, xFinal
420    ){
421      return (f.length===arguments.length)
422        ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
423                                          pApp, xFunc, xStep, xFinal, 0)
424        : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length);
425    };
426
427    /* Documented in the api object's initializer. */
428    capi.sqlite3_create_window_function = function f(
429      pDb, funcName, nArg, eTextRep, pApp,
430      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
431      xFinal,  //void (*xFinal)(sqlite3_context*)
432      xValue,  //void (*xFinal)(sqlite3_context*)
433      xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**)
434      xDestroy //void (*xDestroy)(void*)
435    ){
436      if(f.length!==arguments.length){
437        return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length);
438      }
439      /* Wrap the callbacks in a WASM-bound functions... */
440      const wasm = capi.wasm;
441      const uninstall = [/*funcs to uninstall on error*/];
442      let rc;
443      try{
444        const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy},
445                                      uninstall);
446        rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
447                                         pApp, ...funcArgs);
448      }catch(e){
449        console.error("sqlite3_create_window_function() setup threw:",e);
450        for(let v of uninstall){
451          wasm.uninstallFunction(v);
452        }
453        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
454                                        "Creation of UDF threw: "+e.message);
455      }
456      return rc;
457    };
458    /**
459       A helper for UDFs implemented in JS and bound to WASM by the
460       client. Given a JS value, udfSetResult(pCtx,X) calls one of the
461       sqlite3_result_xyz(pCtx,...)  routines, depending on X's data
462       type:
463
464       - `null`: sqlite3_result_null()
465       - `boolean`: sqlite3_result_int()
466       - `number`: sqlite3_result_int() or sqlite3_result_double()
467       - `string`: sqlite3_result_text()
468       - Uint8Array or Int8Array: sqlite3_result_blob()
469       - `undefined`: indicates that the UDF called one of the
470         `sqlite3_result_xyz()` routines on its own, making this
471         function a no-op. Results are _undefined_ if this function is
472         passed the `undefined` value but did _not_ call one of the
473         `sqlite3_result_xyz()` routines.
474
475       Anything else triggers sqlite3_result_error().
476    */
477    capi.sqlite3_create_function_v2.udfSetResult =
478      capi.sqlite3_create_function.udfSetResult =
479      capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
480
481    /**
482       A helper for UDFs implemented in JS and bound to WASM by the
483       client. When passed the
484       (argc,argv) values from the UDF-related functions which receive
485       them (xFunc, xStep, xInverse), it creates a JS array
486       representing those arguments, converting each to JS in a manner
487       appropriate to its data type: numeric, text, blob
488       (Uint8Array), or null.
489
490       Results are undefined if it's passed anything other than those
491       two arguments from those specific contexts.
492
493       Thus an argc of 4 will result in a length-4 array containing
494       the converted values from the corresponding argv.
495
496       The conversion will throw only on allocation error or an internal
497       error.
498    */
499    capi.sqlite3_create_function_v2.udfConvertArgs =
500      capi.sqlite3_create_function.udfConvertArgs =
501      capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
502
503    /**
504       A helper for UDFs implemented in JS and bound to WASM by the
505       client. It expects to be a passed `(sqlite3_context*, Error)`
506       (an exception object or message string). And it sets the
507       current UDF's result to sqlite3_result_error_nomem() or
508       sqlite3_result_error(), depending on whether the 2nd argument
509       is a sqlite3.WasmAllocError object or not.
510    */
511    capi.sqlite3_create_function_v2.udfSetError =
512      capi.sqlite3_create_function.udfSetError =
513      capi.sqlite3_create_window_function.udfSetError = __udfSetError;
514
515  }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
516
517  if(1){/* Special-case handling of sqlite3_prepare_v2() and
518           sqlite3_prepare_v3() */
519    /**
520       Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
521    */
522    const __prepare = Object.create(null);
523    /**
524       This binding expects a JS string as its 2nd argument and
525       null as its final argument. In order to compile multiple
526       statements from a single string, the "full" impl (see
527       below) must be used.
528    */
529    __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
530                                 "int", ["sqlite3*", "string",
531                                         "int"/*ignored for this impl!*/,
532                                         "int", "**",
533                                         "**"/*MUST be 0 or null or undefined!*/]);
534    /**
535       Impl which requires that the 2nd argument be a pointer
536       to the SQL string, instead of being converted to a
537       string. This variant is necessary for cases where we
538       require a non-NULL value for the final argument
539       (exec()'ing multiple statements from one input
540       string). For simpler cases, where only the first
541       statement in the SQL string is required, the wrapper
542       named sqlite3_prepare_v2() is sufficient and easier to
543       use because it doesn't require dealing with pointers.
544    */
545    __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
546                                "int", ["sqlite3*", "*", "int", "int",
547                                        "**", "**"]);
548
549    /* Documented in the api object's initializer. */
550    capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
551      if(f.length!==arguments.length){
552        return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
553      }
554      const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
555      switch(typeof xSql){
556          case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
557          case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
558          default:
559            return util.sqlite3_wasm_db_error(
560              pDb, capi.SQLITE_MISUSE,
561              "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
562            );
563      }
564    };
565
566    /* Documented in the api object's initializer. */
567    capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
568      return (f.length===arguments.length)
569        ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
570        : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length);
571    };
572  }/*sqlite3_prepare_v2/v3()*/;
573
574  {/* Import C-level constants and structs... */
575    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
576    if(!cJson){
577      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
578           "static buffer size!");
579    }
580    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
581    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
582    for(const t of ['access', 'blobFinalizers', 'dataTypes',
583                    'encodings', 'fcntl', 'flock', 'ioCap',
584                    'openFlags', 'prepareFlags', 'resultCodes',
585                    'serialize', 'syncFlags', 'trace', 'udfFlags',
586                    'version'
587                   ]){
588      for(const e of Object.entries(wasm.ctype[t])){
589        // ^^^ [k,v] there triggers a buggy code transormation via one
590        // of the Emscripten-driven optimizers.
591        capi[e[0]] = e[1];
592      }
593    }
594    const __rcMap = Object.create(null);
595    for(const t of ['resultCodes']){
596      for(const e of Object.entries(wasm.ctype[t])){
597        __rcMap[e[1]] = e[0];
598      }
599    }
600    /**
601       For the given integer, returns the SQLITE_xxx result code as a
602       string, or undefined if no such mapping is found.
603    */
604    capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc];
605    /* Bind all registered C-side structs... */
606    const notThese = Object.assign(Object.create(null),{
607      // Structs NOT to register
608      WasmTestStruct: true
609    });
610    if(!util.isUIThread()){
611      /* We remove the kvvfs VFS from Worker threads below. */
612      notThese.sqlite3_kvvfs_methods = true;
613    }
614    for(const s of wasm.ctype.structs){
615      if(!notThese[s.name]){
616        capi[s.name] = sqlite3.StructBinder(s);
617      }
618    }
619  }/*end C constant imports*/
620
621  const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
622  if( pKvvfs ){/* kvvfs-specific glue */
623    if(util.isUIThread()){
624      const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
625        wasm.exports.sqlite3_wasm_kvvfs_methods()
626      );
627      delete capi.sqlite3_kvvfs_methods;
628
629      const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
630            pstack = wasm.pstack,
631            pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
632
633      const kvvfsStorage = (zClass)=>
634            ((115/*=='s'*/===wasm.getMemValue(zClass))
635             ? sessionStorage : localStorage);
636
637      const kvvfsImpls = {
638        xRead: (zClass, zKey, zBuf, nBuf)=>{
639          const stack = pstack.pointer,
640                astack = wasm.scopedAllocPush();
641          try {
642            const zXKey = kvvfsMakeKey(zClass,zKey);
643            if(!zXKey) return -3/*OOM*/;
644            const jKey = wasm.cstringToJs(zXKey);
645            const jV = kvvfsStorage(zClass).getItem(jKey);
646            if(!jV) return -1;
647            const nV = jV.length /* Note that we are relying 100% on v being
648                                    ASCII so that jV.length is equal to the
649                                    C-string's byte length. */;
650            if(nBuf<=0) return nV;
651            else if(1===nBuf){
652              wasm.setMemValue(zBuf, 0);
653              return nV;
654            }
655            const zV = wasm.scopedAllocCString(jV);
656            if(nBuf > nV + 1) nBuf = nV + 1;
657            wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
658            wasm.setMemValue(zBuf + nBuf - 1, 0);
659            return nBuf - 1;
660          }catch(e){
661            console.error("kvstorageRead()",e);
662            return -2;
663          }finally{
664            pstack.restore(stack);
665            wasm.scopedAllocPop(astack);
666          }
667        },
668        xWrite: (zClass, zKey, zData)=>{
669          const stack = pstack.pointer;
670          try {
671            const zXKey = kvvfsMakeKey(zClass,zKey);
672            if(!zXKey) return 1/*OOM*/;
673            const jKey = wasm.cstringToJs(zXKey);
674            kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData));
675            return 0;
676          }catch(e){
677            console.error("kvstorageWrite()",e);
678            return capi.SQLITE_IOERR;
679          }finally{
680            pstack.restore(stack);
681          }
682        },
683        xDelete: (zClass, zKey)=>{
684          const stack = pstack.pointer;
685          try {
686            const zXKey = kvvfsMakeKey(zClass,zKey);
687            if(!zXKey) return 1/*OOM*/;
688            kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey));
689            return 0;
690          }catch(e){
691            console.error("kvstorageDelete()",e);
692            return capi.SQLITE_IOERR;
693          }finally{
694            pstack.restore(stack);
695          }
696        }
697      }/*kvvfsImpls*/;
698      for(let k of Object.keys(kvvfsImpls)){
699        kvvfsMethods[kvvfsMethods.memberKey(k)] =
700          wasm.installFunction(
701            kvvfsMethods.memberSignature(k),
702            kvvfsImpls[k]
703          );
704      }
705    }else{
706      /* Worker thread: unregister kvvfs to avoid it being used
707         for anything other than local/sessionStorage. It "can"
708         be used that way but it's not really intended to be. */
709      capi.sqlite3_vfs_unregister(pKvvfs);
710    }
711  }/*pKvvfs*/
712
713});
714