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