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. */
[
"sqlite3_progress_handler", undefined, [
"sqlite3*", "int", wasm.xWrap.FuncPtrAdapter({
"sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({
name: 'xProgressHandler',
signature: 'i(p)',
bindScope: 'context',
@ -180,7 +180,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
"**", "**", "*", "*", "*"],
["sqlite3_total_changes", "int", "sqlite3*"],
["sqlite3_trace_v2", "int", "sqlite3*", "int",
wasm.xWrap.FuncPtrAdapter({
new wasm.xWrap.FuncPtrAdapter({
name: 'sqlite3_trace_v2::callback',
signature: 'i(ippp)',
contextKey: (argIndex, argv)=>'sqlite3@'+argv[0]
@ -469,14 +469,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'sqlite3*','string','int','*',
new wasm.xWrap.FuncPtrAdapter({
/* int(*xCompare)(void*,int,const void*,int,const void*) */
name: 'xCompare',
name: 'sqlite3_create_collation_v2::xCompare',
signature: 'i(pipip)',
bindScope: 'context',
contextKey: __collationContextKey
}),
new wasm.xWrap.FuncPtrAdapter({
/* void(*xDestroy(void*) */
name: 'xDestroy',
name: 'sqlite3_create_collation_v2::xDestroy',
signature: 'v(p)',
bindScope: 'context',
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
xWrap(). This type is recognized by xWrap() as a proxy for
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
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
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
but a property named contextKey (described below) is.
- contextKey (function): is only used if bindScope is not set or
is 'context'. This function gets passed (argIndex,argv), where
argIndex is the index of _this_ function pointer in its
_wrapping_ function's arguments and argv is the _current_
still-being-xWrap()-processed args array. All arguments to the
left of argIndex will have been processed by xWrap() by the
time this is called. argv[argIndex] will be the value the user
passed in to the xWrap()'d function for the argument this
FuncPtrAdapter is mapped to. Arguments to the right of
argv[argIndex] will not yet have been converted before this is
called. The function must return a key which uniquely
- contextKey (function): is only used if bindScope is 'context'
or if bindScope is not set and this function is, in which case
'context' is assumed. This function gets passed
(argIndex,argv), where argIndex is the index of _this_ function
pointer in its _wrapping_ function's arguments and argv is the
_current_ still-being-xWrap()-processed args array. All
arguments to the left of argIndex will have been processed by
xWrap() by the time this is called. argv[argIndex] will be the
value the user passed in to the xWrap()'d function for the
argument this FuncPtrAdapter is mapped to. Arguments to the
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_
FuncPtrAdapter instance (other instances are not considered),
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
not actually bind any functions. Its convertArg() method is
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) {
if(!(this instanceof xArg.FuncPtrAdapter)){
return new xArg.FuncPtrAdapter(opt);
xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter {
constructor(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)){
opt.bindScope = 'context';
}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 = {
static bindScopes = [
'transient', 'context', 'singleton'
];
/* Dummy impl. Overwritten per-instance as needed. */
contextKey: function(argIndex,argv){
contextKey(argIndex,argv){
return this;
},
}
/* Returns this objects mapping for the given context key, in the
form of an an array, creating the mapping if needed. The key
may be anything suitable for use in a Map. */
contextMap: function(key){
contextMap(key){
const cm = (this.__cmap || (this.__cmap = new Map));
let rc = cm.get(key);
if(undefined===rc) cm.set(key, (rc = []));
return rc;
},
}
/**
Gets called via xWrap() to "convert" v to a WASM-bound function
pointer. If v is one of (a pointer, null, undefined) then
(v||0) is returned and any earlier function installed by this
mapping _might_, depending on how it's bound, be
uninstalled. If v is not one of those types, it must be a
Function, for which it creates (if needed) a WASM function
binding and returns the WASM pointer to that binding. If this
instance is not in 'transient' mode, it will remember the
binding for at least the next call, to avoid recreating the
function binding unnecessarily.
mapping _might_, depending on how it's bound, be uninstalled.
If v is not one of those types, it must be a Function, for
which it creates (if needed) a WASM function binding and
returns the WASM pointer to that binding. If this instance is
not in 'transient' mode, it will remember the binding for at
least the next call, to avoid recreating the function binding
unnecessarily.
If it's passed a pointer(ish) value for v, it does _not_
perform any function binding, so this object's bindMode is
irrelevant for such cases.
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.
See the parent class's convertArg() docs for details on what
exactly the 2nd and 3rd arguments are.
*/
convertArg: function(v,argIndex,argv){
convertArg(v,argIndex,argv){
//console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
let pair = this.singleton;
if(!pair && this.isContext){
@ -1650,7 +1674,7 @@ self.WhWasmUtilInstaller = function(target){
this.signature+".");
}
}/*convertArg()*/
}/*FuncPtrAdapter.prototype*/;
}/*FuncPtrAdapter*/;
const __xArgAdapterCheck =
(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...*/;
if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType);
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);
}
const cxw = cache.xWrap;
@ -1842,9 +1866,9 @@ self.WhWasmUtilInstaller = function(target){
The public interface of argument adapters is that they take
ONE argument and return a (possibly) converted result for
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
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
args, is also an implementation detail and subject to
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.
D 2022-12-15T02:26:13.244
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:28:55.691
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
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/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
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-prologue.js 86eb4488f2be85e68c23ebcfad0834c24b6075e1645c67890cc4163c462efac1
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/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
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.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
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.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P c1d5261b222bbf94c20e558089f3d2eae6a88b6d739225ee4f7d0338e0e59994
R 918e16f92d23cc300804b300dc026e25
P fa278022afd6dd6e499d26f74a8359f3e9973e1680772059ce331b64e77ec582
R e784d3c7094c47f7ba465262d6340c12
U stephan
Z e5f6c496f6861a48778111a469988478
Z 0c197c6d7ed7bd2414c2cff61c07cad7
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
fa278022afd6dd6e499d26f74a8359f3e9973e1680772059ce331b64e77ec582
10cfe3fae6f680d3ecc3b0afbbf628ce91e34e3757b19dd27c231f0daf44232a