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