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 /** 5863e9ec2fSstephan Add some descriptive xWrap() aliases for '*' intended to (A) 5963e9ec2fSstephan initially improve readability/correctness of capi.signatures 6063e9ec2fSstephan and (B) eventually perhaps provide automatic conversion from 6163e9ec2fSstephan higher-level representations, e.g. capi.sqlite3_vfs to 6263e9ec2fSstephan `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. 633961b263Sstephan */ 643961b263Sstephan const aPtr = wasm.xWrap.argAdapter('*'); 653961b263Sstephan wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); 665b915007Sstephan wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); 673961b263Sstephan 683961b263Sstephan /** 693961b263Sstephan Populate api object with sqlite3_...() by binding the "raw" wasm 703961b263Sstephan exports into type-converting proxies using wasm.xWrap(). 713961b263Sstephan */ 723961b263Sstephan for(const e of wasm.bindingSignatures){ 733961b263Sstephan capi[e[0]] = wasm.xWrap.apply(null, e); 743961b263Sstephan } 7573079dbaSstephan for(const e of wasm.bindingSignatures.wasm){ 7673079dbaSstephan capi.wasm[e[0]] = wasm.xWrap.apply(null, e); 7773079dbaSstephan } 783961b263Sstephan 79e3cd6760Sstephan /* For C API functions which cannot work properly unless 803961b263Sstephan wasm.bigIntEnabled is true, install a bogus impl which 813961b263Sstephan throws if called when bigIntEnabled is false. */ 823961b263Sstephan const fI64Disabled = function(fname){ 833961b263Sstephan return ()=>toss(fname+"() disabled due to lack", 843961b263Sstephan "of BigInt support in this build."); 853961b263Sstephan }; 863961b263Sstephan for(const e of wasm.bindingSignatures.int64){ 873961b263Sstephan capi[e[0]] = wasm.bigIntEnabled 883961b263Sstephan ? wasm.xWrap.apply(null, e) 893961b263Sstephan : fI64Disabled(e[0]); 903961b263Sstephan } 913961b263Sstephan 923961b263Sstephan if(wasm.exports.sqlite3_wasm_db_error){ 933961b263Sstephan util.sqlite3_wasm_db_error = capi.wasm.xWrap( 943961b263Sstephan 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string' 953961b263Sstephan ); 963961b263Sstephan }else{ 973961b263Sstephan util.sqlite3_wasm_db_error = function(pDb,errCode,msg){ 983961b263Sstephan console.warn("sqlite3_wasm_db_error() is not exported.",arguments); 993961b263Sstephan return errCode; 1003961b263Sstephan }; 1013961b263Sstephan } 1023961b263Sstephan 1033961b263Sstephan /** 1043961b263Sstephan When registering a VFS and its related components it may be 1053961b263Sstephan necessary to ensure that JS keeps a reference to them to keep 1063961b263Sstephan them from getting garbage collected. Simply pass each such value 1073961b263Sstephan to this function and a reference will be held to it for the life 1083961b263Sstephan of the app. 1093961b263Sstephan */ 1103961b263Sstephan capi.sqlite3_vfs_register.addReference = function f(...args){ 1113961b263Sstephan if(!f._) f._ = []; 1123961b263Sstephan f._.push(...args); 1133961b263Sstephan }; 1143961b263Sstephan 1153961b263Sstephan }/*xWrap() bindings*/; 1163961b263Sstephan 1173961b263Sstephan /** 118e67a0f40Sstephan Internal helper to assist in validating call argument counts in 119e67a0f40Sstephan the hand-written sqlite3_xyz() wrappers. We do this only for 120e67a0f40Sstephan consistency with non-special-case wrappings. 121e67a0f40Sstephan */ 122e67a0f40Sstephan const __dbArgcMismatch = (pDb,f,n)=>{ 123e67a0f40Sstephan return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE, 124e67a0f40Sstephan f+"() requires "+n+" argument"+ 125e67a0f40Sstephan (1===n?'':'s')+"."); 126e67a0f40Sstephan }; 127e67a0f40Sstephan 128e67a0f40Sstephan /** 129e67a0f40Sstephan Helper for flexible-string conversions which require a 130e67a0f40Sstephan byte-length counterpart argument. Passed a value and its 131e67a0f40Sstephan ostensible length, this function returns [V,N], where V 132e67a0f40Sstephan is either v or a transformed copy of v and N is either n, 133e67a0f40Sstephan -1, or the byte length of v (if it's a byte array). 134e67a0f40Sstephan */ 135e67a0f40Sstephan const __flexiString = function(v,n){ 136e67a0f40Sstephan if('string'===typeof v){ 137e67a0f40Sstephan n = -1; 138e67a0f40Sstephan }else if(util.isSQLableTypedArray(v)){ 139e67a0f40Sstephan n = v.byteLength; 140e67a0f40Sstephan v = util.typedArrayToString(v); 141e67a0f40Sstephan }else if(Array.isArray(v)){ 142e67a0f40Sstephan v = v.join(''); 143e67a0f40Sstephan n = -1; 144e67a0f40Sstephan } 145e67a0f40Sstephan return [v, n]; 146e67a0f40Sstephan }; 147e67a0f40Sstephan 148e67a0f40Sstephan if(1){/* Special-case handling of sqlite3_exec() */ 149e67a0f40Sstephan const __exec = wasm.xWrap("sqlite3_exec", "int", 150e67a0f40Sstephan ["sqlite3*", "flexible-string", "*", "*", "**"]); 151e67a0f40Sstephan /* Documented in the api object's initializer. */ 152e67a0f40Sstephan capi.sqlite3_exec = function(pDb, sql, callback, pVoid, pErrMsg){ 153e67a0f40Sstephan if(5!==arguments.length){ 154e67a0f40Sstephan return __dbArgcMismatch(pDb,"sqlite3_exec",5); 155e67a0f40Sstephan }else if('function' !== typeof callback){ 156e67a0f40Sstephan return __exec(pDb, sql, callback, pVoid, pErrMsg); 157e67a0f40Sstephan } 158e67a0f40Sstephan /* Wrap the callback in a WASM-bound function and convert the callback's 159e67a0f40Sstephan `(char**)` arguments to arrays of strings... */ 160e67a0f40Sstephan const wasm = capi.wasm; 161e67a0f40Sstephan const cbwrap = function(pVoid, nCols, pColVals, pColNames){ 162e67a0f40Sstephan let rc = capi.SQLITE_ERROR; 163e67a0f40Sstephan try { 164e67a0f40Sstephan let aVals = [], aNames = [], i = 0, offset = 0; 165e67a0f40Sstephan for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){ 166e67a0f40Sstephan aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) ); 167e67a0f40Sstephan aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) ); 168e67a0f40Sstephan } 169e67a0f40Sstephan rc = callback(pVoid, nCols, aVals, aNames) | 0; 170e67a0f40Sstephan /* The first 2 args of the callback are useless for JS but 171e67a0f40Sstephan we want the JS mapping of the C API to be as close to the 172e67a0f40Sstephan C API as possible. */ 173e67a0f40Sstephan }catch(e){ 174e67a0f40Sstephan /* If we set the db error state here, the higher-level exec() call 175e67a0f40Sstephan replaces it with its own, so we have no way of reporting the 176e67a0f40Sstephan exception message except the console. We must not propagate 177e67a0f40Sstephan exceptions through the C API. */ 178e67a0f40Sstephan } 179e67a0f40Sstephan return rc; 180e67a0f40Sstephan }; 181e67a0f40Sstephan let pFunc, rc; 182e67a0f40Sstephan try{ 183e67a0f40Sstephan pFunc = wasm.installFunction("ipipp", cbwrap); 184e67a0f40Sstephan rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg); 185e67a0f40Sstephan }catch(e){ 186e67a0f40Sstephan rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 187e67a0f40Sstephan "Error running exec(): "+e.message); 188e67a0f40Sstephan }finally{ 189e67a0f40Sstephan if(pFunc) wasm.uninstallFunction(pFunc); 190e67a0f40Sstephan } 191e67a0f40Sstephan return rc; 192e67a0f40Sstephan }; 193e67a0f40Sstephan }/*sqlite3_exec() proxy*/; 194e67a0f40Sstephan 195e67a0f40Sstephan if(1){/* Special-case handling of sqlite3_prepare_v2() and 196e67a0f40Sstephan sqlite3_prepare_v3() */ 197e67a0f40Sstephan /** 1983961b263Sstephan Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). 1993961b263Sstephan */ 2003961b263Sstephan const __prepare = Object.create(null); 2013961b263Sstephan /** 2023961b263Sstephan This binding expects a JS string as its 2nd argument and 2033961b263Sstephan null as its final argument. In order to compile multiple 2043961b263Sstephan statements from a single string, the "full" impl (see 2053961b263Sstephan below) must be used. 2063961b263Sstephan */ 2073961b263Sstephan __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', 2083961b263Sstephan "int", ["sqlite3*", "string", 209453af2f6Sstephan "int"/*ignored for this impl!*/, 2103961b263Sstephan "int", "**", 2113961b263Sstephan "**"/*MUST be 0 or null or undefined!*/]); 2123961b263Sstephan /** 2133961b263Sstephan Impl which requires that the 2nd argument be a pointer 2143961b263Sstephan to the SQL string, instead of being converted to a 2153961b263Sstephan string. This variant is necessary for cases where we 2163961b263Sstephan require a non-NULL value for the final argument 2173961b263Sstephan (exec()'ing multiple statements from one input 2183961b263Sstephan string). For simpler cases, where only the first 2193961b263Sstephan statement in the SQL string is required, the wrapper 2203961b263Sstephan named sqlite3_prepare_v2() is sufficient and easier to 2213961b263Sstephan use because it doesn't require dealing with pointers. 2223961b263Sstephan */ 2233961b263Sstephan __prepare.full = wasm.xWrap('sqlite3_prepare_v3', 2243961b263Sstephan "int", ["sqlite3*", "*", "int", "int", 2253961b263Sstephan "**", "**"]); 2263961b263Sstephan 2273961b263Sstephan /* Documented in the api object's initializer. */ 2283961b263Sstephan capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ 229e67a0f40Sstephan if(6!==arguments.length){ 230e67a0f40Sstephan return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",6); 231e67a0f40Sstephan } 232e67a0f40Sstephan const [xSql, xSqlLen] = __flexiString(sql, sqlLen); 233e67a0f40Sstephan switch(typeof xSql){ 234e67a0f40Sstephan case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); 235e67a0f40Sstephan case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); 2363961b263Sstephan default: 2373961b263Sstephan return util.sqlite3_wasm_db_error( 2383961b263Sstephan pDb, capi.SQLITE_MISUSE, 2393961b263Sstephan "Invalid SQL argument type for sqlite3_prepare_v2/v3()." 2403961b263Sstephan ); 2413961b263Sstephan } 2423961b263Sstephan }; 2433961b263Sstephan 244e67a0f40Sstephan /* Documented in the api object's initializer. */ 245e67a0f40Sstephan capi.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){ 246e67a0f40Sstephan return (5==arguments.length) 247e67a0f40Sstephan ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) 248e67a0f40Sstephan : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",5); 249e67a0f40Sstephan }; 250e67a0f40Sstephan }/*sqlite3_prepare_v2/v3()*/; 2513961b263Sstephan 25263e9ec2fSstephan if(1){// Extend wasm.pstack, now that the wasm utils are installed 25363e9ec2fSstephan /** 25463e9ec2fSstephan Allocates n chunks, each sz bytes, as a single memory block and 25563e9ec2fSstephan returns the addresses as an array of n element, each holding 25663e9ec2fSstephan the address of one chunk. 25763e9ec2fSstephan 25863e9ec2fSstephan Throws a WasmAllocError if allocation fails. 25963e9ec2fSstephan 26063e9ec2fSstephan Example: 26163e9ec2fSstephan 26263e9ec2fSstephan ``` 26363e9ec2fSstephan const [p1, p2, p3] = wasm.pstack.allocChunks(3,4); 26463e9ec2fSstephan ``` 26563e9ec2fSstephan */ 26663e9ec2fSstephan wasm.pstack.allocChunks = (n,sz)=>{ 26763e9ec2fSstephan const mem = wasm.pstack.alloc(n * sz); 26863e9ec2fSstephan const rc = []; 26963e9ec2fSstephan let i = 0, offset = 0; 27063e9ec2fSstephan for(; i < n; offset = (sz * ++i)){ 27163e9ec2fSstephan rc.push(mem + offset); 27263e9ec2fSstephan } 27363e9ec2fSstephan return rc; 27463e9ec2fSstephan }; 27563e9ec2fSstephan 27663e9ec2fSstephan /** 27763e9ec2fSstephan A convenience wrapper for allocChunks() which sizes each chunks 27863e9ec2fSstephan as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if 279*9fdce595Sstephan safePtrSize is falsy). 28063e9ec2fSstephan 28163e9ec2fSstephan How it returns its result differs depending on its first 28263e9ec2fSstephan argument: if it's 1, it returns a single pointer value. If it's 28363e9ec2fSstephan more than 1, it returns the same as allocChunks(). 28463e9ec2fSstephan 285*9fdce595Sstephan When a returned pointers will refer to a 64-bit value, e.g. a 286*9fdce595Sstephan double or int64, and that value must be written or fetched, 28763e9ec2fSstephan e.g. using wasm.setMemValue() or wasm.getMemValue(), it is 28863e9ec2fSstephan important that the pointer in question be aligned to an 8-byte 28963e9ec2fSstephan boundary or else it will not be fetched or written properly and 29063e9ec2fSstephan will corrupt or read neighboring memory. 29163e9ec2fSstephan 292*9fdce595Sstephan However, when all pointers involved point to "small" data, it 293*9fdce595Sstephan is safe to pass a falsy value to save to memory. 29463e9ec2fSstephan */ 29563e9ec2fSstephan wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{ 29663e9ec2fSstephan return 1===n 29763e9ec2fSstephan ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) 29863e9ec2fSstephan : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); 29963e9ec2fSstephan }; 30063e9ec2fSstephan }/*wasm.pstack filler*/ 30163e9ec2fSstephan 3023961b263Sstephan /** 3033961b263Sstephan Install JS<->C struct bindings for the non-opaque struct types we 3043961b263Sstephan need... */ 3053961b263Sstephan sqlite3.StructBinder = self.Jaccwabyt({ 3063961b263Sstephan heap: 0 ? wasm.memory : wasm.heap8u, 3073961b263Sstephan alloc: wasm.alloc, 3083961b263Sstephan dealloc: wasm.dealloc, 3093961b263Sstephan functionTable: wasm.functionTable, 3103961b263Sstephan bigIntEnabled: wasm.bigIntEnabled, 3113961b263Sstephan memberPrefix: '$' 3123961b263Sstephan }); 3133961b263Sstephan delete self.Jaccwabyt; 3143961b263Sstephan 3153961b263Sstephan {/* Import C-level constants and structs... */ 3163961b263Sstephan const cJson = wasm.xCall('sqlite3_wasm_enum_json'); 3173961b263Sstephan if(!cJson){ 3183961b263Sstephan toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", 3193961b263Sstephan "static buffer size!"); 3203961b263Sstephan } 3213961b263Sstephan wasm.ctype = JSON.parse(wasm.cstringToJs(cJson)); 3223961b263Sstephan //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); 3233961b263Sstephan for(const t of ['access', 'blobFinalizers', 'dataTypes', 3245b915007Sstephan 'encodings', 'fcntl', 'flock', 'ioCap', 3253961b263Sstephan 'openFlags', 'prepareFlags', 'resultCodes', 32653d4e01dSstephan 'serialize', 'syncFlags', 'udfFlags', 32753d4e01dSstephan 'version' 3283961b263Sstephan ]){ 329f75c0c70Sstephan for(const e of Object.entries(wasm.ctype[t])){ 330f75c0c70Sstephan // ^^^ [k,v] there triggers a buggy code transormation via one 331f75c0c70Sstephan // of the Emscripten-driven optimizers. 332f75c0c70Sstephan capi[e[0]] = e[1]; 3333961b263Sstephan } 3343961b263Sstephan } 3351f095d48Sstephan const __rcMap = Object.create(null); 3361f095d48Sstephan for(const t of ['resultCodes']){ 3371f095d48Sstephan for(const e of Object.entries(wasm.ctype[t])){ 3381f095d48Sstephan __rcMap[e[1]] = e[0]; 3391f095d48Sstephan } 3401f095d48Sstephan } 3411f095d48Sstephan /** 3421f095d48Sstephan For the given integer, returns the SQLITE_xxx result code as a 3431f095d48Sstephan string, or undefined if no such mapping is found. 3441f095d48Sstephan */ 34532781427Sstephan capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc]; 3463961b263Sstephan /* Bind all registered C-side structs... */ 3473961b263Sstephan for(const s of wasm.ctype.structs){ 3483961b263Sstephan capi[s.name] = sqlite3.StructBinder(s); 3493961b263Sstephan } 3501f095d48Sstephan }/*end C constant imports*/ 351d18f1bbfSstephan 352d18f1bbfSstephan sqlite3.version = Object.assign(Object.create(null),{ 353d18f1bbfSstephan library: sqlite3.capi.sqlite3_libversion(), 354d18f1bbfSstephan sourceId: sqlite3.capi.sqlite3_sourceid() 355d18f1bbfSstephan }); 356e3cd6760Sstephan}); 357e67a0f40Sstephan 358