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_prepare_v2() and
196      sqlite3_prepare_v3() */
197    /**
198       Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
199    */
200    const __prepare = Object.create(null);
201    /**
202       This binding expects a JS string as its 2nd argument and
203       null as its final argument. In order to compile multiple
204       statements from a single string, the "full" impl (see
205       below) must be used.
206    */
207    __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
208                                 "int", ["sqlite3*", "string",
209                                         "int"/*ignored for this impl!*/,
210                                         "int", "**",
211                                         "**"/*MUST be 0 or null or undefined!*/]);
212    /**
213       Impl which requires that the 2nd argument be a pointer
214       to the SQL string, instead of being converted to a
215       string. This variant is necessary for cases where we
216       require a non-NULL value for the final argument
217       (exec()'ing multiple statements from one input
218       string). For simpler cases, where only the first
219       statement in the SQL string is required, the wrapper
220       named sqlite3_prepare_v2() is sufficient and easier to
221       use because it doesn't require dealing with pointers.
222    */
223    __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
224                                "int", ["sqlite3*", "*", "int", "int",
225                                        "**", "**"]);
226
227    /* Documented in the api object's initializer. */
228    capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
229      if(6!==arguments.length){
230        return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",6);
231      }
232      const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
233      switch(typeof xSql){
234          case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
235          case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
236          default:
237            return util.sqlite3_wasm_db_error(
238              pDb, capi.SQLITE_MISUSE,
239              "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
240            );
241      }
242    };
243
244    /* Documented in the api object's initializer. */
245    capi.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){
246      return (5==arguments.length)
247        ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
248        : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",5);
249    };
250  }/*sqlite3_prepare_v2/v3()*/;
251
252  if(1){// Extend wasm.pstack, now that the wasm utils are installed
253    /**
254       Allocates n chunks, each sz bytes, as a single memory block and
255       returns the addresses as an array of n element, each holding
256       the address of one chunk.
257
258       Throws a WasmAllocError if allocation fails.
259
260       Example:
261
262       ```
263       const [p1, p2, p3] = wasm.pstack.allocChunks(3,4);
264       ```
265    */
266    wasm.pstack.allocChunks = (n,sz)=>{
267      const mem = wasm.pstack.alloc(n * sz);
268      const rc = [];
269      let i = 0, offset = 0;
270      for(; i < n; offset = (sz * ++i)){
271        rc.push(mem + offset);
272      }
273      return rc;
274    };
275
276    /**
277       A convenience wrapper for allocChunks() which sizes each chunks
278       as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
279       safePtrSize is truthy).
280
281       How it returns its result differs depending on its first
282       argument: if it's 1, it returns a single pointer value. If it's
283       more than 1, it returns the same as allocChunks().
284
285       When one of the pointers refers to a 64-bit value, e.g. a
286       double or int64, and that value must be written or fetch,
287       e.g. using wasm.setMemValue() or wasm.getMemValue(), it is
288       important that the pointer in question be aligned to an 8-byte
289       boundary or else it will not be fetched or written properly and
290       will corrupt or read neighboring memory.
291
292       However, when all pointers involved are "small", it is safe to
293       pass a falsy value to save to memory.
294    */
295    wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{
296      return 1===n
297        ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof)
298        : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof);
299    };
300  }/*wasm.pstack filler*/
301
302  /**
303     Install JS<->C struct bindings for the non-opaque struct types we
304     need... */
305  sqlite3.StructBinder = self.Jaccwabyt({
306    heap: 0 ? wasm.memory : wasm.heap8u,
307    alloc: wasm.alloc,
308    dealloc: wasm.dealloc,
309    functionTable: wasm.functionTable,
310    bigIntEnabled: wasm.bigIntEnabled,
311    memberPrefix: '$'
312  });
313  delete self.Jaccwabyt;
314
315  {/* Import C-level constants and structs... */
316    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
317    if(!cJson){
318      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
319           "static buffer size!");
320    }
321    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
322    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
323    for(const t of ['access', 'blobFinalizers', 'dataTypes',
324                    'encodings', 'fcntl', 'flock', 'ioCap',
325                    'openFlags', 'prepareFlags', 'resultCodes',
326                    'serialize', 'syncFlags', 'udfFlags',
327                    'version'
328                   ]){
329      for(const e of Object.entries(wasm.ctype[t])){
330        // ^^^ [k,v] there triggers a buggy code transormation via one
331        // of the Emscripten-driven optimizers.
332        capi[e[0]] = e[1];
333      }
334    }
335    const __rcMap = Object.create(null);
336    for(const t of ['resultCodes']){
337      for(const e of Object.entries(wasm.ctype[t])){
338        __rcMap[e[1]] = e[0];
339      }
340    }
341    /**
342       For the given integer, returns the SQLITE_xxx result code as a
343       string, or undefined if no such mapping is found.
344    */
345    capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc];
346    /* Bind all registered C-side structs... */
347    for(const s of wasm.ctype.structs){
348      capi[s.name] = sqlite3.StructBinder(s);
349    }
350  }/*end C constant imports*/
351
352  sqlite3.version = Object.assign(Object.create(null),{
353    library: sqlite3.capi.sqlite3_libversion(),
354    sourceId: sqlite3.capi.sqlite3_sourceid()
355  });
356});
357
358