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
59    /**
60       Populate api object with sqlite3_...() by binding the "raw" wasm
61       exports into type-converting proxies using wasm.xWrap().
62    */
63    for(const e of wasm.bindingSignatures){
64      capi[e[0]] = wasm.xWrap.apply(null, e);
65    }
66    for(const e of wasm.bindingSignatures.wasm){
67      capi.wasm[e[0]] = wasm.xWrap.apply(null, e);
68    }
69
70    /* For C API functions which cannot work properly unless
71       wasm.bigIntEnabled is true, install a bogus impl which
72       throws if called when bigIntEnabled is false. */
73    const fI64Disabled = function(fname){
74      return ()=>toss(fname+"() disabled due to lack",
75                      "of BigInt support in this build.");
76    };
77    for(const e of wasm.bindingSignatures.int64){
78      capi[e[0]] = wasm.bigIntEnabled
79        ? wasm.xWrap.apply(null, e)
80        : fI64Disabled(e[0]);
81    }
82
83    if(wasm.exports.sqlite3_wasm_db_error){
84      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
85        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
86      );
87    }else{
88      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
89        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
90        return errCode;
91      };
92    }
93
94    /**
95       When registering a VFS and its related components it may be
96       necessary to ensure that JS keeps a reference to them to keep
97       them from getting garbage collected. Simply pass each such value
98       to this function and a reference will be held to it for the life
99       of the app.
100    */
101    capi.sqlite3_vfs_register.addReference = function f(...args){
102      if(!f._) f._ = [];
103      f._.push(...args);
104    };
105
106  }/*xWrap() bindings*/;
107
108  /**
109     Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
110  */
111  const __prepare = Object.create(null);
112  /**
113     This binding expects a JS string as its 2nd argument and
114     null as its final argument. In order to compile multiple
115     statements from a single string, the "full" impl (see
116     below) must be used.
117  */
118  __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
119                               "int", ["sqlite3*", "string",
120                                       "int"/*ignored for this impl!*/,
121                                       "int", "**",
122                                       "**"/*MUST be 0 or null or undefined!*/]);
123  /**
124     Impl which requires that the 2nd argument be a pointer
125     to the SQL string, instead of being converted to a
126     string. This variant is necessary for cases where we
127     require a non-NULL value for the final argument
128     (exec()'ing multiple statements from one input
129     string). For simpler cases, where only the first
130     statement in the SQL string is required, the wrapper
131     named sqlite3_prepare_v2() is sufficient and easier to
132     use because it doesn't require dealing with pointers.
133  */
134  __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
135                              "int", ["sqlite3*", "*", "int", "int",
136                                      "**", "**"]);
137
138  /* Documented in the api object's initializer. */
139  capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
140    if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql);
141    switch(typeof sql){
142        case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null);
143        case 'number': return __prepare.full(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail);
144        default:
145          return util.sqlite3_wasm_db_error(
146            pDb, capi.SQLITE_MISUSE,
147            "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
148          );
149    }
150  };
151
152  capi.sqlite3_prepare_v2 =
153    (pDb, sql, sqlLen, ppStmt, pzTail)=>capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail);
154
155  /**
156     Install JS<->C struct bindings for the non-opaque struct types we
157     need... */
158  sqlite3.StructBinder = self.Jaccwabyt({
159    heap: 0 ? wasm.memory : wasm.heap8u,
160    alloc: wasm.alloc,
161    dealloc: wasm.dealloc,
162    functionTable: wasm.functionTable,
163    bigIntEnabled: wasm.bigIntEnabled,
164    memberPrefix: '$'
165  });
166  delete self.Jaccwabyt;
167
168  {/* Import C-level constants and structs... */
169    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
170    if(!cJson){
171      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
172           "static buffer size!");
173    }
174    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
175    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
176    for(const t of ['access', 'blobFinalizers', 'dataTypes',
177                    'encodings', 'flock', 'ioCap',
178                    'openFlags', 'prepareFlags', 'resultCodes',
179                    'syncFlags', 'udfFlags', 'version'
180                   ]){
181      for(const [k,v] of Object.entries(wasm.ctype[t])){
182        capi[k] = v;
183      }
184    }
185    /* Bind all registered C-side structs... */
186    for(const s of wasm.ctype.structs){
187      capi[s.name] = sqlite3.StructBinder(s);
188    }
189  }
190});
191