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
31       the `pointer` arg handler to check for a `pointer`
32       property. This can be used to permit, e.g., passing
33       an SQLite3.DB instance to a C-style sqlite3_xxx function
34       which takes an `sqlite3*` argument.
35    */
36    const oldP = wasm.xWrap.argAdapter('pointer');
37    const adapter = function(v){
38      if(v && 'object'===typeof 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 oldP(v);
44    };
45    wasm.xWrap.argAdapter('.pointer', adapter);
46  } /* ".pointer" xWrap() argument adapter */
47
48  // WhWasmUtil.xWrap() bindings...
49  {
50    /**
51       Add some descriptive xWrap() aliases for '*' intended to
52       (A) initially improve readability/correctness of capi.signatures
53       and (B) eventually perhaps provide some sort of type-safety
54       in their conversions.
55    */
56    const aPtr = wasm.xWrap.argAdapter('*');
57    wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
58    wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
59
60    /**
61       Populate api object with sqlite3_...() by binding the "raw" wasm
62       exports into type-converting proxies using wasm.xWrap().
63    */
64    for(const e of wasm.bindingSignatures){
65      capi[e[0]] = wasm.xWrap.apply(null, e);
66    }
67    for(const e of wasm.bindingSignatures.wasm){
68      capi.wasm[e[0]] = wasm.xWrap.apply(null, e);
69    }
70
71    /* For C API functions which cannot work properly unless
72       wasm.bigIntEnabled is true, install a bogus impl which
73       throws if called when bigIntEnabled is false. */
74    const fI64Disabled = function(fname){
75      return ()=>toss(fname+"() disabled due to lack",
76                      "of BigInt support in this build.");
77    };
78    for(const e of wasm.bindingSignatures.int64){
79      capi[e[0]] = wasm.bigIntEnabled
80        ? wasm.xWrap.apply(null, e)
81        : fI64Disabled(e[0]);
82    }
83
84    if(wasm.exports.sqlite3_wasm_db_error){
85      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
86        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
87      );
88    }else{
89      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
90        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
91        return errCode;
92      };
93    }
94
95    /**
96       When registering a VFS and its related components it may be
97       necessary to ensure that JS keeps a reference to them to keep
98       them from getting garbage collected. Simply pass each such value
99       to this function and a reference will be held to it for the life
100       of the app.
101    */
102    capi.sqlite3_vfs_register.addReference = function f(...args){
103      if(!f._) f._ = [];
104      f._.push(...args);
105    };
106
107  }/*xWrap() bindings*/;
108
109  /**
110     Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
111  */
112  const __prepare = Object.create(null);
113  /**
114     This binding expects a JS string as its 2nd argument and
115     null as its final argument. In order to compile multiple
116     statements from a single string, the "full" impl (see
117     below) must be used.
118  */
119  __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
120                               "int", ["sqlite3*", "string",
121                                       "int"/*ignored for this impl!*/,
122                                       "int", "**",
123                                       "**"/*MUST be 0 or null or undefined!*/]);
124  /**
125     Impl which requires that the 2nd argument be a pointer
126     to the SQL string, instead of being converted to a
127     string. This variant is necessary for cases where we
128     require a non-NULL value for the final argument
129     (exec()'ing multiple statements from one input
130     string). For simpler cases, where only the first
131     statement in the SQL string is required, the wrapper
132     named sqlite3_prepare_v2() is sufficient and easier to
133     use because it doesn't require dealing with pointers.
134  */
135  __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
136                              "int", ["sqlite3*", "*", "int", "int",
137                                      "**", "**"]);
138
139  /* Documented in the api object's initializer. */
140  capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
141    if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql);
142    switch(typeof sql){
143        case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null);
144        case 'number': return __prepare.full(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail);
145        default:
146          return util.sqlite3_wasm_db_error(
147            pDb, capi.SQLITE_MISUSE,
148            "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
149          );
150    }
151  };
152
153  capi.sqlite3_prepare_v2 =
154    (pDb, sql, sqlLen, ppStmt, pzTail)=>capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail);
155
156  /**
157     Install JS<->C struct bindings for the non-opaque struct types we
158     need... */
159  sqlite3.StructBinder = self.Jaccwabyt({
160    heap: 0 ? wasm.memory : wasm.heap8u,
161    alloc: wasm.alloc,
162    dealloc: wasm.dealloc,
163    functionTable: wasm.functionTable,
164    bigIntEnabled: wasm.bigIntEnabled,
165    memberPrefix: '$'
166  });
167  delete self.Jaccwabyt;
168
169  {/* Import C-level constants and structs... */
170    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
171    if(!cJson){
172      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
173           "static buffer size!");
174    }
175    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
176    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
177    for(const t of ['access', 'blobFinalizers', 'dataTypes',
178                    'encodings', 'fcntl', 'flock', 'ioCap',
179                    'openFlags', 'prepareFlags', 'resultCodes',
180                    'syncFlags', 'udfFlags', 'version'
181                   ]){
182      for(const e of Object.entries(wasm.ctype[t])){
183        // ^^^ [k,v] there triggers a buggy code transormation via one
184        // of the Emscripten-driven optimizers.
185        capi[e[0]] = e[1];
186      }
187    }
188    const __rcMap = Object.create(null);
189    for(const t of ['resultCodes']){
190      for(const e of Object.entries(wasm.ctype[t])){
191        __rcMap[e[1]] = e[0];
192      }
193    }
194    /**
195       For the given integer, returns the SQLITE_xxx result code as a
196       string, or undefined if no such mapping is found.
197    */
198    capi.sqlite3_wasm_rc_str = (rc)=>__rcMap[rc];
199    /* Bind all registered C-side structs... */
200    for(const s of wasm.ctype.structs){
201      capi[s.name] = sqlite3.StructBinder(s);
202    }
203  }/*end C constant imports*/
204});
205