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