aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/api/sqlite3-api-glue.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm/api/sqlite3-api-glue.js')
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js211
1 files changed, 211 insertions, 0 deletions
diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js
new file mode 100644
index 000000000..e962c93b6
--- /dev/null
+++ b/ext/wasm/api/sqlite3-api-glue.js
@@ -0,0 +1,211 @@
+/*
+ 2022-07-22
+
+ The author disclaims copyright to this source code. In place of a
+ legal notice, here is a blessing:
+
+ * May you do good and not evil.
+ * May you find forgiveness for yourself and forgive others.
+ * May you share freely, never taking more than you give.
+
+ ***********************************************************************
+
+ This file glues together disparate pieces of JS which are loaded in
+ previous steps of the sqlite3-api.js bootstrapping process:
+ sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It
+ initializes the main API pieces so that the downstream components
+ (e.g. sqlite3-api-oo1.js) have all that they need.
+*/
+(function(self){
+ 'use strict';
+ const toss = (...args)=>{throw new Error(args.join(' '))};
+
+ self.sqlite3 = self.sqlite3ApiBootstrap({
+ Module: Module /* ==> Emscripten-style Module object. Currently
+ needs to be exposed here for test code. NOT part
+ of the public API. */,
+ exports: Module['asm'],
+ memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */,
+ bigIntEnabled: !!self.BigInt64Array,
+ allocExportName: 'malloc',
+ deallocExportName: 'free'
+ });
+ delete self.sqlite3ApiBootstrap;
+
+ const sqlite3 = self.sqlite3;
+ const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
+ self.WhWasmUtilInstaller(capi.wasm);
+ delete self.WhWasmUtilInstaller;
+
+ if(0){
+ /* "The problem" is that the following isn't type-safe.
+ OTOH, nothing about WASM pointers is. */
+ /**
+ Add the `.pointer` xWrap() signature entry to extend
+ the `pointer` arg handler to check for a `pointer`
+ property. This can be used to permit, e.g., passing
+ an SQLite3.DB instance to a C-style sqlite3_xxx function
+ which takes an `sqlite3*` argument.
+ */
+ const oldP = wasm.xWrap.argAdapter('pointer');
+ const adapter = function(v){
+ if(v && 'object'===typeof v && v.constructor){
+ const x = v.pointer;
+ if(Number.isInteger(x)) return x;
+ else toss("Invalid (object) type for pointer-type argument.");
+ }
+ return oldP(v);
+ };
+ wasm.xWrap.argAdapter('.pointer', adapter);
+ }
+
+ // WhWasmUtil.xWrap() bindings...
+ {
+ /**
+ Add some descriptive xWrap() aliases for '*' intended to
+ (A) initially improve readability/correctness of capi.signatures
+ and (B) eventually perhaps provide some sort of type-safety
+ in their conversions.
+ */
+ const aPtr = wasm.xWrap.argAdapter('*');
+ wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
+
+ /**
+ Populate api object with sqlite3_...() by binding the "raw" wasm
+ exports into type-converting proxies using wasm.xWrap().
+ */
+ for(const e of wasm.bindingSignatures){
+ capi[e[0]] = wasm.xWrap.apply(null, e);
+ }
+
+ /* For functions which cannot work properly unless
+ wasm.bigIntEnabled is true, install a bogus impl which
+ throws if called when bigIntEnabled is false. */
+ const fI64Disabled = function(fname){
+ return ()=>toss(fname+"() disabled due to lack",
+ "of BigInt support in this build.");
+ };
+ for(const e of wasm.bindingSignatures.int64){
+ capi[e[0]] = wasm.bigIntEnabled
+ ? wasm.xWrap.apply(null, e)
+ : fI64Disabled(e[0]);
+ }
+
+ if(wasm.exports.sqlite3_wasm_db_error){
+ util.sqlite3_wasm_db_error = capi.wasm.xWrap(
+ 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
+ );
+ }else{
+ util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
+ console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
+ return errCode;
+ };
+ }
+
+ /**
+ When registering a VFS and its related components it may be
+ necessary to ensure that JS keeps a reference to them to keep
+ them from getting garbage collected. Simply pass each such value
+ to this function and a reference will be held to it for the life
+ of the app.
+ */
+ capi.sqlite3_vfs_register.addReference = function f(...args){
+ if(!f._) f._ = [];
+ f._.push(...args);
+ };
+
+ }/*xWrap() bindings*/;
+
+ /**
+ Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
+ */
+ const __prepare = Object.create(null);
+ /**
+ This binding expects a JS string as its 2nd argument and
+ null as its final argument. In order to compile multiple
+ statements from a single string, the "full" impl (see
+ below) must be used.
+ */
+ __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
+ "int", ["sqlite3*", "string",
+ "int"/*MUST always be negative*/,
+ "int", "**",
+ "**"/*MUST be 0 or null or undefined!*/]);
+ /**
+ Impl which requires that the 2nd argument be a pointer
+ to the SQL string, instead of being converted to a
+ string. This variant is necessary for cases where we
+ require a non-NULL value for the final argument
+ (exec()'ing multiple statements from one input
+ string). For simpler cases, where only the first
+ statement in the SQL string is required, the wrapper
+ named sqlite3_prepare_v2() is sufficient and easier to
+ use because it doesn't require dealing with pointers.
+ */
+ __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
+ "int", ["sqlite3*", "*", "int", "int",
+ "**", "**"]);
+
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
+ /* 2022-07-08: xWrap() 'string' arg handling may be able do this
+ special-case handling for us. It needs to be tested. Or maybe
+ not: we always want to treat pzTail as null when passed a
+ non-pointer SQL string and the argument adapters don't have
+ enough state to know that. Maybe they could/should, by passing
+ the currently-collected args as an array as the 2nd arg to the
+ argument adapters? Or maybe we collect all args in an array,
+ pass that to an optional post-args-collected callback, and give
+ it a chance to manipulate the args before we pass them on? */
+ if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql);
+ switch(typeof sql){
+ case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null);
+ case 'number': return __prepare.full(pDb, sql, sqlLen||-1, prepFlags, ppStmt, pzTail);
+ default:
+ return util.sqlite3_wasm_db_error(
+ pDb, capi.SQLITE_MISUSE,
+ "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
+ );
+ }
+ };
+
+ capi.sqlite3_prepare_v2 =
+ (pDb, sql, sqlLen, ppStmt, pzTail)=>capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail);
+
+ /**
+ Install JS<->C struct bindings for the non-opaque struct types we
+ need... */
+ sqlite3.StructBinder = self.Jaccwabyt({
+ heap: 0 ? wasm.memory : wasm.heap8u,
+ alloc: wasm.alloc,
+ dealloc: wasm.dealloc,
+ functionTable: wasm.functionTable,
+ bigIntEnabled: wasm.bigIntEnabled,
+ memberPrefix: '$'
+ });
+ delete self.Jaccwabyt;
+
+ {/* Import C-level constants and structs... */
+ const cJson = wasm.xCall('sqlite3_wasm_enum_json');
+ if(!cJson){
+ toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
+ "static buffer size!");
+ }
+ wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
+ //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
+ for(const t of ['access', 'blobFinalizers', 'dataTypes',
+ 'encodings', 'flock', 'ioCap',
+ 'openFlags', 'prepareFlags', 'resultCodes',
+ 'syncFlags', 'udfFlags', 'version'
+ ]){
+ for(const [k,v] of Object.entries(wasm.ctype[t])){
+ capi[k] = v;
+ }
+ }
+ /* Bind all registered C-side structs... */
+ for(const s of wasm.ctype.structs){
+ capi[s.name] = sqlite3.StructBinder(s);
+ }
+ }
+
+})(self);