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(pDb, sql, callback, pVoid, pErrMsg){
153      if(5!==arguments.length){
154        return __dbArgcMismatch(pDb,"sqlite3_exec",5);
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    const sqlite3CreateFunction = wasm.xWrap(
197      "sqlite3_create_function_v2", "int",
198      ["sqlite3*", "string", "int", "int", "*",
199       "*", "*", "*", "*"]
200    );
201    const __setResult = function(pCx, val){
202      switch(typeof val) {
203          case 'boolean':
204            capi.sqlite3_result_int(pCx, val ? 1 : 0);
205            break;
206          case 'number': {
207            (util.isInt32(val)
208             ? capi.sqlite3_result_int
209             : capi.sqlite3_result_double)(pCx, val);
210            break;
211          }
212          case 'string':
213            capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
214            break;
215          case 'object':
216            if(null===val) {
217              capi.sqlite3_result_null(pCx);
218              break;
219            }else if(util.isBindableTypedArray(val)){
220              const pBlob = wasm.allocFromTypedArray(val);
221              capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
222                                       capi.SQLITE_TRANSIENT);
223              wasm.dealloc(pBlob);
224              break;
225            }
226            // else fall through
227          default:
228            toss3("Don't not how to handle this UDF result value:",val);
229      };
230    }/*__setResult()*/;
231    const __extractArgs = function(argc, pArgv){
232      let i, pVal, valType, arg;
233      const tgt = [];
234      for(i = 0; i < argc; ++i){
235        pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
236        /**
237           Curiously: despite ostensibly requiring 8-byte
238           alignment, the pArgv array is parcelled into chunks of
239           4 bytes (1 pointer each). The values those point to
240           have 8-byte alignment but the individual argv entries
241           do not.
242        */
243        valType = capi.sqlite3_value_type(pVal);
244        switch(valType){
245            case capi.SQLITE_INTEGER:
246            case capi.SQLITE_FLOAT:
247              arg = capi.sqlite3_value_double(pVal);
248              break;
249            case capi.SQLITE_TEXT:
250              arg = capi.sqlite3_value_text(pVal);
251              break;
252            case capi.SQLITE_BLOB:{
253              const n = capi.sqlite3_value_bytes(pVal);
254              const pBlob = capi.sqlite3_value_blob(pVal);
255              arg = new Uint8Array(n);
256              let i;
257              const heap = n ? wasm.heap8() : false;
258              for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
259              break;
260            }
261            case capi.SQLITE_NULL:
262              arg = null; break;
263            default:
264              toss3("Unhandled sqlite3_value_type()",valType,
265                    "is possibly indicative of incorrect",
266                    "pointer size assumption.");
267        }
268        tgt.push(arg);
269      }
270      return tgt;
271    }/*__extractArgs()*/;
272
273    const __setCxErr = (pCx, e)=>{
274      if(e instanceof capi.WasmAllocError){
275        capi.sqlite3_result_error_nomem(pCx);
276      }else{
277        capi.sqlite3_result_error(pCx, e.message, -1);
278      }
279    };
280
281    const __xFunc = function(callback){
282      return function(pCx, argc, pArgv){
283        try{__setResult(pCx, callback(...__extractArgs(argc, pArgv)))}
284        catch(e){ __setCxErr(pCx, e) }
285      };
286    };
287
288    const __xStep = function(callback){
289      return function(pCx, argc, pArgv){
290        try{ callback(...__extractArgs(argc, pArgv)) }
291        catch(e){ __setCxErr(pCx, e) }
292      };
293    };
294
295    const __xFinal = function(callback){
296      return function(pCx){
297        try{ __setResult(pCx, callback()) }
298        catch(e){ __setCxErr(pCx, e) }
299      };
300    };
301
302    const __xDestroy = function(callback){
303      return function(pVoid){
304        try{ callback(pVoid) }
305        catch(e){
306          console.error("UDF xDestroy method threw:",e);
307        }
308      };
309    };
310    /* Documented in the api object's initializer. */
311    capi.sqlite3_create_function_v2 = function f(
312      pDb, funcName, nArg, eTextRep, pApp,
313      xFunc,   //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
314      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
315      xFinal,  //void (*xFinal)(sqlite3_context*)
316      xDestroy //void (*xDestroy)(void*)
317    ){
318      if(9!==arguments.length){
319        return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",9);
320      }
321      if(!f._sigs){
322        f._wrap = Object.assign(Object.create(null), {
323          xFunc:    {sig:'v(pip)', f:__xFunc},
324          xStep:    {sig:'v(pip)', f:__xStep},
325          xFinal:   {sig:'v(p)',   f:__xFinal},
326          xDestroy: {sig:'v(p)',   f:__xDestroy}
327        });
328      }
329      const callbacks = [];
330      /* Wrap the callbacks in a WASM-bound functions... */
331      const wasm = capi.wasm;
332      const funcArgs = [], uninstall = [/*funcs to uninstall on error*/],
333            theFuncs = {xFunc, xStep, xFinal, xDestroy};
334      let rc;
335      try{
336        let k;
337        for(k in theFuncs){
338          let fArg = theFuncs[k];
339          if('function'===typeof fArg){
340            const w = f._wrap[k];
341            fArg = wasm.installFunction(w.sig, w.f(fArg));
342            uninstall.push(fArg);
343          }
344          funcArgs.push(fArg);
345        }
346        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
347                                   pApp, ...funcArgs);
348      }catch(e){
349        console.error("sqlite3_create_function_v2() setup threw:",e);
350        for(let v of uninstall){
351          wasm.uninstallFunction(v);
352        }
353        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
354                                        "Creation of UDF threw: "+e.message);
355      }
356      return rc;
357    };
358
359    capi.sqlite3_create_function = function(
360      pDb, funcName, nArg, eTextRep, pApp,
361      xFunc, xStep, xFinal
362    ){
363      if(8!==arguments.length){
364        return __dbArgcMismatch(pDb,"sqlite3_create_function",8);
365      }
366      return capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
367                                             pApp, xFunc, xStep, xFinal, 0);
368
369    };
370  }/*sqlite3_create_function_v2() proxy*/;
371
372  if(1){/* Special-case handling of sqlite3_prepare_v2() and
373      sqlite3_prepare_v3() */
374    /**
375       Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
376    */
377    const __prepare = Object.create(null);
378    /**
379       This binding expects a JS string as its 2nd argument and
380       null as its final argument. In order to compile multiple
381       statements from a single string, the "full" impl (see
382       below) must be used.
383    */
384    __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
385                                 "int", ["sqlite3*", "string",
386                                         "int"/*ignored for this impl!*/,
387                                         "int", "**",
388                                         "**"/*MUST be 0 or null or undefined!*/]);
389    /**
390       Impl which requires that the 2nd argument be a pointer
391       to the SQL string, instead of being converted to a
392       string. This variant is necessary for cases where we
393       require a non-NULL value for the final argument
394       (exec()'ing multiple statements from one input
395       string). For simpler cases, where only the first
396       statement in the SQL string is required, the wrapper
397       named sqlite3_prepare_v2() is sufficient and easier to
398       use because it doesn't require dealing with pointers.
399    */
400    __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
401                                "int", ["sqlite3*", "*", "int", "int",
402                                        "**", "**"]);
403
404    /* Documented in the api object's initializer. */
405    capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
406      if(6!==arguments.length){
407        return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",6);
408      }
409      const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
410      switch(typeof xSql){
411          case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
412          case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
413          default:
414            return util.sqlite3_wasm_db_error(
415              pDb, capi.SQLITE_MISUSE,
416              "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
417            );
418      }
419    };
420
421    /* Documented in the api object's initializer. */
422    capi.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){
423      return (5==arguments.length)
424        ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
425        : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",5);
426    };
427  }/*sqlite3_prepare_v2/v3()*/;
428
429  if(1){// Extend wasm.pstack, now that the wasm utils are installed
430    /**
431       Allocates n chunks, each sz bytes, as a single memory block and
432       returns the addresses as an array of n element, each holding
433       the address of one chunk.
434
435       Throws a WasmAllocError if allocation fails.
436
437       Example:
438
439       ```
440       const [p1, p2, p3] = wasm.pstack.allocChunks(3,4);
441       ```
442    */
443    wasm.pstack.allocChunks = (n,sz)=>{
444      const mem = wasm.pstack.alloc(n * sz);
445      const rc = [];
446      let i = 0, offset = 0;
447      for(; i < n; offset = (sz * ++i)){
448        rc.push(mem + offset);
449      }
450      return rc;
451    };
452
453    /**
454       A convenience wrapper for allocChunks() which sizes each chunks
455       as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
456       safePtrSize is falsy).
457
458       How it returns its result differs depending on its first
459       argument: if it's 1, it returns a single pointer value. If it's
460       more than 1, it returns the same as allocChunks().
461
462       When a returned pointers will refer to a 64-bit value, e.g. a
463       double or int64, and that value must be written or fetched,
464       e.g. using wasm.setMemValue() or wasm.getMemValue(), it is
465       important that the pointer in question be aligned to an 8-byte
466       boundary or else it will not be fetched or written properly and
467       will corrupt or read neighboring memory.
468
469       However, when all pointers involved point to "small" data, it
470       is safe to pass a falsy value to save to memory.
471    */
472    wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{
473      return 1===n
474        ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof)
475        : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof);
476    };
477  }/*wasm.pstack filler*/
478
479  /**
480     Install JS<->C struct bindings for the non-opaque struct types we
481     need... */
482  sqlite3.StructBinder = self.Jaccwabyt({
483    heap: 0 ? wasm.memory : wasm.heap8u,
484    alloc: wasm.alloc,
485    dealloc: wasm.dealloc,
486    functionTable: wasm.functionTable,
487    bigIntEnabled: wasm.bigIntEnabled,
488    memberPrefix: '$'
489  });
490  delete self.Jaccwabyt;
491
492  {/* Import C-level constants and structs... */
493    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
494    if(!cJson){
495      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
496           "static buffer size!");
497    }
498    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
499    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
500    for(const t of ['access', 'blobFinalizers', 'dataTypes',
501                    'encodings', 'fcntl', 'flock', 'ioCap',
502                    'openFlags', 'prepareFlags', 'resultCodes',
503                    'serialize', 'syncFlags', 'udfFlags',
504                    'version'
505                   ]){
506      for(const e of Object.entries(wasm.ctype[t])){
507        // ^^^ [k,v] there triggers a buggy code transormation via one
508        // of the Emscripten-driven optimizers.
509        capi[e[0]] = e[1];
510      }
511    }
512    const __rcMap = Object.create(null);
513    for(const t of ['resultCodes']){
514      for(const e of Object.entries(wasm.ctype[t])){
515        __rcMap[e[1]] = e[0];
516      }
517    }
518    /**
519       For the given integer, returns the SQLITE_xxx result code as a
520       string, or undefined if no such mapping is found.
521    */
522    capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc];
523    /* Bind all registered C-side structs... */
524    for(const s of wasm.ctype.structs){
525      capi[s.name] = sqlite3.StructBinder(s);
526    }
527  }/*end C constant imports*/
528
529  sqlite3.version = Object.assign(Object.create(null),{
530    library: sqlite3.capi.sqlite3_libversion(),
531    sourceId: sqlite3.capi.sqlite3_sourceid()
532  });
533});
534
535