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 capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
23  self.WhWasmUtilInstaller(capi.wasm);
24  delete self.WhWasmUtilInstaller;
25
26  if(0){
27    /*  "The problem" is that the following isn't type-safe.
28        OTOH, nothing about WASM pointers is. */
29    /**
30       Add the `.pointer` xWrap() signature entry to extend the
31       `pointer` arg handler to check for a `pointer` property. This
32       can be used to permit, e.g., passing an sqlite3.oo1.DB instance
33       to a C-style sqlite3_xxx function which takes an `sqlite3*`
34       argument.
35    */
36    const xPointer = wasm.xWrap.argAdapter('pointer');
37    const adapter = function(v){
38      if(v && v.constructor){
39        const x = v.pointer;
40        if(Number.isInteger(x)) return x;
41        else toss("Invalid (object) type for .pointer-type argument.");
42      }
43      return xPointer(v);
44    };
45    wasm.xWrap.argAdapter('.pointer', adapter);
46  } /* ".pointer" xWrap() argument adapter */
47
48  if(1){/* Convert Arrays and certain TypedArrays to strings for
49           'flexible-string'-type arguments */
50    const xString = wasm.xWrap.argAdapter('string');
51    wasm.xWrap.argAdapter(
52      'flexible-string', (v)=>xString(util.arrayToString(v))
53    );
54  }
55
56  if(1){// WhWasmUtil.xWrap() bindings...
57    /**
58       Add some descriptive xWrap() aliases for '*' intended to (A)
59       initially improve readability/correctness of capi.signatures
60       and (B) eventually perhaps provide automatic conversion from
61       higher-level representations, e.g. capi.sqlite3_vfs to
62       `sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
63    */
64    const aPtr = wasm.xWrap.argAdapter('*');
65    wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
66    wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
67
68    /**
69       Populate api object with sqlite3_...() by binding the "raw" wasm
70       exports into type-converting proxies using wasm.xWrap().
71    */
72    for(const e of wasm.bindingSignatures){
73      capi[e[0]] = wasm.xWrap.apply(null, e);
74    }
75    for(const e of wasm.bindingSignatures.wasm){
76      capi.wasm[e[0]] = wasm.xWrap.apply(null, e);
77    }
78
79    /* For C API functions which cannot work properly unless
80       wasm.bigIntEnabled is true, install a bogus impl which
81       throws if called when bigIntEnabled is false. */
82    const fI64Disabled = function(fname){
83      return ()=>toss(fname+"() disabled due to lack",
84                      "of BigInt support in this build.");
85    };
86    for(const e of wasm.bindingSignatures.int64){
87      capi[e[0]] = wasm.bigIntEnabled
88        ? wasm.xWrap.apply(null, e)
89        : fI64Disabled(e[0]);
90    }
91
92    if(wasm.exports.sqlite3_wasm_db_error){
93      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
94        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
95      );
96    }else{
97      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
98        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
99        return errCode;
100      };
101    }
102
103    /**
104       When registering a VFS and its related components it may be
105       necessary to ensure that JS keeps a reference to them to keep
106       them from getting garbage collected. Simply pass each such value
107       to this function and a reference will be held to it for the life
108       of the app.
109    */
110    capi.sqlite3_vfs_register.addReference = function f(...args){
111      if(!f._) f._ = [];
112      f._.push(...args);
113    };
114
115  }/*xWrap() bindings*/;
116
117  /**
118     Internal helper to assist in validating call argument counts in
119     the hand-written sqlite3_xyz() wrappers. We do this only for
120     consistency with non-special-case wrappings.
121  */
122  const __dbArgcMismatch = (pDb,f,n)=>{
123    return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
124                                              f+"() requires "+n+" argument"+
125                                              (1===n?'':'s')+".");
126  };
127
128  /**
129     Helper for flexible-string conversions which require a
130     byte-length counterpart argument. Passed a value and its
131     ostensible length, this function returns [V,N], where V
132     is either v or a transformed copy of v and N is either n,
133     -1, or the byte length of v (if it's a byte array).
134  */
135  const __flexiString = function(v,n){
136    if('string'===typeof v){
137      n = -1;
138    }else if(util.isSQLableTypedArray(v)){
139      n = v.byteLength;
140      v = util.typedArrayToString(v);
141    }else if(Array.isArray(v)){
142      v = v.join('');
143      n = -1;
144    }
145    return [v, n];
146  };
147
148  if(1){/* Special-case handling of sqlite3_exec() */
149    const __exec = wasm.xWrap("sqlite3_exec", "int",
150                              ["sqlite3*", "flexible-string", "*", "*", "**"]);
151    /* Documented in the api object's initializer. */
152    capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
153      if(f.length!==arguments.length){
154        return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
155      }else if('function' !== typeof callback){
156        return __exec(pDb, sql, callback, pVoid, pErrMsg);
157      }
158      /* Wrap the callback in a WASM-bound function and convert the callback's
159         `(char**)` arguments to arrays of strings... */
160      const wasm = capi.wasm;
161      const cbwrap = function(pVoid, nCols, pColVals, pColNames){
162        let rc = capi.SQLITE_ERROR;
163        try {
164          let aVals = [], aNames = [], i = 0, offset = 0;
165          for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
166            aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
167            aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
168          }
169          rc = callback(pVoid, nCols, aVals, aNames) | 0;
170          /* The first 2 args of the callback are useless for JS but
171             we want the JS mapping of the C API to be as close to the
172             C API as possible. */
173        }catch(e){
174          /* If we set the db error state here, the higher-level exec() call
175             replaces it with its own, so we have no way of reporting the
176             exception message except the console. We must not propagate
177             exceptions through the C API. */
178        }
179        return rc;
180      };
181      let pFunc, rc;
182      try{
183        pFunc = wasm.installFunction("ipipp", cbwrap);
184        rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
185      }catch(e){
186        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
187                                        "Error running exec(): "+e.message);
188      }finally{
189        if(pFunc) wasm.uninstallFunction(pFunc);
190      }
191      return rc;
192    };
193  }/*sqlite3_exec() proxy*/;
194
195  if(1){/* Special-case handling of sqlite3_create_function_v2()
196           and sqlite3_create_window_function() */
197    const sqlite3CreateFunction = wasm.xWrap(
198      "sqlite3_create_function_v2", "int",
199      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
200       "int"/*eTextRep*/, "*"/*pApp*/,
201       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/]
202    );
203    const sqlite3CreateWindowFunction = wasm.xWrap(
204      "sqlite3_create_window_function", "int",
205      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
206       "int"/*eTextRep*/, "*"/*pApp*/,
207       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/,
208       "*"/*xInverse*/, "*"/*xDestroy*/]
209    );
210    const __setResult = function(pCx, val){
211      switch(typeof val) {
212          case 'boolean':
213            capi.sqlite3_result_int(pCx, val ? 1 : 0);
214            break;
215          case 'number': {
216            (util.isInt32(val)
217             ? capi.sqlite3_result_int
218             : capi.sqlite3_result_double)(pCx, val);
219            break;
220          }
221          case 'string':
222            capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
223            break;
224          case 'object':
225            if(null===val/*yes, typeof null === 'object'*/) {
226              capi.sqlite3_result_null(pCx);
227              break;
228            }else if(util.isBindableTypedArray(val)){
229              const pBlob = wasm.allocFromTypedArray(val);
230              capi.sqlite3_result_blob(
231                pCx, pBlob, val.byteLength,
232                wasm.exports[sqlite3.config.deallocExportName]
233              );
234              break;
235            }
236            // else fall through
237          default:
238            toss3("Don't not how to handle this UDF result value:",val);
239      };
240    }/*__setResult()*/;
241    const __extractArgs = function(argc, pArgv){
242      let i, pVal, valType, arg;
243      const tgt = [];
244      for(i = 0; i < argc; ++i){
245        pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
246        /**
247           Curiously: despite ostensibly requiring 8-byte
248           alignment, the pArgv array is parcelled into chunks of
249           4 bytes (1 pointer each). The values those point to
250           have 8-byte alignment but the individual argv entries
251           do not.
252        */
253        valType = capi.sqlite3_value_type(pVal);
254        switch(valType){
255            case capi.SQLITE_INTEGER:
256            case capi.SQLITE_FLOAT:
257              arg = capi.sqlite3_value_double(pVal);
258              break;
259            case capi.SQLITE_TEXT:
260              arg = capi.sqlite3_value_text(pVal);
261              break;
262            case capi.SQLITE_BLOB:{
263              const n = capi.sqlite3_value_bytes(pVal);
264              const pBlob = capi.sqlite3_value_blob(pVal);
265              arg = new Uint8Array(n);
266              let i;
267              const heap = n ? wasm.heap8() : false;
268              for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
269              break;
270            }
271            case capi.SQLITE_NULL:
272              arg = null; break;
273            default:
274              toss3("Unhandled sqlite3_value_type()",valType,
275                    "is possibly indicative of incorrect",
276                    "pointer size assumption.");
277        }
278        tgt.push(arg);
279      }
280      return tgt;
281    }/*__extractArgs()*/;
282
283    const __setCxErr = (pCx, e)=>{
284      if(e instanceof capi.WasmAllocError){
285        capi.sqlite3_result_error_nomem(pCx);
286      }else{
287        capi.sqlite3_result_error(pCx, e.message, -1);
288      }
289    };
290
291    /* TODO: pass on the pCx pointer to all callbacks. This requires
292       fixing test code and updating oodles of docs. Once that is in place,
293       export sqlite3_aggregate_context().
294    */
295
296    const __xFunc = function(callback){
297      return function(pCx, argc, pArgv){
298        try{ __setResult(pCx, callback(...__extractArgs(argc, pArgv))) }
299        catch(e){ __setCxErr(pCx, e) }
300      };
301    };
302
303    const __xInverseAndStep = function(callback){
304      return function(pCx, argc, pArgv){
305        try{ callback(...__extractArgs(argc, pArgv)) }
306        catch(e){ __setCxErr(pCx, e) }
307      };
308    };
309
310    const __xFinalAndValue = function(callback){
311      return function(pCx){
312        try{ __setResult(pCx, callback()) }
313        catch(e){ __setCxErr(pCx, e) }
314      };
315    };
316
317    const __xDestroy = function(callback){
318      return function(pVoid){
319        try{ callback(pVoid) }
320        catch(e){ console.error("UDF xDestroy method threw:",e) }
321      };
322    };
323
324    const __xMap = Object.assign(Object.create(null), {
325      xFunc:    {sig:'v(pip)', f:__xFunc},
326      xStep:    {sig:'v(pip)', f:__xInverseAndStep},
327      xInverse: {sig:'v(pip)', f:__xInverseAndStep},
328      xFinal:   {sig:'v(p)',   f:__xFinalAndValue},
329      xValue:   {sig:'v(p)',   f:__xFinalAndValue},
330      xDestroy: {sig:'v(p)',   f:__xDestroy}
331    });
332
333    const __xWrapFuncs = function(theFuncs, tgtUninst){
334      const rc = []
335      let k;
336      for(k in theFuncs){
337        let fArg = theFuncs[k];
338        if('function'===typeof fArg){
339          const w = __xMap[k];
340          fArg = wasm.installFunction(w.sig, w.f(fArg));
341          tgtUninst.push(fArg);
342        }
343        rc.push(fArg);
344      }
345      return rc;
346    };
347
348    /* Documented in the api object's initializer. */
349    capi.sqlite3_create_function_v2 = function f(
350      pDb, funcName, nArg, eTextRep, pApp,
351      xFunc,   //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
352      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
353      xFinal,  //void (*xFinal)(sqlite3_context*)
354      xDestroy //void (*xDestroy)(void*)
355    ){
356      if(f.length!==arguments.length){
357        return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length);
358      }
359      /* Wrap the callbacks in a WASM-bound functions... */
360      const wasm = capi.wasm;
361      const uninstall = [/*funcs to uninstall on error*/];
362      let rc;
363      try{
364        const funcArgs =  __xWrapFuncs({xFunc, xStep, xFinal, xDestroy},
365                                       uninstall);
366        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
367                                   pApp, ...funcArgs);
368      }catch(e){
369        console.error("sqlite3_create_function_v2() setup threw:",e);
370        for(let v of uninstall){
371          wasm.uninstallFunction(v);
372        }
373        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
374                                        "Creation of UDF threw: "+e.message);
375      }
376      return rc;
377    };
378
379    capi.sqlite3_create_function = function f(
380      pDb, funcName, nArg, eTextRep, pApp,
381      xFunc, xStep, xFinal
382    ){
383      return (f.length===arguments.length)
384        ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
385                                          pApp, xFunc, xStep, xFinal, 0)
386        : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length);
387    };
388
389    /* Documented in the api object's initializer. */
390    capi.sqlite3_create_window_function = function f(
391      pDb, funcName, nArg, eTextRep, pApp,
392      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
393      xFinal,  //void (*xFinal)(sqlite3_context*)
394      xValue,  //void (*xFinal)(sqlite3_context*)
395      xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**)
396      xDestroy //void (*xDestroy)(void*)
397    ){
398      if(f.length!==arguments.length){
399        return __dbArgcMismatch(pDb,"sqlite3_create_window_function",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({xStep, xFinal, xValue, xInverse, 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  }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
422
423  if(1){/* Special-case handling of sqlite3_prepare_v2() and
424           sqlite3_prepare_v3() */
425    /**
426       Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
427    */
428    const __prepare = Object.create(null);
429    /**
430       This binding expects a JS string as its 2nd argument and
431       null as its final argument. In order to compile multiple
432       statements from a single string, the "full" impl (see
433       below) must be used.
434    */
435    __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
436                                 "int", ["sqlite3*", "string",
437                                         "int"/*ignored for this impl!*/,
438                                         "int", "**",
439                                         "**"/*MUST be 0 or null or undefined!*/]);
440    /**
441       Impl which requires that the 2nd argument be a pointer
442       to the SQL string, instead of being converted to a
443       string. This variant is necessary for cases where we
444       require a non-NULL value for the final argument
445       (exec()'ing multiple statements from one input
446       string). For simpler cases, where only the first
447       statement in the SQL string is required, the wrapper
448       named sqlite3_prepare_v2() is sufficient and easier to
449       use because it doesn't require dealing with pointers.
450    */
451    __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
452                                "int", ["sqlite3*", "*", "int", "int",
453                                        "**", "**"]);
454
455    /* Documented in the api object's initializer. */
456    capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
457      if(f.length!==arguments.length){
458        return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
459      }
460      const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
461      switch(typeof xSql){
462          case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
463          case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
464          default:
465            return util.sqlite3_wasm_db_error(
466              pDb, capi.SQLITE_MISUSE,
467              "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
468            );
469      }
470    };
471
472    /* Documented in the api object's initializer. */
473    capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
474      return (f.length===arguments.length)
475        ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
476        : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length);
477    };
478  }/*sqlite3_prepare_v2/v3()*/;
479
480  if(1){// Extend wasm.pstack, now that the wasm utils are installed
481    /**
482       Allocates n chunks, each sz bytes, as a single memory block and
483       returns the addresses as an array of n element, each holding
484       the address of one chunk.
485
486       Throws a WasmAllocError if allocation fails.
487
488       Example:
489
490       ```
491       const [p1, p2, p3] = wasm.pstack.allocChunks(3,4);
492       ```
493    */
494    wasm.pstack.allocChunks = (n,sz)=>{
495      const mem = wasm.pstack.alloc(n * sz);
496      const rc = [];
497      let i = 0, offset = 0;
498      for(; i < n; offset = (sz * ++i)){
499        rc.push(mem + offset);
500      }
501      return rc;
502    };
503
504    /**
505       A convenience wrapper for allocChunks() which sizes each chunks
506       as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
507       safePtrSize is falsy).
508
509       How it returns its result differs depending on its first
510       argument: if it's 1, it returns a single pointer value. If it's
511       more than 1, it returns the same as allocChunks().
512
513       When a returned pointers will refer to a 64-bit value, e.g. a
514       double or int64, and that value must be written or fetched,
515       e.g. using wasm.setMemValue() or wasm.getMemValue(), it is
516       important that the pointer in question be aligned to an 8-byte
517       boundary or else it will not be fetched or written properly and
518       will corrupt or read neighboring memory.
519
520       However, when all pointers involved point to "small" data, it
521       is safe to pass a falsy value to save to memory.
522    */
523    wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{
524      return 1===n
525        ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof)
526        : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof);
527    };
528  }/*wasm.pstack filler*/
529
530  /**
531     Install JS<->C struct bindings for the non-opaque struct types we
532     need... */
533  sqlite3.StructBinder = self.Jaccwabyt({
534    heap: 0 ? wasm.memory : wasm.heap8u,
535    alloc: wasm.alloc,
536    dealloc: wasm.dealloc,
537    functionTable: wasm.functionTable,
538    bigIntEnabled: wasm.bigIntEnabled,
539    memberPrefix: '$'
540  });
541  delete self.Jaccwabyt;
542
543  {/* Import C-level constants and structs... */
544    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
545    if(!cJson){
546      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
547           "static buffer size!");
548    }
549    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
550    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
551    for(const t of ['access', 'blobFinalizers', 'dataTypes',
552                    'encodings', 'fcntl', 'flock', 'ioCap',
553                    'openFlags', 'prepareFlags', 'resultCodes',
554                    'serialize', 'syncFlags', 'udfFlags',
555                    'version'
556                   ]){
557      for(const e of Object.entries(wasm.ctype[t])){
558        // ^^^ [k,v] there triggers a buggy code transormation via one
559        // of the Emscripten-driven optimizers.
560        capi[e[0]] = e[1];
561      }
562    }
563    const __rcMap = Object.create(null);
564    for(const t of ['resultCodes']){
565      for(const e of Object.entries(wasm.ctype[t])){
566        __rcMap[e[1]] = e[0];
567      }
568    }
569    /**
570       For the given integer, returns the SQLITE_xxx result code as a
571       string, or undefined if no such mapping is found.
572    */
573    capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc];
574    /* Bind all registered C-side structs... */
575    for(const s of wasm.ctype.structs){
576      capi[s.name] = sqlite3.StructBinder(s);
577    }
578  }/*end C constant imports*/
579
580  sqlite3.version = Object.assign(Object.create(null),{
581    library: sqlite3.capi.sqlite3_libversion(),
582    sourceId: sqlite3.capi.sqlite3_sourceid()
583  });
584});
585
586