diff options
author | stephan <stephan@noemail.net> | 2024-07-10 08:33:52 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2024-07-10 08:33:52 +0000 |
commit | 64ef4582c2018dac671282c3432c72a998549d24 (patch) | |
tree | e8ae4df29a2f060192b6145c1c03f300df65909c /ext/wasm/api/sqlite3-api-glue.js | |
parent | 68d700cac6a99535655e3afd82a5f935dd123588 (diff) | |
download | sqlite-64ef4582c2018dac671282c3432c72a998549d24.tar.gz sqlite-64ef4582c2018dac671282c3432c72a998549d24.zip |
Rename some JS files for consistency. This affects only the build process, not the deliverables.
FossilOrigin-Name: bcef3f71a2f68768819d9f716f2c29e752fb173df1506469c8669d95ecc2ff50
Diffstat (limited to 'ext/wasm/api/sqlite3-api-glue.js')
-rw-r--r-- | ext/wasm/api/sqlite3-api-glue.js | 1876 |
1 files changed, 0 insertions, 1876 deletions
diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js deleted file mode 100644 index 83b2ee172..000000000 --- a/ext/wasm/api/sqlite3-api-glue.js +++ /dev/null @@ -1,1876 +0,0 @@ -/* - 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 of the infrastructure that they - need. -*/ -globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - 'use strict'; - const toss = (...args)=>{throw new Error(args.join(' '))}; - const toss3 = sqlite3.SQLite3Error.toss; - const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util; - globalThis.WhWasmUtilInstaller(wasm); - delete globalThis.WhWasmUtilInstaller; - - if(0){ - /** - Please keep this block around as a maintenance reminder - that we cannot rely on this type of check. - - This block fails on Safari, per a report at - https://sqlite.org/forum/forumpost/e5b20e1feb. - - It turns out that what Safari serves from the indirect function - table (e.g. wasm.functionEntry(X)) is anonymous functions which - wrap the WASM functions, rather than returning the WASM - functions themselves. That means comparison of such functions - is useless for determining whether or not we have a specific - function from wasm.exports. i.e. if function X is indirection - function table entry N then wasm.exports.X is not equal to - wasm.functionEntry(N) in Safari, despite being so in the other - browsers. - */ - /** - Find a mapping for SQLITE_WASM_DEALLOC, which the API - guarantees is a WASM pointer to the same underlying function as - wasm.dealloc() (noting that wasm.dealloc() is permitted to be a - JS wrapper around the WASM function). There is unfortunately no - O(1) algorithm for finding this pointer: we have to walk the - WASM indirect function table to find it. However, experience - indicates that that particular function is always very close to - the front of the table (it's been entry #3 in all relevant - tests). - */ - const dealloc = wasm.exports[sqlite3.config.deallocExportName]; - const nFunc = wasm.functionTable().length; - let i; - for(i = 0; i < nFunc; ++i){ - const e = wasm.functionEntry(i); - if(dealloc === e){ - capi.SQLITE_WASM_DEALLOC = i; - break; - } - } - if(dealloc !== wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)){ - toss("Internal error: cannot find function pointer for SQLITE_WASM_DEALLOC."); - } - } - - /** - Signatures for the WASM-exported C-side functions. Each entry - is an array with 2+ elements: - - [ "c-side name", - "result type" (wasm.xWrap() syntax), - [arg types in xWrap() syntax] - // ^^^ this needn't strictly be an array: it can be subsequent - // elements instead: [x,y,z] is equivalent to x,y,z - ] - - Note that support for the API-specific data types in the - result/argument type strings gets plugged in at a later phase in - the API initialization process. - */ - wasm.bindingSignatures = [ - // Please keep these sorted by function name! - ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], - /* sqlite3_auto_extension() has a hand-written binding. */ - /* sqlite3_bind_blob() and sqlite3_bind_text() have hand-written - bindings to permit more flexible inputs. */ - ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], - ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"], - ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"], - ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"], - ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"], - ["sqlite3_bind_pointer", "int", - "sqlite3_stmt*", "int", "*", "string:static", "*"], - ["sqlite3_busy_handler","int", [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - signature: 'i(pi)', - contextKey: (argv,argIndex)=>argv[0/* sqlite3* */] - }), - "*" - ]], - ["sqlite3_busy_timeout","int", "sqlite3*", "int"], - /* sqlite3_cancel_auto_extension() has a hand-written binding. */ - /* sqlite3_close_v2() is implemented by hand to perform some - extra work. */ - ["sqlite3_changes", "int", "sqlite3*"], - ["sqlite3_clear_bindings","int", "sqlite3_stmt*"], - ["sqlite3_collation_needed", "int", "sqlite3*", "*", "*"/*=>v(ppis)*/], - ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"], - ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"], - ["sqlite3_column_count", "int", "sqlite3_stmt*"], - ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"], - ["sqlite3_column_int","int", "sqlite3_stmt*", "int"], - ["sqlite3_column_name","string", "sqlite3_stmt*", "int"], - ["sqlite3_column_text","string", "sqlite3_stmt*", "int"], - ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], - ["sqlite3_column_value","sqlite3_value*", "sqlite3_stmt*", "int"], - ["sqlite3_commit_hook", "void*", [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: 'sqlite3_commit_hook', - signature: 'i(p)', - contextKey: (argv)=>argv[0/* sqlite3* */] - }), - '*' - ]], - ["sqlite3_compileoption_get", "string", "int"], - ["sqlite3_compileoption_used", "int", "string"], - ["sqlite3_complete", "int", "string:flexible"], - ["sqlite3_context_db_handle", "sqlite3*", "sqlite3_context*"], - - /* sqlite3_create_function(), sqlite3_create_function_v2(), and - sqlite3_create_window_function() use hand-written bindings to - simplify handling of their function-type arguments. */ - /* sqlite3_create_collation() and sqlite3_create_collation_v2() - use hand-written bindings to simplify passing of the callback - function. - ["sqlite3_create_collation", "int", - "sqlite3*", "string", "int",//SQLITE_UTF8 is the only legal value - "*", "*"], - ["sqlite3_create_collation_v2", "int", - "sqlite3*", "string", "int",//SQLITE_UTF8 is the only legal value - "*", "*", "*"], - */ - ["sqlite3_data_count", "int", "sqlite3_stmt*"], - ["sqlite3_db_filename", "string", "sqlite3*", "string"], - ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"], - ["sqlite3_db_name", "string", "sqlite3*", "int"], - ["sqlite3_db_status", "int", "sqlite3*", "int", "*", "*", "int"], - ["sqlite3_errcode", "int", "sqlite3*"], - ["sqlite3_errmsg", "string", "sqlite3*"], - ["sqlite3_error_offset", "int", "sqlite3*"], - ["sqlite3_errstr", "string", "int"], - ["sqlite3_exec", "int", [ - "sqlite3*", "string:flexible", - new wasm.xWrap.FuncPtrAdapter({ - signature: 'i(pipp)', - bindScope: 'transient', - callProxy: (callback)=>{ - let aNames; - return (pVoid, nCols, pColVals, pColNames)=>{ - try { - const aVals = wasm.cArgvToJs(nCols, pColVals); - if(!aNames) aNames = wasm.cArgvToJs(nCols, pColNames); - return callback(aVals, aNames) | 0; - }catch(e){ - /* If we set the db error state here, the higher-level - exec() call replaces it with its own, so we have no way - of reporting the exception message except the console. We - must not propagate exceptions through the C API. Though - we make an effort to report OOM here, sqlite3_exec() - translates that into SQLITE_ABORT as well. */ - return e.resultCode || capi.SQLITE_ERROR; - } - } - } - }), - "*", "**" - ]], - ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], - ["sqlite3_extended_errcode", "int", "sqlite3*"], - ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], - ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], - ["sqlite3_finalize", "int", "sqlite3_stmt*"], - ["sqlite3_free", undefined,"*"], - ["sqlite3_get_autocommit", "int", "sqlite3*"], - ["sqlite3_get_auxdata", "*", "sqlite3_context*", "int"], - ["sqlite3_initialize", undefined], - /*["sqlite3_interrupt", undefined, "sqlite3*" - ^^^ we cannot actually currently support this because JS is - single-threaded and we don't have a portable way to access a DB - from 2 SharedWorkers concurrently. ],*/ - ["sqlite3_keyword_count", "int"], - ["sqlite3_keyword_name", "int", ["int", "**", "*"]], - ["sqlite3_keyword_check", "int", ["string", "int"]], - ["sqlite3_libversion", "string"], - ["sqlite3_libversion_number", "int"], - ["sqlite3_limit", "int", ["sqlite3*", "int", "int"]], - ["sqlite3_malloc", "*","int"], - ["sqlite3_open", "int", "string", "*"], - ["sqlite3_open_v2", "int", "string", "*", "int", "string"], - /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled - separately due to us requiring two different sets of semantics - for those, depending on how their SQL argument is provided. */ - /* sqlite3_randomness() uses a hand-written wrapper to extend - the range of supported argument types. */ - ["sqlite3_progress_handler", undefined, [ - "sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({ - name: 'xProgressHandler', - signature: 'i(p)', - bindScope: 'context', - contextKey: (argv,argIndex)=>argv[0/* sqlite3* */] - }), "*" - ]], - ["sqlite3_realloc", "*","*","int"], - ["sqlite3_reset", "int", "sqlite3_stmt*"], - /* sqlite3_reset_auto_extension() has a hand-written binding. */ - ["sqlite3_result_blob", undefined, "sqlite3_context*", "*", "int", "*"], - ["sqlite3_result_double", undefined, "sqlite3_context*", "f64"], - ["sqlite3_result_error", undefined, "sqlite3_context*", "string", "int"], - ["sqlite3_result_error_code", undefined, "sqlite3_context*", "int"], - ["sqlite3_result_error_nomem", undefined, "sqlite3_context*"], - ["sqlite3_result_error_toobig", undefined, "sqlite3_context*"], - ["sqlite3_result_int", undefined, "sqlite3_context*", "int"], - ["sqlite3_result_null", undefined, "sqlite3_context*"], - ["sqlite3_result_pointer", undefined, - "sqlite3_context*", "*", "string:static", "*"], - ["sqlite3_result_subtype", undefined, "sqlite3_value*", "int"], - ["sqlite3_result_text", undefined, "sqlite3_context*", "string", "int", "*"], - ["sqlite3_result_zeroblob", undefined, "sqlite3_context*", "int"], - ["sqlite3_rollback_hook", "void*", [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: 'sqlite3_rollback_hook', - signature: 'v(p)', - contextKey: (argv)=>argv[0/* sqlite3* */] - }), - '*' - ]], - ["sqlite3_set_authorizer", "int", [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: "sqlite3_set_authorizer::xAuth", - signature: "i(pi"+"ssss)", - contextKey: (argv, argIndex)=>argv[0/*(sqlite3*)*/], - callProxy: (callback)=>{ - return (pV, iCode, s0, s1, s2, s3)=>{ - try{ - s0 = s0 && wasm.cstrToJs(s0); s1 = s1 && wasm.cstrToJs(s1); - s2 = s2 && wasm.cstrToJs(s2); s3 = s3 && wasm.cstrToJs(s3); - return callback(pV, iCode, s0, s1, s2, s3) || 0; - }catch(e){ - return e.resultCode || capi.SQLITE_ERROR; - } - } - } - }), - "*"/*pUserData*/ - ]], - ["sqlite3_set_auxdata", undefined, [ - "sqlite3_context*", "int", "*", - new wasm.xWrap.FuncPtrAdapter({ - name: 'xDestroyAuxData', - signature: 'v(*)', - contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */] - }) - ]], - ["sqlite3_shutdown", undefined], - ["sqlite3_sourceid", "string"], - ["sqlite3_sql", "string", "sqlite3_stmt*"], - ["sqlite3_status", "int", "int", "*", "*", "int"], - ["sqlite3_step", "int", "sqlite3_stmt*"], - ["sqlite3_stmt_isexplain", "int", ["sqlite3_stmt*"]], - ["sqlite3_stmt_readonly", "int", ["sqlite3_stmt*"]], - ["sqlite3_stmt_status", "int", "sqlite3_stmt*", "int", "int"], - ["sqlite3_strglob", "int", "string","string"], - ["sqlite3_stricmp", "int", "string", "string"], - ["sqlite3_strlike", "int", "string", "string","int"], - ["sqlite3_strnicmp", "int", "string", "string", "int"], - ["sqlite3_table_column_metadata", "int", - "sqlite3*", "string", "string", "string", - "**", "**", "*", "*", "*"], - ["sqlite3_total_changes", "int", "sqlite3*"], - ["sqlite3_trace_v2", "int", [ - "sqlite3*", "int", - new wasm.xWrap.FuncPtrAdapter({ - name: 'sqlite3_trace_v2::callback', - signature: 'i(ippp)', - contextKey: (argv,argIndex)=>argv[0/* sqlite3* */] - }), - "*" - ]], - ["sqlite3_txn_state", "int", ["sqlite3*","string"]], - /* Note that sqlite3_uri_...() have very specific requirements for - their first C-string arguments, so we cannot perform any value - conversion on those. */ - ["sqlite3_uri_boolean", "int", "sqlite3_filename", "string", "int"], - ["sqlite3_uri_key", "string", "sqlite3_filename", "int"], - ["sqlite3_uri_parameter", "string", "sqlite3_filename", "string"], - ["sqlite3_user_data","void*", "sqlite3_context*"], - ["sqlite3_value_blob", "*", "sqlite3_value*"], - ["sqlite3_value_bytes","int", "sqlite3_value*"], - ["sqlite3_value_double","f64", "sqlite3_value*"], - ["sqlite3_value_dup", "sqlite3_value*", "sqlite3_value*"], - ["sqlite3_value_free", undefined, "sqlite3_value*"], - ["sqlite3_value_frombind", "int", "sqlite3_value*"], - ["sqlite3_value_int","int", "sqlite3_value*"], - ["sqlite3_value_nochange", "int", "sqlite3_value*"], - ["sqlite3_value_numeric_type", "int", "sqlite3_value*"], - ["sqlite3_value_pointer", "*", "sqlite3_value*", "string:static"], - ["sqlite3_value_subtype", "int", "sqlite3_value*"], - ["sqlite3_value_text", "string", "sqlite3_value*"], - ["sqlite3_value_type", "int", "sqlite3_value*"], - ["sqlite3_vfs_find", "*", "string"], - ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"], - ["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"] - ]/*wasm.bindingSignatures*/; - - if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ - /* ^^^ "the problem" is that this is an option feature and the - build-time function-export list does not currently take - optional features into account. */ - wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); - } - -//#if enable-see - if(wasm.exports.sqlite3_key_v2 instanceof Function){ - /** - This code is capable of using an SEE build but note that an SEE - WASM build is generally incompatible with SEE's license - conditions. It is permitted for use internally in organizations - which have licensed SEE, but not for public sites because - exposing an SEE build of sqlite3.wasm effectively provides all - clients with a working copy of the commercial SEE code. - */ - wasm.bindingSignatures.push( - ["sqlite3_key", "int", "sqlite3*", "string", "int"], - ["sqlite3_key_v2","int","sqlite3*","string","*","int"], - ["sqlite3_rekey", "int", "sqlite3*", "string", "int"], - ["sqlite3_rekey_v2", "int", "sqlite3*", "string", "*", "int"], - ["sqlite3_activate_see", undefined, "string"] - ); - } -//#endif enable-see - - /** - Functions which require BigInt (int64) support are separated from - the others because we need to conditionally bind them or apply - dummy impls, depending on the capabilities of the environment. - (That said: we never actually build without BigInt support, - and such builds are untested.) - - Note that not all of these functions directly require int64 - but are only for use with APIs which require int64. For example, - the vtab-related functions. - */ - wasm.bindingSignatures.int64 = [ - ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], - ["sqlite3_changes64","i64", ["sqlite3*"]], - ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], - ["sqlite3_create_module", "int", - ["sqlite3*","string","sqlite3_module*","*"]], - ["sqlite3_create_module_v2", "int", - ["sqlite3*","string","sqlite3_module*","*","*"]], - ["sqlite3_declare_vtab", "int", ["sqlite3*", "string:flexible"]], - ["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"] - /* Careful! Short version: de/serialize() are problematic because they - might use a different allocator than the user for managing the - deserialized block. de/serialize() are ONLY safe to use with - sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. Because - of this, the canonical builds of sqlite3.wasm/js guarantee that - sqlite3.wasm.alloc() and friends use those allocators. Custom builds - may not guarantee that, however. */, - ["sqlite3_drop_modules", "int", ["sqlite3*", "**"]], - ["sqlite3_last_insert_rowid", "i64", ["sqlite3*"]], - ["sqlite3_malloc64", "*","i64"], - ["sqlite3_msize", "i64", "*"], - ["sqlite3_overload_function", "int", ["sqlite3*","string","int"]], - ["sqlite3_preupdate_blobwrite", "int", "sqlite3*"], - ["sqlite3_preupdate_count", "int", "sqlite3*"], - ["sqlite3_preupdate_depth", "int", "sqlite3*"], - ["sqlite3_preupdate_hook", "*", [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: 'sqlite3_preupdate_hook', - signature: "v(ppippjj)", - contextKey: (argv)=>argv[0/* sqlite3* */], - callProxy: (callback)=>{ - return (p,db,op,zDb,zTbl,iKey1,iKey2)=>{ - callback(p, db, op, wasm.cstrToJs(zDb), wasm.cstrToJs(zTbl), - iKey1, iKey2); - }; - } - }), - "*" - ]], - ["sqlite3_preupdate_new", "int", ["sqlite3*", "int", "**"]], - ["sqlite3_preupdate_old", "int", ["sqlite3*", "int", "**"]], - ["sqlite3_realloc64", "*","*", "i64"], - ["sqlite3_result_int64", undefined, "*", "i64"], - ["sqlite3_result_zeroblob64", "int", "*", "i64"], - ["sqlite3_serialize","*", "sqlite3*", "string", "*", "int"], - ["sqlite3_set_last_insert_rowid", undefined, ["sqlite3*", "i64"]], - ["sqlite3_status64", "int", "int", "*", "*", "int"], - ["sqlite3_total_changes64", "i64", ["sqlite3*"]], - ["sqlite3_update_hook", "*", [ - "sqlite3*", - new wasm.xWrap.FuncPtrAdapter({ - name: 'sqlite3_update_hook', - signature: "v(iippj)", - contextKey: (argv)=>argv[0/* sqlite3* */], - callProxy: (callback)=>{ - return (p,op,z0,z1,rowid)=>{ - callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid); - }; - } - }), - "*" - ]], - ["sqlite3_uri_int64", "i64", ["sqlite3_filename", "string", "i64"]], - ["sqlite3_value_int64","i64", "sqlite3_value*"], - ["sqlite3_vtab_collation","string","sqlite3_index_info*","int"], - ["sqlite3_vtab_distinct","int", "sqlite3_index_info*"], - ["sqlite3_vtab_in","int", "sqlite3_index_info*", "int", "int"], - ["sqlite3_vtab_in_first", "int", "sqlite3_value*", "**"], - ["sqlite3_vtab_in_next", "int", "sqlite3_value*", "**"], - /*["sqlite3_vtab_config" is variadic and requires a hand-written - proxy.] */ - ["sqlite3_vtab_nochange","int", "sqlite3_context*"], - ["sqlite3_vtab_on_conflict","int", "sqlite3*"], - ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"] - ]; - - // Add session/changeset APIs... - if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){ - /** - FuncPtrAdapter options for session-related callbacks with the - native signature "i(ps)". This proxy converts the 2nd argument - from a C string to a JS string before passing the arguments on - to the client-provided JS callback. - */ - const __ipsProxy = { - signature: 'i(ps)', - callProxy:(callback)=>{ - return (p,s)=>{ - try{return callback(p, wasm.cstrToJs(s)) | 0} - catch(e){return e.resultCode || capi.SQLITE_ERROR} - } - } - }; - - wasm.bindingSignatures.int64.push(...[ - ['sqlite3changegroup_add', 'int', ['sqlite3_changegroup*', 'int', 'void*']], - ['sqlite3changegroup_add_strm', 'int', [ - 'sqlite3_changegroup*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changegroup_delete', undefined, ['sqlite3_changegroup*']], - ['sqlite3changegroup_new', 'int', ['**']], - ['sqlite3changegroup_output', 'int', ['sqlite3_changegroup*', 'int*', '**']], - ['sqlite3changegroup_output_strm', 'int', [ - 'sqlite3_changegroup*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xOutput', signature: 'i(ppi)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changeset_apply', 'int', [ - 'sqlite3*', 'int', 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xFilter', bindScope: 'transient', ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changeset_apply_strm', 'int', [ - 'sqlite3*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xFilter', bindScope: 'transient', ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changeset_apply_v2', 'int', [ - 'sqlite3*', 'int', 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xFilter', bindScope: 'transient', ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' - }), - 'void*', '**', 'int*', 'int' - - ]], - ['sqlite3changeset_apply_v2_strm', 'int', [ - 'sqlite3*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xFilter', bindScope: 'transient', ...__ipsProxy - }), - new wasm.xWrap.FuncPtrAdapter({ - name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' - }), - 'void*', '**', 'int*', 'int' - ]], - ['sqlite3changeset_concat', 'int', ['int','void*', 'int', 'void*', 'int*', '**']], - ['sqlite3changeset_concat_strm', 'int', [ - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInputA', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInputB', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xOutput', signature: 'i(ppi)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changeset_conflict', 'int', ['sqlite3_changeset_iter*', 'int', '**']], - ['sqlite3changeset_finalize', 'int', ['sqlite3_changeset_iter*']], - ['sqlite3changeset_fk_conflicts', 'int', ['sqlite3_changeset_iter*', 'int*']], - ['sqlite3changeset_invert', 'int', ['int', 'void*', 'int*', '**']], - ['sqlite3changeset_invert_strm', 'int', [ - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xOutput', signature: 'i(ppi)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changeset_new', 'int', ['sqlite3_changeset_iter*', 'int', '**']], - ['sqlite3changeset_next', 'int', ['sqlite3_changeset_iter*']], - ['sqlite3changeset_old', 'int', ['sqlite3_changeset_iter*', 'int', '**']], - ['sqlite3changeset_op', 'int', [ - 'sqlite3_changeset_iter*', '**', 'int*', 'int*','int*' - ]], - ['sqlite3changeset_pk', 'int', ['sqlite3_changeset_iter*', '**', 'int*']], - ['sqlite3changeset_start', 'int', ['**', 'int', '*']], - ['sqlite3changeset_start_strm', 'int', [ - '**', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3changeset_start_v2', 'int', ['**', 'int', '*', 'int']], - ['sqlite3changeset_start_v2_strm', 'int', [ - '**', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*', 'int' - ]], - ['sqlite3session_attach', 'int', ['sqlite3_session*', 'string']], - ['sqlite3session_changeset', 'int', ['sqlite3_session*', 'int*', '**']], - ['sqlite3session_changeset_size', 'i64', ['sqlite3_session*']], - ['sqlite3session_changeset_strm', 'int', [ - 'sqlite3_session*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xOutput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3session_config', 'int', ['int', 'void*']], - ['sqlite3session_create', 'int', ['sqlite3*', 'string', '**']], - //sqlite3session_delete() is bound manually - ['sqlite3session_diff', 'int', ['sqlite3_session*', 'string', 'string', '**']], - ['sqlite3session_enable', 'int', ['sqlite3_session*', 'int']], - ['sqlite3session_indirect', 'int', ['sqlite3_session*', 'int']], - ['sqlite3session_isempty', 'int', ['sqlite3_session*']], - ['sqlite3session_memory_used', 'i64', ['sqlite3_session*']], - ['sqlite3session_object_config', 'int', ['sqlite3_session*', 'int', 'void*']], - ['sqlite3session_patchset', 'int', ['sqlite3_session*', '*', '**']], - ['sqlite3session_patchset_strm', 'int', [ - 'sqlite3_session*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xOutput', signature: 'i(ppp)', bindScope: 'transient' - }), - 'void*' - ]], - ['sqlite3session_table_filter', undefined, [ - 'sqlite3_session*', - new wasm.xWrap.FuncPtrAdapter({ - name: 'xFilter', ...__ipsProxy, - contextKey: (argv,argIndex)=>argv[0/* (sqlite3_session*) */] - }), - '*' - ]] - ]); - }/*session/changeset APIs*/ - - /** - Functions which are intended solely for API-internal use by the - WASM components, not client code. These get installed into - sqlite3.util. Some of them get exposed to clients via variants - in sqlite3_js_...(). - - 2024-01-11: these were renamed, with two underscores in the - prefix, to ensure that clients do not accidentally depend on - them. They have always been documented as internal-use-only, so - no clients "should" be depending on the old names. - */ - wasm.bindingSignatures.wasmInternal = [ - ["sqlite3__wasm_db_reset", "int", "sqlite3*"], - ["sqlite3__wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"], - [/* DO NOT USE. This is deprecated since 2023-08-11 because it can - trigger assert() in debug builds when used with file sizes - which are not sizes to a multiple of a valid db page size. */ - "sqlite3__wasm_vfs_create_file", "int", "sqlite3_vfs*","string","*", "int" - ], - ["sqlite3__wasm_posix_create_file", "int", "string","*", "int"], - ["sqlite3__wasm_vfs_unlink", "int", "sqlite3_vfs*","string"], - ["sqlite3__wasm_qfmt_token","string:dealloc", "string","int"] - ]; - - /** - Install JS<->C struct bindings for the non-opaque struct types we - need... */ - sqlite3.StructBinder = globalThis.Jaccwabyt({ - heap: 0 ? wasm.memory : wasm.heap8u, - alloc: wasm.alloc, - dealloc: wasm.dealloc, - bigIntEnabled: wasm.bigIntEnabled, - memberPrefix: /* Never change this: this prefix is baked into any - amount of code and client-facing docs. */ '$' - }); - delete globalThis.Jaccwabyt; - - {// wasm.xWrap() bindings... - - /* Convert Arrays and certain TypedArrays to strings for - 'string:flexible'-type arguments */ - const __xString = wasm.xWrap.argAdapter('string'); - wasm.xWrap.argAdapter( - 'string:flexible', (v)=>__xString(util.flexibleString(v)) - ); - - /** - The 'string:static' argument adapter treats its argument as - either... - - - WASM pointer: assumed to be a long-lived C-string which gets - returned as-is. - - - Anything else: gets coerced to a JS string for use as a map - key. If a matching entry is found (as described next), it is - returned, else wasm.allocCString() is used to create a a new - string, map its pointer to (''+v) for the remainder of the - application's life, and returns that pointer value for this - call and all future calls which are passed a - string-equivalent argument. - - Use case: sqlite3_bind_pointer() and sqlite3_result_pointer() - call for "a static string and preferably a string - literal." This converter is used to ensure that the string - value seen by those functions is long-lived and behaves as they - need it to. - */ - wasm.xWrap.argAdapter( - 'string:static', - function(v){ - if(wasm.isPtr(v)) return v; - v = ''+v; - let rc = this[v]; - return rc || (this[v] = wasm.allocCString(v)); - }.bind(Object.create(null)) - ); - - /** - Add some descriptive xWrap() aliases for '*' intended to (A) - initially improve readability/correctness of - wasm.bindingSignatures and (B) provide automatic conversion - from higher-level representations, e.g. capi.sqlite3_vfs to - `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. - */ - const __xArgPtr = wasm.xWrap.argAdapter('*'); - const nilType = function(){ - /*a class which no value can ever be an instance of*/ - }; - wasm.xWrap.argAdapter('sqlite3_filename', __xArgPtr) - ('sqlite3_context*', __xArgPtr) - ('sqlite3_value*', __xArgPtr) - ('void*', __xArgPtr) - ('sqlite3_changegroup*', __xArgPtr) - ('sqlite3_changeset_iter*', __xArgPtr) - ('sqlite3_session*', __xArgPtr) - ('sqlite3_stmt*', (v)=> - __xArgPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType)) - ? v.pointer : v)) - ('sqlite3*', (v)=> - __xArgPtr((v instanceof (sqlite3?.oo1?.DB || nilType)) - ? v.pointer : v)) - ('sqlite3_index_info*', (v)=> - __xArgPtr((v instanceof (capi.sqlite3_index_info || nilType)) - ? v.pointer : v)) - ('sqlite3_module*', (v)=> - __xArgPtr((v instanceof (capi.sqlite3_module || nilType)) - ? v.pointer : v)) - /** - `sqlite3_vfs*`: - - - v is-a string: use the result of sqlite3_vfs_find(v) but - throw if it returns 0. - - v is-a capi.sqlite3_vfs: use v.pointer. - - Else return the same as the `'*'` argument conversion. - */ - ('sqlite3_vfs*', (v)=>{ - if('string'===typeof v){ - /* A NULL sqlite3_vfs pointer will be treated as the default - VFS in many contexts. We specifically do not want that - behavior here. */ - return capi.sqlite3_vfs_find(v) - || sqlite3.SQLite3Error.toss( - capi.SQLITE_NOTFOUND, - "Unknown sqlite3_vfs name:", v - ); - } - return __xArgPtr((v instanceof (capi.sqlite3_vfs || nilType)) - ? v.pointer : v); - }); - - const __xRcPtr = wasm.xWrap.resultAdapter('*'); - wasm.xWrap.resultAdapter('sqlite3*', __xRcPtr) - ('sqlite3_context*', __xRcPtr) - ('sqlite3_stmt*', __xRcPtr) - ('sqlite3_value*', __xRcPtr) - ('sqlite3_vfs*', __xRcPtr) - ('void*', __xRcPtr); - - /** - Populate api object with sqlite3_...() by binding the "raw" wasm - exports into type-converting proxies using wasm.xWrap(). - */ - if(0 === wasm.exports.sqlite3_step.length){ - /* This environment wraps exports in nullary functions, which means - we must disable the arg-count validation we otherwise perform - on the wrappers. */ - wasm.xWrap.doArgcCheck = false; - sqlite3.config.warn( - "Disabling sqlite3.wasm.xWrap.doArgcCheck due to environmental quirks." - ); - } - for(const e of wasm.bindingSignatures){ - capi[e[0]] = wasm.xWrap.apply(null, e); - } - for(const e of wasm.bindingSignatures.wasmInternal){ - util[e[0]] = wasm.xWrap.apply(null, e); - } - - /* For C API functions which cannot work properly unless - wasm.bigIntEnabled is true, install a bogus impl which throws - if called when bigIntEnabled is false. The alternative would be - to elide these functions altogether, which seems likely to - cause more confusion. */ - const fI64Disabled = function(fname){ - return ()=>toss(fname+"() is unavailable 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]); - } - - /* There's no need to expose bindingSignatures to clients, - implicitly making it part of the public interface. */ - delete wasm.bindingSignatures; - - if(wasm.exports.sqlite3__wasm_db_error){ - const __db_err = wasm.xWrap( - 'sqlite3__wasm_db_error', 'int', 'sqlite3*', 'int', 'string' - ); - /** - Sets the given db's error state. Accepts: - - - (sqlite3*, int code, string msg) - - (sqlite3*, Error e [,string msg = ''+e]) - - If passed a WasmAllocError, the message is ignored and the - result code is SQLITE_NOMEM. If passed any other Error type, - the result code defaults to SQLITE_ERROR unless the Error - object has a resultCode property, in which case that is used - (e.g. SQLite3Error has that). If passed a non-WasmAllocError - exception, the message string defaults to theError.message. - - Returns the resulting code. Pass (pDb,0,0) to clear the error - state. - */ - util.sqlite3__wasm_db_error = function(pDb, resultCode, message){ - if(resultCode instanceof sqlite3.WasmAllocError){ - resultCode = capi.SQLITE_NOMEM; - message = 0 /*avoid allocating message string*/; - }else if(resultCode instanceof Error){ - message = message || ''+resultCode; - resultCode = (resultCode.resultCode || capi.SQLITE_ERROR); - } - return pDb ? __db_err(pDb, resultCode, message) : resultCode; - }; - }else{ - util.sqlite3__wasm_db_error = function(pDb,errCode,msg){ - console.warn("sqlite3__wasm_db_error() is not exported.",arguments); - return errCode; - }; - } - }/*xWrap() bindings*/ - - {/* 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!"); - } - //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); - wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); - // Groups of SQLITE_xyz macros... - const defineGroups = ['access', 'authorizer', - 'blobFinalizers', 'changeset', - 'config', 'dataTypes', - 'dbConfig', 'dbStatus', - 'encodings', 'fcntl', 'flock', 'ioCap', - 'limits', 'openFlags', - 'prepareFlags', 'resultCodes', - 'sqlite3Status', - 'stmtStatus', 'syncFlags', - 'trace', 'txnState', 'udfFlags', - 'version' ]; - if(wasm.bigIntEnabled){ - defineGroups.push('serialize', 'session', 'vtab'); - } - for(const t of defineGroups){ - for(const e of Object.entries(wasm.ctype[t])){ - // ^^^ [k,v] there triggers a buggy code transformation via - // one of the Emscripten-driven optimizers. - capi[e[0]] = e[1]; - } - } - if(!wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)){ - toss("Internal error: cannot resolve exported function", - "entry SQLITE_WASM_DEALLOC (=="+capi.SQLITE_WASM_DEALLOC+")."); - } - const __rcMap = Object.create(null); - for(const t of ['resultCodes']){ - for(const e of Object.entries(wasm.ctype[t])){ - __rcMap[e[1]] = e[0]; - } - } - /** - For the given integer, returns the SQLITE_xxx result code as a - string, or undefined if no such mapping is found. - */ - capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc]; - /* Bind all registered C-side structs... */ - const notThese = Object.assign(Object.create(null),{ - // For each struct to NOT register, map its name to true: - WasmTestStruct: true, - /* We unregister the kvvfs VFS from Worker threads below. */ - sqlite3_kvvfs_methods: !util.isUIThread(), - /* sqlite3_index_info and friends require int64: */ - sqlite3_index_info: !wasm.bigIntEnabled, - sqlite3_index_constraint: !wasm.bigIntEnabled, - sqlite3_index_orderby: !wasm.bigIntEnabled, - sqlite3_index_constraint_usage: !wasm.bigIntEnabled - }); - for(const s of wasm.ctype.structs){ - if(!notThese[s.name]){ - capi[s.name] = sqlite3.StructBinder(s); - } - } - if(capi.sqlite3_index_info){ - /* Move these inner structs into sqlite3_index_info. Binding - ** them to WASM requires that we create global-scope structs to - ** model them with, but those are no longer needed after we've - ** passed them to StructBinder. */ - for(const k of ['sqlite3_index_constraint', - 'sqlite3_index_orderby', - 'sqlite3_index_constraint_usage']){ - capi.sqlite3_index_info[k] = capi[k]; - delete capi[k]; - } - capi.sqlite3_vtab_config = wasm.xWrap( - 'sqlite3__wasm_vtab_config','int',[ - 'sqlite3*', 'int', 'int'] - ); - }/* end vtab-related setup */ - }/*end C constant and struct imports*/ - - /** - Internal helper to assist in validating call argument counts in - the hand-written sqlite3_xyz() wrappers. We do this only for - consistency with non-special-case wrappings. - */ - const __dbArgcMismatch = (pDb,f,n)=>{ - return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, - f+"() requires "+n+" argument"+ - (1===n?"":'s')+"."); - }; - - /** Code duplication reducer for functions which take an encoding - argument and require SQLITE_UTF8. Sets the db error code to - SQLITE_FORMAT and returns that code. */ - const __errEncoding = (pDb)=>{ - return util.sqlite3__wasm_db_error( - pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding." - ); - }; - - /** - __dbCleanupMap is infrastructure for recording registration of - UDFs and collations so that sqlite3_close_v2() can clean up any - automated JS-to-WASM function conversions installed by those. - */ - const __argPDb = (pDb)=>wasm.xWrap.argAdapter('sqlite3*')(pDb); - const __argStr = (str)=>wasm.isPtr(str) ? wasm.cstrToJs(str) : str; - const __dbCleanupMap = function( - pDb, mode/*0=remove, >0=create if needed, <0=do not create if missing*/ - ){ - pDb = __argPDb(pDb); - let m = this.dbMap.get(pDb); - if(!mode){ - this.dbMap.delete(pDb); - return m; - }else if(!m && mode>0){ - this.dbMap.set(pDb, (m = Object.create(null))); - } - return m; - }.bind(Object.assign(Object.create(null),{ - dbMap: new Map - })); - - __dbCleanupMap.addCollation = function(pDb, name){ - const m = __dbCleanupMap(pDb, 1); - if(!m.collation) m.collation = new Set; - m.collation.add(__argStr(name).toLowerCase()); - }; - - __dbCleanupMap._addUDF = function(pDb, name, arity, map){ - /* Map UDF name to a Set of arity values */ - name = __argStr(name).toLowerCase(); - let u = map.get(name); - if(!u) map.set(name, (u = new Set)); - u.add((arity<0) ? -1 : arity); - }; - - __dbCleanupMap.addFunction = function(pDb, name, arity){ - const m = __dbCleanupMap(pDb, 1); - if(!m.udf) m.udf = new Map; - this._addUDF(pDb, name, arity, m.udf); - }; - - __dbCleanupMap.addWindowFunc = function(pDb, name, arity){ - const m = __dbCleanupMap(pDb, 1); - if(!m.wudf) m.wudf = new Map; - this._addUDF(pDb, name, arity, m.wudf); - }; - - /** - Intended to be called _only_ from sqlite3_close_v2(), - passed its non-0 db argument. - - This function frees up certain automatically-installed WASM - function bindings which were installed on behalf of the given db, - as those may otherwise leak. - - Notable caveat: this is only ever run via - sqlite3.capi.sqlite3_close_v2(). If a client, for whatever - reason, uses sqlite3.wasm.exports.sqlite3_close_v2() (the - function directly exported from WASM), this cleanup will not - happen. - - This is not a silver bullet for avoiding automation-related - leaks but represents "an honest effort." - - The issue being addressed here is covered at: - - https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr - */ - __dbCleanupMap.cleanup = function(pDb){ - pDb = __argPDb(pDb); - //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false; - /** - Installing NULL functions in the C API will remove those - bindings. The FuncPtrAdapter which sits between us and the C - API will also treat that as an opportunity to - wasm.uninstallFunction() any WASM function bindings it has - installed for pDb. - */ - const closeArgs = [pDb]; - for(const name of [ - 'sqlite3_busy_handler', - 'sqlite3_commit_hook', - 'sqlite3_preupdate_hook', - 'sqlite3_progress_handler', - 'sqlite3_rollback_hook', - 'sqlite3_set_authorizer', - 'sqlite3_trace_v2', - 'sqlite3_update_hook' - ]) { - const x = wasm.exports[name]; - closeArgs.length = x.length/*==argument count*/ - /* recall that undefined entries translate to 0 when passed to - WASM. */; - try{ capi[name](...closeArgs) } - catch(e){ - console.warn("close-time call of",name+"(",closeArgs,") threw:",e); - } - } - const m = __dbCleanupMap(pDb, 0); - if(!m) return; - if(m.collation){ - for(const name of m.collation){ - try{ - capi.sqlite3_create_collation_v2( - pDb, name, capi.SQLITE_UTF8, 0, 0, 0 - ); - }catch(e){ - /*ignored*/ - } - } - delete m.collation; - } - let i; - for(i = 0; i < 2; ++i){ /* Clean up UDFs... */ - const fmap = i ? m.wudf : m.udf; - if(!fmap) continue; - const func = i - ? capi.sqlite3_create_window_function - : capi.sqlite3_create_function_v2; - for(const e of fmap){ - const name = e[0], arities = e[1]; - const fargs = [pDb, name, 0/*arity*/, capi.SQLITE_UTF8, 0, 0, 0, 0, 0]; - if(i) fargs.push(0); - for(const arity of arities){ - try{ fargs[2] = arity; func.apply(null, fargs); } - catch(e){/*ignored*/} - } - arities.clear(); - } - fmap.clear(); - } - delete m.udf; - delete m.wudf; - }/*__dbCleanupMap.cleanup()*/; - - {/* Binding of sqlite3_close_v2() */ - const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*"); - capi.sqlite3_close_v2 = function(pDb){ - if(1!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_close_v2', 1); - if(pDb){ - try{__dbCleanupMap.cleanup(pDb)} catch(e){/*ignored*/} - } - return __sqlite3CloseV2(pDb); - }; - }/*sqlite3_close_v2()*/ - - if(capi.sqlite3session_table_filter){ - const __sqlite3SessionDelete = wasm.xWrap( - 'sqlite3session_delete', undefined, ['sqlite3_session*'] - ); - capi.sqlite3session_delete = function(pSession){ - if(1!==arguments.length){ - return __dbArgcMismatch(pDb, 'sqlite3session_delete', 1); - /* Yes, we're returning a value from a void function. That seems - like the lesser evil compared to not maintaining arg-count - consistency as we do with other similar bindings. */ - } - else if(pSession){ - //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true; - capi.sqlite3session_table_filter(pSession, 0, 0); - } - __sqlite3SessionDelete(pSession); - }; - } - - {/* Bindings for sqlite3_create_collation[_v2]() */ - // contextKey() impl for wasm.xWrap.FuncPtrAdapter - const contextKey = (argv,argIndex)=>{ - return 'argv['+argIndex+']:'+argv[0/* sqlite3* */]+ - ':'+wasm.cstrToJs(argv[1/* collation name */]).toLowerCase() - }; - const __sqlite3CreateCollationV2 = wasm.xWrap( - 'sqlite3_create_collation_v2', 'int', [ - 'sqlite3*', 'string', 'int', '*', - new wasm.xWrap.FuncPtrAdapter({ - /* int(*xCompare)(void*,int,const void*,int,const void*) */ - name: 'xCompare', signature: 'i(pipip)', contextKey - }), - new wasm.xWrap.FuncPtrAdapter({ - /* void(*xDestroy(void*) */ - name: 'xDestroy', signature: 'v(p)', contextKey - }) - ] - ); - - /** - Works exactly like C's sqlite3_create_collation_v2() except that: - - 1) It returns capi.SQLITE_FORMAT if the 3rd argument contains - any encoding-related value other than capi.SQLITE_UTF8. No - other encodings are supported. As a special case, if the - bottom 4 bits of that argument are 0, SQLITE_UTF8 is - assumed. - - 2) It accepts JS functions for its function-pointer arguments, - for which it will install WASM-bound proxies. The bindings - are "permanent," in that they will stay in the WASM environment - until it shuts down unless the client calls this again with the - same collation name and a value of 0 or null for the - the function pointer(s). - - For consistency with the C API, it requires the same number of - arguments. It returns capi.SQLITE_MISUSE if passed any other - argument count. - - Returns 0 on success, non-0 on error, in which case the error - state of pDb (of type `sqlite3*` or argument-convertible to it) - may contain more information. - */ - capi.sqlite3_create_collation_v2 = function(pDb,zName,eTextRep,pArg,xCompare,xDestroy){ - if(6!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_create_collation_v2', 6); - else if( 0 === (eTextRep & 0xf) ){ - eTextRep |= capi.SQLITE_UTF8; - }else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){ - return __errEncoding(pDb); - } - try{ - const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy); - if(0===rc && xCompare instanceof Function){ - __dbCleanupMap.addCollation(pDb, zName); - } - return rc; - }catch(e){ - return util.sqlite3__wasm_db_error(pDb, e); - } - }; - - capi.sqlite3_create_collation = (pDb,zName,eTextRep,pArg,xCompare)=>{ - return (5===arguments.length) - ? capi.sqlite3_create_collation_v2(pDb,zName,eTextRep,pArg,xCompare,0) - : __dbArgcMismatch(pDb, 'sqlite3_create_collation', 5); - }; - - }/*sqlite3_create_collation() and friends*/ - - {/* Special-case handling of sqlite3_create_function_v2() - and sqlite3_create_window_function(). */ - /** FuncPtrAdapter for contextKey() for sqlite3_create_function() - and friends. */ - const contextKey = function(argv,argIndex){ - return ( - argv[0/* sqlite3* */] - +':'+(argv[2/*number of UDF args*/] < 0 ? -1 : argv[2]) - +':'+argIndex/*distinct for each xAbc callback type*/ - +':'+wasm.cstrToJs(argv[1]).toLowerCase() - ) - }; - - /** - JS proxies for the various sqlite3_create[_window]_function() - callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter. - */ - const __cfProxy = Object.assign(Object.create(null), { - xInverseAndStep: { - signature:'v(pip)', contextKey, - callProxy: (callback)=>{ - return (pCtx, argc, pArgv)=>{ - try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) } - catch(e){ capi.sqlite3_result_error_js(pCtx, e) } - }; - } - }, - xFinalAndValue: { - signature:'v(p)', contextKey, - callProxy: (callback)=>{ - return (pCtx)=>{ - try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) } - catch(e){ capi.sqlite3_result_error_js(pCtx, e) } - }; - } - }, - xFunc: { - signature:'v(pip)', contextKey, - callProxy: (callback)=>{ - return (pCtx, argc, pArgv)=>{ - try{ - capi.sqlite3_result_js( - pCtx, - callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) - ); - }catch(e){ - //console.error('xFunc() caught:',e); - capi.sqlite3_result_error_js(pCtx, e); - } - }; - } - }, - xDestroy: { - signature:'v(p)', contextKey, - //Arguable: a well-behaved destructor doesn't require a proxy. - callProxy: (callback)=>{ - return (pVoid)=>{ - try{ callback(pVoid) } - catch(e){ console.error("UDF xDestroy method threw:",e) } - }; - } - } - })/*__cfProxy*/; - - const __sqlite3CreateFunction = wasm.xWrap( - "sqlite3_create_function_v2", "int", [ - "sqlite3*", "string"/*funcName*/, "int"/*nArg*/, - "int"/*eTextRep*/, "*"/*pApp*/, - new wasm.xWrap.FuncPtrAdapter({name: 'xFunc', ...__cfProxy.xFunc}), - new wasm.xWrap.FuncPtrAdapter({name: 'xStep', ...__cfProxy.xInverseAndStep}), - new wasm.xWrap.FuncPtrAdapter({name: 'xFinal', ...__cfProxy.xFinalAndValue}), - new wasm.xWrap.FuncPtrAdapter({name: 'xDestroy', ...__cfProxy.xDestroy}) - ] - ); - - const __sqlite3CreateWindowFunction = wasm.xWrap( - "sqlite3_create_window_function", "int", [ - "sqlite3*", "string"/*funcName*/, "int"/*nArg*/, - "int"/*eTextRep*/, "*"/*pApp*/, - new wasm.xWrap.FuncPtrAdapter({name: 'xStep', ...__cfProxy.xInverseAndStep}), - new wasm.xWrap.FuncPtrAdapter({name: 'xFinal', ...__cfProxy.xFinalAndValue}), - new wasm.xWrap.FuncPtrAdapter({name: 'xValue', ...__cfProxy.xFinalAndValue}), - new wasm.xWrap.FuncPtrAdapter({name: 'xInverse', ...__cfProxy.xInverseAndStep}), - new wasm.xWrap.FuncPtrAdapter({name: 'xDestroy', ...__cfProxy.xDestroy}) - ] - ); - - /* Documented in the api object's initializer. */ - capi.sqlite3_create_function_v2 = function f( - pDb, funcName, nArg, eTextRep, pApp, - xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**) - xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) - xFinal, //void (*xFinal)(sqlite3_context*) - xDestroy //void (*xDestroy)(void*) - ){ - if( f.length!==arguments.length ){ - return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length); - }else if( 0 === (eTextRep & 0xf) ){ - eTextRep |= capi.SQLITE_UTF8; - }else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){ - return __errEncoding(pDb); - } - try{ - const rc = __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, - pApp, xFunc, xStep, xFinal, xDestroy); - if(0===rc && (xFunc instanceof Function - || xStep instanceof Function - || xFinal instanceof Function - || xDestroy instanceof Function)){ - __dbCleanupMap.addFunction(pDb, funcName, nArg); - } - return rc; - }catch(e){ - console.error("sqlite3_create_function_v2() setup threw:",e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: "+e); - } - }; - - /* Documented in the api object's initializer. */ - capi.sqlite3_create_function = function f( - pDb, funcName, nArg, eTextRep, pApp, - xFunc, xStep, xFinal - ){ - return (f.length===arguments.length) - ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, - pApp, xFunc, xStep, xFinal, 0) - : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length); - }; - - /* Documented in the api object's initializer. */ - capi.sqlite3_create_window_function = function f( - pDb, funcName, nArg, eTextRep, pApp, - xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) - xFinal, //void (*xFinal)(sqlite3_context*) - xValue, //void (*xValue)(sqlite3_context*) - xInverse,//void (*xInverse)(sqlite3_context*,int,sqlite3_value**) - xDestroy //void (*xDestroy)(void*) - ){ - if( f.length!==arguments.length ){ - return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length); - }else if( 0 === (eTextRep & 0xf) ){ - eTextRep |= capi.SQLITE_UTF8; - }else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){ - return __errEncoding(pDb); - } - try{ - const rc = __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, - pApp, xStep, xFinal, xValue, - xInverse, xDestroy); - if(0===rc && (xStep instanceof Function - || xFinal instanceof Function - || xValue instanceof Function - || xInverse instanceof Function - || xDestroy instanceof Function)){ - __dbCleanupMap.addWindowFunc(pDb, funcName, nArg); - } - return rc; - }catch(e){ - console.error("sqlite3_create_window_function() setup threw:",e); - return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: "+e); - } - }; - /** - A _deprecated_ alias for capi.sqlite3_result_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfSetResult = - capi.sqlite3_create_function.udfSetResult = - capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js; - - /** - A _deprecated_ alias for capi.sqlite3_values_to_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfConvertArgs = - capi.sqlite3_create_function.udfConvertArgs = - capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js; - - /** - A _deprecated_ alias for capi.sqlite3_result_error_js() which - predates the addition of that function in the public API. - */ - capi.sqlite3_create_function_v2.udfSetError = - capi.sqlite3_create_function.udfSetError = - capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js; - - }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; - - {/* Special-case handling of sqlite3_prepare_v2() and - sqlite3_prepare_v3() */ - - /** - Helper for string:flexible conversions which require a - byte-length counterpart argument. Passed a value and its - ostensible length, this function returns [V,N], where V is - either v or a transformed copy of v and N is either n, -1, or - the byte length of v (if it's a byte array or ArrayBuffer). - */ - const __flexiString = (v,n)=>{ - if('string'===typeof v){ - n = -1; - }else if(util.isSQLableTypedArray(v)){ - n = v.byteLength; - v = util.typedArrayToString( - (v instanceof ArrayBuffer) ? new Uint8Array(v) : v - ); - }else if(Array.isArray(v)){ - v = v.join(""); - n = -1; - } - return [v, n]; - }; - - /** - Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). - */ - const __prepare = { - /** - 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. - */ - basic: wasm.xWrap('sqlite3_prepare_v3', - "int", ["sqlite3*", "string", - "int"/*ignored for this impl!*/, - "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. - */ - full: wasm.xWrap('sqlite3_prepare_v3', - "int", ["sqlite3*", "*", "int", "int", - "**", "**"]) - }; - - /* Documented in the capi object's initializer. */ - capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ - if(f.length!==arguments.length){ - return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); - } - const [xSql, xSqlLen] = __flexiString(sql, sqlLen); - switch(typeof xSql){ - case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); - case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); - default: - return util.sqlite3__wasm_db_error( - pDb, capi.SQLITE_MISUSE, - "Invalid SQL argument type for sqlite3_prepare_v2/v3()." - ); - } - }; - - /* Documented in the capi object's initializer. */ - capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){ - return (f.length===arguments.length) - ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) - : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length); - }; - - }/*sqlite3_prepare_v2/v3()*/ - - {/*sqlite3_bind_text/blob()*/ - const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [ - "sqlite3_stmt*", "int", "string", "int", "*" - ]); - const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [ - "sqlite3_stmt*", "int", "*", "int", "*" - ]); - - /** Documented in the capi object's initializer. */ - capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy){ - if(f.length!==arguments.length){ - return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), - "sqlite3_bind_text", f.length); - }else if(wasm.isPtr(text) || null===text){ - return __bindText(pStmt, iCol, text, nText, xDestroy); - }else if(text instanceof ArrayBuffer){ - text = new Uint8Array(text); - }else if(Array.isArray(pMem)){ - text = pMem.join(''); - } - let p, n; - try{ - if(util.isSQLableTypedArray(text)){ - p = wasm.allocFromTypedArray(text); - n = text.byteLength; - }else if('string'===typeof text){ - [p, n] = wasm.allocCString(text); - }else{ - return util.sqlite3__wasm_db_error( - capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, - "Invalid 3rd argument type for sqlite3_bind_text()." - ); - } - return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); - }catch(e){ - wasm.dealloc(p); - return util.sqlite3__wasm_db_error( - capi.sqlite3_db_handle(pStmt), e - ); - } - }/*sqlite3_bind_text()*/; - - /** Documented in the capi object's initializer. */ - capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy){ - if(f.length!==arguments.length){ - return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), - "sqlite3_bind_blob", f.length); - }else if(wasm.isPtr(pMem) || null===pMem){ - return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy); - }else if(pMem instanceof ArrayBuffer){ - pMem = new Uint8Array(pMem); - }else if(Array.isArray(pMem)){ - pMem = pMem.join(''); - } - let p, n; - try{ - if(util.isBindableTypedArray(pMem)){ - p = wasm.allocFromTypedArray(pMem); - n = nMem>=0 ? nMem : pMem.byteLength; - }else if('string'===typeof pMem){ - [p, n] = wasm.allocCString(pMem); - }else{ - return util.sqlite3__wasm_db_error( - capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, - "Invalid 3rd argument type for sqlite3_bind_blob()." - ); - } - return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC); - }catch(e){ - wasm.dealloc(p); - return util.sqlite3__wasm_db_error( - capi.sqlite3_db_handle(pStmt), e - ); - } - }/*sqlite3_bind_blob()*/; - - }/*sqlite3_bind_text/blob()*/ - - {/* sqlite3_config() */ - /** - Wraps a small subset of the C API's sqlite3_config() options. - Unsupported options trigger the return of capi.SQLITE_NOTFOUND. - Passing fewer than 2 arguments triggers return of - capi.SQLITE_MISUSE. - */ - capi.sqlite3_config = function(op, ...args){ - if(arguments.length<2) return capi.SQLITE_MISUSE; - switch(op){ - case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN: // 20 /* int */ - case capi.SQLITE_CONFIG_MEMSTATUS:// 9 /* boolean */ - case capi.SQLITE_CONFIG_SMALL_MALLOC: // 27 /* boolean */ - case capi.SQLITE_CONFIG_SORTERREF_SIZE: // 28 /* int nByte */ - case capi.SQLITE_CONFIG_STMTJRNL_SPILL: // 26 /* int nByte */ - case capi.SQLITE_CONFIG_URI:// 17 /* int */ - return wasm.exports.sqlite3__wasm_config_i(op, args[0]); - case capi.SQLITE_CONFIG_LOOKASIDE: // 13 /* int int */ - return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]); - case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: // 29 /* sqlite3_int64 */ - return wasm.exports.sqlite3__wasm_config_j(op, args[0]); - case capi.SQLITE_CONFIG_GETMALLOC: // 5 /* sqlite3_mem_methods* */ - case capi.SQLITE_CONFIG_GETMUTEX: // 11 /* sqlite3_mutex_methods* */ - case capi.SQLITE_CONFIG_GETPCACHE2: // 19 /* sqlite3_pcache_methods2* */ - case capi.SQLITE_CONFIG_GETPCACHE: // 15 /* no-op */ - case capi.SQLITE_CONFIG_HEAP: // 8 /* void*, int nByte, int min */ - case capi.SQLITE_CONFIG_LOG: // 16 /* xFunc, void* */ - case capi.SQLITE_CONFIG_MALLOC:// 4 /* sqlite3_mem_methods* */ - case capi.SQLITE_CONFIG_MMAP_SIZE: // 22 /* sqlite3_int64, sqlite3_int64 */ - case capi.SQLITE_CONFIG_MULTITHREAD: // 2 /* nil */ - case capi.SQLITE_CONFIG_MUTEX: // 10 /* sqlite3_mutex_methods* */ - case capi.SQLITE_CONFIG_PAGECACHE: // 7 /* void*, int sz, int N */ - case capi.SQLITE_CONFIG_PCACHE2: // 18 /* sqlite3_pcache_methods2* */ - case capi.SQLITE_CONFIG_PCACHE: // 14 /* no-op */ - case capi.SQLITE_CONFIG_PCACHE_HDRSZ: // 24 /* int *psz */ - case capi.SQLITE_CONFIG_PMASZ: // 25 /* unsigned int szPma */ - case capi.SQLITE_CONFIG_SERIALIZED: // 3 /* nil */ - case capi.SQLITE_CONFIG_SINGLETHREAD: // 1 /* nil */: - case capi.SQLITE_CONFIG_SQLLOG: // 21 /* xSqllog, void* */ - case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: // 23 /* int nByte */ - default: - /* maintenance note: we specifically do not include - SQLITE_CONFIG_ROWID_IN_VIEW here, on the grounds that - it's only for legacy support and no apps written with - this API require that. */ - return capi.SQLITE_NOTFOUND; - } - }; - }/* sqlite3_config() */ - - {/*auto-extension bindings.*/ - const __autoExtFptr = new Set; - - capi.sqlite3_auto_extension = function(fPtr){ - if( fPtr instanceof Function ){ - fPtr = wasm.installFunction('i(ppp)', fPtr); - }else if( 1!==arguments.length || !wasm.isPtr(fPtr) ){ - return capi.SQLITE_MISUSE; - } - const rc = wasm.exports.sqlite3_auto_extension(fPtr); - if( fPtr!==arguments[0] ){ - if(0===rc) __autoExtFptr.add(fPtr); - else wasm.uninstallFunction(fPtr); - } - return rc; - }; - - capi.sqlite3_cancel_auto_extension = function(fPtr){ - /* We do not do an automatic JS-to-WASM function conversion here - because it would be senseless: the converted pointer would - never possibly match an already-installed one. */; - if(!fPtr || 1!==arguments.length || !wasm.isPtr(fPtr)) return 0; - return wasm.exports.sqlite3_cancel_auto_extension(fPtr); - /* Note that it "cannot happen" that a client passes a pointer which - is in __autoExtFptr because __autoExtFptr only contains automatic - conversions created inside sqlite3_auto_extension() and - never exposed to the client. */ - }; - - capi.sqlite3_reset_auto_extension = function(){ - wasm.exports.sqlite3_reset_auto_extension(); - for(const fp of __autoExtFptr) wasm.uninstallFunction(fp); - __autoExtFptr.clear(); - }; - }/* auto-extension */ - - const pKvvfs = capi.sqlite3_vfs_find("kvvfs"); - if( pKvvfs ){/* kvvfs-specific glue */ - if(util.isUIThread()){ - const kvvfsMethods = new capi.sqlite3_kvvfs_methods( - wasm.exports.sqlite3__wasm_kvvfs_methods() - ); - delete capi.sqlite3_kvvfs_methods; - - const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack, - pstack = wasm.pstack; - - const kvvfsStorage = (zClass)=> - ((115/*=='s'*/===wasm.peek(zClass)) - ? sessionStorage : localStorage); - - /** - Implementations for members of the object referred to by - sqlite3__wasm_kvvfs_methods(). We swap out the native - implementations with these, which use localStorage or - sessionStorage for their backing store. - */ - const kvvfsImpls = { - xRead: (zClass, zKey, zBuf, nBuf)=>{ - const stack = pstack.pointer, - astack = wasm.scopedAllocPush(); - try { - const zXKey = kvvfsMakeKey(zClass,zKey); - if(!zXKey) return -3/*OOM*/; - const jKey = wasm.cstrToJs(zXKey); - const jV = kvvfsStorage(zClass).getItem(jKey); - if(!jV) return -1; - const nV = jV.length /* Note that we are relying 100% on v being - ASCII so that jV.length is equal to the - C-string's byte length. */; - if(nBuf<=0) return nV; - else if(1===nBuf){ - wasm.poke(zBuf, 0); - return nV; - } - const zV = wasm.scopedAllocCString(jV); - if(nBuf > nV + 1) nBuf = nV + 1; - wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1); - wasm.poke(zBuf + nBuf - 1, 0); - return nBuf - 1; - }catch(e){ - console.error("kvstorageRead()",e); - return -2; - }finally{ - pstack.restore(stack); - wasm.scopedAllocPop(astack); - } - }, - xWrite: (zClass, zKey, zData)=>{ - const stack = pstack.pointer; - try { - const zXKey = kvvfsMakeKey(zClass,zKey); - if(!zXKey) return 1/*OOM*/; - const jKey = wasm.cstrToJs(zXKey); - kvvfsStorage(zClass).setItem(jKey, wasm.cstrToJs(zData)); - return 0; - }catch(e){ - console.error("kvstorageWrite()",e); - return capi.SQLITE_IOERR; - }finally{ - pstack.restore(stack); - } - }, - xDelete: (zClass, zKey)=>{ - const stack = pstack.pointer; - try { - const zXKey = kvvfsMakeKey(zClass,zKey); - if(!zXKey) return 1/*OOM*/; - kvvfsStorage(zClass).removeItem(wasm.cstrToJs(zXKey)); - return 0; - }catch(e){ - console.error("kvstorageDelete()",e); - return capi.SQLITE_IOERR; - }finally{ - pstack.restore(stack); - } - } - }/*kvvfsImpls*/; - for(const k of Object.keys(kvvfsImpls)){ - kvvfsMethods[kvvfsMethods.memberKey(k)] = - wasm.installFunction( - kvvfsMethods.memberSignature(k), - kvvfsImpls[k] - ); - } - }else{ - /* Worker thread: unregister kvvfs to avoid it being used - for anything other than local/sessionStorage. It "can" - be used that way but it's not really intended to be. */ - capi.sqlite3_vfs_unregister(pKvvfs); - } - }/*pKvvfs*/ - - /* Warn if client-level code makes use of FuncPtrAdapter. */ - wasm.xWrap.FuncPtrAdapter.warnOnUse = true; - - const StructBinder = sqlite3.StructBinder - /* we require a local alias b/c StructBinder is removed from the sqlite3 - object during the final steps of the API cleanup. */; - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructBinder.StructType - target object. - - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error, e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc. - - As a special case, if the given function is a pointer, then - `wasm.functionEntry()` is used to validate that it is a known - function. If so, it is used as-is with no extra level of proxying - or cleanup, else an exception is thrown. It is legal to pass a - value of 0, indicating a NULL pointer, with the caveat that 0 - _is_ a legal function pointer in WASM but it will not be accepted - as such _here_. (Justification: the function at address zero must - be one which initially came from the WASM module, not a method we - want to bind to a virtual table or VFS.) - - This function returns a proxy for itself which is bound to tgt - and takes 2 args (name,func). That function returns the same - thing as this one, permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - - ACHTUNG: because we cannot generically know how to transform JS - exceptions into result codes, the installed functions do no - automatic catching of exceptions. It is critical, to avoid - undefined behavior in the C layer, that methods mapped via - this function do not throw. The exception, as it were, to that - rule is... - - If applyArgcCheck is true then each JS function (as opposed to - function pointers) gets wrapped in a proxy which asserts that it - is passed the expected number of arguments, throwing if the - argument count does not match expectations. That is only intended - for dev-time usage for sanity checking, and may leave the C - environment in an undefined state. - */ - const installMethod = function callee( - tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck - ){ - if(!(tgt instanceof StructBinder.StructType)){ - toss("Usage error: target object is-not-a StructType."); - }else if(!(func instanceof Function) && !wasm.isPtr(func)){ - toss("Usage errror: expecting a Function or WASM pointer to one."); - } - if(1===arguments.length){ - return (n,f)=>callee(tgt, n, f, applyArgcCheck); - } - if(!callee.argcProxy){ - callee.argcProxy = function(tgt, funcName, func,sig){ - return function(...args){ - if(func.length!==arguments.length){ - toss("Argument mismatch for", - tgt.structInfo.name+"::"+funcName - +": Native signature is:",sig); - } - return func.apply(this, args); - } - }; - /* An ondispose() callback for use with - StructBinder-created types. */ - callee.removeFuncList = function(){ - if(this.ondispose.__removeFuncList){ - this.ondispose.__removeFuncList.forEach( - (v,ndx)=>{ - if('number'===typeof v){ - try{wasm.uninstallFunction(v)} - catch(e){/*ignore*/} - } - /* else it's a descriptive label for the next number in - the list. */ - } - ); - delete this.ondispose.__removeFuncList; - } - }; - }/*static init*/ - const sigN = tgt.memberSignature(name); - if(sigN.length<2){ - toss("Member",name,"does not have a function pointer signature:",sigN); - } - const memKey = tgt.memberKey(name); - const fProxy = (applyArgcCheck && !wasm.isPtr(func)) - /** This middle-man proxy is only for use during development, to - confirm that we always pass the proper number of - arguments. We know that the C-level code will always use the - correct argument count. */ - ? callee.argcProxy(tgt, memKey, func, sigN) - : func; - if(wasm.isPtr(fProxy)){ - if(fProxy && !wasm.functionEntry(fProxy)){ - toss("Pointer",fProxy,"is not a WASM function table entry."); - } - tgt[memKey] = fProxy; - }else{ - const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); - tgt[memKey] = pFunc; - if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){ - tgt.addOnDispose('ondispose.__removeFuncList handler', - callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - } - return (n,f)=>callee(tgt, n, f, applyArgcCheck); - }/*installMethod*/; - installMethod.installMethodArgcCheck = false; - - /** - Installs methods into the given StructBinder.StructType-type - instance. Each entry in the given methods object must map to a - known member of the given StructType, else an exception will be - triggered. See installMethod() for more details, including the - semantics of the 3rd argument. - - As an exception to the above, if any two or more methods in the - 2nd argument are the exact same function, installMethod() is - _not_ called for the 2nd and subsequent instances, and instead - those instances get assigned the same method pointer which is - created for the first instance. This optimization is primarily to - accommodate special handling of sqlite3_module::xConnect and - xCreate methods. - - On success, returns its first argument. Throws on error. - */ - const installMethods = function( - structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck - ){ - const seen = new Map /* map of <Function, memberName> */; - for(const k of Object.keys(methods)){ - const m = methods[k]; - const prior = seen.get(m); - if(prior){ - const mkey = structInstance.memberKey(k); - structInstance[mkey] = structInstance[structInstance.memberKey(prior)]; - }else{ - installMethod(structInstance, k, m, applyArgcCheck); - seen.set(m, k); - } - } - return structInstance; - }; - - /** - Equivalent to calling installMethod(this,...arguments) with a - first argument of this object. If called with 1 or 2 arguments - and the first is an object, it's instead equivalent to calling - installMethods(this,...arguments). - */ - StructBinder.StructType.prototype.installMethod = function callee( - name, func, applyArgcCheck = installMethod.installMethodArgcCheck - ){ - return (arguments.length < 3 && name && 'object'===typeof name) - ? installMethods(this, ...arguments) - : installMethod(this, ...arguments); - }; - - /** - Equivalent to calling installMethods() with a first argument - of this object. - */ - StructBinder.StructType.prototype.installMethods = function( - methods, applyArgcCheck = installMethod.installMethodArgcCheck - ){ - return installMethods(this, methods, applyArgcCheck); - }; - -}); |