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