13961b263Sstephan/* 23961b263Sstephan 2022-07-22 33961b263Sstephan 43961b263Sstephan The author disclaims copyright to this source code. In place of a 53961b263Sstephan legal notice, here is a blessing: 63961b263Sstephan 73961b263Sstephan * May you do good and not evil. 83961b263Sstephan * May you find forgiveness for yourself and forgive others. 93961b263Sstephan * May you share freely, never taking more than you give. 103961b263Sstephan 113961b263Sstephan *********************************************************************** 123961b263Sstephan 133961b263Sstephan This file glues together disparate pieces of JS which are loaded in 143961b263Sstephan previous steps of the sqlite3-api.js bootstrapping process: 153961b263Sstephan sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It 163961b263Sstephan initializes the main API pieces so that the downstream components 173961b263Sstephan (e.g. sqlite3-api-oo1.js) have all that they need. 183961b263Sstephan*/ 19e3cd6760Sstephanself.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 203961b263Sstephan 'use strict'; 213961b263Sstephan const toss = (...args)=>{throw new Error(args.join(' '))}; 223961b263Sstephan const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util; 233961b263Sstephan self.WhWasmUtilInstaller(capi.wasm); 243961b263Sstephan delete self.WhWasmUtilInstaller; 253961b263Sstephan 263961b263Sstephan if(0){ 273961b263Sstephan /* "The problem" is that the following isn't type-safe. 283961b263Sstephan OTOH, nothing about WASM pointers is. */ 293961b263Sstephan /** 30d9801185Sstephan Add the `.pointer` xWrap() signature entry to extend the 31d9801185Sstephan `pointer` arg handler to check for a `pointer` property. This 32d9801185Sstephan can be used to permit, e.g., passing an sqlite3.oo1.DB instance 33d9801185Sstephan to a C-style sqlite3_xxx function which takes an `sqlite3*` 34d9801185Sstephan argument. 353961b263Sstephan */ 36e67a0f40Sstephan const xPointer = wasm.xWrap.argAdapter('pointer'); 373961b263Sstephan const adapter = function(v){ 38e67a0f40Sstephan if(v && v.constructor){ 393961b263Sstephan const x = v.pointer; 403961b263Sstephan if(Number.isInteger(x)) return x; 41e67a0f40Sstephan else toss("Invalid (object) type for .pointer-type argument."); 423961b263Sstephan } 43e67a0f40Sstephan return xPointer(v); 443961b263Sstephan }; 453961b263Sstephan wasm.xWrap.argAdapter('.pointer', adapter); 46e3cd6760Sstephan } /* ".pointer" xWrap() argument adapter */ 473961b263Sstephan 48e67a0f40Sstephan if(1){/* Convert Arrays and certain TypedArrays to strings for 49e67a0f40Sstephan 'flexible-string'-type arguments */ 50e67a0f40Sstephan const xString = wasm.xWrap.argAdapter('string'); 51e67a0f40Sstephan wasm.xWrap.argAdapter( 52e67a0f40Sstephan 'flexible-string', (v)=>xString(util.arrayToString(v)) 53e67a0f40Sstephan ); 54e67a0f40Sstephan } 55e67a0f40Sstephan 56e67a0f40Sstephan if(1){// WhWasmUtil.xWrap() bindings... 573961b263Sstephan /** 583961b263Sstephan Add some descriptive xWrap() aliases for '*' intended to 593961b263Sstephan (A) initially improve readability/correctness of capi.signatures 603961b263Sstephan and (B) eventually perhaps provide some sort of type-safety 613961b263Sstephan in their conversions. 623961b263Sstephan */ 633961b263Sstephan const aPtr = wasm.xWrap.argAdapter('*'); 643961b263Sstephan wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); 655b915007Sstephan wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); 663961b263Sstephan 673961b263Sstephan /** 683961b263Sstephan Populate api object with sqlite3_...() by binding the "raw" wasm 693961b263Sstephan exports into type-converting proxies using wasm.xWrap(). 703961b263Sstephan */ 713961b263Sstephan for(const e of wasm.bindingSignatures){ 723961b263Sstephan capi[e[0]] = wasm.xWrap.apply(null, e); 733961b263Sstephan } 7473079dbaSstephan for(const e of wasm.bindingSignatures.wasm){ 7573079dbaSstephan capi.wasm[e[0]] = wasm.xWrap.apply(null, e); 7673079dbaSstephan } 773961b263Sstephan 78e3cd6760Sstephan /* For C API functions which cannot work properly unless 793961b263Sstephan wasm.bigIntEnabled is true, install a bogus impl which 803961b263Sstephan throws if called when bigIntEnabled is false. */ 813961b263Sstephan const fI64Disabled = function(fname){ 823961b263Sstephan return ()=>toss(fname+"() disabled due to lack", 833961b263Sstephan "of BigInt support in this build."); 843961b263Sstephan }; 853961b263Sstephan for(const e of wasm.bindingSignatures.int64){ 863961b263Sstephan capi[e[0]] = wasm.bigIntEnabled 873961b263Sstephan ? wasm.xWrap.apply(null, e) 883961b263Sstephan : fI64Disabled(e[0]); 893961b263Sstephan } 903961b263Sstephan 913961b263Sstephan if(wasm.exports.sqlite3_wasm_db_error){ 923961b263Sstephan util.sqlite3_wasm_db_error = capi.wasm.xWrap( 933961b263Sstephan 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string' 943961b263Sstephan ); 953961b263Sstephan }else{ 963961b263Sstephan util.sqlite3_wasm_db_error = function(pDb,errCode,msg){ 973961b263Sstephan console.warn("sqlite3_wasm_db_error() is not exported.",arguments); 983961b263Sstephan return errCode; 993961b263Sstephan }; 1003961b263Sstephan } 1013961b263Sstephan 1023961b263Sstephan /** 1033961b263Sstephan When registering a VFS and its related components it may be 1043961b263Sstephan necessary to ensure that JS keeps a reference to them to keep 1053961b263Sstephan them from getting garbage collected. Simply pass each such value 1063961b263Sstephan to this function and a reference will be held to it for the life 1073961b263Sstephan of the app. 1083961b263Sstephan */ 1093961b263Sstephan capi.sqlite3_vfs_register.addReference = function f(...args){ 1103961b263Sstephan if(!f._) f._ = []; 1113961b263Sstephan f._.push(...args); 1123961b263Sstephan }; 1133961b263Sstephan 1143961b263Sstephan }/*xWrap() bindings*/; 1153961b263Sstephan 1163961b263Sstephan /** 117e67a0f40Sstephan Internal helper to assist in validating call argument counts in 118e67a0f40Sstephan the hand-written sqlite3_xyz() wrappers. We do this only for 119e67a0f40Sstephan consistency with non-special-case wrappings. 120e67a0f40Sstephan */ 121e67a0f40Sstephan const __dbArgcMismatch = (pDb,f,n)=>{ 122e67a0f40Sstephan return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE, 123e67a0f40Sstephan f+"() requires "+n+" argument"+ 124e67a0f40Sstephan (1===n?'':'s')+"."); 125e67a0f40Sstephan }; 126e67a0f40Sstephan 127e67a0f40Sstephan /** 128e67a0f40Sstephan Helper for flexible-string conversions which require a 129e67a0f40Sstephan byte-length counterpart argument. Passed a value and its 130e67a0f40Sstephan ostensible length, this function returns [V,N], where V 131e67a0f40Sstephan is either v or a transformed copy of v and N is either n, 132e67a0f40Sstephan -1, or the byte length of v (if it's a byte array). 133e67a0f40Sstephan */ 134e67a0f40Sstephan const __flexiString = function(v,n){ 135e67a0f40Sstephan if('string'===typeof v){ 136e67a0f40Sstephan n = -1; 137e67a0f40Sstephan }else if(util.isSQLableTypedArray(v)){ 138e67a0f40Sstephan n = v.byteLength; 139e67a0f40Sstephan v = util.typedArrayToString(v); 140e67a0f40Sstephan }else if(Array.isArray(v)){ 141e67a0f40Sstephan v = v.join(''); 142e67a0f40Sstephan n = -1; 143e67a0f40Sstephan } 144e67a0f40Sstephan return [v, n]; 145e67a0f40Sstephan }; 146e67a0f40Sstephan 147e67a0f40Sstephan if(1){/* Special-case handling of sqlite3_exec() */ 148e67a0f40Sstephan const __exec = wasm.xWrap("sqlite3_exec", "int", 149e67a0f40Sstephan ["sqlite3*", "flexible-string", "*", "*", "**"]); 150e67a0f40Sstephan /* Documented in the api object's initializer. */ 151e67a0f40Sstephan capi.sqlite3_exec = function(pDb, sql, callback, pVoid, pErrMsg){ 152e67a0f40Sstephan if(5!==arguments.length){ 153e67a0f40Sstephan return __dbArgcMismatch(pDb,"sqlite3_exec",5); 154e67a0f40Sstephan }else if('function' !== typeof callback){ 155e67a0f40Sstephan return __exec(pDb, sql, callback, pVoid, pErrMsg); 156e67a0f40Sstephan } 157e67a0f40Sstephan /* Wrap the callback in a WASM-bound function and convert the callback's 158e67a0f40Sstephan `(char**)` arguments to arrays of strings... */ 159e67a0f40Sstephan const wasm = capi.wasm; 160e67a0f40Sstephan const cbwrap = function(pVoid, nCols, pColVals, pColNames){ 161e67a0f40Sstephan let rc = capi.SQLITE_ERROR; 162e67a0f40Sstephan try { 163e67a0f40Sstephan let aVals = [], aNames = [], i = 0, offset = 0; 164e67a0f40Sstephan for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){ 165e67a0f40Sstephan aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) ); 166e67a0f40Sstephan aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) ); 167e67a0f40Sstephan } 168e67a0f40Sstephan rc = callback(pVoid, nCols, aVals, aNames) | 0; 169e67a0f40Sstephan /* The first 2 args of the callback are useless for JS but 170e67a0f40Sstephan we want the JS mapping of the C API to be as close to the 171e67a0f40Sstephan C API as possible. */ 172e67a0f40Sstephan }catch(e){ 173e67a0f40Sstephan /* If we set the db error state here, the higher-level exec() call 174e67a0f40Sstephan replaces it with its own, so we have no way of reporting the 175e67a0f40Sstephan exception message except the console. We must not propagate 176e67a0f40Sstephan exceptions through the C API. */ 177e67a0f40Sstephan } 178e67a0f40Sstephan return rc; 179e67a0f40Sstephan }; 180e67a0f40Sstephan let pFunc, rc; 181e67a0f40Sstephan try{ 182e67a0f40Sstephan pFunc = wasm.installFunction("ipipp", cbwrap); 183e67a0f40Sstephan rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg); 184e67a0f40Sstephan }catch(e){ 185e67a0f40Sstephan rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 186e67a0f40Sstephan "Error running exec(): "+e.message); 187e67a0f40Sstephan }finally{ 188e67a0f40Sstephan if(pFunc) wasm.uninstallFunction(pFunc); 189e67a0f40Sstephan } 190e67a0f40Sstephan return rc; 191e67a0f40Sstephan }; 192e67a0f40Sstephan }/*sqlite3_exec() proxy*/; 193e67a0f40Sstephan 194e67a0f40Sstephan if(1){/* Special-case handling of sqlite3_prepare_v2() and 195e67a0f40Sstephan sqlite3_prepare_v3() */ 196e67a0f40Sstephan /** 1973961b263Sstephan Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). 1983961b263Sstephan */ 1993961b263Sstephan const __prepare = Object.create(null); 2003961b263Sstephan /** 2013961b263Sstephan This binding expects a JS string as its 2nd argument and 2023961b263Sstephan null as its final argument. In order to compile multiple 2033961b263Sstephan statements from a single string, the "full" impl (see 2043961b263Sstephan below) must be used. 2053961b263Sstephan */ 2063961b263Sstephan __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', 2073961b263Sstephan "int", ["sqlite3*", "string", 208453af2f6Sstephan "int"/*ignored for this impl!*/, 2093961b263Sstephan "int", "**", 2103961b263Sstephan "**"/*MUST be 0 or null or undefined!*/]); 2113961b263Sstephan /** 2123961b263Sstephan Impl which requires that the 2nd argument be a pointer 2133961b263Sstephan to the SQL string, instead of being converted to a 2143961b263Sstephan string. This variant is necessary for cases where we 2153961b263Sstephan require a non-NULL value for the final argument 2163961b263Sstephan (exec()'ing multiple statements from one input 2173961b263Sstephan string). For simpler cases, where only the first 2183961b263Sstephan statement in the SQL string is required, the wrapper 2193961b263Sstephan named sqlite3_prepare_v2() is sufficient and easier to 2203961b263Sstephan use because it doesn't require dealing with pointers. 2213961b263Sstephan */ 2223961b263Sstephan __prepare.full = wasm.xWrap('sqlite3_prepare_v3', 2233961b263Sstephan "int", ["sqlite3*", "*", "int", "int", 2243961b263Sstephan "**", "**"]); 2253961b263Sstephan 2263961b263Sstephan /* Documented in the api object's initializer. */ 2273961b263Sstephan capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ 228e67a0f40Sstephan if(6!==arguments.length){ 229e67a0f40Sstephan return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",6); 230e67a0f40Sstephan } 231e67a0f40Sstephan const [xSql, xSqlLen] = __flexiString(sql, sqlLen); 232e67a0f40Sstephan switch(typeof xSql){ 233e67a0f40Sstephan case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); 234e67a0f40Sstephan case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); 2353961b263Sstephan default: 2363961b263Sstephan return util.sqlite3_wasm_db_error( 2373961b263Sstephan pDb, capi.SQLITE_MISUSE, 2383961b263Sstephan "Invalid SQL argument type for sqlite3_prepare_v2/v3()." 2393961b263Sstephan ); 2403961b263Sstephan } 2413961b263Sstephan }; 2423961b263Sstephan 243e67a0f40Sstephan /* Documented in the api object's initializer. */ 244e67a0f40Sstephan capi.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){ 245e67a0f40Sstephan return (5==arguments.length) 246e67a0f40Sstephan ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) 247e67a0f40Sstephan : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",5); 248e67a0f40Sstephan }; 249e67a0f40Sstephan }/*sqlite3_prepare_v2/v3()*/; 2503961b263Sstephan 2513961b263Sstephan /** 2523961b263Sstephan Install JS<->C struct bindings for the non-opaque struct types we 2533961b263Sstephan need... */ 2543961b263Sstephan sqlite3.StructBinder = self.Jaccwabyt({ 2553961b263Sstephan heap: 0 ? wasm.memory : wasm.heap8u, 2563961b263Sstephan alloc: wasm.alloc, 2573961b263Sstephan dealloc: wasm.dealloc, 2583961b263Sstephan functionTable: wasm.functionTable, 2593961b263Sstephan bigIntEnabled: wasm.bigIntEnabled, 2603961b263Sstephan memberPrefix: '$' 2613961b263Sstephan }); 2623961b263Sstephan delete self.Jaccwabyt; 2633961b263Sstephan 2643961b263Sstephan {/* Import C-level constants and structs... */ 2653961b263Sstephan const cJson = wasm.xCall('sqlite3_wasm_enum_json'); 2663961b263Sstephan if(!cJson){ 2673961b263Sstephan toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", 2683961b263Sstephan "static buffer size!"); 2693961b263Sstephan } 2703961b263Sstephan wasm.ctype = JSON.parse(wasm.cstringToJs(cJson)); 2713961b263Sstephan //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); 2723961b263Sstephan for(const t of ['access', 'blobFinalizers', 'dataTypes', 2735b915007Sstephan 'encodings', 'fcntl', 'flock', 'ioCap', 2743961b263Sstephan 'openFlags', 'prepareFlags', 'resultCodes', 27553d4e01dSstephan 'serialize', 'syncFlags', 'udfFlags', 27653d4e01dSstephan 'version' 2773961b263Sstephan ]){ 278f75c0c70Sstephan for(const e of Object.entries(wasm.ctype[t])){ 279f75c0c70Sstephan // ^^^ [k,v] there triggers a buggy code transormation via one 280f75c0c70Sstephan // of the Emscripten-driven optimizers. 281f75c0c70Sstephan capi[e[0]] = e[1]; 2823961b263Sstephan } 2833961b263Sstephan } 2841f095d48Sstephan const __rcMap = Object.create(null); 2851f095d48Sstephan for(const t of ['resultCodes']){ 2861f095d48Sstephan for(const e of Object.entries(wasm.ctype[t])){ 2871f095d48Sstephan __rcMap[e[1]] = e[0]; 2881f095d48Sstephan } 2891f095d48Sstephan } 2901f095d48Sstephan /** 2911f095d48Sstephan For the given integer, returns the SQLITE_xxx result code as a 2921f095d48Sstephan string, or undefined if no such mapping is found. 2931f095d48Sstephan */ 294*32781427Sstephan capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc]; 2953961b263Sstephan /* Bind all registered C-side structs... */ 2963961b263Sstephan for(const s of wasm.ctype.structs){ 2973961b263Sstephan capi[s.name] = sqlite3.StructBinder(s); 2983961b263Sstephan } 2991f095d48Sstephan }/*end C constant imports*/ 300d18f1bbfSstephan 301d18f1bbfSstephan sqlite3.version = Object.assign(Object.create(null),{ 302d18f1bbfSstephan library: sqlite3.capi.sqlite3_libversion(), 303d18f1bbfSstephan sourceId: sqlite3.capi.sqlite3_sourceid() 304d18f1bbfSstephan }); 305e3cd6760Sstephan}); 306e67a0f40Sstephan 307