1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Internal refactoring of how sqlite3.wasm.xWrap() handles JS-to-C function pointer conversions, to enable similar conversions to be added more easily.

FossilOrigin-Name: 10cfe3fae6f680d3ecc3b0afbbf628ce91e34e3757b19dd27c231f0daf44232a
This commit is contained in:
stephan
2022-12-15 02:28:55 +00:00
parent d60061616f
commit 73b471964b
4 changed files with 100 additions and 76 deletions

View File

@ -138,7 +138,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
the range of supported argument types. */ the range of supported argument types. */
[ [
"sqlite3_progress_handler", undefined, [ "sqlite3_progress_handler", undefined, [
"sqlite3*", "int", wasm.xWrap.FuncPtrAdapter({ "sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({
name: 'xProgressHandler', name: 'xProgressHandler',
signature: 'i(p)', signature: 'i(p)',
bindScope: 'context', bindScope: 'context',
@ -180,7 +180,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
"**", "**", "*", "*", "*"], "**", "**", "*", "*", "*"],
["sqlite3_total_changes", "int", "sqlite3*"], ["sqlite3_total_changes", "int", "sqlite3*"],
["sqlite3_trace_v2", "int", "sqlite3*", "int", ["sqlite3_trace_v2", "int", "sqlite3*", "int",
wasm.xWrap.FuncPtrAdapter({ new wasm.xWrap.FuncPtrAdapter({
name: 'sqlite3_trace_v2::callback', name: 'sqlite3_trace_v2::callback',
signature: 'i(ippp)', signature: 'i(ippp)',
contextKey: (argIndex, argv)=>'sqlite3@'+argv[0] contextKey: (argIndex, argv)=>'sqlite3@'+argv[0]
@ -469,14 +469,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'sqlite3*','string','int','*', 'sqlite3*','string','int','*',
new wasm.xWrap.FuncPtrAdapter({ new wasm.xWrap.FuncPtrAdapter({
/* int(*xCompare)(void*,int,const void*,int,const void*) */ /* int(*xCompare)(void*,int,const void*,int,const void*) */
name: 'xCompare', name: 'sqlite3_create_collation_v2::xCompare',
signature: 'i(pipip)', signature: 'i(pipip)',
bindScope: 'context', bindScope: 'context',
contextKey: __collationContextKey contextKey: __collationContextKey
}), }),
new wasm.xWrap.FuncPtrAdapter({ new wasm.xWrap.FuncPtrAdapter({
/* void(*xDestroy(void*) */ /* void(*xDestroy(void*) */
name: 'xDestroy', name: 'sqlite3_create_collation_v2::xDestroy',
signature: 'v(p)', signature: 'v(p)',
bindScope: 'context', bindScope: 'context',
contextKey: __collationContextKey contextKey: __collationContextKey

View File

@ -1473,8 +1473,40 @@ self.WhWasmUtilInstaller = function(target){
} }
/** /**
EXPERIMENTAL! DO NOT USE IN CLIENT CODE! Internal-use-only base class for FuncPtrAdapter and potentially
additional stateful argument adapter classes.
Note that its main interface (convertArg()) is strictly
internal, not to be exposed to client code, as it may still
need re-shaping. Only the constructors of concrete subclasses
should be exposed to clients, and those in such a way that
does not hinder internal redesign of the convertArg()
interface.
*/
const AbstractArgAdapter = class {
constructor(opt){
this.name = opt.name;
}
/**
Gets called via xWrap() to "convert" v to whatever type
this specific class supports.
argIndex is the argv index of _this_ argument in the
being-xWrap()'d call. argv is the current argument list
undergoing xWrap() argument conversion. argv entries to the
left of argIndex will have already undergone transformation and
those to the right will not have (they will have the values the
client-level code passed in, awaiting conversion). The RHS
indexes must never be relied upon for anything because their
types are indeterminate, whereas the LHS values will be
WASM-compatible values by the time this is called.
*/
convertArg(v,argIndex,argv){
toss("AbstractArgAdapter must be subclassed.");
}
};
/**
An attempt at adding function pointer conversion support to An attempt at adding function pointer conversion support to
xWrap(). This type is recognized by xWrap() as a proxy for xWrap(). This type is recognized by xWrap() as a proxy for
converting a JS function to a C-side function, either converting a JS function to a C-side function, either
@ -1482,7 +1514,7 @@ self.WhWasmUtilInstaller = function(target){
or semi-contextual, where it may keep track of a single binding or semi-contextual, where it may keep track of a single binding
for a given context and uninstall the binding if it's replaced. for a given context and uninstall the binding if it's replaced.
Requires an options object with these properties: The constructor requires an options object with these properties:
- name (optional): string describing the function binding. This - name (optional): string describing the function binding. This
is solely for debugging and error-reporting purposes. If not is solely for debugging and error-reporting purposes. If not
@ -1514,17 +1546,18 @@ self.WhWasmUtilInstaller = function(target){
context. This mode is the default if bindScope is _not_ set context. This mode is the default if bindScope is _not_ set
but a property named contextKey (described below) is. but a property named contextKey (described below) is.
- contextKey (function): is only used if bindScope is not set or - contextKey (function): is only used if bindScope is 'context'
is 'context'. This function gets passed (argIndex,argv), where or if bindScope is not set and this function is, in which case
argIndex is the index of _this_ function pointer in its 'context' is assumed. This function gets passed
_wrapping_ function's arguments and argv is the _current_ (argIndex,argv), where argIndex is the index of _this_ function
still-being-xWrap()-processed args array. All arguments to the pointer in its _wrapping_ function's arguments and argv is the
left of argIndex will have been processed by xWrap() by the _current_ still-being-xWrap()-processed args array. All
time this is called. argv[argIndex] will be the value the user arguments to the left of argIndex will have been processed by
passed in to the xWrap()'d function for the argument this xWrap() by the time this is called. argv[argIndex] will be the
FuncPtrAdapter is mapped to. Arguments to the right of value the user passed in to the xWrap()'d function for the
argv[argIndex] will not yet have been converted before this is argument this FuncPtrAdapter is mapped to. Arguments to the
called. The function must return a key which uniquely right of argv[argIndex] will not yet have been converted before
this is called. The function must return a key which uniquely
identifies this function mapping context for _this_ identifies this function mapping context for _this_
FuncPtrAdapter instance (other instances are not considered), FuncPtrAdapter instance (other instances are not considered),
taking into account that C functions often take some sort of taking into account that C functions often take some sort of
@ -1547,74 +1580,65 @@ self.WhWasmUtilInstaller = function(target){
The constructor only saves the above state for later, and does The constructor only saves the above state for later, and does
not actually bind any functions. Its convertArg() method is not actually bind any functions. Its convertArg() method is
called via xWrap() to perform any bindings. called via xWrap() to perform any bindings.
If this is called like a function, instead of a constructor,
it behaves as if it were called like a constructor.
*/ */
xArg.FuncPtrAdapter = function ctor(opt) { xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter {
if(!(this instanceof xArg.FuncPtrAdapter)){ constructor(opt) {
return new xArg.FuncPtrAdapter(opt); super(opt);
this.signature = opt.signature;
if(!opt.bindScope && (opt.contextKey instanceof Function)){
opt.bindScope = 'context';
}else if(FuncPtrAdapter.bindScopes.indexOf(opt.bindScope)<0){
toss("Invalid options.bindScope ("+opt.bindMod+") for FuncPtrAdapter. "+
"Expecting one of: ("+FuncPtrAdapter.bindScopes.join(', ')+')');
}
this.bindScope = opt.bindScope;
if(opt.contextKey) this.contextKey = opt.contextKey /*else inherit one*/;
this.isTransient = 'transient'===this.bindScope;
this.isContext = 'context'===this.bindScope;
if( ('singleton'===this.bindScope) ) this.singleton = [];
else this.singleton = undefined;
//console.warn("FuncPtrAdapter()",opt,this);
} }
this.signature = opt.signature;
if(!opt.bindScope && (opt.contextKey instanceof Function)){ static bindScopes = [
opt.bindScope = 'context'; 'transient', 'context', 'singleton'
}else if(ctor.bindScopes.indexOf(opt.bindScope)<0){ ];
toss("Invalid options.bindScope ("+opt.bindMod+") for FuncPtrAdapter. "+
"Expecting one of: ("+ctor.bindScopes.join(', ')+')');
}
this.bindScope = opt.bindScope;
this.name = opt.name || '';
if(opt.contextKey) this.contextKey = opt.contextKey /*else inherit one*/;
this.isTransient = 'transient'===this.bindScope;
this.isContext = 'context'===this.bindScope;
if( ('singleton'===this.bindScope) ) this.singleton = [];
else this.singleton = undefined;
//console.warn("FuncPtrAdapter()",opt,this);
};
xArg.FuncPtrAdapter.bindScopes = [
'transient', 'context', 'singleton'
];
xArg.FuncPtrAdapter.prototype = {
/* Dummy impl. Overwritten per-instance as needed. */ /* Dummy impl. Overwritten per-instance as needed. */
contextKey: function(argIndex,argv){ contextKey(argIndex,argv){
return this; return this;
}, }
/* Returns this objects mapping for the given context key, in the /* Returns this objects mapping for the given context key, in the
form of an an array, creating the mapping if needed. The key form of an an array, creating the mapping if needed. The key
may be anything suitable for use in a Map. */ may be anything suitable for use in a Map. */
contextMap: function(key){ contextMap(key){
const cm = (this.__cmap || (this.__cmap = new Map)); const cm = (this.__cmap || (this.__cmap = new Map));
let rc = cm.get(key); let rc = cm.get(key);
if(undefined===rc) cm.set(key, (rc = [])); if(undefined===rc) cm.set(key, (rc = []));
return rc; return rc;
}, }
/** /**
Gets called via xWrap() to "convert" v to a WASM-bound function Gets called via xWrap() to "convert" v to a WASM-bound function
pointer. If v is one of (a pointer, null, undefined) then pointer. If v is one of (a pointer, null, undefined) then
(v||0) is returned and any earlier function installed by this (v||0) is returned and any earlier function installed by this
mapping _might_, depending on how it's bound, be mapping _might_, depending on how it's bound, be uninstalled.
uninstalled. If v is not one of those types, it must be a If v is not one of those types, it must be a Function, for
Function, for which it creates (if needed) a WASM function which it creates (if needed) a WASM function binding and
binding and returns the WASM pointer to that binding. If this returns the WASM pointer to that binding. If this instance is
instance is not in 'transient' mode, it will remember the not in 'transient' mode, it will remember the binding for at
binding for at least the next call, to avoid recreating the least the next call, to avoid recreating the function binding
function binding unnecessarily. unnecessarily.
If it's passed a pointer(ish) value for v, it does _not_ If it's passed a pointer(ish) value for v, it does _not_
perform any function binding, so this object's bindMode is perform any function binding, so this object's bindMode is
irrelevant for such cases. irrelevant for such cases.
argIndex is the argv index of _this_ argument in the See the parent class's convertArg() docs for details on what
being-xWrap()'d call. argv is the current argument list exactly the 2nd and 3rd arguments are.
undergoing xWrap() argument conversion. argv entries to the
left of argIndex will have already undergone transformation and
those to the right will not have (they will have the values the
client-level code passed in, awaiting conversion). The RHS
indexes must never be relied upon for anything because their
types are indeterminate, whereas the LHS values will be
WASM-compatible values by the time this is called.
*/ */
convertArg: function(v,argIndex,argv){ convertArg(v,argIndex,argv){
//console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v); //console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
let pair = this.singleton; let pair = this.singleton;
if(!pair && this.isContext){ if(!pair && this.isContext){
@ -1650,7 +1674,7 @@ self.WhWasmUtilInstaller = function(target){
this.signature+"."); this.signature+".");
} }
}/*convertArg()*/ }/*convertArg()*/
}/*FuncPtrAdapter.prototype*/; }/*FuncPtrAdapter*/;
const __xArgAdapterCheck = const __xArgAdapterCheck =
(t)=>xArg.get(t) || toss("Argument adapter not found:",t); (t)=>xArg.get(t) || toss("Argument adapter not found:",t);
@ -1823,7 +1847,7 @@ self.WhWasmUtilInstaller = function(target){
/*Verify the arg type conversions are valid...*/; /*Verify the arg type conversions are valid...*/;
if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType); if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType);
for(const t of argTypes){ for(const t of argTypes){
if(t instanceof xArg.FuncPtrAdapter) xArg.set(t, (...args)=>t.convertArg(...args)); if(t instanceof AbstractArgAdapter) xArg.set(t, (...args)=>t.convertArg(...args));
else __xArgAdapterCheck(t); else __xArgAdapterCheck(t);
} }
const cxw = cache.xWrap; const cxw = cache.xWrap;
@ -1842,9 +1866,9 @@ self.WhWasmUtilInstaller = function(target){
The public interface of argument adapters is that they take The public interface of argument adapters is that they take
ONE argument and return a (possibly) converted result for ONE argument and return a (possibly) converted result for
it. The passing-on of arguments after the first is an it. The passing-on of arguments after the first is an
internal impl. detail for the sake of FuncPtrAdapter, and internal impl. detail for the sake of AbstractArgAdapter, and
not to be relied on or documented for other cases. The fact not to be relied on or documented for other cases. The fact
that this is how FuncPtrAdapter.convertArgs() gets its 2nd+ that this is how AbstractArgAdapter.convertArgs() gets its 2nd+
arguments, and how FuncPtrAdapter.contextKey() gets its arguments, and how FuncPtrAdapter.contextKey() gets its
args, is also an implementation detail and subject to args, is also an implementation detail and subject to
change. i.e. the public interface of 1 argument is stable. change. i.e. the public interface of 1 argument is stable.

View File

@ -1,5 +1,5 @@
C Remove\san\sunnecessary/obsolete\sEmscripten-specific\sexport. C Internal\srefactoring\sof\show\ssqlite3.wasm.xWrap()\shandles\sJS-to-C\sfunction\spointer\sconversions,\sto\senable\ssimilar\sconversions\sto\sbe\sadded\smore\seasily.
D 2022-12-15T02:26:13.244 D 2022-12-15T02:28:55.691
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -503,7 +503,7 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
F ext/wasm/api/sqlite3-api-glue.js a8010bf3fa184886d1e9e76b0d0d1e290b5fa25130510644a59e91a18f779ef4 F ext/wasm/api/sqlite3-api-glue.js 2e1087b870290a3a613d59ed626bde1f6da7c9c211cd36e41df1ee8b2932b508
F ext/wasm/api/sqlite3-api-oo1.js c0c4ccc269cccee657ffd03f094da7e270e1367b7928926b3730d543555a12a6 F ext/wasm/api/sqlite3-api-oo1.js c0c4ccc269cccee657ffd03f094da7e270e1367b7928926b3730d543555a12a6
F ext/wasm/api/sqlite3-api-prologue.js 86eb4488f2be85e68c23ebcfad0834c24b6075e1645c67890cc4163c462efac1 F ext/wasm/api/sqlite3-api-prologue.js 86eb4488f2be85e68c23ebcfad0834c24b6075e1645c67890cc4163c462efac1
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f
F ext/wasm/common/whwasmutil.js 8bec2460dfb515986faf854c011b7ae4f2cc1b2a128d9afa74ca57ee6057bdaa F ext/wasm/common/whwasmutil.js e8934d24518f99a8995e2da5a4f308d36c13bea90d36a0396585e2debba94d57
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P c1d5261b222bbf94c20e558089f3d2eae6a88b6d739225ee4f7d0338e0e59994 P fa278022afd6dd6e499d26f74a8359f3e9973e1680772059ce331b64e77ec582
R 918e16f92d23cc300804b300dc026e25 R e784d3c7094c47f7ba465262d6340c12
U stephan U stephan
Z e5f6c496f6861a48778111a469988478 Z 0c197c6d7ed7bd2414c2cff61c07cad7
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
fa278022afd6dd6e499d26f74a8359f3e9973e1680772059ce331b64e77ec582 10cfe3fae6f680d3ecc3b0afbbf628ce91e34e3757b19dd27c231f0daf44232a