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(' '))};
22510a9d1cSstephan  const toss3 = sqlite3.SQLite3Error.toss;
233961b263Sstephan  const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
243961b263Sstephan  self.WhWasmUtilInstaller(capi.wasm);
253961b263Sstephan  delete self.WhWasmUtilInstaller;
263961b263Sstephan
273961b263Sstephan  if(0){
283961b263Sstephan    /*  "The problem" is that the following isn't type-safe.
293961b263Sstephan        OTOH, nothing about WASM pointers is. */
303961b263Sstephan    /**
31d9801185Sstephan       Add the `.pointer` xWrap() signature entry to extend the
32d9801185Sstephan       `pointer` arg handler to check for a `pointer` property. This
33d9801185Sstephan       can be used to permit, e.g., passing an sqlite3.oo1.DB instance
34d9801185Sstephan       to a C-style sqlite3_xxx function which takes an `sqlite3*`
35d9801185Sstephan       argument.
363961b263Sstephan    */
37e67a0f40Sstephan    const xPointer = wasm.xWrap.argAdapter('pointer');
383961b263Sstephan    const adapter = function(v){
39e67a0f40Sstephan      if(v && v.constructor){
403961b263Sstephan        const x = v.pointer;
413961b263Sstephan        if(Number.isInteger(x)) return x;
42e67a0f40Sstephan        else toss("Invalid (object) type for .pointer-type argument.");
433961b263Sstephan      }
44e67a0f40Sstephan      return xPointer(v);
453961b263Sstephan    };
463961b263Sstephan    wasm.xWrap.argAdapter('.pointer', adapter);
47e3cd6760Sstephan  } /* ".pointer" xWrap() argument adapter */
483961b263Sstephan
49e67a0f40Sstephan  if(1){/* Convert Arrays and certain TypedArrays to strings for
50e67a0f40Sstephan           'flexible-string'-type arguments */
51e67a0f40Sstephan    const xString = wasm.xWrap.argAdapter('string');
52e67a0f40Sstephan    wasm.xWrap.argAdapter(
53*ef9cd12eSstephan      'flexible-string', (v)=>xString(util.flexibleString(v))
54e67a0f40Sstephan    );
55e67a0f40Sstephan  }
56e67a0f40Sstephan
57e67a0f40Sstephan  if(1){// WhWasmUtil.xWrap() bindings...
583961b263Sstephan    /**
5963e9ec2fSstephan       Add some descriptive xWrap() aliases for '*' intended to (A)
6063e9ec2fSstephan       initially improve readability/correctness of capi.signatures
6163e9ec2fSstephan       and (B) eventually perhaps provide automatic conversion from
6263e9ec2fSstephan       higher-level representations, e.g. capi.sqlite3_vfs to
6363e9ec2fSstephan       `sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
643961b263Sstephan    */
653961b263Sstephan    const aPtr = wasm.xWrap.argAdapter('*');
66510a9d1cSstephan    wasm.xWrap.argAdapter('sqlite3*', aPtr)
67510a9d1cSstephan    ('sqlite3_stmt*', aPtr)
68510a9d1cSstephan    ('sqlite3_context*', aPtr)
69510a9d1cSstephan    ('sqlite3_value*', aPtr)
70510a9d1cSstephan    ('void*', aPtr);
71510a9d1cSstephan    wasm.xWrap.resultAdapter('sqlite3*', aPtr)
72510a9d1cSstephan    ('sqlite3_stmt*', aPtr)
73510a9d1cSstephan    ('sqlite3_context*', aPtr)
74510a9d1cSstephan    ('void*', aPtr);
753961b263Sstephan
763961b263Sstephan    /**
773961b263Sstephan       Populate api object with sqlite3_...() by binding the "raw" wasm
783961b263Sstephan       exports into type-converting proxies using wasm.xWrap().
793961b263Sstephan    */
803961b263Sstephan    for(const e of wasm.bindingSignatures){
813961b263Sstephan      capi[e[0]] = wasm.xWrap.apply(null, e);
823961b263Sstephan    }
8373079dbaSstephan    for(const e of wasm.bindingSignatures.wasm){
8473079dbaSstephan      capi.wasm[e[0]] = wasm.xWrap.apply(null, e);
8573079dbaSstephan    }
863961b263Sstephan
87e3cd6760Sstephan    /* For C API functions which cannot work properly unless
883961b263Sstephan       wasm.bigIntEnabled is true, install a bogus impl which
893961b263Sstephan       throws if called when bigIntEnabled is false. */
903961b263Sstephan    const fI64Disabled = function(fname){
913961b263Sstephan      return ()=>toss(fname+"() disabled due to lack",
923961b263Sstephan                      "of BigInt support in this build.");
933961b263Sstephan    };
943961b263Sstephan    for(const e of wasm.bindingSignatures.int64){
953961b263Sstephan      capi[e[0]] = wasm.bigIntEnabled
963961b263Sstephan        ? wasm.xWrap.apply(null, e)
973961b263Sstephan        : fI64Disabled(e[0]);
983961b263Sstephan    }
993961b263Sstephan
1003961b263Sstephan    if(wasm.exports.sqlite3_wasm_db_error){
1013961b263Sstephan      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
1023961b263Sstephan        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
1033961b263Sstephan      );
1043961b263Sstephan    }else{
1053961b263Sstephan      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
1063961b263Sstephan        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
1073961b263Sstephan        return errCode;
1083961b263Sstephan      };
1093961b263Sstephan    }
1103961b263Sstephan
1113961b263Sstephan    /**
1123961b263Sstephan       When registering a VFS and its related components it may be
1133961b263Sstephan       necessary to ensure that JS keeps a reference to them to keep
1143961b263Sstephan       them from getting garbage collected. Simply pass each such value
1153961b263Sstephan       to this function and a reference will be held to it for the life
1163961b263Sstephan       of the app.
1173961b263Sstephan    */
1183961b263Sstephan    capi.sqlite3_vfs_register.addReference = function f(...args){
1193961b263Sstephan      if(!f._) f._ = [];
1203961b263Sstephan      f._.push(...args);
1213961b263Sstephan    };
1223961b263Sstephan
1233961b263Sstephan  }/*xWrap() bindings*/;
1243961b263Sstephan
1253961b263Sstephan  /**
126e67a0f40Sstephan     Internal helper to assist in validating call argument counts in
127e67a0f40Sstephan     the hand-written sqlite3_xyz() wrappers. We do this only for
128e67a0f40Sstephan     consistency with non-special-case wrappings.
129e67a0f40Sstephan  */
130e67a0f40Sstephan  const __dbArgcMismatch = (pDb,f,n)=>{
131e67a0f40Sstephan    return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
132e67a0f40Sstephan                                              f+"() requires "+n+" argument"+
133e67a0f40Sstephan                                              (1===n?'':'s')+".");
134e67a0f40Sstephan  };
135e67a0f40Sstephan
136e67a0f40Sstephan  /**
137e67a0f40Sstephan     Helper for flexible-string conversions which require a
138e67a0f40Sstephan     byte-length counterpart argument. Passed a value and its
139e67a0f40Sstephan     ostensible length, this function returns [V,N], where V
140e67a0f40Sstephan     is either v or a transformed copy of v and N is either n,
141e67a0f40Sstephan     -1, or the byte length of v (if it's a byte array).
142e67a0f40Sstephan  */
143e67a0f40Sstephan  const __flexiString = function(v,n){
144e67a0f40Sstephan    if('string'===typeof v){
145e67a0f40Sstephan      n = -1;
146e67a0f40Sstephan    }else if(util.isSQLableTypedArray(v)){
147e67a0f40Sstephan      n = v.byteLength;
148e67a0f40Sstephan      v = util.typedArrayToString(v);
149e67a0f40Sstephan    }else if(Array.isArray(v)){
150e67a0f40Sstephan      v = v.join('');
151e67a0f40Sstephan      n = -1;
152e67a0f40Sstephan    }
153e67a0f40Sstephan    return [v, n];
154e67a0f40Sstephan  };
155e67a0f40Sstephan
156e67a0f40Sstephan  if(1){/* Special-case handling of sqlite3_exec() */
157e67a0f40Sstephan    const __exec = wasm.xWrap("sqlite3_exec", "int",
158e67a0f40Sstephan                              ["sqlite3*", "flexible-string", "*", "*", "**"]);
159e67a0f40Sstephan    /* Documented in the api object's initializer. */
16092ede964Sstephan    capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
16192ede964Sstephan      if(f.length!==arguments.length){
16292ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
163e67a0f40Sstephan      }else if('function' !== typeof callback){
164e67a0f40Sstephan        return __exec(pDb, sql, callback, pVoid, pErrMsg);
165e67a0f40Sstephan      }
166e67a0f40Sstephan      /* Wrap the callback in a WASM-bound function and convert the callback's
167e67a0f40Sstephan         `(char**)` arguments to arrays of strings... */
168e67a0f40Sstephan      const wasm = capi.wasm;
169e67a0f40Sstephan      const cbwrap = function(pVoid, nCols, pColVals, pColNames){
170e67a0f40Sstephan        let rc = capi.SQLITE_ERROR;
171e67a0f40Sstephan        try {
172e67a0f40Sstephan          let aVals = [], aNames = [], i = 0, offset = 0;
173e67a0f40Sstephan          for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
174e67a0f40Sstephan            aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
175e67a0f40Sstephan            aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
176e67a0f40Sstephan          }
177e67a0f40Sstephan          rc = callback(pVoid, nCols, aVals, aNames) | 0;
178e67a0f40Sstephan          /* The first 2 args of the callback are useless for JS but
179e67a0f40Sstephan             we want the JS mapping of the C API to be as close to the
180e67a0f40Sstephan             C API as possible. */
181e67a0f40Sstephan        }catch(e){
182e67a0f40Sstephan          /* If we set the db error state here, the higher-level exec() call
183e67a0f40Sstephan             replaces it with its own, so we have no way of reporting the
184e67a0f40Sstephan             exception message except the console. We must not propagate
185e67a0f40Sstephan             exceptions through the C API. */
186e67a0f40Sstephan        }
187e67a0f40Sstephan        return rc;
188e67a0f40Sstephan      };
189e67a0f40Sstephan      let pFunc, rc;
190e67a0f40Sstephan      try{
191e67a0f40Sstephan        pFunc = wasm.installFunction("ipipp", cbwrap);
192e67a0f40Sstephan        rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
193e67a0f40Sstephan      }catch(e){
194e67a0f40Sstephan        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
195e67a0f40Sstephan                                        "Error running exec(): "+e.message);
196e67a0f40Sstephan      }finally{
197e67a0f40Sstephan        if(pFunc) wasm.uninstallFunction(pFunc);
198e67a0f40Sstephan      }
199e67a0f40Sstephan      return rc;
200e67a0f40Sstephan    };
201e67a0f40Sstephan  }/*sqlite3_exec() proxy*/;
202e67a0f40Sstephan
20392ede964Sstephan  if(1){/* Special-case handling of sqlite3_create_function_v2()
20492ede964Sstephan           and sqlite3_create_window_function() */
2059892883eSstephan    const sqlite3CreateFunction = wasm.xWrap(
2069892883eSstephan      "sqlite3_create_function_v2", "int",
20792ede964Sstephan      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
20892ede964Sstephan       "int"/*eTextRep*/, "*"/*pApp*/,
20992ede964Sstephan       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/]
21092ede964Sstephan    );
21192ede964Sstephan    const sqlite3CreateWindowFunction = wasm.xWrap(
21292ede964Sstephan      "sqlite3_create_window_function", "int",
21392ede964Sstephan      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
21492ede964Sstephan       "int"/*eTextRep*/, "*"/*pApp*/,
21592ede964Sstephan       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/,
21692ede964Sstephan       "*"/*xInverse*/, "*"/*xDestroy*/]
2179892883eSstephan    );
218510a9d1cSstephan
219*ef9cd12eSstephan    const __udfSetResult = function(pCtx, val){
220*ef9cd12eSstephan      //console.warn("udfSetResult",typeof val, val);
2219892883eSstephan      switch(typeof val) {
2229892883eSstephan          case 'boolean':
223510a9d1cSstephan            capi.sqlite3_result_int(pCtx, val ? 1 : 0);
224510a9d1cSstephan            break;
225510a9d1cSstephan          case 'bigint':
226510a9d1cSstephan            if(wasm.bigIntEnabled){
227510a9d1cSstephan              if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
228510a9d1cSstephan              else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
229510a9d1cSstephan            }else if(util.bigIntFits32(val)){
230510a9d1cSstephan              capi.sqlite3_result_int(pCtx, Number(val));
231510a9d1cSstephan            }else if(util.bigIntFitsDouble(val)){
232510a9d1cSstephan              capi.sqlite3_result_double(pCtx, Number(val));
233510a9d1cSstephan            }else{
234510a9d1cSstephan              toss3("BigInt value",val.toString(),"is too BigInt.");
235510a9d1cSstephan            }
2369892883eSstephan            break;
2379892883eSstephan          case 'number': {
2389892883eSstephan            (util.isInt32(val)
2399892883eSstephan             ? capi.sqlite3_result_int
240510a9d1cSstephan             : capi.sqlite3_result_double)(pCtx, val);
2419892883eSstephan            break;
2429892883eSstephan          }
2439892883eSstephan          case 'string':
244510a9d1cSstephan            capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
2459892883eSstephan            break;
2469892883eSstephan          case 'object':
24792ede964Sstephan            if(null===val/*yes, typeof null === 'object'*/) {
248510a9d1cSstephan              capi.sqlite3_result_null(pCtx);
2499892883eSstephan              break;
2509892883eSstephan            }else if(util.isBindableTypedArray(val)){
2519892883eSstephan              const pBlob = wasm.allocFromTypedArray(val);
25292ede964Sstephan              capi.sqlite3_result_blob(
253510a9d1cSstephan                pCtx, pBlob, val.byteLength,
25492ede964Sstephan                wasm.exports[sqlite3.config.deallocExportName]
25592ede964Sstephan              );
2569892883eSstephan              break;
2579892883eSstephan            }
2589892883eSstephan            // else fall through
2599892883eSstephan          default:
260510a9d1cSstephan          toss3("Don't not how to handle this UDF result value:",(typeof val), val);
2619892883eSstephan      };
262*ef9cd12eSstephan    }/*__udfSetResult()*/;
263510a9d1cSstephan
264*ef9cd12eSstephan    const __udfConvertArgs = function(argc, pArgv){
2659892883eSstephan      let i, pVal, valType, arg;
2669892883eSstephan      const tgt = [];
2679892883eSstephan      for(i = 0; i < argc; ++i){
2689892883eSstephan        pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
2699892883eSstephan        /**
2709892883eSstephan           Curiously: despite ostensibly requiring 8-byte
2719892883eSstephan           alignment, the pArgv array is parcelled into chunks of
2729892883eSstephan           4 bytes (1 pointer each). The values those point to
2739892883eSstephan           have 8-byte alignment but the individual argv entries
2749892883eSstephan           do not.
2759892883eSstephan        */
2769892883eSstephan        valType = capi.sqlite3_value_type(pVal);
2779892883eSstephan        switch(valType){
2789892883eSstephan            case capi.SQLITE_INTEGER:
279510a9d1cSstephan              if(wasm.bigIntEnabled){
280510a9d1cSstephan                arg = capi.sqlite3_value_int64(pVal);
281510a9d1cSstephan                if(util.bigIntFitsDouble(arg)) arg = Number(arg);
282510a9d1cSstephan              }
283510a9d1cSstephan              else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
284510a9d1cSstephan              break;
2859892883eSstephan            case capi.SQLITE_FLOAT:
2869892883eSstephan              arg = capi.sqlite3_value_double(pVal);
2879892883eSstephan              break;
2889892883eSstephan            case capi.SQLITE_TEXT:
2899892883eSstephan              arg = capi.sqlite3_value_text(pVal);
2909892883eSstephan              break;
2919892883eSstephan            case capi.SQLITE_BLOB:{
2929892883eSstephan              const n = capi.sqlite3_value_bytes(pVal);
2939892883eSstephan              const pBlob = capi.sqlite3_value_blob(pVal);
294510a9d1cSstephan              if(n && !pBlob) sqlite3.WasmAllocError.toss(
295510a9d1cSstephan                "Cannot allocate memory for blob argument of",n,"byte(s)"
296510a9d1cSstephan              );
297510a9d1cSstephan              arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
2989892883eSstephan              break;
2999892883eSstephan            }
3009892883eSstephan            case capi.SQLITE_NULL:
3019892883eSstephan              arg = null; break;
3029892883eSstephan            default:
3039892883eSstephan              toss3("Unhandled sqlite3_value_type()",valType,
3049892883eSstephan                    "is possibly indicative of incorrect",
3059892883eSstephan                    "pointer size assumption.");
3069892883eSstephan        }
3079892883eSstephan        tgt.push(arg);
3089892883eSstephan      }
3099892883eSstephan      return tgt;
310*ef9cd12eSstephan    }/*__udfConvertArgs()*/;
3119892883eSstephan
312*ef9cd12eSstephan    const __udfSetError = (pCtx, e)=>{
313510a9d1cSstephan      if(e instanceof sqlite3.WasmAllocError){
314510a9d1cSstephan        capi.sqlite3_result_error_nomem(pCtx);
3159892883eSstephan      }else{
316510a9d1cSstephan        capi.sqlite3_result_error(pCtx, e.message, -1);
3179892883eSstephan      }
3189892883eSstephan    };
3199892883eSstephan
3209892883eSstephan    const __xFunc = function(callback){
321510a9d1cSstephan      return function(pCtx, argc, pArgv){
322*ef9cd12eSstephan        try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
323510a9d1cSstephan        catch(e){
324510a9d1cSstephan          //console.error('xFunc() caught:',e);
325*ef9cd12eSstephan          __udfSetError(pCtx, e);
326510a9d1cSstephan        }
3279892883eSstephan      };
3289892883eSstephan    };
3299892883eSstephan
33092ede964Sstephan    const __xInverseAndStep = function(callback){
331510a9d1cSstephan      return function(pCtx, argc, pArgv){
332*ef9cd12eSstephan        try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
333*ef9cd12eSstephan        catch(e){ __udfSetError(pCtx, e) }
3349892883eSstephan      };
3359892883eSstephan    };
3369892883eSstephan
33792ede964Sstephan    const __xFinalAndValue = function(callback){
338510a9d1cSstephan      return function(pCtx){
339*ef9cd12eSstephan        try{ __udfSetResult(pCtx, callback(pCtx)) }
340*ef9cd12eSstephan        catch(e){ __udfSetError(pCtx, e) }
3419892883eSstephan      };
3429892883eSstephan    };
3439892883eSstephan
3449892883eSstephan    const __xDestroy = function(callback){
3459892883eSstephan      return function(pVoid){
3469892883eSstephan        try{ callback(pVoid) }
34792ede964Sstephan        catch(e){ console.error("UDF xDestroy method threw:",e) }
34892ede964Sstephan      };
34992ede964Sstephan    };
35092ede964Sstephan
35192ede964Sstephan    const __xMap = Object.assign(Object.create(null), {
35292ede964Sstephan      xFunc:    {sig:'v(pip)', f:__xFunc},
35392ede964Sstephan      xStep:    {sig:'v(pip)', f:__xInverseAndStep},
35492ede964Sstephan      xInverse: {sig:'v(pip)', f:__xInverseAndStep},
35592ede964Sstephan      xFinal:   {sig:'v(p)',   f:__xFinalAndValue},
35692ede964Sstephan      xValue:   {sig:'v(p)',   f:__xFinalAndValue},
35792ede964Sstephan      xDestroy: {sig:'v(p)',   f:__xDestroy}
35892ede964Sstephan    });
35992ede964Sstephan
36092ede964Sstephan    const __xWrapFuncs = function(theFuncs, tgtUninst){
36192ede964Sstephan      const rc = []
36292ede964Sstephan      let k;
36392ede964Sstephan      for(k in theFuncs){
36492ede964Sstephan        let fArg = theFuncs[k];
36592ede964Sstephan        if('function'===typeof fArg){
36692ede964Sstephan          const w = __xMap[k];
36792ede964Sstephan          fArg = wasm.installFunction(w.sig, w.f(fArg));
36892ede964Sstephan          tgtUninst.push(fArg);
3699892883eSstephan        }
37092ede964Sstephan        rc.push(fArg);
37192ede964Sstephan      }
37292ede964Sstephan      return rc;
3739892883eSstephan    };
37492ede964Sstephan
3759892883eSstephan    /* Documented in the api object's initializer. */
3769892883eSstephan    capi.sqlite3_create_function_v2 = function f(
3779892883eSstephan      pDb, funcName, nArg, eTextRep, pApp,
3789892883eSstephan      xFunc,   //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
3799892883eSstephan      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
3809892883eSstephan      xFinal,  //void (*xFinal)(sqlite3_context*)
3819892883eSstephan      xDestroy //void (*xDestroy)(void*)
3829892883eSstephan    ){
38392ede964Sstephan      if(f.length!==arguments.length){
38492ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length);
3859892883eSstephan      }
3869892883eSstephan      /* Wrap the callbacks in a WASM-bound functions... */
3879892883eSstephan      const wasm = capi.wasm;
38892ede964Sstephan      const uninstall = [/*funcs to uninstall on error*/];
3899892883eSstephan      let rc;
3909892883eSstephan      try{
39192ede964Sstephan        const funcArgs =  __xWrapFuncs({xFunc, xStep, xFinal, xDestroy},
39292ede964Sstephan                                       uninstall);
3939892883eSstephan        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
3949892883eSstephan                                   pApp, ...funcArgs);
3959892883eSstephan      }catch(e){
3969892883eSstephan        console.error("sqlite3_create_function_v2() setup threw:",e);
3979892883eSstephan        for(let v of uninstall){
3989892883eSstephan          wasm.uninstallFunction(v);
3999892883eSstephan        }
4009892883eSstephan        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
4019892883eSstephan                                        "Creation of UDF threw: "+e.message);
4029892883eSstephan      }
4039892883eSstephan      return rc;
4049892883eSstephan    };
4059892883eSstephan
40692ede964Sstephan    capi.sqlite3_create_function = function f(
4079892883eSstephan      pDb, funcName, nArg, eTextRep, pApp,
4089892883eSstephan      xFunc, xStep, xFinal
4099892883eSstephan    ){
41092ede964Sstephan      return (f.length===arguments.length)
41192ede964Sstephan        ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
41292ede964Sstephan                                          pApp, xFunc, xStep, xFinal, 0)
41392ede964Sstephan        : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length);
4149892883eSstephan    };
41592ede964Sstephan
41692ede964Sstephan    /* Documented in the api object's initializer. */
41792ede964Sstephan    capi.sqlite3_create_window_function = function f(
41892ede964Sstephan      pDb, funcName, nArg, eTextRep, pApp,
41992ede964Sstephan      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
42092ede964Sstephan      xFinal,  //void (*xFinal)(sqlite3_context*)
42192ede964Sstephan      xValue,  //void (*xFinal)(sqlite3_context*)
42292ede964Sstephan      xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**)
42392ede964Sstephan      xDestroy //void (*xDestroy)(void*)
42492ede964Sstephan    ){
42592ede964Sstephan      if(f.length!==arguments.length){
42692ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length);
42792ede964Sstephan      }
42892ede964Sstephan      /* Wrap the callbacks in a WASM-bound functions... */
42992ede964Sstephan      const wasm = capi.wasm;
43092ede964Sstephan      const uninstall = [/*funcs to uninstall on error*/];
43192ede964Sstephan      let rc;
43292ede964Sstephan      try{
43392ede964Sstephan        const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy},
43492ede964Sstephan                                      uninstall);
43592ede964Sstephan        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
43692ede964Sstephan                                   pApp, ...funcArgs);
43792ede964Sstephan      }catch(e){
43892ede964Sstephan        console.error("sqlite3_create_function_v2() setup threw:",e);
43992ede964Sstephan        for(let v of uninstall){
44092ede964Sstephan          wasm.uninstallFunction(v);
44192ede964Sstephan        }
44292ede964Sstephan        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
44392ede964Sstephan                                        "Creation of UDF threw: "+e.message);
44492ede964Sstephan      }
44592ede964Sstephan      return rc;
44692ede964Sstephan    };
447510a9d1cSstephan    /**
448510a9d1cSstephan       A helper for UDFs implemented in JS and bound to WASM by the
449*ef9cd12eSstephan       client. Given a JS value, udfSetResult(pCtx,X) calls one of the
450510a9d1cSstephan       sqlite3_result_xyz(pCtx,...)  routines, depending on X's data
451510a9d1cSstephan       type:
452510a9d1cSstephan
453510a9d1cSstephan       - `null`: sqlite3_result_null()
454510a9d1cSstephan       - `boolean`: sqlite3_result_int()
455510a9d1cSstephan       - `number': sqlite3_result_int() or sqlite3_result_double()
456510a9d1cSstephan       - `string`: sqlite3_result_text()
457510a9d1cSstephan       - Uint8Array or Int8Array: sqlite3_result_blob()
458510a9d1cSstephan
459510a9d1cSstephan       Anything else triggers sqlite3_result_error().
460510a9d1cSstephan    */
461*ef9cd12eSstephan    capi.sqlite3_create_function_v2.udfSetResult =
462*ef9cd12eSstephan      capi.sqlite3_create_function.udfSetResult =
463*ef9cd12eSstephan      capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
464510a9d1cSstephan
465510a9d1cSstephan    /**
466510a9d1cSstephan       A helper for UDFs implemented in JS and bound to WASM by the
467510a9d1cSstephan       client. When passed the
468510a9d1cSstephan       (argc,argv) values from the UDF-related functions which receive
469510a9d1cSstephan       them (xFunc, xStep, xInverse), it creates a JS array
470510a9d1cSstephan       representing those arguments, converting each to JS in a manner
471510a9d1cSstephan       appropriate to its data type: numeric, text, blob
472510a9d1cSstephan       (Uint8Array()), or null.
473510a9d1cSstephan
474510a9d1cSstephan       Results are undefined if it's passed anything other than those
475510a9d1cSstephan       two arguments from those specific contexts.
476510a9d1cSstephan
477510a9d1cSstephan       Thus an argc of 4 will result in a length-4 array containing
478510a9d1cSstephan       the converted values from the corresponding argv.
479510a9d1cSstephan
480510a9d1cSstephan       The conversion will throw only on allocation error or an internal
481510a9d1cSstephan       error.
482510a9d1cSstephan    */
483*ef9cd12eSstephan    capi.sqlite3_create_function_v2.udfConvertArgs =
484*ef9cd12eSstephan      capi.sqlite3_create_function.udfConvertArgs =
485*ef9cd12eSstephan      capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
486510a9d1cSstephan
487510a9d1cSstephan    /**
488510a9d1cSstephan       A helper for UDFs implemented in JS and bound to WASM by the
489510a9d1cSstephan       client. It expects to be a passed `(sqlite3_context*, Error)`
490510a9d1cSstephan       (i.e. an exception object). And it sets the current UDF's
491510a9d1cSstephan       result to sqlite3_result_error_nomem() or sqlite3_result_error(),
492510a9d1cSstephan       depending on whether the 2nd argument is a
493510a9d1cSstephan       sqlite3.WasmAllocError object or not.
494510a9d1cSstephan    */
495*ef9cd12eSstephan    capi.sqlite3_create_function_v2.udfSetError =
496*ef9cd12eSstephan      capi.sqlite3_create_function.udfSetError =
497*ef9cd12eSstephan      capi.sqlite3_create_window_function.udfSetError = __udfSetError;
49892ede964Sstephan
49992ede964Sstephan  }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
5009892883eSstephan
501e67a0f40Sstephan  if(1){/* Special-case handling of sqlite3_prepare_v2() and
502e67a0f40Sstephan           sqlite3_prepare_v3() */
503e67a0f40Sstephan    /**
5043961b263Sstephan       Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
5053961b263Sstephan    */
5063961b263Sstephan    const __prepare = Object.create(null);
5073961b263Sstephan    /**
5083961b263Sstephan       This binding expects a JS string as its 2nd argument and
5093961b263Sstephan       null as its final argument. In order to compile multiple
5103961b263Sstephan       statements from a single string, the "full" impl (see
5113961b263Sstephan       below) must be used.
5123961b263Sstephan    */
5133961b263Sstephan    __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
5143961b263Sstephan                                 "int", ["sqlite3*", "string",
515453af2f6Sstephan                                         "int"/*ignored for this impl!*/,
5163961b263Sstephan                                         "int", "**",
5173961b263Sstephan                                         "**"/*MUST be 0 or null or undefined!*/]);
5183961b263Sstephan    /**
5193961b263Sstephan       Impl which requires that the 2nd argument be a pointer
5203961b263Sstephan       to the SQL string, instead of being converted to a
5213961b263Sstephan       string. This variant is necessary for cases where we
5223961b263Sstephan       require a non-NULL value for the final argument
5233961b263Sstephan       (exec()'ing multiple statements from one input
5243961b263Sstephan       string). For simpler cases, where only the first
5253961b263Sstephan       statement in the SQL string is required, the wrapper
5263961b263Sstephan       named sqlite3_prepare_v2() is sufficient and easier to
5273961b263Sstephan       use because it doesn't require dealing with pointers.
5283961b263Sstephan    */
5293961b263Sstephan    __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
5303961b263Sstephan                                "int", ["sqlite3*", "*", "int", "int",
5313961b263Sstephan                                        "**", "**"]);
5323961b263Sstephan
5333961b263Sstephan    /* Documented in the api object's initializer. */
5343961b263Sstephan    capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
53592ede964Sstephan      if(f.length!==arguments.length){
53692ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
537e67a0f40Sstephan      }
538e67a0f40Sstephan      const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
539e67a0f40Sstephan      switch(typeof xSql){
540e67a0f40Sstephan          case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
541e67a0f40Sstephan          case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
5423961b263Sstephan          default:
5433961b263Sstephan            return util.sqlite3_wasm_db_error(
5443961b263Sstephan              pDb, capi.SQLITE_MISUSE,
5453961b263Sstephan              "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
5463961b263Sstephan            );
5473961b263Sstephan      }
5483961b263Sstephan    };
5493961b263Sstephan
550e67a0f40Sstephan    /* Documented in the api object's initializer. */
55192ede964Sstephan    capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
55292ede964Sstephan      return (f.length===arguments.length)
553e67a0f40Sstephan        ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
55492ede964Sstephan        : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length);
555e67a0f40Sstephan    };
556e67a0f40Sstephan  }/*sqlite3_prepare_v2/v3()*/;
5573961b263Sstephan
55863e9ec2fSstephan  if(1){// Extend wasm.pstack, now that the wasm utils are installed
55963e9ec2fSstephan    /**
56063e9ec2fSstephan       Allocates n chunks, each sz bytes, as a single memory block and
56163e9ec2fSstephan       returns the addresses as an array of n element, each holding
56263e9ec2fSstephan       the address of one chunk.
56363e9ec2fSstephan
56463e9ec2fSstephan       Throws a WasmAllocError if allocation fails.
56563e9ec2fSstephan
56663e9ec2fSstephan       Example:
56763e9ec2fSstephan
56863e9ec2fSstephan       ```
56963e9ec2fSstephan       const [p1, p2, p3] = wasm.pstack.allocChunks(3,4);
57063e9ec2fSstephan       ```
57163e9ec2fSstephan    */
57263e9ec2fSstephan    wasm.pstack.allocChunks = (n,sz)=>{
57363e9ec2fSstephan      const mem = wasm.pstack.alloc(n * sz);
57463e9ec2fSstephan      const rc = [];
57563e9ec2fSstephan      let i = 0, offset = 0;
57663e9ec2fSstephan      for(; i < n; offset = (sz * ++i)){
57763e9ec2fSstephan        rc.push(mem + offset);
57863e9ec2fSstephan      }
57963e9ec2fSstephan      return rc;
58063e9ec2fSstephan    };
58163e9ec2fSstephan
58263e9ec2fSstephan    /**
58363e9ec2fSstephan       A convenience wrapper for allocChunks() which sizes each chunks
58463e9ec2fSstephan       as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
5859fdce595Sstephan       safePtrSize is falsy).
58663e9ec2fSstephan
58763e9ec2fSstephan       How it returns its result differs depending on its first
58863e9ec2fSstephan       argument: if it's 1, it returns a single pointer value. If it's
58963e9ec2fSstephan       more than 1, it returns the same as allocChunks().
59063e9ec2fSstephan
5919fdce595Sstephan       When a returned pointers will refer to a 64-bit value, e.g. a
5929fdce595Sstephan       double or int64, and that value must be written or fetched,
59363e9ec2fSstephan       e.g. using wasm.setMemValue() or wasm.getMemValue(), it is
59463e9ec2fSstephan       important that the pointer in question be aligned to an 8-byte
59563e9ec2fSstephan       boundary or else it will not be fetched or written properly and
59663e9ec2fSstephan       will corrupt or read neighboring memory.
59763e9ec2fSstephan
5989fdce595Sstephan       However, when all pointers involved point to "small" data, it
5999fdce595Sstephan       is safe to pass a falsy value to save to memory.
60063e9ec2fSstephan    */
60163e9ec2fSstephan    wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{
60263e9ec2fSstephan      return 1===n
60363e9ec2fSstephan        ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof)
60463e9ec2fSstephan        : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof);
60563e9ec2fSstephan    };
60663e9ec2fSstephan  }/*wasm.pstack filler*/
60763e9ec2fSstephan
6083961b263Sstephan  /**
6093961b263Sstephan     Install JS<->C struct bindings for the non-opaque struct types we
6103961b263Sstephan     need... */
6113961b263Sstephan  sqlite3.StructBinder = self.Jaccwabyt({
6123961b263Sstephan    heap: 0 ? wasm.memory : wasm.heap8u,
6133961b263Sstephan    alloc: wasm.alloc,
6143961b263Sstephan    dealloc: wasm.dealloc,
6153961b263Sstephan    functionTable: wasm.functionTable,
6163961b263Sstephan    bigIntEnabled: wasm.bigIntEnabled,
6173961b263Sstephan    memberPrefix: '$'
6183961b263Sstephan  });
6193961b263Sstephan  delete self.Jaccwabyt;
6203961b263Sstephan
6213961b263Sstephan  {/* Import C-level constants and structs... */
6223961b263Sstephan    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
6233961b263Sstephan    if(!cJson){
6243961b263Sstephan      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
6253961b263Sstephan           "static buffer size!");
6263961b263Sstephan    }
6273961b263Sstephan    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
6283961b263Sstephan    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
6293961b263Sstephan    for(const t of ['access', 'blobFinalizers', 'dataTypes',
6305b915007Sstephan                    'encodings', 'fcntl', 'flock', 'ioCap',
6313961b263Sstephan                    'openFlags', 'prepareFlags', 'resultCodes',
63253d4e01dSstephan                    'serialize', 'syncFlags', 'udfFlags',
63353d4e01dSstephan                    'version'
6343961b263Sstephan                   ]){
635f75c0c70Sstephan      for(const e of Object.entries(wasm.ctype[t])){
636f75c0c70Sstephan        // ^^^ [k,v] there triggers a buggy code transormation via one
637f75c0c70Sstephan        // of the Emscripten-driven optimizers.
638f75c0c70Sstephan        capi[e[0]] = e[1];
6393961b263Sstephan      }
6403961b263Sstephan    }
6411f095d48Sstephan    const __rcMap = Object.create(null);
6421f095d48Sstephan    for(const t of ['resultCodes']){
6431f095d48Sstephan      for(const e of Object.entries(wasm.ctype[t])){
6441f095d48Sstephan        __rcMap[e[1]] = e[0];
6451f095d48Sstephan      }
6461f095d48Sstephan    }
6471f095d48Sstephan    /**
6481f095d48Sstephan       For the given integer, returns the SQLITE_xxx result code as a
6491f095d48Sstephan       string, or undefined if no such mapping is found.
6501f095d48Sstephan    */
65132781427Sstephan    capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc];
6523961b263Sstephan    /* Bind all registered C-side structs... */
6533961b263Sstephan    for(const s of wasm.ctype.structs){
6543961b263Sstephan      capi[s.name] = sqlite3.StructBinder(s);
6553961b263Sstephan    }
6561f095d48Sstephan  }/*end C constant imports*/
657d18f1bbfSstephan
658d18f1bbfSstephan  sqlite3.version = Object.assign(Object.create(null),{
659d18f1bbfSstephan    library: sqlite3.capi.sqlite3_libversion(),
660d18f1bbfSstephan    sourceId: sqlite3.capi.sqlite3_sourceid()
661d18f1bbfSstephan  });
662e3cd6760Sstephan});
663e67a0f40Sstephan
664