js: implement a hand-written wrapper for sqlite3_create_function_v2() which converts, if necessary, JS-function-type args to WASM function wrappers. Replace DB.createFunction() impl with the new one.
FossilOrigin-Name: 435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c
This commit is contained in:
parent
9fdce595eb
commit
9892883e9e
@ -47,13 +47,9 @@ browser client:
|
||||
independent spinoff project, conceived for the sqlite3 project but
|
||||
maintained separately.
|
||||
- `sqlite3-api-glue.js`\
|
||||
Invokes the function exposed by `sqlite3-api-prologue.js`, passing
|
||||
it a configuration object to configure it for the current WASM
|
||||
toolchain (noting that it currently requires Emscripten), then
|
||||
removes that function from the global scope. The result of this file
|
||||
is a global-scope `sqlite3` object which acts as a namespace for the
|
||||
API's functionality. This object gets removed from the global scope
|
||||
after the following files have attached their own features to it.
|
||||
Invokes functionality exposed by the previous two files to
|
||||
flesh out low-level parts of `sqlite3-api-prologue.js`. Most of
|
||||
these pieces related to the `sqlite3.capi.wasm` object.
|
||||
- `sqlite3-api-oo1.js`\
|
||||
Provides a high-level object-oriented wrapper to the lower-level C
|
||||
API, colloquially known as OO API #1. Its API is similar to other
|
||||
|
@ -192,6 +192,183 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
};
|
||||
}/*sqlite3_exec() proxy*/;
|
||||
|
||||
if(1){/* Special-case handling of sqlite3_create_function_v2() */
|
||||
const sqlite3CreateFunction = wasm.xWrap(
|
||||
"sqlite3_create_function_v2", "int",
|
||||
["sqlite3*", "string", "int", "int", "*",
|
||||
"*", "*", "*", "*"]
|
||||
);
|
||||
const __setResult = function(pCx, val){
|
||||
switch(typeof val) {
|
||||
case 'boolean':
|
||||
capi.sqlite3_result_int(pCx, val ? 1 : 0);
|
||||
break;
|
||||
case 'number': {
|
||||
(util.isInt32(val)
|
||||
? capi.sqlite3_result_int
|
||||
: capi.sqlite3_result_double)(pCx, val);
|
||||
break;
|
||||
}
|
||||
case 'string':
|
||||
capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 'object':
|
||||
if(null===val) {
|
||||
capi.sqlite3_result_null(pCx);
|
||||
break;
|
||||
}else if(util.isBindableTypedArray(val)){
|
||||
const pBlob = wasm.allocFromTypedArray(val);
|
||||
capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
|
||||
capi.SQLITE_TRANSIENT);
|
||||
wasm.dealloc(pBlob);
|
||||
break;
|
||||
}
|
||||
// else fall through
|
||||
default:
|
||||
toss3("Don't not how to handle this UDF result value:",val);
|
||||
};
|
||||
}/*__setResult()*/;
|
||||
const __extractArgs = function(argc, pArgv){
|
||||
let i, pVal, valType, arg;
|
||||
const tgt = [];
|
||||
for(i = 0; i < argc; ++i){
|
||||
pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
|
||||
/**
|
||||
Curiously: despite ostensibly requiring 8-byte
|
||||
alignment, the pArgv array is parcelled into chunks of
|
||||
4 bytes (1 pointer each). The values those point to
|
||||
have 8-byte alignment but the individual argv entries
|
||||
do not.
|
||||
*/
|
||||
valType = capi.sqlite3_value_type(pVal);
|
||||
switch(valType){
|
||||
case capi.SQLITE_INTEGER:
|
||||
case capi.SQLITE_FLOAT:
|
||||
arg = capi.sqlite3_value_double(pVal);
|
||||
break;
|
||||
case capi.SQLITE_TEXT:
|
||||
arg = capi.sqlite3_value_text(pVal);
|
||||
break;
|
||||
case capi.SQLITE_BLOB:{
|
||||
const n = capi.sqlite3_value_bytes(pVal);
|
||||
const pBlob = capi.sqlite3_value_blob(pVal);
|
||||
arg = new Uint8Array(n);
|
||||
let i;
|
||||
const heap = n ? wasm.heap8() : false;
|
||||
for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
|
||||
break;
|
||||
}
|
||||
case capi.SQLITE_NULL:
|
||||
arg = null; break;
|
||||
default:
|
||||
toss3("Unhandled sqlite3_value_type()",valType,
|
||||
"is possibly indicative of incorrect",
|
||||
"pointer size assumption.");
|
||||
}
|
||||
tgt.push(arg);
|
||||
}
|
||||
return tgt;
|
||||
}/*__extractArgs()*/;
|
||||
|
||||
const __setCxErr = (pCx, e)=>{
|
||||
if(e instanceof capi.WasmAllocError){
|
||||
capi.sqlite3_result_error_nomem(pCx);
|
||||
}else{
|
||||
capi.sqlite3_result_error(pCx, e.message, -1);
|
||||
}
|
||||
};
|
||||
|
||||
const __xFunc = function(callback){
|
||||
return function(pCx, argc, pArgv){
|
||||
try{__setResult(pCx, callback(...__extractArgs(argc, pArgv)))}
|
||||
catch(e){ __setCxErr(pCx, e) }
|
||||
};
|
||||
};
|
||||
|
||||
const __xStep = function(callback){
|
||||
return function(pCx, argc, pArgv){
|
||||
try{ callback(...__extractArgs(argc, pArgv)) }
|
||||
catch(e){ __setCxErr(pCx, e) }
|
||||
};
|
||||
};
|
||||
|
||||
const __xFinal = function(callback){
|
||||
return function(pCx){
|
||||
try{ __setResult(pCx, callback()) }
|
||||
catch(e){ __setCxErr(pCx, e) }
|
||||
};
|
||||
};
|
||||
|
||||
const __xDestroy = function(callback){
|
||||
return function(pVoid){
|
||||
try{ callback(pVoid) }
|
||||
catch(e){
|
||||
console.error("UDF xDestroy method threw:",e);
|
||||
}
|
||||
};
|
||||
};
|
||||
/* 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(9!==arguments.length){
|
||||
return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",9);
|
||||
}
|
||||
if(!f._sigs){
|
||||
f._wrap = Object.assign(Object.create(null), {
|
||||
xFunc: {sig:'v(pip)', f:__xFunc},
|
||||
xStep: {sig:'v(pip)', f:__xStep},
|
||||
xFinal: {sig:'v(p)', f:__xFinal},
|
||||
xDestroy: {sig:'v(p)', f:__xDestroy}
|
||||
});
|
||||
}
|
||||
const callbacks = [];
|
||||
/* Wrap the callbacks in a WASM-bound functions... */
|
||||
const wasm = capi.wasm;
|
||||
const funcArgs = [], uninstall = [/*funcs to uninstall on error*/],
|
||||
theFuncs = {xFunc, xStep, xFinal, xDestroy};
|
||||
let rc;
|
||||
try{
|
||||
let k;
|
||||
for(k in theFuncs){
|
||||
let fArg = theFuncs[k];
|
||||
if('function'===typeof fArg){
|
||||
const w = f._wrap[k];
|
||||
fArg = wasm.installFunction(w.sig, w.f(fArg));
|
||||
uninstall.push(fArg);
|
||||
}
|
||||
funcArgs.push(fArg);
|
||||
}
|
||||
rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
|
||||
pApp, ...funcArgs);
|
||||
}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 rc;
|
||||
};
|
||||
|
||||
capi.sqlite3_create_function = function(
|
||||
pDb, funcName, nArg, eTextRep, pApp,
|
||||
xFunc, xStep, xFinal
|
||||
){
|
||||
if(8!==arguments.length){
|
||||
return __dbArgcMismatch(pDb,"sqlite3_create_function",8);
|
||||
}
|
||||
return capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
|
||||
pApp, xFunc, xStep, xFinal, 0);
|
||||
|
||||
};
|
||||
}/*sqlite3_create_function_v2() proxy*/;
|
||||
|
||||
if(1){/* Special-case handling of sqlite3_prepare_v2() and
|
||||
sqlite3_prepare_v3() */
|
||||
/**
|
||||
|
@ -37,12 +37,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
it.
|
||||
*/
|
||||
const __ptrMap = new WeakMap();
|
||||
/**
|
||||
Map of DB instances to objects, each object being a map of UDF
|
||||
names to wasm function _pointers_ added to that DB handle via
|
||||
createFunction().
|
||||
*/
|
||||
const __udfMap = new WeakMap();
|
||||
/**
|
||||
Map of DB instances to objects, each object being a map of Stmt
|
||||
wasm pointers to Stmt objects.
|
||||
@ -154,7 +148,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
this.filename = fnJs;
|
||||
__ptrMap.set(this, ptr);
|
||||
__stmtMap.set(this, Object.create(null));
|
||||
__udfMap.set(this, Object.create(null));
|
||||
};
|
||||
|
||||
/**
|
||||
@ -453,12 +446,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
|
||||
if(s && s.pointer) s.finalize();
|
||||
});
|
||||
Object.values(__udfMap.get(this)).forEach(
|
||||
wasm.uninstallFunction.bind(capi.wasm)
|
||||
);
|
||||
__ptrMap.delete(this);
|
||||
__stmtMap.delete(this);
|
||||
__udfMap.delete(this);
|
||||
capi.sqlite3_close_v2(pDb);
|
||||
if(this.onclose && (this.onclose.after instanceof Function)){
|
||||
try{this.onclose.after(this)}
|
||||
@ -785,15 +774,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
On success, returns this object. Throws on error.
|
||||
|
||||
When called from SQL, arguments to the UDF, and its result,
|
||||
will be converted between JS and SQL with as much fidelity
|
||||
as is feasible, triggering an exception if a type
|
||||
conversion cannot be determined. Some freedom is afforded
|
||||
to numeric conversions due to friction between the JS and C
|
||||
worlds: integers which are larger than 32 bits will be
|
||||
treated as doubles, as JS does not support 64-bit integers
|
||||
and it is (as of this writing) illegal to use WASM
|
||||
functions which take or return 64-bit integers from JS.
|
||||
When called from SQL arguments to the UDF, and its result,
|
||||
will be converted between JS and SQL with as much fidelity as
|
||||
is feasible, triggering an exception if a type conversion
|
||||
cannot be determined. The docs for sqlite3_create_function_v2()
|
||||
describe the conversions in more detail.
|
||||
|
||||
The optional options object may contain flags to modify how
|
||||
the function is defined:
|
||||
@ -813,11 +798,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
- .deterministic = SQLITE_DETERMINISTIC
|
||||
- .directOnly = SQLITE_DIRECTONLY
|
||||
- .innocuous = SQLITE_INNOCUOUS
|
||||
|
||||
Maintenance reminder: the ability to add new
|
||||
WASM-accessible functions to the runtime requires that the
|
||||
WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
|
||||
flag.
|
||||
*/
|
||||
createFunction: function f(name, callback,opt){
|
||||
switch(arguments.length){
|
||||
@ -840,113 +820,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}else if('string' !== typeof name){
|
||||
toss3("Invalid arguments: missing function name.");
|
||||
}
|
||||
if(!f._extractArgs){
|
||||
/* Static init */
|
||||
f._extractArgs = function(argc, pArgv){
|
||||
let i, pVal, valType, arg;
|
||||
const tgt = [];
|
||||
for(i = 0; i < argc; ++i){
|
||||
pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
|
||||
/**
|
||||
Curiously: despite ostensibly requiring 8-byte
|
||||
alignment, the pArgv array is parcelled into chunks of
|
||||
4 bytes (1 pointer each). The values those point to
|
||||
have 8-byte alignment but the individual argv entries
|
||||
do not.
|
||||
*/
|
||||
valType = capi.sqlite3_value_type(pVal);
|
||||
switch(valType){
|
||||
case capi.SQLITE_INTEGER:
|
||||
case capi.SQLITE_FLOAT:
|
||||
arg = capi.sqlite3_value_double(pVal);
|
||||
break;
|
||||
case capi.SQLITE_TEXT:
|
||||
arg = capi.sqlite3_value_text(pVal);
|
||||
break;
|
||||
case capi.SQLITE_BLOB:{
|
||||
const n = capi.sqlite3_value_bytes(pVal);
|
||||
const pBlob = capi.sqlite3_value_blob(pVal);
|
||||
arg = new Uint8Array(n);
|
||||
let i;
|
||||
const heap = n ? wasm.heap8() : false;
|
||||
for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
|
||||
break;
|
||||
}
|
||||
case capi.SQLITE_NULL:
|
||||
arg = null; break;
|
||||
default:
|
||||
toss3("Unhandled sqlite3_value_type()",valType,
|
||||
"is possibly indicative of incorrect",
|
||||
"pointer size assumption.");
|
||||
}
|
||||
tgt.push(arg);
|
||||
}
|
||||
return tgt;
|
||||
}/*_extractArgs()*/;
|
||||
f._setResult = function(pCx, val){
|
||||
switch(typeof val) {
|
||||
case 'boolean':
|
||||
capi.sqlite3_result_int(pCx, val ? 1 : 0);
|
||||
break;
|
||||
case 'number': {
|
||||
(util.isInt32(val)
|
||||
? capi.sqlite3_result_int
|
||||
: capi.sqlite3_result_double)(pCx, val);
|
||||
break;
|
||||
}
|
||||
case 'string':
|
||||
capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 'object':
|
||||
if(null===val) {
|
||||
capi.sqlite3_result_null(pCx);
|
||||
break;
|
||||
}else if(util.isBindableTypedArray(val)){
|
||||
const pBlob = wasm.allocFromTypedArray(val);
|
||||
capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
|
||||
capi.SQLITE_TRANSIENT);
|
||||
wasm.dealloc(pBlob);
|
||||
break;
|
||||
}
|
||||
// else fall through
|
||||
default:
|
||||
toss3("Don't not how to handle this UDF result value:",val);
|
||||
};
|
||||
}/*_setResult()*/;
|
||||
}/*static init*/
|
||||
const wrapper = function(pCx, argc, pArgv){
|
||||
try{
|
||||
f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv)));
|
||||
}catch(e){
|
||||
if(e instanceof capi.WasmAllocError){
|
||||
capi.sqlite3_result_error_nomem(pCx);
|
||||
}else{
|
||||
capi.sqlite3_result_error(pCx, e.message, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
const pUdf = wasm.installFunction(wrapper, "v(iii)");
|
||||
let fFlags = 0 /*flags for sqlite3_create_function_v2()*/;
|
||||
if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC;
|
||||
if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY;
|
||||
if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS;
|
||||
name = name.toLowerCase();
|
||||
try {
|
||||
DB.checkRc(this, capi.sqlite3_create_function_v2(
|
||||
this.pointer, name,
|
||||
(opt.hasOwnProperty('arity') ? +opt.arity : callback.length),
|
||||
capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
|
||||
capi.SQLITE_UTF8 | fFlags, null/*pApp*/, callback,
|
||||
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
|
||||
}catch(e){
|
||||
wasm.uninstallFunction(pUdf);
|
||||
throw e;
|
||||
}
|
||||
const udfMap = __udfMap.get(this);
|
||||
if(udfMap[name]){
|
||||
try{wasm.uninstallFunction(udfMap[name])}
|
||||
catch(e){/*ignore*/}
|
||||
}
|
||||
udfMap[name] = pUdf;
|
||||
return this;
|
||||
}/*createFunction()*/,
|
||||
/**
|
||||
|
@ -285,29 +285,94 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
*/
|
||||
const capi = {
|
||||
/**
|
||||
When using sqlite3_open_v2() it is important to keep the following
|
||||
in mind:
|
||||
sqlite3_create_function_v2() differs from its native
|
||||
counterpart only in the following ways:
|
||||
|
||||
https://www.sqlite.org/c3ref/open.html
|
||||
1) The fourth argument (`eTextRep`) argument must not specify
|
||||
any encoding other than sqlite.SQLITE_UTF8. The JS API does not
|
||||
currently support any other encoding and likely never
|
||||
will. This function does not replace that argument on its own
|
||||
because it may contain other flags.
|
||||
|
||||
- The flags for use with its 3rd argument are installed in this
|
||||
object using their C-side names, e.g. SQLITE_OPEN_CREATE.
|
||||
2) Any of the four final arguments may be either WASM pointers
|
||||
(assumed to be function pointers) or JS Functions. In the
|
||||
latter case, each gets bound to WASM using
|
||||
sqlite3.capi.wasm.installFunction() and that wrapper is passed
|
||||
on to the native implementation.
|
||||
|
||||
- If the combination of flags passed to it are invalid,
|
||||
behavior is undefined. Thus is is never okay to call this
|
||||
with fewer than 3 arguments, as JS will default the
|
||||
missing arguments to `undefined`, which will result in a
|
||||
flag value of 0. Most of the available SQLITE_OPEN_xxx
|
||||
flags are meaningless in the WASM build, e.g. the mutext-
|
||||
and cache-related flags, but they are retained in this
|
||||
API for consistency's sake.
|
||||
The semantics of JS functions are:
|
||||
|
||||
- The final argument to this function specifies the VFS to use,
|
||||
which is largely (but not entirely!) meaningless in the WASM
|
||||
environment. It may be null, undefined, or 0 to denote the
|
||||
default.
|
||||
xFunc: is passed `(arrayOfValues)`. Its return value becomes
|
||||
the new SQL function's result.
|
||||
|
||||
xStep: is passed `(arrayOfValues)`. Its return value is
|
||||
ignored.
|
||||
|
||||
xFinal: is passed no arguments. Its return value becomes the
|
||||
new aggragate SQL function's result.
|
||||
|
||||
xDestroy: is passed `(void*)`. Its return value is ignored. The
|
||||
pointer passed to it is the one from the 5th argument to
|
||||
sqlite3_create_function_v2().
|
||||
|
||||
Note that JS callback implementations have different call
|
||||
signatures than their native counterparts (namely, they do not
|
||||
get passed an `sqlite3_context*` argument) because practice has
|
||||
shown that this is almost always more convenient and desirable
|
||||
in JS code. Clients which need access to the full range of
|
||||
native arguments will have to create a WASM-bound function
|
||||
themselves (using wasm.installFunction() or equivalent) and
|
||||
pass that function's WASM pointer to this function, rather than
|
||||
passing a JS function. Be warned, however, that working with
|
||||
UDFs at that level from JS is quite tedious.
|
||||
|
||||
For xFunc(), xStep(), and xFinal():
|
||||
|
||||
- When called from SQL, arguments to the UDF, and its result,
|
||||
will be converted between JS and SQL with as much fidelity as
|
||||
is feasible, triggering an exception if a type conversion
|
||||
cannot be determined. Some freedom is afforded to numeric
|
||||
conversions due to friction between the JS and C worlds:
|
||||
integers which are larger than 32 bits will be treated as
|
||||
doubles. TODO: use BigInt support if enabled. That feature
|
||||
was added after this functionality was implemented.
|
||||
|
||||
If any JS-side functions throw, those exceptions are
|
||||
intercepted and converted to database-side errors with
|
||||
the exception of xFinal(): any exception from it is
|
||||
ignored, possibly generating a console.error() message.
|
||||
Destructors must not throw.
|
||||
|
||||
Once installed, there is currently no way to uninstall the
|
||||
bound methods from WASM. They can be uninstalled from the
|
||||
database as documented in the C API, but this wrapper currently
|
||||
has no infrastructure in place to also free the WASM-bound JS
|
||||
wrappers, effectively resulting in a memory leak if the client
|
||||
uninstalls the UDF. Improving that is a potential TODO, but
|
||||
removing client-installed UDFs is rare in practice.
|
||||
|
||||
Maintenance reminder: the ability to add new
|
||||
WASM-accessible functions to the runtime requires that the
|
||||
WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
|
||||
flag.
|
||||
*/
|
||||
sqlite3_open_v2: function(filename,dbPtrPtr,flags,vfsStr){}/*installed later*/,
|
||||
sqlite3_create_function_v2: function(
|
||||
pDb, funcName, nArg, eTextRep, pApp,
|
||||
xFunc, //function(arrayOfValues)
|
||||
xStep, //function(arrayOfValues)
|
||||
xFinal, //function()
|
||||
xDestroy //function(void*)
|
||||
){/*installed later*/},
|
||||
/**
|
||||
Equivalent to passing the same arguments to
|
||||
sqlite3_create_function_v2(), with 0 as the final argument.
|
||||
*/
|
||||
sqlite3_create_function:function(
|
||||
pDb, funcName, nArg, eTextRep, pApp,
|
||||
xFunc, //function(arrayOfValues)
|
||||
xStep, //function(arrayOfValues)
|
||||
xFinal //function()
|
||||
){/*installed later*/},
|
||||
/**
|
||||
The sqlite3_prepare_v3() binding handles two different uses
|
||||
with differing JS/WASM semantics:
|
||||
@ -642,8 +707,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
|
||||
["sqlite3_compileoption_get", "string", "int"],
|
||||
["sqlite3_compileoption_used", "int", "string"],
|
||||
["sqlite3_create_function_v2", "int",
|
||||
"sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"],
|
||||
/* sqlite3_create_function_v2() is handled separate to simplify conversion
|
||||
of its callback argument */
|
||||
["sqlite3_data_count", "int", "sqlite3_stmt*"],
|
||||
["sqlite3_db_filename", "string", "sqlite3*", "string"],
|
||||
["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
|
||||
|
@ -15,11 +15,17 @@
|
||||
|
||||
https://fossil.wanderinghorse.net/r/jaccwabyt
|
||||
|
||||
and sqlite3:
|
||||
|
||||
https://sqlite.org
|
||||
|
||||
This file is kept in sync between both of those trees.
|
||||
|
||||
Maintenance reminder: If you're reading this in a tree other than
|
||||
the Jaccwabyt tree, note that this copy may be replaced with
|
||||
one of those listed above, note that this copy may be replaced with
|
||||
upstream copies of that one from time to time. Thus the code
|
||||
installed by this function "should not" be edited outside of that
|
||||
project, else it risks getting overwritten.
|
||||
installed by this function "should not" be edited outside of those
|
||||
projects, else it risks getting overwritten.
|
||||
*/
|
||||
/**
|
||||
This function is intended to simplify porting around various bits
|
||||
@ -109,8 +115,8 @@
|
||||
following symbols:
|
||||
|
||||
- `memory`: a WebAssembly.Memory object representing the WASM
|
||||
memory. _Alternately_, the `memory` property can be set on the
|
||||
target instance, in particular if the WASM heap memory is
|
||||
memory. _Alternately_, the `memory` property can be set as
|
||||
`target.memory`, in particular if the WASM heap memory is
|
||||
initialized in JS an _imported_ into WASM, as opposed to being
|
||||
initialized in WASM and exported to JS.
|
||||
|
||||
@ -132,7 +138,11 @@
|
||||
false. If it is false, certain BigInt-related features will trigger
|
||||
an exception if invoked. This property, if not set when this is
|
||||
called, will get a default value of true only if the BigInt64Array
|
||||
constructor is available, else it will default to false.
|
||||
constructor is available, else it will default to false. Note that
|
||||
having the BigInt type is not sufficient for full int64 integration
|
||||
with WASM: the target WASM file must also have been built with
|
||||
that support. In Emscripten that's done using the `-sWASM_BIGINT`
|
||||
flag.
|
||||
|
||||
Some optional APIs require that the target have the following
|
||||
methods:
|
||||
@ -295,7 +305,7 @@ self.WhWasmUtilInstaller = function(target){
|
||||
an integer as the first argument and unsigned is truthy then
|
||||
the "U" (unsigned) variant of that view is returned, else the
|
||||
signed variant is returned. If passed a TypedArray value, the
|
||||
2nd argument is ignores. Note that Float32Array and
|
||||
2nd argument is ignored. Note that Float32Array and
|
||||
Float64Array views are not supported by this function.
|
||||
|
||||
Note that growth of the heap will invalidate any references to
|
||||
@ -511,7 +521,7 @@ self.WhWasmUtilInstaller = function(target){
|
||||
if(2!==arguments.length){
|
||||
toss("installFunction() requires exactly 2 arguments");
|
||||
}
|
||||
if('string'===typeof func && sig instanceof Function){
|
||||
if('string'===typeof func){
|
||||
const x = sig;
|
||||
sig = func;
|
||||
func = x;
|
||||
@ -1137,7 +1147,7 @@ self.WhWasmUtilInstaller = function(target){
|
||||
e.g. using setMemValue() or getMemValue(), it is important that
|
||||
the pointer in question be aligned to an 8-byte boundary or else
|
||||
it will not be fetched or written properly and will corrupt or
|
||||
read neighboring memory. It i only safe to pass false when the
|
||||
read neighboring memory. It is only safe to pass false when the
|
||||
client code is certain that it will only get/fetch 4-byte values
|
||||
(or smaller).
|
||||
*/
|
||||
@ -1239,12 +1249,12 @@ self.WhWasmUtilInstaller = function(target){
|
||||
return v ? xcv.arg[ptrIR](v) : null;
|
||||
};
|
||||
xcv.result.string = (i)=>target.cstringToJs(i);
|
||||
xcv.result['string:free'] = function(i){
|
||||
xcv.result['string:free'] = (i)=>{
|
||||
try { return i ? target.cstringToJs(i) : null }
|
||||
finally{ target.dealloc(i) }
|
||||
};
|
||||
xcv.result.json = (i)=>JSON.parse(target.cstringToJs(i));
|
||||
xcv.result['json:free'] = function(i){
|
||||
xcv.result['json:free'] = (i)=>{
|
||||
try{ return i ? JSON.parse(target.cstringToJs(i)) : null }
|
||||
finally{ target.dealloc(i) }
|
||||
}
|
||||
@ -1374,14 +1384,26 @@ self.WhWasmUtilInstaller = function(target){
|
||||
C-string, ownership of which has just been transfered to the
|
||||
caller. It copies the C-string to a JS string, frees the
|
||||
C-string, and returns the JS string. If such a result value is
|
||||
NULL, the JS result is `null`.
|
||||
NULL, the JS result is `null`. Achtung: when using an API which
|
||||
returns results from a specific allocator, e.g. `my_malloc()`,
|
||||
this conversion _is not legal_. Instead, an equivalent conversion
|
||||
which uses the appropriate deallocator is required. For example:
|
||||
|
||||
```js
|
||||
target.xWrap.resultAdaptor('string:my_free',(i)=>{
|
||||
try { return i ? target.cstringToJs(i) : null }
|
||||
finally{ target.exports.my_free(i) }
|
||||
};
|
||||
```
|
||||
|
||||
- `json` (results): treats the result as a const C-string and
|
||||
returns the result of passing the converted-to-JS string to
|
||||
JSON.parse(). Returns `null` if the C-string is a NULL pointer.
|
||||
|
||||
- `json:free` (results): works exactly like `string:free` but
|
||||
returns the same thing as the `json` adapter.
|
||||
returns the same thing as the `json` adapter. Note the
|
||||
warning in `string:free` regarding maching allocators and
|
||||
deallocators.
|
||||
|
||||
The type names for results and arguments are validated when
|
||||
xWrap() is called and any unknown names will trigger an
|
||||
@ -1532,11 +1554,10 @@ self.WhWasmUtilInstaller = function(target){
|
||||
type name, as documented for xWrap() (use a falsy value or an
|
||||
empty array for nullary functions). The 4th+ arguments are
|
||||
arguments for the call, with the special case that if the 4th
|
||||
argument is an array, it is used as the arguments for the call
|
||||
(again, falsy or an empty array for nullary functions). Returns
|
||||
the converted result of the call.
|
||||
argument is an array, it is used as the arguments for the
|
||||
call. Returns the converted result of the call.
|
||||
|
||||
This is just a thin wrapp around xWrap(). If the given function
|
||||
This is just a thin wrapper around xWrap(). If the given function
|
||||
is to be called more than once, it's more efficient to use
|
||||
xWrap() to create a wrapper, then to call that wrapper as many
|
||||
times as needed. For one-shot calls, however, this variant is
|
||||
|
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Doc\stypo\sfixes.
|
||||
D 2022-10-02T03:14:38.605
|
||||
C js:\simplement\sa\shand-written\swrapper\sfor\ssqlite3_create_function_v2()\swhich\sconverts,\sif\snecessary,\sJS-function-type\sargs\sto\sWASM\sfunction\swrappers.\sReplace\sDB.createFunction()\simpl\swith\sthe\snew\sone.
|
||||
D 2022-10-02T18:47:39.889
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -478,17 +478,17 @@ F ext/wasm/GNUmakefile b313a82060c733c990b91afa981e10f5e21a0b33a483f33b739ce932e
|
||||
F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c6b05641d733227a995cc05a2ba7ddc9ef2323081ae0cae5ed1d1f83d5b54b36
|
||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||
F ext/wasm/api/README.md b6d19392984fdd428ae7260512d45742160c06baefed9da703da3fdeb68e7124
|
||||
F ext/wasm/api/README.md 1e350b611465566cfa2e5eccf7c9b29a34f48ee38bbf6d5fb086dd06ce32b3ff
|
||||
F ext/wasm/api/extern-post-js.js dc68cbf552d8ea085181400a6963907c32e0b088b03ffd8969b1869fea246629
|
||||
F ext/wasm/api/extern-pre-js.js 20143b16b672d0a576fbf768a786d12ee1f84e222126477072389b992542a5b2
|
||||
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
|
||||
F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
|
||||
F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99
|
||||
F ext/wasm/api/sqlite3-api-glue.js 20b98987dd0c6d02ca308e0cf9299750d618459e0876b354380b87ae2dc5cba0
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 066e67f3033e1b300140d431557c468f5cd0a4c17253f156e05b8a2e2c802da7
|
||||
F ext/wasm/api/sqlite3-api-glue.js d834733fbdc216beb65382655e61a436e29018412005aab413ff022d4e5c25eb
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 48d2269544301cd97726ef2d9a82e4384350d12dcf832fa417f211811ae5272d
|
||||
F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 9b0c5150f0129b3dc558fec0bfc9c8bf7ebda402b58965bcf98b2ed117b55239
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 32795679b72a5ad685c58a9599d10dac04787907f623825408d539b703e080a4
|
||||
F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6
|
||||
@ -497,7 +497,7 @@ F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62
|
||||
F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05
|
||||
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||
F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962
|
||||
F ext/wasm/common/whwasmutil.js af9bb6586d8a2ab7c55c7f1554bb6b0328a7bd6a86ea5fa9ef5273a738156ac3
|
||||
F ext/wasm/common/whwasmutil.js 427eb8b695bd5f38497601a6bda933e83d1a5900b75f5f1af8dbb381898d2ee4
|
||||
F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f
|
||||
F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4
|
||||
F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8
|
||||
@ -2029,8 +2029,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 eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3
|
||||
R c02b54a31af3ce39f04c3d79de05d508
|
||||
P e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8
|
||||
R 905afd6e962345097748ac52db8a0889
|
||||
U stephan
|
||||
Z a6c5eed947d3361089be1f642079a7a3
|
||||
Z 8de102f5519af227a27849d21742ab64
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8
|
||||
435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c
|
Loading…
Reference in New Issue
Block a user