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*/
19(function(self){
20  'use strict';
21  const toss = (...args)=>{throw new Error(args.join(' '))};
22
23  self.sqlite3 = self.sqlite3ApiBootstrap({
24    Module: Module /* ==> Emscripten-style Module object. Currently
25                      needs to be exposed here for test code. NOT part
26                      of the public API. */,
27    exports: Module['asm'],
28    memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */,
29    bigIntEnabled: !!self.BigInt64Array,
30    allocExportName: 'malloc',
31    deallocExportName: 'free'
32  });
33  delete self.sqlite3ApiBootstrap;
34
35  const sqlite3 = self.sqlite3;
36  const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
37  self.WhWasmUtilInstaller(capi.wasm);
38  delete self.WhWasmUtilInstaller;
39
40  if(0){
41    /*  "The problem" is that the following isn't type-safe.
42        OTOH, nothing about WASM pointers is. */
43    /**
44       Add the `.pointer` xWrap() signature entry to extend
45       the `pointer` arg handler to check for a `pointer`
46       property. This can be used to permit, e.g., passing
47       an SQLite3.DB instance to a C-style sqlite3_xxx function
48       which takes an `sqlite3*` argument.
49    */
50    const oldP = wasm.xWrap.argAdapter('pointer');
51    const adapter = function(v){
52      if(v && 'object'===typeof v && v.constructor){
53        const x = v.pointer;
54        if(Number.isInteger(x)) return x;
55        else toss("Invalid (object) type for pointer-type argument.");
56      }
57      return oldP(v);
58    };
59    wasm.xWrap.argAdapter('.pointer', adapter);
60  }
61
62  // WhWasmUtil.xWrap() bindings...
63  {
64    /**
65       Add some descriptive xWrap() aliases for '*' intended to
66       (A) initially improve readability/correctness of capi.signatures
67       and (B) eventually perhaps provide some sort of type-safety
68       in their conversions.
69    */
70    const aPtr = wasm.xWrap.argAdapter('*');
71    wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
72
73    /**
74       Populate api object with sqlite3_...() by binding the "raw" wasm
75       exports into type-converting proxies using wasm.xWrap().
76    */
77    for(const e of wasm.bindingSignatures){
78      capi[e[0]] = wasm.xWrap.apply(null, e);
79    }
80
81    /* For functions which cannot work properly unless
82       wasm.bigIntEnabled is true, install a bogus impl which
83       throws if called when bigIntEnabled is false. */
84    const fI64Disabled = function(fname){
85      return ()=>toss(fname+"() disabled due to lack",
86                      "of BigInt support in this build.");
87    };
88    for(const e of wasm.bindingSignatures.int64){
89      capi[e[0]] = wasm.bigIntEnabled
90        ? wasm.xWrap.apply(null, e)
91        : fI64Disabled(e[0]);
92    }
93
94    if(wasm.exports.sqlite3_wasm_db_error){
95      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
96        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
97      );
98    }else{
99      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
100        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
101        return errCode;
102      };
103    }
104
105    /**
106       When registering a VFS and its related components it may be
107       necessary to ensure that JS keeps a reference to them to keep
108       them from getting garbage collected. Simply pass each such value
109       to this function and a reference will be held to it for the life
110       of the app.
111    */
112    capi.sqlite3_vfs_register.addReference = function f(...args){
113      if(!f._) f._ = [];
114      f._.push(...args);
115    };
116
117  }/*xWrap() bindings*/;
118
119  /**
120     Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
121  */
122  const __prepare = Object.create(null);
123  /**
124     This binding expects a JS string as its 2nd argument and
125     null as its final argument. In order to compile multiple
126     statements from a single string, the "full" impl (see
127     below) must be used.
128  */
129  __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
130                               "int", ["sqlite3*", "string",
131                                       "int"/*MUST always be negative*/,
132                                       "int", "**",
133                                       "**"/*MUST be 0 or null or undefined!*/]);
134  /**
135     Impl which requires that the 2nd argument be a pointer
136     to the SQL string, instead of being converted to a
137     string. This variant is necessary for cases where we
138     require a non-NULL value for the final argument
139     (exec()'ing multiple statements from one input
140     string). For simpler cases, where only the first
141     statement in the SQL string is required, the wrapper
142     named sqlite3_prepare_v2() is sufficient and easier to
143     use because it doesn't require dealing with pointers.
144  */
145  __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
146                              "int", ["sqlite3*", "*", "int", "int",
147                                      "**", "**"]);
148
149  /* Documented in the api object's initializer. */
150  capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
151    /* 2022-07-08: xWrap() 'string' arg handling may be able do this
152       special-case handling for us. It needs to be tested. Or maybe
153       not: we always want to treat pzTail as null when passed a
154       non-pointer SQL string and the argument adapters don't have
155       enough state to know that. Maybe they could/should, by passing
156       the currently-collected args as an array as the 2nd arg to the
157       argument adapters? Or maybe we collect all args in an array,
158       pass that to an optional post-args-collected callback, and give
159       it a chance to manipulate the args before we pass them on? */
160    if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql);
161    switch(typeof sql){
162        case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null);
163        case 'number': return __prepare.full(pDb, sql, sqlLen||-1, prepFlags, ppStmt, pzTail);
164        default:
165          return util.sqlite3_wasm_db_error(
166            pDb, capi.SQLITE_MISUSE,
167            "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
168          );
169    }
170  };
171
172  capi.sqlite3_prepare_v2 =
173    (pDb, sql, sqlLen, ppStmt, pzTail)=>capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail);
174
175  /**
176     Install JS<->C struct bindings for the non-opaque struct types we
177     need... */
178  sqlite3.StructBinder = self.Jaccwabyt({
179    heap: 0 ? wasm.memory : wasm.heap8u,
180    alloc: wasm.alloc,
181    dealloc: wasm.dealloc,
182    functionTable: wasm.functionTable,
183    bigIntEnabled: wasm.bigIntEnabled,
184    memberPrefix: '$'
185  });
186  delete self.Jaccwabyt;
187
188  {/* Import C-level constants and structs... */
189    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
190    if(!cJson){
191      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
192           "static buffer size!");
193    }
194    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
195    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
196    for(const t of ['access', 'blobFinalizers', 'dataTypes',
197                    'encodings', 'flock', 'ioCap',
198                    'openFlags', 'prepareFlags', 'resultCodes',
199                    'syncFlags', 'udfFlags', 'version'
200                   ]){
201      for(const [k,v] of Object.entries(wasm.ctype[t])){
202        capi[k] = v;
203      }
204    }
205    /* Bind all registered C-side structs... */
206    for(const s of wasm.ctype.structs){
207      capi[s.name] = sqlite3.StructBinder(s);
208    }
209  }
210
211})(self);
212