"sqlite3*",
new wasm.xWrap.FuncPtrAdapter({
signature: 'i(pi)',
- contextKey: (argIndex,argv)=>'sqlite3@'+argv[0]
+ contextKey: (argv,argIndex)=>'sqlite3@'+argv[0]
}),
"*"
]],
name: 'xProgressHandler',
signature: 'i(p)',
bindScope: 'context',
- contextKey: (argIndex,argv)=>'sqlite3@'+argv[0]
+ contextKey: (argv,argIndex)=>'sqlite3@'+argv[0]
}), "*"
]
],
new wasm.xWrap.FuncPtrAdapter({
name: 'sqlite3_trace_v2::callback',
signature: 'i(ippp)',
- contextKey: (argIndex, argv)=>'sqlite3@'+argv[0]
+ contextKey: (argv,argIndex)=>'sqlite3@'+argv[0]
}), "*"],
["sqlite3_txn_state", "int", ["sqlite3*","string"]],
/* Note that sqlite3_uri_...() have very specific requirements for
);
};
- if(1){/* Bindings for sqlite3_create_collation[_v2]() */
- const __collationContextKey = (argIndex,argv)=>{
+ {/* Bindings for sqlite3_create_collation[_v2]() */
+ // contextKey() impl for wasm.xWrap.FuncPtrAdapter
+ const contextKey = (argv,argIndex)=>{
return 'argv['+argIndex+']:sqlite3@'+argv[0]+
':'+wasm.cstrToJs(argv[1]).toLowerCase()
};
- const __ccv2 = wasm.xWrap(
- 'sqlite3_create_collation_v2', 'int',
- 'sqlite3*','string','int','*',
- new wasm.xWrap.FuncPtrAdapter({
- /* int(*xCompare)(void*,int,const void*,int,const void*) */
- name: 'sqlite3_create_collation_v2::xCompare',
- signature: 'i(pipip)',
- bindScope: 'context',
- contextKey: __collationContextKey
- }),
- new wasm.xWrap.FuncPtrAdapter({
- /* void(*xDestroy(void*) */
- name: 'sqlite3_create_collation_v2::xDestroy',
- signature: 'v(p)',
- bindScope: 'context',
- contextKey: __collationContextKey
- })
+ 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
+ })
+ ]
);
/**
}else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){
return __errEncoding(pDb);
}
- let rc, pfCompare, pfDestroy;
- try{
- rc = __ccv2(pDb, zName, eTextRep, pArg, xCompare, xDestroy);
+ try{
+ return __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy);
}catch(e){
- rc = util.sqlite3_wasm_db_error(pDb, e);
+ return util.sqlite3_wasm_db_error(pDb, e);
}
- return rc;
};
capi.sqlite3_create_collation = (pDb,zName,eTextRep,pArg,xCompare)=>{
}/*sqlite3_exec() proxy*/;
{/* Special-case handling of sqlite3_create_function_v2()
- and sqlite3_create_window_function() */
- /* Maintenance reminder: FuncPtrAdapter is not expressive enough
- to be able to perform these mappings. */
- const sqlite3CreateFunction = wasm.xWrap(
- "sqlite3_create_function_v2", "int",
- ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
- "int"/*eTextRep*/, "*"/*pApp*/,
- "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/]
- );
-
- const sqlite3CreateWindowFunction = wasm.xWrap(
- "sqlite3_create_window_function", "int",
- ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/,
- "int"/*eTextRep*/, "*"/*pApp*/,
- "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/,
- "*"/*xInverse*/, "*"/*xDestroy*/]
- );
-
- const __xFunc = function(callback){
- return function(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);
- }
- };
- };
-
- const __xInverseAndStep = function(callback){
- return function(pCtx, argc, pArgv){
- try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) }
- catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
- };
- };
-
- const __xFinalAndValue = function(callback){
- return function(pCtx){
- try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) }
- catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
- };
- };
-
- const __xDestroy = function(callback){
- return function(pVoid){
- try{ callback(pVoid) }
- catch(e){ console.error("UDF xDestroy method threw:",e) }
- };
+ and sqlite3_create_window_function(). */
+ /**
+ FuncPtrAdapter for contextKey() for sqlite3_create_function().
+ */
+ const contextKey = function(argv,argIndex){
+ return (
+ 'sqlite3@'+argv[0]
+ +':'+argIndex
+ +':'+wasm.cstrToJs(argv[1]).toLowerCase()
+ )
};
- const __xMap = Object.assign(Object.create(null), {
- xFunc: {sig:'v(pip)', f:__xFunc},
- xStep: {sig:'v(pip)', f:__xInverseAndStep},
- xInverse: {sig:'v(pip)', f:__xInverseAndStep},
- xFinal: {sig:'v(p)', f:__xFinalAndValue},
- xValue: {sig:'v(p)', f:__xFinalAndValue},
- xDestroy: {sig:'v(p)', f:__xDestroy}
- });
-
- /* Internal helper for sqlite3_create_function() and friends. */
- const __xWrapFuncs = function(theKeys, theFuncs, tgtUninst){
- const rc = []
- for(const k of theKeys){
- let fArg = theFuncs[k];
- if('function'===typeof fArg){
- const w = __xMap[k] || toss3("Internal error in __xWrapFuncs: invalid key:",k);
- fArg = wasm.installFunction(w.sig, w.f(fArg));
- tgtUninst.push(fArg);
+ /**
+ 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) }
+ };
}
- rc.push(fArg);
}
- return rc;
- };
+ })/*__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(
}else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){
return __errEncoding(pDb);
}
- /* Wrap the callbacks in a WASM-bound functions... */
- const uninstall = [/*funcs to uninstall on error*/];
- let rc;
try{
- const funcArgs = __xWrapFuncs(['xFunc','xStep','xFinal','xDestroy'],
- {xFunc, xStep, xFinal, xDestroy},
- uninstall);
- rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
- pApp, ...funcArgs);
+ return __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
+ pApp, xFunc, xStep, xFinal, xDestroy);
}catch(e){
console.error("sqlite3_create_function_v2() setup threw:",e);
- for(let v of uninstall){
- wasm.uninstallFunction(v);
- }
- rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
- "Creation of UDF threw: "+e.message);
+ return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e);
}
- return rc;
};
+ /* Documented in the api object's initializer. */
capi.sqlite3_create_function = function f(
pDb, funcName, nArg, eTextRep, pApp,
xFunc, xStep, xFinal
pDb, funcName, nArg, eTextRep, pApp,
xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
xFinal, //void (*xFinal)(sqlite3_context*)
- xValue, //void (*xFinal)(sqlite3_context*)
- xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**)
+ xValue, //void (*xValue)(sqlite3_context*)
+ xInverse,//void (*xInverse)(sqlite3_context*,int,sqlite3_value**)
xDestroy //void (*xDestroy)(void*)
){
if( f.length!==arguments.length ){
}else if( capi.SQLITE_UTF8 !== (eTextRep & 0xf) ){
return __errEncoding(pDb);
}
- /* Wrap the callbacks in a WASM-bound functions... */
- const uninstall = [/*funcs to uninstall on error*/];
- let rc;
try{
- const funcArgs = __xWrapFuncs(['xStep','xFinal','xValue','xInverse','xDestroy'],
- {xStep, xFinal, xValue, xInverse, xDestroy},
- uninstall);
- rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
- pApp, ...funcArgs);
+ return __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
+ pApp, xStep, xFinal, xValue,
+ xInverse, xDestroy);
}catch(e){
console.error("sqlite3_create_window_function() setup threw:",e);
- for(let v of uninstall){
- wasm.uninstallFunction(v);
- }
- rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
- "Creation of UDF threw: "+e.message);
+ return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e);
}
- return rc;
};
/**
A _deprecated_ alias for capi.sqlite3_result_js() which
types are indeterminate, whereas the LHS values will be
WASM-compatible values by the time this is called.
*/
- convertArg(v,argIndex,argv){
+ convertArg(v,argv,argIndex){
toss("AbstractArgAdapter must be subclassed.");
}
};
context. This mode is the default if bindScope is _not_ set
but a property named contextKey (described below) is.
+ - callProxy (function): if set, this must be a function which
+ will act as a proxy for any "converted" JS function. It is
+ passed the being-converted function value and must return
+ either that function or a function which acts on its
+ behalf. The returned function will be the one which gets
+ installed into the WASM function table. The proxy must perform
+ any required argument conversion (noting that it will be called
+ from C code, so will receive C-format arguments) before passing
+ them on to the being-converted function. Whether or not the
+ proxy itself must return a value depends on the context. If it
+ does, it must be a WASM-friendly value, as it will be returning
+ from a call made from native code.
+
- 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
+ 'context' is assumed. This function gets bound to this object,
+ so its "this" is this object. It gets passed (argv,argIndex),
+ 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
state object as one or more of their arguments. As an example,
if the xWrap()'d function takes `(int,T*,functionPtr,X*)` and
- this FuncPtrAdapter is the argv[2]nd arg, contextKey(2,argv)
+ this FuncPtrAdapter is the argv[2]nd arg, contextKey(argv,2)
might return 'T@'+argv[1], or even just argv[1]. Note,
however, that the (X*) argument will not yet have been
processed by the time this is called and should not be used as
use their pointers in the key because most C-strings in this
constellation are transient.
- Yes, that ^^^ is a bit awkward, but it's what we have.
+ Yes, that ^^^ is quite awkward, but it's what we have.
The constructor only saves the above state for later, and does
not actually bind any functions. Its convertArg() method is
if( ('singleton'===this.bindScope) ) this.singleton = [];
else this.singleton = undefined;
//console.warn("FuncPtrAdapter()",opt,this);
+ this.callProxy = (opt.callProxy instanceof Function)
+ ? opt.callProxy : undefined;
}
static bindScopes = [
];
/* Dummy impl. Overwritten per-instance as needed. */
- contextKey(argIndex,argv){
+ contextKey(argv,argIndex){
return this;
}
See the parent class's convertArg() docs for details on what
exactly the 2nd and 3rd arguments are.
*/
- convertArg(v,argIndex,argv){
+ convertArg(v,argv,argIndex){
//console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
let pair = this.singleton;
if(!pair && this.isContext){
- pair = this.contextMap(this.contextKey(argIndex, argv));
+ pair = this.contextMap(this.contextKey(argv,argIndex));
}
if(pair && pair[0]===v) return pair[1];
if(v instanceof Function){
+ /* Install a WASM binding and return its pointer. */
+ if(this.callProxy) v = this.callProxy(v);
const fp = __installFunction(v, this.signature, this.isTransient);
if(pair){
/* Replace existing stashed mapping */
}else if(target.isPtr(v) || null===v || undefined===v){
if(pair && pair[1] && pair[1]!==v){
/* uninstall stashed mapping and replace stashed mapping with v. */
- //console.warn("FuncPtrAdapter is uninstalling function", this.contextKey(argIndex,argv),v);
+ //console.warn("FuncPtrAdapter is uninstalling function", this.contextKey(argv,argIndex),v);
try{target.uninstallFunction(pair[1])}
catch(e){/*ignored*/}
- pair[0] = pair[1] = (v || 0);
+ pair[0] = pair[1] = (v | 0);
}
return v || 0;
}else{
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 AbstractArgAdapter, and
- not to be relied on or documented for other cases. The fact
- 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.
- The fact that any arguments may be passed in after that one,
- and what those arguments are, is _not_ part of the public
- interface and is _not_ stable.
+ internal implementation detail for the sake of
+ AbstractArgAdapter, and not to be relied on or documented
+ for other cases. The fact 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. The fact that any
+ arguments may be passed in after that one, and what those
+ arguments are, is _not_ part of the public interface and is
+ _not_ stable.
*/
- for(const i in args) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], i, args);
+ for(const i in args) args[i] = cxw.convertArgNoCheck(
+ argTypes[i], args[i], args, i
+ );
return cxw.convertResultNoCheck(resultType, xf.apply(null,args));
}finally{
target.scopedAllocPop(scope);