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:
stephan 2022-10-02 18:47:39 +00:00
parent 9fdce595eb
commit 9892883e9e
7 changed files with 324 additions and 182 deletions

View File

@ -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

View File

@ -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() */
/**

View File

@ -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()*/,
/**

View File

@ -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*"],

View File

@ -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

View File

@ -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.

View File

@ -1 +1 @@
e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8
435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c