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;
238948fbeeSstephan  const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
248948fbeeSstephan  self.WhWasmUtilInstaller(wasm);
253961b263Sstephan  delete self.WhWasmUtilInstaller;
263961b263Sstephan
273961b263Sstephan  /**
287ff8da87Sstephan     Install JS<->C struct bindings for the non-opaque struct types we
297ff8da87Sstephan     need... */
307ff8da87Sstephan  sqlite3.StructBinder = self.Jaccwabyt({
317ff8da87Sstephan    heap: 0 ? wasm.memory : wasm.heap8u,
327ff8da87Sstephan    alloc: wasm.alloc,
337ff8da87Sstephan    dealloc: wasm.dealloc,
347ff8da87Sstephan    functionTable: wasm.functionTable,
357ff8da87Sstephan    bigIntEnabled: wasm.bigIntEnabled,
367ff8da87Sstephan    memberPrefix: '$'
377ff8da87Sstephan  });
387ff8da87Sstephan  delete self.Jaccwabyt;
397ff8da87Sstephan
407ff8da87Sstephan  if(0){
417ff8da87Sstephan    /*  "The problem" is that the following isn't even remotely
427ff8da87Sstephan        type-safe. OTOH, nothing about WASM pointers is. */
437ff8da87Sstephan    const argPointer = wasm.xWrap.argAdapter('*');
447ff8da87Sstephan    wasm.xWrap.argAdapter('StructType', (v)=>{
457ff8da87Sstephan      if(v && v.constructor && v instanceof StructBinder.StructType){
467ff8da87Sstephan        v = v.pointer;
473961b263Sstephan      }
48de868175Sstephan      return wasm.isPtr(v)
497ff8da87Sstephan        ? argPointer(v)
507ff8da87Sstephan        : toss("Invalid (object) type for StructType-type argument.");
517ff8da87Sstephan    });
527ff8da87Sstephan  }
533961b263Sstephan
540f32760eSstephan  {/* Convert Arrays and certain TypedArrays to strings for
55e67a0f40Sstephan      'flexible-string'-type arguments */
56e67a0f40Sstephan    const xString = wasm.xWrap.argAdapter('string');
57e67a0f40Sstephan    wasm.xWrap.argAdapter(
58ef9cd12eSstephan      'flexible-string', (v)=>xString(util.flexibleString(v))
59e67a0f40Sstephan    );
60e67a0f40Sstephan  }
61e67a0f40Sstephan
62e67a0f40Sstephan  if(1){// WhWasmUtil.xWrap() bindings...
633961b263Sstephan    /**
6463e9ec2fSstephan       Add some descriptive xWrap() aliases for '*' intended to (A)
6563e9ec2fSstephan       initially improve readability/correctness of capi.signatures
6663e9ec2fSstephan       and (B) eventually perhaps provide automatic conversion from
6763e9ec2fSstephan       higher-level representations, e.g. capi.sqlite3_vfs to
6863e9ec2fSstephan       `sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
693961b263Sstephan    */
703961b263Sstephan    const aPtr = wasm.xWrap.argAdapter('*');
71510a9d1cSstephan    wasm.xWrap.argAdapter('sqlite3*', aPtr)
72510a9d1cSstephan    ('sqlite3_stmt*', aPtr)
73510a9d1cSstephan    ('sqlite3_context*', aPtr)
74510a9d1cSstephan    ('sqlite3_value*', aPtr)
75842c5ee8Sstephan    ('sqlite3_vfs*', aPtr)
76510a9d1cSstephan    ('void*', aPtr);
77510a9d1cSstephan    wasm.xWrap.resultAdapter('sqlite3*', aPtr)
78510a9d1cSstephan    ('sqlite3_context*', aPtr)
79842c5ee8Sstephan    ('sqlite3_stmt*', aPtr)
80842c5ee8Sstephan    ('sqlite3_vfs*', aPtr)
81510a9d1cSstephan    ('void*', aPtr);
823961b263Sstephan
833961b263Sstephan    /**
843961b263Sstephan       Populate api object with sqlite3_...() by binding the "raw" wasm
853961b263Sstephan       exports into type-converting proxies using wasm.xWrap().
863961b263Sstephan    */
873961b263Sstephan    for(const e of wasm.bindingSignatures){
883961b263Sstephan      capi[e[0]] = wasm.xWrap.apply(null, e);
893961b263Sstephan    }
9073079dbaSstephan    for(const e of wasm.bindingSignatures.wasm){
918948fbeeSstephan      wasm[e[0]] = wasm.xWrap.apply(null, e);
9273079dbaSstephan    }
933961b263Sstephan
94e3cd6760Sstephan    /* For C API functions which cannot work properly unless
953961b263Sstephan       wasm.bigIntEnabled is true, install a bogus impl which
963961b263Sstephan       throws if called when bigIntEnabled is false. */
973961b263Sstephan    const fI64Disabled = function(fname){
983961b263Sstephan      return ()=>toss(fname+"() disabled due to lack",
993961b263Sstephan                      "of BigInt support in this build.");
1003961b263Sstephan    };
1013961b263Sstephan    for(const e of wasm.bindingSignatures.int64){
1023961b263Sstephan      capi[e[0]] = wasm.bigIntEnabled
1033961b263Sstephan        ? wasm.xWrap.apply(null, e)
1043961b263Sstephan        : fI64Disabled(e[0]);
1053961b263Sstephan    }
1063961b263Sstephan
107f03ddccaSstephan    /* There's no(?) need to expose bindingSignatures to clients,
108f03ddccaSstephan       implicitly making it part of the public interface. */
109f03ddccaSstephan    delete wasm.bindingSignatures;
110f03ddccaSstephan
1113961b263Sstephan    if(wasm.exports.sqlite3_wasm_db_error){
1128948fbeeSstephan      util.sqlite3_wasm_db_error = wasm.xWrap(
1133961b263Sstephan        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
1143961b263Sstephan      );
1153961b263Sstephan    }else{
1163961b263Sstephan      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
1173961b263Sstephan        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
1183961b263Sstephan        return errCode;
1193961b263Sstephan      };
1203961b263Sstephan    }
1213961b263Sstephan
122f03ddccaSstephan  }/*xWrap() bindings*/;
123f03ddccaSstephan
1243961b263Sstephan  /**
1253961b263Sstephan     When registering a VFS and its related components it may be
1263961b263Sstephan     necessary to ensure that JS keeps a reference to them to keep
1273961b263Sstephan     them from getting garbage collected. Simply pass each such value
1283961b263Sstephan     to this function and a reference will be held to it for the life
1293961b263Sstephan     of the app.
1303961b263Sstephan  */
1313961b263Sstephan  capi.sqlite3_vfs_register.addReference = function f(...args){
1323961b263Sstephan    if(!f._) f._ = [];
1333961b263Sstephan    f._.push(...args);
1343961b263Sstephan  };
1353961b263Sstephan
1363961b263Sstephan  /**
137e67a0f40Sstephan     Internal helper to assist in validating call argument counts in
138e67a0f40Sstephan     the hand-written sqlite3_xyz() wrappers. We do this only for
139e67a0f40Sstephan     consistency with non-special-case wrappings.
140e67a0f40Sstephan  */
141e67a0f40Sstephan  const __dbArgcMismatch = (pDb,f,n)=>{
142e67a0f40Sstephan    return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
143e67a0f40Sstephan                                              f+"() requires "+n+" argument"+
144*4df2ab57Sstephan                                              (1===n?"":'s')+".");
145e67a0f40Sstephan  };
146e67a0f40Sstephan
147e67a0f40Sstephan  /**
148e67a0f40Sstephan     Helper for flexible-string conversions which require a
149e67a0f40Sstephan     byte-length counterpart argument. Passed a value and its
150e67a0f40Sstephan     ostensible length, this function returns [V,N], where V
151e67a0f40Sstephan     is either v or a transformed copy of v and N is either n,
152e67a0f40Sstephan     -1, or the byte length of v (if it's a byte array).
153e67a0f40Sstephan  */
154e67a0f40Sstephan  const __flexiString = function(v,n){
155e67a0f40Sstephan    if('string'===typeof v){
156e67a0f40Sstephan      n = -1;
157e67a0f40Sstephan    }else if(util.isSQLableTypedArray(v)){
158e67a0f40Sstephan      n = v.byteLength;
159e67a0f40Sstephan      v = util.typedArrayToString(v);
160e67a0f40Sstephan    }else if(Array.isArray(v)){
161*4df2ab57Sstephan      v = v.join("");
162e67a0f40Sstephan      n = -1;
163e67a0f40Sstephan    }
164e67a0f40Sstephan    return [v, n];
165e67a0f40Sstephan  };
166e67a0f40Sstephan
167e67a0f40Sstephan  if(1){/* Special-case handling of sqlite3_exec() */
168e67a0f40Sstephan    const __exec = wasm.xWrap("sqlite3_exec", "int",
169e67a0f40Sstephan                              ["sqlite3*", "flexible-string", "*", "*", "**"]);
170e67a0f40Sstephan    /* Documented in the api object's initializer. */
17192ede964Sstephan    capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
17292ede964Sstephan      if(f.length!==arguments.length){
17392ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
174e67a0f40Sstephan      }else if('function' !== typeof callback){
175e67a0f40Sstephan        return __exec(pDb, sql, callback, pVoid, pErrMsg);
176e67a0f40Sstephan      }
177e67a0f40Sstephan      /* Wrap the callback in a WASM-bound function and convert the callback's
178e67a0f40Sstephan         `(char**)` arguments to arrays of strings... */
179e67a0f40Sstephan      const cbwrap = function(pVoid, nCols, pColVals, pColNames){
180e67a0f40Sstephan        let rc = capi.SQLITE_ERROR;
181e67a0f40Sstephan        try {
182e67a0f40Sstephan          let aVals = [], aNames = [], i = 0, offset = 0;
183e67a0f40Sstephan          for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
184e67a0f40Sstephan            aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
185e67a0f40Sstephan            aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
186e67a0f40Sstephan          }
187e67a0f40Sstephan          rc = callback(pVoid, nCols, aVals, aNames) | 0;
188e67a0f40Sstephan          /* The first 2 args of the callback are useless for JS but
189e67a0f40Sstephan             we want the JS mapping of the C API to be as close to the
190e67a0f40Sstephan             C API as possible. */
191e67a0f40Sstephan        }catch(e){
192e67a0f40Sstephan          /* If we set the db error state here, the higher-level exec() call
193e67a0f40Sstephan             replaces it with its own, so we have no way of reporting the
194e67a0f40Sstephan             exception message except the console. We must not propagate
195e67a0f40Sstephan             exceptions through the C API. */
196e67a0f40Sstephan        }
197e67a0f40Sstephan        return rc;
198e67a0f40Sstephan      };
199e67a0f40Sstephan      let pFunc, rc;
200e67a0f40Sstephan      try{
201e67a0f40Sstephan        pFunc = wasm.installFunction("ipipp", cbwrap);
202e67a0f40Sstephan        rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
203e67a0f40Sstephan      }catch(e){
204e67a0f40Sstephan        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
205e67a0f40Sstephan                                        "Error running exec(): "+e.message);
206e67a0f40Sstephan      }finally{
207e67a0f40Sstephan        if(pFunc) wasm.uninstallFunction(pFunc);
208e67a0f40Sstephan      }
209e67a0f40Sstephan      return rc;
210e67a0f40Sstephan    };
211e67a0f40Sstephan  }/*sqlite3_exec() proxy*/;
212e67a0f40Sstephan
21392ede964Sstephan  if(1){/* Special-case handling of sqlite3_create_function_v2()
21492ede964Sstephan           and sqlite3_create_window_function() */
2159892883eSstephan    const sqlite3CreateFunction = wasm.xWrap(
2169892883eSstephan      "sqlite3_create_function_v2", "int",
21792ede964Sstephan      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
21892ede964Sstephan       "int"/*eTextRep*/, "*"/*pApp*/,
21992ede964Sstephan       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/]
22092ede964Sstephan    );
22192ede964Sstephan    const sqlite3CreateWindowFunction = wasm.xWrap(
22292ede964Sstephan      "sqlite3_create_window_function", "int",
22392ede964Sstephan      ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
22492ede964Sstephan       "int"/*eTextRep*/, "*"/*pApp*/,
22592ede964Sstephan       "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/,
22692ede964Sstephan       "*"/*xInverse*/, "*"/*xDestroy*/]
2279892883eSstephan    );
228510a9d1cSstephan
229ef9cd12eSstephan    const __udfSetResult = function(pCtx, val){
230ef9cd12eSstephan      //console.warn("udfSetResult",typeof val, val);
2319892883eSstephan      switch(typeof val) {
2326f3286caSstephan          case 'undefined':
2336f3286caSstephan            /* Assume that the client already called sqlite3_result_xxx(). */
2346f3286caSstephan            break;
2359892883eSstephan          case 'boolean':
236510a9d1cSstephan            capi.sqlite3_result_int(pCtx, val ? 1 : 0);
237510a9d1cSstephan            break;
238510a9d1cSstephan          case 'bigint':
239510a9d1cSstephan            if(wasm.bigIntEnabled){
240510a9d1cSstephan              if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
241510a9d1cSstephan              else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
242510a9d1cSstephan            }else if(util.bigIntFits32(val)){
243510a9d1cSstephan              capi.sqlite3_result_int(pCtx, Number(val));
244510a9d1cSstephan            }else if(util.bigIntFitsDouble(val)){
245510a9d1cSstephan              capi.sqlite3_result_double(pCtx, Number(val));
246510a9d1cSstephan            }else{
247510a9d1cSstephan              toss3("BigInt value",val.toString(),"is too BigInt.");
248510a9d1cSstephan            }
2499892883eSstephan            break;
2509892883eSstephan          case 'number': {
2519892883eSstephan            (util.isInt32(val)
2529892883eSstephan             ? capi.sqlite3_result_int
253510a9d1cSstephan             : capi.sqlite3_result_double)(pCtx, val);
2549892883eSstephan            break;
2559892883eSstephan          }
2569892883eSstephan          case 'string':
257510a9d1cSstephan            capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
2589892883eSstephan            break;
2599892883eSstephan          case 'object':
26092ede964Sstephan            if(null===val/*yes, typeof null === 'object'*/) {
261510a9d1cSstephan              capi.sqlite3_result_null(pCtx);
2629892883eSstephan              break;
2639892883eSstephan            }else if(util.isBindableTypedArray(val)){
2649892883eSstephan              const pBlob = wasm.allocFromTypedArray(val);
26592ede964Sstephan              capi.sqlite3_result_blob(
266510a9d1cSstephan                pCtx, pBlob, val.byteLength,
26792ede964Sstephan                wasm.exports[sqlite3.config.deallocExportName]
26892ede964Sstephan              );
2699892883eSstephan              break;
2709892883eSstephan            }
2719892883eSstephan            // else fall through
2729892883eSstephan          default:
273510a9d1cSstephan          toss3("Don't not how to handle this UDF result value:",(typeof val), val);
2749892883eSstephan      };
275ef9cd12eSstephan    }/*__udfSetResult()*/;
276510a9d1cSstephan
277ef9cd12eSstephan    const __udfConvertArgs = function(argc, pArgv){
2789892883eSstephan      let i, pVal, valType, arg;
2799892883eSstephan      const tgt = [];
2809892883eSstephan      for(i = 0; i < argc; ++i){
2819892883eSstephan        pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
2829892883eSstephan        /**
2839892883eSstephan           Curiously: despite ostensibly requiring 8-byte
2849892883eSstephan           alignment, the pArgv array is parcelled into chunks of
2859892883eSstephan           4 bytes (1 pointer each). The values those point to
2869892883eSstephan           have 8-byte alignment but the individual argv entries
2879892883eSstephan           do not.
2889892883eSstephan        */
2899892883eSstephan        valType = capi.sqlite3_value_type(pVal);
2909892883eSstephan        switch(valType){
2919892883eSstephan            case capi.SQLITE_INTEGER:
292510a9d1cSstephan              if(wasm.bigIntEnabled){
293510a9d1cSstephan                arg = capi.sqlite3_value_int64(pVal);
294510a9d1cSstephan                if(util.bigIntFitsDouble(arg)) arg = Number(arg);
295510a9d1cSstephan              }
296510a9d1cSstephan              else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
297510a9d1cSstephan              break;
2989892883eSstephan            case capi.SQLITE_FLOAT:
2999892883eSstephan              arg = capi.sqlite3_value_double(pVal);
3009892883eSstephan              break;
3019892883eSstephan            case capi.SQLITE_TEXT:
3029892883eSstephan              arg = capi.sqlite3_value_text(pVal);
3039892883eSstephan              break;
3049892883eSstephan            case capi.SQLITE_BLOB:{
3059892883eSstephan              const n = capi.sqlite3_value_bytes(pVal);
3069892883eSstephan              const pBlob = capi.sqlite3_value_blob(pVal);
307510a9d1cSstephan              if(n && !pBlob) sqlite3.WasmAllocError.toss(
308510a9d1cSstephan                "Cannot allocate memory for blob argument of",n,"byte(s)"
309510a9d1cSstephan              );
310510a9d1cSstephan              arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
3119892883eSstephan              break;
3129892883eSstephan            }
3139892883eSstephan            case capi.SQLITE_NULL:
3149892883eSstephan              arg = null; break;
3159892883eSstephan            default:
3169892883eSstephan              toss3("Unhandled sqlite3_value_type()",valType,
3179892883eSstephan                    "is possibly indicative of incorrect",
3189892883eSstephan                    "pointer size assumption.");
3199892883eSstephan        }
3209892883eSstephan        tgt.push(arg);
3219892883eSstephan      }
3229892883eSstephan      return tgt;
323ef9cd12eSstephan    }/*__udfConvertArgs()*/;
3249892883eSstephan
325ef9cd12eSstephan    const __udfSetError = (pCtx, e)=>{
326510a9d1cSstephan      if(e instanceof sqlite3.WasmAllocError){
327510a9d1cSstephan        capi.sqlite3_result_error_nomem(pCtx);
3289892883eSstephan      }else{
3296f3286caSstephan        const msg = ('string'===typeof e) ? e : e.message;
3306f3286caSstephan        capi.sqlite3_result_error(pCtx, msg, -1);
3319892883eSstephan      }
3329892883eSstephan    };
3339892883eSstephan
3349892883eSstephan    const __xFunc = function(callback){
335510a9d1cSstephan      return function(pCtx, argc, pArgv){
336ef9cd12eSstephan        try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
337510a9d1cSstephan        catch(e){
338510a9d1cSstephan          //console.error('xFunc() caught:',e);
339ef9cd12eSstephan          __udfSetError(pCtx, e);
340510a9d1cSstephan        }
3419892883eSstephan      };
3429892883eSstephan    };
3439892883eSstephan
34492ede964Sstephan    const __xInverseAndStep = function(callback){
345510a9d1cSstephan      return function(pCtx, argc, pArgv){
346ef9cd12eSstephan        try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
347ef9cd12eSstephan        catch(e){ __udfSetError(pCtx, e) }
3489892883eSstephan      };
3499892883eSstephan    };
3509892883eSstephan
35192ede964Sstephan    const __xFinalAndValue = function(callback){
352510a9d1cSstephan      return function(pCtx){
353ef9cd12eSstephan        try{ __udfSetResult(pCtx, callback(pCtx)) }
354ef9cd12eSstephan        catch(e){ __udfSetError(pCtx, e) }
3559892883eSstephan      };
3569892883eSstephan    };
3579892883eSstephan
3589892883eSstephan    const __xDestroy = function(callback){
3599892883eSstephan      return function(pVoid){
3609892883eSstephan        try{ callback(pVoid) }
36192ede964Sstephan        catch(e){ console.error("UDF xDestroy method threw:",e) }
36292ede964Sstephan      };
36392ede964Sstephan    };
36492ede964Sstephan
36592ede964Sstephan    const __xMap = Object.assign(Object.create(null), {
36692ede964Sstephan      xFunc:    {sig:'v(pip)', f:__xFunc},
36792ede964Sstephan      xStep:    {sig:'v(pip)', f:__xInverseAndStep},
36892ede964Sstephan      xInverse: {sig:'v(pip)', f:__xInverseAndStep},
36992ede964Sstephan      xFinal:   {sig:'v(p)',   f:__xFinalAndValue},
37092ede964Sstephan      xValue:   {sig:'v(p)',   f:__xFinalAndValue},
37192ede964Sstephan      xDestroy: {sig:'v(p)',   f:__xDestroy}
37292ede964Sstephan    });
37392ede964Sstephan
37492ede964Sstephan    const __xWrapFuncs = function(theFuncs, tgtUninst){
37592ede964Sstephan      const rc = []
37692ede964Sstephan      let k;
37792ede964Sstephan      for(k in theFuncs){
37892ede964Sstephan        let fArg = theFuncs[k];
37992ede964Sstephan        if('function'===typeof fArg){
38092ede964Sstephan          const w = __xMap[k];
38192ede964Sstephan          fArg = wasm.installFunction(w.sig, w.f(fArg));
38292ede964Sstephan          tgtUninst.push(fArg);
3839892883eSstephan        }
38492ede964Sstephan        rc.push(fArg);
38592ede964Sstephan      }
38692ede964Sstephan      return rc;
3879892883eSstephan    };
38892ede964Sstephan
3899892883eSstephan    /* Documented in the api object's initializer. */
3909892883eSstephan    capi.sqlite3_create_function_v2 = function f(
3919892883eSstephan      pDb, funcName, nArg, eTextRep, pApp,
3929892883eSstephan      xFunc,   //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
3939892883eSstephan      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
3949892883eSstephan      xFinal,  //void (*xFinal)(sqlite3_context*)
3959892883eSstephan      xDestroy //void (*xDestroy)(void*)
3969892883eSstephan    ){
39792ede964Sstephan      if(f.length!==arguments.length){
39892ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length);
3999892883eSstephan      }
4009892883eSstephan      /* Wrap the callbacks in a WASM-bound functions... */
40192ede964Sstephan      const uninstall = [/*funcs to uninstall on error*/];
4029892883eSstephan      let rc;
4039892883eSstephan      try{
40492ede964Sstephan        const funcArgs =  __xWrapFuncs({xFunc, xStep, xFinal, xDestroy},
40592ede964Sstephan                                       uninstall);
4069892883eSstephan        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
4079892883eSstephan                                   pApp, ...funcArgs);
4089892883eSstephan      }catch(e){
4099892883eSstephan        console.error("sqlite3_create_function_v2() setup threw:",e);
4109892883eSstephan        for(let v of uninstall){
4119892883eSstephan          wasm.uninstallFunction(v);
4129892883eSstephan        }
4139892883eSstephan        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
4149892883eSstephan                                        "Creation of UDF threw: "+e.message);
4159892883eSstephan      }
4169892883eSstephan      return rc;
4179892883eSstephan    };
4189892883eSstephan
41992ede964Sstephan    capi.sqlite3_create_function = function f(
4209892883eSstephan      pDb, funcName, nArg, eTextRep, pApp,
4219892883eSstephan      xFunc, xStep, xFinal
4229892883eSstephan    ){
42392ede964Sstephan      return (f.length===arguments.length)
42492ede964Sstephan        ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
42592ede964Sstephan                                          pApp, xFunc, xStep, xFinal, 0)
42692ede964Sstephan        : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length);
4279892883eSstephan    };
42892ede964Sstephan
42992ede964Sstephan    /* Documented in the api object's initializer. */
43092ede964Sstephan    capi.sqlite3_create_window_function = function f(
43192ede964Sstephan      pDb, funcName, nArg, eTextRep, pApp,
43292ede964Sstephan      xStep,   //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
43392ede964Sstephan      xFinal,  //void (*xFinal)(sqlite3_context*)
43492ede964Sstephan      xValue,  //void (*xFinal)(sqlite3_context*)
43592ede964Sstephan      xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**)
43692ede964Sstephan      xDestroy //void (*xDestroy)(void*)
43792ede964Sstephan    ){
43892ede964Sstephan      if(f.length!==arguments.length){
43992ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length);
44092ede964Sstephan      }
44192ede964Sstephan      /* Wrap the callbacks in a WASM-bound functions... */
44292ede964Sstephan      const uninstall = [/*funcs to uninstall on error*/];
44392ede964Sstephan      let rc;
44492ede964Sstephan      try{
44592ede964Sstephan        const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy},
44692ede964Sstephan                                      uninstall);
447a6ca996eSstephan        rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
44892ede964Sstephan                                         pApp, ...funcArgs);
44992ede964Sstephan      }catch(e){
450a6ca996eSstephan        console.error("sqlite3_create_window_function() setup threw:",e);
45192ede964Sstephan        for(let v of uninstall){
45292ede964Sstephan          wasm.uninstallFunction(v);
45392ede964Sstephan        }
45492ede964Sstephan        rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
45592ede964Sstephan                                        "Creation of UDF threw: "+e.message);
45692ede964Sstephan      }
45792ede964Sstephan      return rc;
45892ede964Sstephan    };
459510a9d1cSstephan    /**
460510a9d1cSstephan       A helper for UDFs implemented in JS and bound to WASM by the
461ef9cd12eSstephan       client. Given a JS value, udfSetResult(pCtx,X) calls one of the
462510a9d1cSstephan       sqlite3_result_xyz(pCtx,...)  routines, depending on X's data
463510a9d1cSstephan       type:
464510a9d1cSstephan
465510a9d1cSstephan       - `null`: sqlite3_result_null()
466510a9d1cSstephan       - `boolean`: sqlite3_result_int()
4678d9e5955Sstephan       - `number`: sqlite3_result_int() or sqlite3_result_double()
468510a9d1cSstephan       - `string`: sqlite3_result_text()
469510a9d1cSstephan       - Uint8Array or Int8Array: sqlite3_result_blob()
4706f3286caSstephan       - `undefined`: indicates that the UDF called one of the
4716f3286caSstephan         `sqlite3_result_xyz()` routines on its own, making this
4726f3286caSstephan         function a no-op. Results are _undefined_ if this function is
4736f3286caSstephan         passed the `undefined` value but did _not_ call one of the
4746f3286caSstephan         `sqlite3_result_xyz()` routines.
475510a9d1cSstephan
476510a9d1cSstephan       Anything else triggers sqlite3_result_error().
477510a9d1cSstephan    */
478ef9cd12eSstephan    capi.sqlite3_create_function_v2.udfSetResult =
479ef9cd12eSstephan      capi.sqlite3_create_function.udfSetResult =
480ef9cd12eSstephan      capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
481510a9d1cSstephan
482510a9d1cSstephan    /**
483510a9d1cSstephan       A helper for UDFs implemented in JS and bound to WASM by the
484510a9d1cSstephan       client. When passed the
485510a9d1cSstephan       (argc,argv) values from the UDF-related functions which receive
486510a9d1cSstephan       them (xFunc, xStep, xInverse), it creates a JS array
487510a9d1cSstephan       representing those arguments, converting each to JS in a manner
488510a9d1cSstephan       appropriate to its data type: numeric, text, blob
4898d9e5955Sstephan       (Uint8Array), or null.
490510a9d1cSstephan
491510a9d1cSstephan       Results are undefined if it's passed anything other than those
492510a9d1cSstephan       two arguments from those specific contexts.
493510a9d1cSstephan
494510a9d1cSstephan       Thus an argc of 4 will result in a length-4 array containing
495510a9d1cSstephan       the converted values from the corresponding argv.
496510a9d1cSstephan
497510a9d1cSstephan       The conversion will throw only on allocation error or an internal
498510a9d1cSstephan       error.
499510a9d1cSstephan    */
500ef9cd12eSstephan    capi.sqlite3_create_function_v2.udfConvertArgs =
501ef9cd12eSstephan      capi.sqlite3_create_function.udfConvertArgs =
502ef9cd12eSstephan      capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
503510a9d1cSstephan
504510a9d1cSstephan    /**
505510a9d1cSstephan       A helper for UDFs implemented in JS and bound to WASM by the
506510a9d1cSstephan       client. It expects to be a passed `(sqlite3_context*, Error)`
5076f3286caSstephan       (an exception object or message string). And it sets the
5086f3286caSstephan       current UDF's result to sqlite3_result_error_nomem() or
5096f3286caSstephan       sqlite3_result_error(), depending on whether the 2nd argument
5106f3286caSstephan       is a sqlite3.WasmAllocError object or not.
511510a9d1cSstephan    */
512ef9cd12eSstephan    capi.sqlite3_create_function_v2.udfSetError =
513ef9cd12eSstephan      capi.sqlite3_create_function.udfSetError =
514ef9cd12eSstephan      capi.sqlite3_create_window_function.udfSetError = __udfSetError;
51592ede964Sstephan
51692ede964Sstephan  }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
5179892883eSstephan
518e67a0f40Sstephan  if(1){/* Special-case handling of sqlite3_prepare_v2() and
519e67a0f40Sstephan           sqlite3_prepare_v3() */
520e67a0f40Sstephan    /**
5213961b263Sstephan       Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
5223961b263Sstephan    */
5233961b263Sstephan    const __prepare = Object.create(null);
5243961b263Sstephan    /**
5253961b263Sstephan       This binding expects a JS string as its 2nd argument and
5263961b263Sstephan       null as its final argument. In order to compile multiple
5273961b263Sstephan       statements from a single string, the "full" impl (see
5283961b263Sstephan       below) must be used.
5293961b263Sstephan    */
5303961b263Sstephan    __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
5313961b263Sstephan                                 "int", ["sqlite3*", "string",
532453af2f6Sstephan                                         "int"/*ignored for this impl!*/,
5333961b263Sstephan                                         "int", "**",
5343961b263Sstephan                                         "**"/*MUST be 0 or null or undefined!*/]);
5353961b263Sstephan    /**
5363961b263Sstephan       Impl which requires that the 2nd argument be a pointer
5373961b263Sstephan       to the SQL string, instead of being converted to a
5383961b263Sstephan       string. This variant is necessary for cases where we
5393961b263Sstephan       require a non-NULL value for the final argument
5403961b263Sstephan       (exec()'ing multiple statements from one input
5413961b263Sstephan       string). For simpler cases, where only the first
5423961b263Sstephan       statement in the SQL string is required, the wrapper
5433961b263Sstephan       named sqlite3_prepare_v2() is sufficient and easier to
5443961b263Sstephan       use because it doesn't require dealing with pointers.
5453961b263Sstephan    */
5463961b263Sstephan    __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
5473961b263Sstephan                                "int", ["sqlite3*", "*", "int", "int",
5483961b263Sstephan                                        "**", "**"]);
5493961b263Sstephan
5503961b263Sstephan    /* Documented in the api object's initializer. */
5513961b263Sstephan    capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
55292ede964Sstephan      if(f.length!==arguments.length){
55392ede964Sstephan        return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
554e67a0f40Sstephan      }
555e67a0f40Sstephan      const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
556e67a0f40Sstephan      switch(typeof xSql){
557e67a0f40Sstephan          case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
558e67a0f40Sstephan          case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
5593961b263Sstephan          default:
5603961b263Sstephan            return util.sqlite3_wasm_db_error(
5613961b263Sstephan              pDb, capi.SQLITE_MISUSE,
5623961b263Sstephan              "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
5633961b263Sstephan            );
5643961b263Sstephan      }
5653961b263Sstephan    };
5663961b263Sstephan
567e67a0f40Sstephan    /* Documented in the api object's initializer. */
56892ede964Sstephan    capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
56992ede964Sstephan      return (f.length===arguments.length)
570e67a0f40Sstephan        ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
57192ede964Sstephan        : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length);
572e67a0f40Sstephan    };
573e67a0f40Sstephan  }/*sqlite3_prepare_v2/v3()*/;
5743961b263Sstephan
5753961b263Sstephan  {/* Import C-level constants and structs... */
5763961b263Sstephan    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
5773961b263Sstephan    if(!cJson){
5783961b263Sstephan      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
5793961b263Sstephan           "static buffer size!");
5803961b263Sstephan    }
5813961b263Sstephan    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
5823961b263Sstephan    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
5833961b263Sstephan    for(const t of ['access', 'blobFinalizers', 'dataTypes',
5845b915007Sstephan                    'encodings', 'fcntl', 'flock', 'ioCap',
5853961b263Sstephan                    'openFlags', 'prepareFlags', 'resultCodes',
5864f5bbedbSstephan                    'serialize', 'syncFlags', 'trace', 'udfFlags',
58753d4e01dSstephan                    'version'
5883961b263Sstephan                   ]){
589f75c0c70Sstephan      for(const e of Object.entries(wasm.ctype[t])){
590f75c0c70Sstephan        // ^^^ [k,v] there triggers a buggy code transormation via one
591f75c0c70Sstephan        // of the Emscripten-driven optimizers.
592f75c0c70Sstephan        capi[e[0]] = e[1];
5933961b263Sstephan      }
5943961b263Sstephan    }
5951f095d48Sstephan    const __rcMap = Object.create(null);
5961f095d48Sstephan    for(const t of ['resultCodes']){
5971f095d48Sstephan      for(const e of Object.entries(wasm.ctype[t])){
5981f095d48Sstephan        __rcMap[e[1]] = e[0];
5991f095d48Sstephan      }
6001f095d48Sstephan    }
6011f095d48Sstephan    /**
6021f095d48Sstephan       For the given integer, returns the SQLITE_xxx result code as a
6031f095d48Sstephan       string, or undefined if no such mapping is found.
6041f095d48Sstephan    */
6058a8244b5Sstephan    capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc];
6063961b263Sstephan    /* Bind all registered C-side structs... */
607d92c652aSstephan    const notThese = Object.assign(Object.create(null),{
608d92c652aSstephan      // Structs NOT to register
609d92c652aSstephan      WasmTestStruct: true
610d92c652aSstephan    });
611a6ca996eSstephan    if(!util.isUIThread()){
612a6ca996eSstephan      /* We remove the kvvfs VFS from Worker threads below. */
613a6ca996eSstephan      notThese.sqlite3_kvvfs_methods = true;
614a6ca996eSstephan    }
6153961b263Sstephan    for(const s of wasm.ctype.structs){
616d92c652aSstephan      if(!notThese[s.name]){
6173961b263Sstephan        capi[s.name] = sqlite3.StructBinder(s);
6183961b263Sstephan      }
619d92c652aSstephan    }
6201f095d48Sstephan  }/*end C constant imports*/
621d18f1bbfSstephan
622a6ca996eSstephan  const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
623a6ca996eSstephan  if( pKvvfs ){/* kvvfs-specific glue */
624a6ca996eSstephan    if(util.isUIThread()){
6254fbf90eeSstephan      const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
6264fbf90eeSstephan        wasm.exports.sqlite3_wasm_kvvfs_methods()
6274fbf90eeSstephan      );
6284fbf90eeSstephan      delete capi.sqlite3_kvvfs_methods;
6294fbf90eeSstephan
6304fbf90eeSstephan      const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
6314fbf90eeSstephan            pstack = wasm.pstack,
6324fbf90eeSstephan            pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
6334fbf90eeSstephan
6344fbf90eeSstephan      const kvvfsStorage = (zClass)=>
6354fbf90eeSstephan            ((115/*=='s'*/===wasm.getMemValue(zClass))
6364fbf90eeSstephan             ? sessionStorage : localStorage);
6374fbf90eeSstephan
63802d15a7eSstephan      /**
63902d15a7eSstephan         Implementations for members of the object referred to by
64002d15a7eSstephan         sqlite3_wasm_kvvfs_methods(). We swap out the native
64102d15a7eSstephan         implementations with these, which use localStorage or
64202d15a7eSstephan         sessionStorage for their backing store.
64302d15a7eSstephan      */
6444fbf90eeSstephan      const kvvfsImpls = {
6454fbf90eeSstephan        xRead: (zClass, zKey, zBuf, nBuf)=>{
6464fbf90eeSstephan          const stack = pstack.pointer,
6474fbf90eeSstephan                astack = wasm.scopedAllocPush();
6484fbf90eeSstephan          try {
6494fbf90eeSstephan            const zXKey = kvvfsMakeKey(zClass,zKey);
6504fbf90eeSstephan            if(!zXKey) return -3/*OOM*/;
6514fbf90eeSstephan            const jKey = wasm.cstringToJs(zXKey);
6524fbf90eeSstephan            const jV = kvvfsStorage(zClass).getItem(jKey);
6534fbf90eeSstephan            if(!jV) return -1;
6544fbf90eeSstephan            const nV = jV.length /* Note that we are relying 100% on v being
6554fbf90eeSstephan                                    ASCII so that jV.length is equal to the
6564fbf90eeSstephan                                    C-string's byte length. */;
6574fbf90eeSstephan            if(nBuf<=0) return nV;
6584fbf90eeSstephan            else if(1===nBuf){
6594fbf90eeSstephan              wasm.setMemValue(zBuf, 0);
6604fbf90eeSstephan              return nV;
6614fbf90eeSstephan            }
6624fbf90eeSstephan            const zV = wasm.scopedAllocCString(jV);
6634fbf90eeSstephan            if(nBuf > nV + 1) nBuf = nV + 1;
6644fbf90eeSstephan            wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
6654fbf90eeSstephan            wasm.setMemValue(zBuf + nBuf - 1, 0);
6664fbf90eeSstephan            return nBuf - 1;
6674fbf90eeSstephan          }catch(e){
6684fbf90eeSstephan            console.error("kvstorageRead()",e);
6694fbf90eeSstephan            return -2;
6704fbf90eeSstephan          }finally{
6714fbf90eeSstephan            pstack.restore(stack);
6724fbf90eeSstephan            wasm.scopedAllocPop(astack);
6734fbf90eeSstephan          }
6744fbf90eeSstephan        },
6754fbf90eeSstephan        xWrite: (zClass, zKey, zData)=>{
6764fbf90eeSstephan          const stack = pstack.pointer;
6774fbf90eeSstephan          try {
6784fbf90eeSstephan            const zXKey = kvvfsMakeKey(zClass,zKey);
6794fbf90eeSstephan            if(!zXKey) return 1/*OOM*/;
6804fbf90eeSstephan            const jKey = wasm.cstringToJs(zXKey);
6814fbf90eeSstephan            kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData));
6824fbf90eeSstephan            return 0;
6834fbf90eeSstephan          }catch(e){
6844fbf90eeSstephan            console.error("kvstorageWrite()",e);
6854fbf90eeSstephan            return capi.SQLITE_IOERR;
6864fbf90eeSstephan          }finally{
6874fbf90eeSstephan            pstack.restore(stack);
6884fbf90eeSstephan          }
6894fbf90eeSstephan        },
6904fbf90eeSstephan        xDelete: (zClass, zKey)=>{
6914fbf90eeSstephan          const stack = pstack.pointer;
6924fbf90eeSstephan          try {
6934fbf90eeSstephan            const zXKey = kvvfsMakeKey(zClass,zKey);
6944fbf90eeSstephan            if(!zXKey) return 1/*OOM*/;
6954fbf90eeSstephan            kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey));
6964fbf90eeSstephan            return 0;
6974fbf90eeSstephan          }catch(e){
6984fbf90eeSstephan            console.error("kvstorageDelete()",e);
6994fbf90eeSstephan            return capi.SQLITE_IOERR;
7004fbf90eeSstephan          }finally{
7014fbf90eeSstephan            pstack.restore(stack);
7024fbf90eeSstephan          }
7034fbf90eeSstephan        }
7044fbf90eeSstephan      }/*kvvfsImpls*/;
70502d15a7eSstephan      for(const k of Object.keys(kvvfsImpls)){
7064fbf90eeSstephan        kvvfsMethods[kvvfsMethods.memberKey(k)] =
7074fbf90eeSstephan          wasm.installFunction(
7084fbf90eeSstephan            kvvfsMethods.memberSignature(k),
7094fbf90eeSstephan            kvvfsImpls[k]
7104fbf90eeSstephan          );
7114fbf90eeSstephan      }
712a6ca996eSstephan    }else{
713a6ca996eSstephan      /* Worker thread: unregister kvvfs to avoid it being used
714a6ca996eSstephan         for anything other than local/sessionStorage. It "can"
715a6ca996eSstephan         be used that way but it's not really intended to be. */
716a6ca996eSstephan      capi.sqlite3_vfs_unregister(pKvvfs);
717a6ca996eSstephan    }
718a6ca996eSstephan  }/*pKvvfs*/
7194fbf90eeSstephan
720d18f1bbfSstephan});
721