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