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    /**
30*d9801185Sstephan       Add the `.pointer` xWrap() signature entry to extend the
31*d9801185Sstephan       `pointer` arg handler to check for a `pointer` property. This
32*d9801185Sstephan       can be used to permit, e.g., passing an sqlite3.oo1.DB instance
33*d9801185Sstephan       to a C-style sqlite3_xxx function which takes an `sqlite3*`
34*d9801185Sstephan       argument.
353961b263Sstephan    */
363961b263Sstephan    const oldP = wasm.xWrap.argAdapter('pointer');
373961b263Sstephan    const adapter = function(v){
383961b263Sstephan      if(v && 'object'===typeof v && v.constructor){
393961b263Sstephan        const x = v.pointer;
403961b263Sstephan        if(Number.isInteger(x)) return x;
413961b263Sstephan        else toss("Invalid (object) type for pointer-type argument.");
423961b263Sstephan      }
433961b263Sstephan      return oldP(v);
443961b263Sstephan    };
453961b263Sstephan    wasm.xWrap.argAdapter('.pointer', adapter);
46e3cd6760Sstephan  } /* ".pointer" xWrap() argument adapter */
473961b263Sstephan
483961b263Sstephan  // WhWasmUtil.xWrap() bindings...
493961b263Sstephan  {
503961b263Sstephan    /**
513961b263Sstephan       Add some descriptive xWrap() aliases for '*' intended to
523961b263Sstephan       (A) initially improve readability/correctness of capi.signatures
533961b263Sstephan       and (B) eventually perhaps provide some sort of type-safety
543961b263Sstephan       in their conversions.
553961b263Sstephan    */
563961b263Sstephan    const aPtr = wasm.xWrap.argAdapter('*');
573961b263Sstephan    wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
585b915007Sstephan    wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
593961b263Sstephan
603961b263Sstephan    /**
613961b263Sstephan       Populate api object with sqlite3_...() by binding the "raw" wasm
623961b263Sstephan       exports into type-converting proxies using wasm.xWrap().
633961b263Sstephan    */
643961b263Sstephan    for(const e of wasm.bindingSignatures){
653961b263Sstephan      capi[e[0]] = wasm.xWrap.apply(null, e);
663961b263Sstephan    }
6773079dbaSstephan    for(const e of wasm.bindingSignatures.wasm){
6873079dbaSstephan      capi.wasm[e[0]] = wasm.xWrap.apply(null, e);
6973079dbaSstephan    }
703961b263Sstephan
71e3cd6760Sstephan    /* For C API functions which cannot work properly unless
723961b263Sstephan       wasm.bigIntEnabled is true, install a bogus impl which
733961b263Sstephan       throws if called when bigIntEnabled is false. */
743961b263Sstephan    const fI64Disabled = function(fname){
753961b263Sstephan      return ()=>toss(fname+"() disabled due to lack",
763961b263Sstephan                      "of BigInt support in this build.");
773961b263Sstephan    };
783961b263Sstephan    for(const e of wasm.bindingSignatures.int64){
793961b263Sstephan      capi[e[0]] = wasm.bigIntEnabled
803961b263Sstephan        ? wasm.xWrap.apply(null, e)
813961b263Sstephan        : fI64Disabled(e[0]);
823961b263Sstephan    }
833961b263Sstephan
843961b263Sstephan    if(wasm.exports.sqlite3_wasm_db_error){
853961b263Sstephan      util.sqlite3_wasm_db_error = capi.wasm.xWrap(
863961b263Sstephan        'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
873961b263Sstephan      );
883961b263Sstephan    }else{
893961b263Sstephan      util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
903961b263Sstephan        console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
913961b263Sstephan        return errCode;
923961b263Sstephan      };
933961b263Sstephan    }
943961b263Sstephan
953961b263Sstephan    /**
963961b263Sstephan       When registering a VFS and its related components it may be
973961b263Sstephan       necessary to ensure that JS keeps a reference to them to keep
983961b263Sstephan       them from getting garbage collected. Simply pass each such value
993961b263Sstephan       to this function and a reference will be held to it for the life
1003961b263Sstephan       of the app.
1013961b263Sstephan    */
1023961b263Sstephan    capi.sqlite3_vfs_register.addReference = function f(...args){
1033961b263Sstephan      if(!f._) f._ = [];
1043961b263Sstephan      f._.push(...args);
1053961b263Sstephan    };
1063961b263Sstephan
1073961b263Sstephan  }/*xWrap() bindings*/;
1083961b263Sstephan
1093961b263Sstephan  /**
1103961b263Sstephan     Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
1113961b263Sstephan  */
1123961b263Sstephan  const __prepare = Object.create(null);
1133961b263Sstephan  /**
1143961b263Sstephan     This binding expects a JS string as its 2nd argument and
1153961b263Sstephan     null as its final argument. In order to compile multiple
1163961b263Sstephan     statements from a single string, the "full" impl (see
1173961b263Sstephan     below) must be used.
1183961b263Sstephan  */
1193961b263Sstephan  __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
1203961b263Sstephan                               "int", ["sqlite3*", "string",
121453af2f6Sstephan                                       "int"/*ignored for this impl!*/,
1223961b263Sstephan                                       "int", "**",
1233961b263Sstephan                                       "**"/*MUST be 0 or null or undefined!*/]);
1243961b263Sstephan  /**
1253961b263Sstephan     Impl which requires that the 2nd argument be a pointer
1263961b263Sstephan     to the SQL string, instead of being converted to a
1273961b263Sstephan     string. This variant is necessary for cases where we
1283961b263Sstephan     require a non-NULL value for the final argument
1293961b263Sstephan     (exec()'ing multiple statements from one input
1303961b263Sstephan     string). For simpler cases, where only the first
1313961b263Sstephan     statement in the SQL string is required, the wrapper
1323961b263Sstephan     named sqlite3_prepare_v2() is sufficient and easier to
1333961b263Sstephan     use because it doesn't require dealing with pointers.
1343961b263Sstephan  */
1353961b263Sstephan  __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
1363961b263Sstephan                              "int", ["sqlite3*", "*", "int", "int",
1373961b263Sstephan                                      "**", "**"]);
1383961b263Sstephan
1393961b263Sstephan  /* Documented in the api object's initializer. */
1403961b263Sstephan  capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
1413961b263Sstephan    if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql);
1423961b263Sstephan    switch(typeof sql){
1433961b263Sstephan        case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null);
144453af2f6Sstephan        case 'number': return __prepare.full(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail);
1453961b263Sstephan        default:
1463961b263Sstephan          return util.sqlite3_wasm_db_error(
1473961b263Sstephan            pDb, capi.SQLITE_MISUSE,
1483961b263Sstephan            "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
1493961b263Sstephan          );
1503961b263Sstephan    }
1513961b263Sstephan  };
1523961b263Sstephan
1533961b263Sstephan  capi.sqlite3_prepare_v2 =
1543961b263Sstephan    (pDb, sql, sqlLen, ppStmt, pzTail)=>capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail);
1553961b263Sstephan
1563961b263Sstephan  /**
1573961b263Sstephan     Install JS<->C struct bindings for the non-opaque struct types we
1583961b263Sstephan     need... */
1593961b263Sstephan  sqlite3.StructBinder = self.Jaccwabyt({
1603961b263Sstephan    heap: 0 ? wasm.memory : wasm.heap8u,
1613961b263Sstephan    alloc: wasm.alloc,
1623961b263Sstephan    dealloc: wasm.dealloc,
1633961b263Sstephan    functionTable: wasm.functionTable,
1643961b263Sstephan    bigIntEnabled: wasm.bigIntEnabled,
1653961b263Sstephan    memberPrefix: '$'
1663961b263Sstephan  });
1673961b263Sstephan  delete self.Jaccwabyt;
1683961b263Sstephan
1693961b263Sstephan  {/* Import C-level constants and structs... */
1703961b263Sstephan    const cJson = wasm.xCall('sqlite3_wasm_enum_json');
1713961b263Sstephan    if(!cJson){
1723961b263Sstephan      toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
1733961b263Sstephan           "static buffer size!");
1743961b263Sstephan    }
1753961b263Sstephan    wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
1763961b263Sstephan    //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
1773961b263Sstephan    for(const t of ['access', 'blobFinalizers', 'dataTypes',
1785b915007Sstephan                    'encodings', 'fcntl', 'flock', 'ioCap',
1793961b263Sstephan                    'openFlags', 'prepareFlags', 'resultCodes',
1803961b263Sstephan                    'syncFlags', 'udfFlags', 'version'
1813961b263Sstephan                   ]){
182f75c0c70Sstephan      for(const e of Object.entries(wasm.ctype[t])){
183f75c0c70Sstephan        // ^^^ [k,v] there triggers a buggy code transormation via one
184f75c0c70Sstephan        // of the Emscripten-driven optimizers.
185f75c0c70Sstephan        capi[e[0]] = e[1];
1863961b263Sstephan      }
1873961b263Sstephan    }
1881f095d48Sstephan    const __rcMap = Object.create(null);
1891f095d48Sstephan    for(const t of ['resultCodes']){
1901f095d48Sstephan      for(const e of Object.entries(wasm.ctype[t])){
1911f095d48Sstephan        __rcMap[e[1]] = e[0];
1921f095d48Sstephan      }
1931f095d48Sstephan    }
1941f095d48Sstephan    /**
1951f095d48Sstephan       For the given integer, returns the SQLITE_xxx result code as a
1961f095d48Sstephan       string, or undefined if no such mapping is found.
1971f095d48Sstephan    */
1981f095d48Sstephan    capi.sqlite3_wasm_rc_str = (rc)=>__rcMap[rc];
1993961b263Sstephan    /* Bind all registered C-side structs... */
2003961b263Sstephan    for(const s of wasm.ctype.structs){
2013961b263Sstephan      capi[s.name] = sqlite3.StructBinder(s);
2023961b263Sstephan    }
2031f095d48Sstephan  }/*end C constant imports*/
204e3cd6760Sstephan});
205