Compare commits

...

9 Commits

Author SHA1 Message Date
stephan 7e4f58bf9c More work towards fts5 customzation in JS.
FossilOrigin-Name: ce2a65d80f7793ae8183e311fc7284b37caa78d747436f22496284dfa57e3533
2023-08-04 08:39:21 +00:00
stephan a31a31715d Minor internal cleanups in the JS-side fts5 cleanup steps.
FossilOrigin-Name: 2666f52e88691201a30dc0f27c294c498bba3f16d5d2dddcadcfac115a3cba20
2023-08-03 22:43:39 +00:00
stephan 75c60b4656 Retain all xDestroy method WASM proxies created for fts5_api::xCreateFunction() until the db is closed, then free them all at once.
FossilOrigin-Name: f4b9743abd4fe24f1604cbcc7f9f95cadd1cefdc053eeabb35dc6773c99d1277
2023-08-03 21:14:58 +00:00
stephan 2c88b4aa24 Rework the fts5 db-close cleanup to avoid that fts5 internally calls a just-unbound-from-wasm xDestroy() method. There is still work to do here to cover the case of fts5 functions being replaced (in which all xDestroy methods have to be retained until the db is closed).
FossilOrigin-Name: 795f22421b96cea27b3883a580d8ce29cc50dfb7d4f852b3d36ede3766c7a24c
2023-08-03 20:45:28 +00:00
stephan 97f052710d Part 2 of the fix from [a0f808363318c00fd1db78b].
FossilOrigin-Name: ce0674b1925138f8f878b11aae0f8420bd968df0959f6dd7e208fb84bcbad07e
2023-08-03 20:02:49 +00:00
stephan e5af3c1cc5 Resolve the timing/ordering issue of a JS-to-WASM-converted xDestroy() function being uninstalled from WASM right before the underlying native call tries to call it. This has been a long-unnoticed bug which appears only when removing such functions or replacing then.
FossilOrigin-Name: a0f808363318c00fd1db78b4271cef8d05a046a36aab1a383e731e40603c6e2a
2023-08-03 18:46:44 +00:00
stephan 79b4153e09 More infrastructure towards binding FTS5 customization support to WASM.
FossilOrigin-Name: 163e3e44b16caa7ceb2805d29a76d8975e66fe429a911995b1c2f8429b080645
2023-08-03 16:21:24 +00:00
stephan 96f6cd5cab Fix a discrepancy between sqlite3_values_to_js() and its API docs/intended behavior.
FossilOrigin-Name: 16658b3c64d7f9690b097d999a8fbdc49dedf484f72174059718a768f61e875b
2023-08-03 16:02:30 +00:00
stephan dd12783730 Initial work on exposing the FTS5 APIs to wasm, per [forum:28402061cb|request in the forum]. This builds and loads the structs into JS but is completely untested.
FossilOrigin-Name: 52c8b73ae3095b7e9f4d5440169c818677e484d85195036a0d82db0d04a428e5
2023-08-03 07:20:10 +00:00
12 changed files with 680 additions and 41 deletions
+9
View File
@@ -349,6 +349,12 @@ EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main)
ifeq (1,$(SQLITE_C_IS_SEE))
EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see)
endif
ifeq (1,$(emcc.WASM_BIGINT))
ifneq (,$(filter -DSQLITE_ENABLE_FTS5,$(SQLITE_OPT)))
EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-fts5)
endif
endif
EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE)
cat $(EXPORTED_FUNCTIONS.api.in) > $@
@@ -375,6 +381,9 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
ifneq (,$(filter -DSQLITE_ENABLE_FTS5,$(SQLITE_OPT)))
sqlite3-api.jses += $(dir.api)/sqlite3-fts5-helper.js
endif
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
# SOAP.js is an external API file which is part of our distribution
@@ -0,0 +1 @@
_fts5_api_from_db
+2 -1
View File
@@ -52,9 +52,10 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
delete globalThis.sqlite3ApiBootstrap;
delete globalThis.sqlite3ApiConfig;
}
delete sqlite3.__dbCleanupMap;
Module.sqlite3 = sqlite3 /* Needed for customized sqlite3InitModule() to be able to
pass the sqlite3 object off to the client. */;
sqlite3.wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
}else{
console.warn("This is not running in an Emscripten module context, so",
"globalThis.sqlite3ApiBootstrap() is _not_ being called due to lack",
+60 -9
View File
@@ -261,6 +261,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}),
"*"/*pUserData*/
]],
/**
Achtung: when specifying an xDestroy() method via
sqlite3_set_auxdata(), it is up to the client to re-set it to
0/NULL at the end of its lifetime (e.g. in the associated UDF's
xFinal() impl), The C library will be able to call the
destructor but _not_ uninstall the temporary WASM-bound proxy
function because it does not have enough information to do
so. Alternately, clients may create the function pointer
themselves using wasm.createFunction() and pass that pointer
here, in which case they avoid creating a stranded "temporary"
function binding.
*/
["sqlite3_set_auxdata", undefined, [
"sqlite3_context*", "int", "*",
new wasm.xWrap.FuncPtrAdapter({
@@ -421,8 +433,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
// Add session/changeset APIs...
if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){
/* ACHTUNG: 2022-12-23: the session/changeset API bindings are
COMPLETELY UNTESTED. */
/**
FuncPtrAdapter options for session-related callbacks with the
native signature "i(ps)". This proxy converts the 2nd argument
@@ -597,6 +607,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
]);
}/*session/changeset APIs*/
if(wasm.bigIntEnabled && !!wasm.exports.fts5_api_from_db){
wasm.bindingSignatures.int64.push(
['fts5_api_from_db', 'fts5_api*', 'sqlite3*']
);
}/* fts5 APIs */
/**
Functions which are intended solely for API-internal use by the
WASM components, not client code. These get installed into
@@ -659,8 +675,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
function(v){
if(wasm.isPtr(v)) return v;
v = ''+v;
let rc = this[v];
return rc || (this[v] = wasm.allocCString(v));
const rc = this[v];
return (undefined===rc) ? (this[v] = wasm.allocCString(v)) : rc;
}.bind(Object.create(null))
);
@@ -717,7 +733,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
});
const __xRcPtr = wasm.xWrap.resultAdapter('*');
wasm.xWrap.resultAdapter('sqlite3*', __xRcPtr)
wasm.xWrap.resultAdapter
('fts5_api*', __xRcPtr)
('sqlite3*', __xRcPtr)
('sqlite3_context*', __xRcPtr)
('sqlite3_stmt*', __xRcPtr)
('sqlite3_value*', __xRcPtr)
@@ -823,6 +841,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'version' ];
if(wasm.bigIntEnabled){
defineGroups.push('serialize', 'session', 'vtab');
if(!!wasm.ctype.fts5){
defineGroups.push('fts5');
}
}
for(const t of defineGroups){
for(const e of Object.entries(wasm.ctype[t])){
@@ -906,9 +927,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
UDFs and collations so that sqlite3_close_v2() can clean up any
automated JS-to-WASM function conversions installed by those.
*/
const __argPDb = (pDb)=>wasm.xWrap.argAdapter('sqlite3*')(pDb);
const __argPDb = wasm.xWrap.argAdapter('sqlite3*');
const __argStr = (str)=>wasm.isPtr(str) ? wasm.cstrToJs(str) : str;
const __dbCleanupMap = function(
const __dbCleanupMap = sqlite3.__dbCleanupMap = function(
pDb, mode/*0=remove, >0=create if needed, <0=do not create if missing*/
){
pDb = __argPDb(pDb);
@@ -973,6 +994,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/
__dbCleanupMap.cleanup = function(pDb){
pDb = __argPDb(pDb);
//console.warn("db cleanup",pDb);
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
/**
Installing NULL functions in the C API will remove those
@@ -1001,6 +1023,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
console.warn("close-time call of",name+"(",closeArgs,") threw:",e);
}
}
for(const callback of __dbCleanupMap.extraCallbacks){
callback(pDb);
}
const m = __dbCleanupMap(pDb, 0);
if(!m) return;
if(m.collation){
@@ -1037,15 +1062,39 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
delete m.udf;
delete m.wudf;
}/*__dbCleanupMap.cleanup()*/;
/**
Downstream code, namely sqlite3-fts5-helper.js, should add any custom
cleanup handlers to __dbCleanupMap.extraCallbacks. Each function in this
array will be called during sqlite3_close_v2() and passed a pointer to
the being-destroyed (sqlite3*) object.
*/
__dbCleanupMap.extraCallbacks = [];
/**
Downstream code, namely sqlite3-fts5-helper.js, should add any
custom cleanup handlers to __dbCleanupMap.postCloseCallbacks.
Each function in this array will be called during
sqlite3_close_v2(), AFTER the db is closed, and passed a pointer
to the being-destroyed (sqlite3*) object. The memory is NOT A
VALID OBJECT but its address is still valid as a lookup key.
*/
__dbCleanupMap.postCloseCallbacks = [];
{/* Binding of sqlite3_close_v2() */
const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*");
const __xArgDb = wasm.xWrap.argAdapter('sqlite3*');
capi.sqlite3_close_v2 = function(pDb){
if(1!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_close_v2', 1);
pDb = __xArgDb(pDb);
if(pDb){
try{__dbCleanupMap.cleanup(pDb)} catch(e){/*ignored*/}
}
return __sqlite3CloseV2(pDb);
const rc = __sqlite3CloseV2(pDb);
if(pDb/*noting that it's not valid anymore*/){
for(const f of __dbCleanupMap.postCloseCallbacks){
try{f(pDb)}catch(e){/*ingored*/}
}
}
return rc;
};
}/*sqlite3_close_v2()*/
@@ -1154,6 +1203,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
/**
JS proxies for the various sqlite3_create[_window]_function()
callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter.
TODO: explore an API option which more closely resembles the
/ext/jni mapping, which is much friendlier at the client level.
*/
const __cfProxy = Object.assign(Object.create(null), {
xInverseAndStep: {
@@ -1665,5 +1717,4 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}
}/*pKvvfs*/
wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
});
+2 -1
View File
@@ -1645,7 +1645,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
do not.
*/
tgt.push(capi.sqlite3_value_to_js(
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)),
throwIfCannotConvert
));
}
return tgt;
+347
View File
@@ -0,0 +1,347 @@
/*
** 2023-08-03
**
** The author disclaims copyright to this source code. In place of a
** legal notice, here is a blessing:
**
** * May you do good and not evil.
** * May you find forgiveness for yourself and forgive others.
** * May you share freely, never taking more than you give.
*/
/**
This file installs sqlite3.fts5, a namespace which exists to assist
in JavaScript-side extension of FTS5.
*/
'use strict';
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
if(!capi.fts5_api_from_db){
return /*this build does not have FTS5*/;
}
const fts = sqlite3.fts5 = Object.create(null);
const __xArgDb = wasm.xWrap.argAdapter('sqlite3*');
/**
Move FTS-specific APIs (installed via automation) from
sqlite3.capi to sqlite3.fts.
*/
for(const c of [
'Fts5ExtensionApi', 'Fts5PhraseIter', 'fts5_api',
'fts5_api_from_db', 'fts5_tokenizer'
]){
fts[c] = capi[c] || toss("Cannot find capi."+c);
delete capi[c];
}
/**
Requires a JS Function intended to be used as an xFunction()
implementation. This function returns a proxy xFunction
wrapper which:
- Converts all of its sqlite3_value arguments to an array
of JS values using sqlite3_values_to_js().
- Calls the given callback, passing it:
(pFtsXApi, pFtsCx, pCtx, array-of-values)
where the first 3 arguments are the first 3 pointers
in the xFunction interface.
The call is intended to set a result value into the db, and may
do so be either (A) explicitly returning non-undefined or (B)
using one of the sqlite3_result_XYZ() functions and returning
undefined. If the callback throws, its exception will be passed
to sqlite3_result_error_js().
*/
fts.xFunctionProxy1 = function(callback){
return (pFtsXApi, pFtsCx, pCtx, argc, pArgv)=>{
try{
capi.sqlite3_result_js(pCtx, callback(
pFtsXApi, pFtsCx, pCtx,
capi.sqlite3_values_to_js(argc, pArgv)
));
}catch(e){
capi.sqlite3_result_error_js(pCtx, e);
}
}
};
/**
Identical to xFunctionProxy1 except that the callback wrapper it
creates does _not_ perform sqlite3_value-to-JS conversion in
advance and calls the callback with:
(pFtsXApi, pFtsCx, pCtx, array-of-ptr-to-sqlite3_value)
It is up to the callback to use the sqlite3_value_XYZ() family of
functions to inspect or convert the values.
*/
fts.xFunctionProxy2 = function(callback){
return (pFtsXApi, pFtsCx, pCtx, argc, pArgv)=>{
try{
const list = [];
let i;
for(i = 0; i < argc; ++i){
list.push( wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)) );
}
capi.sqlite3_result_js(pCtx, callback(
pFtsXApi, pFtsCx, pCtx, list
));
}catch(e){
capi.sqlite3_result_error_js(pCtx, e);
}
}
};
/**
JS-to-WASM arg adapter for xCreateFunction()'s xFunction arg.
This binds JS impls of xFunction to WASM so that they can be
called from native code. Its context is limited to the
combination of ((fts5_api*) + functionNameCaseInsensitive), and
will replace any existing impl for subsequent invocations for the
same combination.
The functions is creates are intended to set a result value into
the db, and may do so be either (A) explicitly returning
non-undefined or (B) using one of the sqlite3_result_XYZ()
functions and returning undefined. If the callback throws, its
exception will be passed to sqlite3_result_error_js().
PENDING DESIGN DECISION: this framework currently converts each
argument in its JS equivalent before passing them on to the
xFunction impl. We could, and possibly should, instead pass a JS
array of sqlite3_value pointers. The advantages would be:
- No in-advance to-JS overhead which xFunction might not use.
Disadvantages include:
- xFunction would be required to call sqlite3_value_to_js(),
or one of the many sqlite3_value_XYZ() functions on their own.
This would be more cumbersome for most users.
Regardless of which approach is chosen here, clients could
provide a function of their own which takes the _other_ approach,
install it with wasm.installFunction(), and then pass that
generated pointer to createFunction(), in which case this layer
does not proxying and passes all native-level arguments as-is to
the client-defined function.
*/
const xFunctionArgAdapter = new wasm.xWrap.FuncPtrAdapter({
name: 'fts5_api::xCreateFunction(xFunction)',
signature: 'v(pppip)',
contextKey: (argv,argIndex)=>{
return (argv[0]/*(fts5_api*)*/
+ wasm.cstrToJs(argv[1]).toLowerCase()/*name*/)
},
callProxy: fts.xFunctionProxy1
});
/** Map of (sqlite3*) to fts.fts5_api. */
const __ftsApiToStruct = Object.create(null);
const __fts5_api_from_db = function(pDb, createIfNeeded){
let rc = __ftsApiToStruct[pDb];
if(!rc && createIfNeeded){
const fapi = fts.fts5_api_from_db(pDb)
|| toss("Internal error - cannot get FTS5 API object for db.");
rc = new fts.fts5_api(fapi);
__ftsApiToStruct[pDb] = rc;
}
return rc;
};
/**
Arrange for WASM functions dynamically created via this API to be
uninstalled when the db they were installed for is closed... */
const __addCleanupForFunc = function(sfapi, name, pDestroy){
if(!sfapi.$$cleanup){
sfapi.$$cleanup = [];
}
sfapi.$$cleanup.push([name.toLowerCase(), pDestroy]);
};
/**
Callback to be invoked via the JS binding of sqlite3_close_v2(),
after the db has been closed, meaning that the argument to this
function is not a valid object. We use its address only as a
lookup key.
*/
sqlite3.__dbCleanupMap.postCloseCallbacks.push(function(pDb){
const sfapi = __fts5_api_from_db(pDb, false);
if(sfapi){
delete __ftsApiToStruct[pDb];
if(sfapi.$$cleanup){
const fapi = sfapi.pointer;
const scope = wasm.scopedAllocPush();
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true;
try{
for(const [name, pDestroy] of sfapi.$$cleanup){
try{
/* Uninstall xFunctionArgAdapter's bindings via a
roundabout approach: its scoping rules uninstall each
new installation at the earliest opportunity, so we
simply need to fake a call with a 0-pointer for the
xFunction callback to uninstall the most recent
one. */
const zName = wasm.scopedAllocCString(name);
const argv = [fapi, zName, 0, 0, 0];
xFunctionArgAdapter.convertArg(argv[3], argv, 3);
/* xDestroy, on the other hand, requires some
hand-holding to ensure we don't prematurely
uninstall these when a function is replaced
(shadowed). */
if(pDestroy) wasm.uninstallFunction(pDestroy);
}catch(e){
sqlite3.config.warn("Could not remove FTS func",name,e);
}
}
}finally{
wasm.scopedAllocPop(scope);
}
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
}
sfapi.dispose();
}
});
const __affirmDbArg = (arg)=>{
arg = __xArgDb(arg);
if(!arg || !wasm.isPtr(arg)) toss("Invalid db argument.");
return arg;
};
/**
Convenience wrapper to fts5_api::xCreateFunction.
Creates a new FTS5 function for the given database. The arguments are:
- db must be either an sqlite3.oo1.DB instance or a WASM pointer
to (sqlite3*).
- name: name (JS string) of the function
- xFunction either a Function or a pointer to a WASM function. In
the former case a WASM-bound wrapper, behaving as documented
for fts5.xFunctionProxy1(), gets installed for the life of the
given db handle. In the latter case the function is
passed-through as-is, with no argument conversion or lifetime
tracking. In the former case the function is called as
documented for xFunctionProxy1() and in the latter it must
return void and is called with args (ptrToFts5ExtensionApi,
ptrToFts5Context, ptrToSqlite3Context, int argc,
C-array-of-sqlite3_value-pointers).
- xDestroy optional Function or pointer to WASM function to call
when the binding is destroyed (when the db handle is
closed). The function will, in this context, always be passed 0
as its only argument. A passed-in function must, however,
have one parameter so that type signature checks will pass.
It must return void and must not throw.
The 2nd and subsequent aruguments may optionally be packed into
a single Object with like-named properties.
This function throws on error, of which there are many potential
candidates. It returns `undefined`.
*/
fts.createFunction = function(db, name, xFunction, xDestroy = 0){
db = __affirmDbArg(db);
if( 2 === arguments.length && 'string' !== typeof name){
xDestroy = name.xDestroy || null;
xFunction = name.xFunction || null;
name = name.name;
}
if( !name || 'string' !== typeof name ) toss("Invalid name argument.");
const sfapi = __fts5_api_from_db(db, true);
let pDestroy = 0;
try{
/** Because of how fts5_api::xCreateFunction() replaces
functions (by prepending new ones to a linked list but
retaining old ones), we cannot use a FuncPtrAdapter to
automatically convert xDestroy, lest we end up uninstalling
a bound-to-wasm JS function's wasm pointer before fts5
cleans it up when the db is closed. */
if(xDestroy instanceof Function){
pDestroy = wasm.installFunction(xDestroy, 'v(p)');
}
const xcf = sfapi.$$xCreateFunction || (
sfapi.$$xCreateFunction = wasm.xWrap(sfapi.$xCreateFunction, 'int', [
'*', 'string', '*', xFunctionArgAdapter, '*'
])
);
const rc = xcf(sfapi.pointer, name, 0, xFunction || 0, pDestroy || xDestroy || 0 );
if(rc) toss(rc,"FTS5::xCreateFunction() failed.");
__addCleanupForFunc(sfapi, name, pDestroy);
}catch(e){
if(pDestroy) wasm.uninstallFunction(pDestroy);
sfapi.dispose();
throw e;
}
};
/**
! UNTESTED
Convenience wrapper for fts5_api::xCreateTokenizer().
- db = the db to install the tokenizer into.
- name = the JS string name of the tokenizer.
- pTokenizer = the tokenizer instance, which must be a
fts5.fts5_tokenizer instance or a valid WASM pointer to one.
- xDestroy = as documented for createFunction().
The C layer makes a bitwise copy of the tokenizer, so any
changes made to it after installation will have no effect.
Throws on error.
*/
const createTokenizer = function(db, name, pTokenizer, xDestroy = 0){
db = __affirmDbArg(db);
if( 2 === arguments.length && 'string' !== typeof name){
pTokenizer = name.pTokenizer;
xDestroy = name.xDestroy || null;
name = name.name;
}
if( !name || 'string' !== typeof name ) toss("Invalid name argument.");
if(pTokenizer instanceof fts.fts5_tokenizer){
pTokenizer = pTokenizer.pointer;
}
if(!pTokenizer || !wasm.isPtr(pTokenizer)){
toss("Invalid pTokenizer argument - must be a valid fts5.fts5_tokenizer",
"instance or a WASM pointer to one.");
}
const sfapi = __fts5_api_from_db(db, true);
let pDestroy = 0;
const stackPos = wasm.pstack.pointer;
try{
if(xDestroy instanceof Function){
pDestroy = wasm.installFunction(xDestroy, 'v(p)');
}
const xct = sfapi.$$xCreateTokenizer || (
sfapi.$$xCreateTokenizer = wasm.xWrap(sfapi.$xCreateTokenizer, 'int', [
'*', 'string', '*', '*', '*'
/* fts5_api*, const char *zName, void *pContext,
fts5_tokenizer *pTokenizer, void(*xDestroy)(void*) */
])
);
const outPtr = wasm.pstack.allocPtr();
const rc = xct(fapi.pointer, name, 0, pTokenizer, pDestroy || xDestroy || 0 );
if(rc) toss(rc,"FTS5::xCreateFunction() failed.");
if(pDestroy) __addCleanupForFunc(sfapi, name, pDestroy);
}catch(e){
if(pDestroy) wasm.uninstallFunction(pDestroy);
sfapi.dispose();
throw e;
}finally{
wasm.pstack.restore(stackPost);
}
};
//fts.createTokenizer = createTokenizer;
}/*sqlite3ApiBootstrap.initializers.push()*/);
+1 -1
View File
@@ -10,7 +10,7 @@
*/
/**
This file installs sqlite3.vfs, and object which exists to assist
This file installs sqlite3.vfs, an object which exists to assist
in the creation of JavaScript implementations of sqlite3_vfs, along
with its virtual table counterpart, sqlite3.vtab.
*/
+145 -5
View File
@@ -96,8 +96,8 @@
#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
#endif
#ifndef SQLITE_ENABLE_FTS4
# define SQLITE_ENABLE_FTS4 1
#ifndef SQLITE_ENABLE_FTS5
# define SQLITE_ENABLE_FTS5 1
#endif
#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
# define SQLITE_ENABLE_MATH_FUNCTIONS 1
@@ -363,6 +363,27 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
return err_code;
}
#ifdef SQLITE_ENABLE_FTS5
/*
** Return a pointer to the fts5_api pointer for database connection db.
** If an error occurs, return NULL and leave an error in the database
** handle (accessible using sqlite3_errcode()/errmsg()).
**
** This function was taken verbatim from the /fts5.html docs.
*/
SQLITE_WASM_EXPORT
fts5_api *fts5_api_from_db(sqlite3 *db){
fts5_api *pRet = 0;
sqlite3_stmt *pStmt = 0;
if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){
sqlite3_bind_pointer(pStmt, 1, (void*)&pRet, "fts5_api_ptr", NULL);
sqlite3_step(pStmt);
}
sqlite3_finalize(pStmt);
return pRet;
}
#endif /*SQLITE_ENABLE_FTS5*/
#if SQLITE_WASM_TESTS
struct WasmTestStruct {
int v4;
@@ -402,7 +423,10 @@ void sqlite3_wasm_test_struct(WasmTestStruct * s){
*/
SQLITE_WASM_EXPORT
const char * sqlite3_wasm_enum_json(void){
static char aBuffer[1024 * 20] = {0} /* where the JSON goes */;
static char aBuffer[1024 * 22] = {0}
/* where the JSON goes. If this buffer is not large enough, this
function will assert (in debug builds) and return 0. When it does
so, this value needs to be increased. */;
int n = 0, nChildren = 0, nStruct = 0
/* output counters for figuring out where commas go */;
char * zPos = &aBuffer[1] /* skip first byte for now to help protect
@@ -651,6 +675,16 @@ const char * sqlite3_wasm_enum_json(void){
DefInt(SQLITE_LOCK_EXCLUSIVE);
} _DefGroup;
#ifdef SQLITE_ENABLE_FTS5
DefGroup(fts5) {
DefInt(FTS5_TOKENIZE_QUERY);
DefInt(FTS5_TOKENIZE_PREFIX);
DefInt(FTS5_TOKENIZE_DOCUMENT);
DefInt(FTS5_TOKENIZE_AUX);
DefInt(FTS5_TOKEN_COLOCATED);
} _DefGroup;
#endif
DefGroup(ioCap) {
DefInt(SQLITE_IOCAP_ATOMIC);
DefInt(SQLITE_IOCAP_ATOMIC512);
@@ -876,7 +910,7 @@ const char * sqlite3_wasm_enum_json(void){
DefInt(SQLITE_STMTSTATUS_FILTER_HIT);
DefInt(SQLITE_STMTSTATUS_MEMUSED);
} _DefGroup;
DefGroup(syncFlags) {
DefInt(SQLITE_SYNC_NORMAL);
DefInt(SQLITE_SYNC_FULL);
@@ -1094,7 +1128,7 @@ const char * sqlite3_wasm_enum_json(void){
M(xShadowName, "i(s)");
} _StructBinder;
#undef CurrentStruct
/**
** Workaround: in order to map the various inner structs from
** sqlite3_index_info, we have to uplift those into constructs we
@@ -1171,6 +1205,112 @@ const char * sqlite3_wasm_enum_json(void){
} _StructBinder;
#undef CurrentStruct
#ifdef SQLITE_ENABLE_FTS5
#define CurrentStruct Fts5PhraseIter
StructBinder {
M(a, "p");
M(b, "p");
} _StructBinder;
#undef CurrentStruct
#define CurrentStruct Fts5ExtensionApi
StructBinder {
M(iVersion, "i");
M(xUserData, "p(p)");// void *(*)(Fts5Context*);
M(xColumnCount, "i(p)");// int (*)(Fts5Context*);
M(xRowCount, "i(pp)");
//^^^ int (*)(Fts5Context*, sqlite3_int64 *pnRow);
M(xColumnTotalSize, "i(pip)");
//^^^ int (*)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
M(xTokenize, "i(ppipp)");
//^^^ int (*)(Fts5Context*,
// const char *pText, int nText, /* Text to tokenize */
// void *pCtx, /* Context passed to xToken() */
// int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
//);
M(xPhraseCount, "i(p)"); // int (*)(Fts5Context*);
M(xPhraseSize, "i(pi)"); // int (*)(Fts5Context*, int iPhrase);
M(xInstCount, "i(pp)"); // int (*)(Fts5Context*, int *pnInst);
M(xInst, "i(pippp)");
//^^^ int (*)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
M(xRowid, "j(p)"); // sqlite3_int64 (*)(Fts5Context*);
M(xColumnText, "i(pipp)");
//^^^ int (*)(Fts5Context*, int iCol, const char **pz, int *pn);
M(xColumnSize, "i(pip)");
//^^^ int (*)(Fts5Context*, int iCol, int *pnToken);
M(xQueryPhrase, "i(pipp)");
//^^^ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
// int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
// );
M(xSetAuxdata, "i(ppp)");
//^^^ int (*)(Fts5Context*, void *pAux, void(*xDelete)(void*));
M(xGetAuxdata, "p(pi)"); // void *(*)(Fts5Context*, int bClear);
M(xPhraseFirst, "i(pippp)");
//^^^ int (*)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
M(xPhraseNext, "v(pppp)");
//^^^ void (*)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
M(xPhraseFirstColumn, "i(pipp)");
//^^^ int (*)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
M(xPhraseNextColumn, "v(ppp)");
//^^^ void (*)(Fts5Context*, Fts5PhraseIter*, int *piCol);
} _StructBinder;
#undef CurrentStruct
#define CurrentStruct fts5_api
StructBinder {
M(iVersion, "i");/* Currently always 2 */
M(xCreateTokenizer, "i(ppppp)");
//^^^ int (*)(
// fts5_api *pApi,
// const char *zName,
// void *pContext,
// fts5_tokenizer *pTokenizer,
// void (*xDestroy)(void*)
// );
M(xFindTokenizer, "i(pppp)");
//^^^ int (*)(
// fts5_api *pApi,
// const char *zName,
// void **ppContext,
// fts5_tokenizer *pTokenizer
// );
M(xCreateFunction, "i(ppppp)");
//^^^ int (*)(
// fts5_api *pApi,
// const char *zName,
// void *pContext,
// fts5_extension_function xFunction,
// void (*xDestroy)(void*)
// );
} _StructBinder;
#undef CurrentStruct
#define CurrentStruct fts5_tokenizer
StructBinder {
M(xCreate, "i(ppip)");
//^^^ int (*)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
M(xDelete, "v(p)");
//^^^ void(Fts5Tokenizer*)
M(xTokenize, "i(ppipip)");
/**^^^ int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags,
const char *pText,
int nText,
int (*xToken)(
void *pCtx,
int tflags,
const char *pToken,
int nToken,
int iStart,
int iEnd
)
); */
} _StructBinder;
#undef CurrentStruct
#endif /* SQLITE_ENABLE_FTS5 */
#if SQLITE_WASM_TESTS
#define CurrentStruct WasmTestStruct
StructBinder {
+44 -9
View File
@@ -613,8 +613,6 @@ globalThis.WhWasmUtilInstaller = function(target){
target.installFunction = (func, sig)=>__installFunction(func, sig, false);
/**
EXPERIMENTAL! DO NOT USE IN CLIENT CODE!
Works exactly like installFunction() but requires that a
scopedAllocPush() is active and uninstalls the given function
when that alloc scope is popped via scopedAllocPop().
@@ -1180,7 +1178,7 @@ globalThis.WhWasmUtilInstaller = function(target){
cache.scopedAlloc.splice(n,1);
for(let p; (p = state.pop()); ){
if(target.functionEntry(p)){
//console.warn("scopedAllocPop() uninstalling transient function",p);
//console.warn("scopedAllocPop() uninstalling function",p);
target.uninstallFunction(p);
}
else target.dealloc(p);
@@ -1384,15 +1382,19 @@ globalThis.WhWasmUtilInstaller = function(target){
conversion of argument or return types, but see xWrap() and
xCallWrapped() for variants which do.
If the first argument is a function is is assumed to be
a WASM-bound function and is used as-is instead of looking up
the function via xGet().
As a special case, if passed only 1 argument after the name and
that argument in an Array, that array's entries become the
function arguments. (This is not an ambiguous case because it's
not legal to pass an Array object to a WASM function.)
*/
target.xCall = function(fname, ...args){
const f = target.xGet(fname);
const f = (fname instanceof Function) ? fname : target.xGet(fname);
if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function.");
if(f.length!==args.length) __argcMismatch(fname,f.length)
if(f.length!==args.length) __argcMismatch(((f===fname) ? f.name : fname),f.length)
/* This is arguably over-pedantic but we want to help clients keep
from shooting themselves in the foot when calling C APIs. */;
return (2===arguments.length && Array.isArray(arguments[1]))
@@ -1539,7 +1541,7 @@ globalThis.WhWasmUtilInstaller = function(target){
jsFuncToWasm().
- bindScope (string): one of ('transient', 'context',
'singleton'). Bind scopes are:
'singleton', 'permanent'). Bind scopes are:
- 'transient': it will convert JS functions to WASM only for
the duration of the xWrap()'d function call, using
@@ -1637,6 +1639,7 @@ globalThis.WhWasmUtilInstaller = function(target){
'and is not intended to be invoked from',
'client-level code. Invoked with:',opt);
}
this.name = opt.name || "unnamed";
this.signature = opt.signature;
if(opt.contextKey instanceof Function){
this.contextKey = opt.contextKey;
@@ -1698,14 +1701,16 @@ globalThis.WhWasmUtilInstaller = function(target){
exactly the 2nd and 3rd arguments are.
*/
convertArg(v,argv,argIndex){
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v);
let pair = this.singleton;
if(!pair && this.isContext){
pair = this.contextMap(this.contextKey(argv,argIndex));
//FuncPtrAdapter.debugOut(this.name, this.signature, "contextKey() =",this.contextKey(argv,argIndex), pair);
}
if(pair && pair[0]===v) return pair[1];
if(v instanceof Function){
/* Install a WASM binding and return its pointer. */
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
if(this.callProxy) v = this.callProxy(v);
const fp = __installFunction(v, this.signature, this.isTransient);
if(FuncPtrAdapter.debugFuncInstall){
@@ -1719,7 +1724,18 @@ globalThis.WhWasmUtilInstaller = function(target){
FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
this.contextKey(argv,argIndex), '@'+pair[1], v);
}
try{target.uninstallFunction(pair[1])}
try{
/* Because the pending native call might rely on the
pointer we're replacing, e.g. as is normally the case
with sqlite3's xDestroy() methods, we don't
immediately uninstall but instead add its pointer to
the scopedAlloc stack, which will be cleared when the
xWrap() mechanism is done calling the native
function. We're relying very much here on xWrap()
having pushed an alloc scope.
*/
cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]);
}
catch(e){/*ignored*/}
}
pair[0] = v;
@@ -1727,13 +1743,14 @@ globalThis.WhWasmUtilInstaller = function(target){
}
return fp;
}else if(target.isPtr(v) || null===v || undefined===v){
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
if(pair && pair[1] && pair[1]!==v){
/* uninstall stashed mapping and replace stashed mapping with v. */
if(FuncPtrAdapter.debugFuncInstall){
FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
this.contextKey(argv,argIndex), '@'+pair[1], v);
}
try{target.uninstallFunction(pair[1])}
try{ cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]) }
catch(e){/*ignored*/}
pair[0] = pair[1] = (v | 0);
}
@@ -1774,11 +1791,29 @@ globalThis.WhWasmUtilInstaller = function(target){
const __xResultAdapterCheck =
(t)=>xResult.get(t) || toss("Result adapter not found:",t);
/**
Fetches the xWrap() argument adapter mapped to t, calls it,
passing in all remaining arguments, and returns the result.
Throws if t is not mapped to an argument converter.
*/
cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
/**
Identical to convertArg() except that it does not perform
an is-defined check on the mapping to t before invoking it.
*/
cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);
/**
Fetches the xWrap() result adapter mapped to t, calls it, passing
it v, and returns the result. Throws if t is not mapped to an
argument converter.
*/
cache.xWrap.convertResult =
(t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
/**
Identical to convertResult() except that it does not perform an
is-defined check on the mapping to t before invoking it.
*/
cache.xWrap.convertResultNoCheck =
(t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));
+52
View File
@@ -2899,6 +2899,58 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
})/*session API sanity tests*/
;/*end of session API group*/;
////////////////////////////////////////////////////////////////////////
T.g('FTS5')
.t({
name: "Sanity checks",
predicate: (sqlite3)=>sqlite3.fts5 || "Missing sqlite3.fts5 namespace.",
test: function(sqlite3){
const db = new sqlite3.oo1.DB();
db.exec([
"create virtual table ft using fts5(a,b);",
"insert into ft(a,b) values",
"('a1','b1'),",
"('a2','b2'),",
"('a3','b3');"
]);
const fts = sqlite3.fts5;
let pApi = fts.fts5_api_from_db(db);
T.assert( !!pApi );
let fApi = new fts.fts5_api(pApi);
T.assert( fApi.$iVersion >= 2 );
fApi.dispose();
fApi = undefined;
let destroyCalled = false;
fts.createFunction(db, {
name: 'mymatch',
xFunction: function(pFtsX, pFtsCx, pCtx, argv){
// Both of these return-value approaches are equivalent:
const rv = "MY+"+argv.join(':');
if(0){
return rv;
}else{
capi.sqlite3_result_text(pCtx, rv, -1, capi.SQLITE_TRANSIENT);
// implicit return of undefined
}
},
xDestroy: function(){
destroyCalled = true;
}
});
let list = db.selectValues(
"select mymatch(ft,a,b) from ft where b match 'b2'"
);
T.assert( 1 === list.length )
.assert( 'MY+a2:b2' === list[0] );
//const fTok = new fts.fts5_tokenizer();
//fTok.installMethods({});
db.close();
T.assert( destroyCalled );
//toss("Testing");
}
})/*FTS5*/
////////////////////////////////////////////////////////////////////////
T.g('OPFS: Origin-Private File System',
+16 -14
View File
@@ -1,5 +1,5 @@
C Performance\soptimization\sfor\sJSON\srendering\slogic.
D 2023-08-02T16:06:02.660
C More\swork\stowards\sfts5\scustomzation\sin\sJS.
D 2023-08-04T08:39:21.730
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -487,10 +487,11 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/GNUmakefile ddf1aede4275e404c7eda782462c33b6406fcd2dd327241f6b22c0f7b80938e4
F ext/wasm/GNUmakefile a33d4acc4fab03bd4a1c76243ec1e43abaeb6e6e29f0971444bcb008ec6420ec
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-fts5 abdb0216e3035d48f797302f8712dd31d917b4dd7dfdb7957428f8046ebaf1b1
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md 5eb44fa02e9c693a1884a3692428647894b0380b24bca120866b7a24c8786134
@@ -499,17 +500,18 @@ F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e
F ext/wasm/api/sqlite3-api-glue.js cc6b0bb093bdb6279d4af259200b7b9e150e3796a8a3a4cd09a4928c43d25e56
F ext/wasm/api/sqlite3-api-cleanup.js 8489ac9933783f36807cfa5d79bfa366008c0443b42bf47bdef41bbfce4aa367
F ext/wasm/api/sqlite3-api-glue.js 861ff5ccfbaa50579c46618be53ac374202ad73cc27eb5077967c35edbb1869d
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
F ext/wasm/api/sqlite3-api-prologue.js cbd7d6ba185f3a844a8b0020e954b49bbc2ca78b305d117bec2ceca21431795a
F ext/wasm/api/sqlite3-api-prologue.js 76258e160bf6a89cc75a7d3c05646a054c8cab7219cd1e10bc20cacaad022131
F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
F ext/wasm/api/sqlite3-fts5-helper.js b7716c46acfc591421aa6f92c3962764666786a92b5bc116e85523c2d15f3f09
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25
F ext/wasm/api/sqlite3-v-helper.js 8dc3da6e69d51f455b64cc88fec7977f528d79ec267cfcdd97b606c8cc4cd156
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 54cee22aacadb9dfaea438d72ac0882249d028c37903208d48c52871290ceff7
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js e7a690e0e78ff4d563f2eca468f91db69f001ff4b79c6d2304cbb6f62dca437d
F ext/wasm/api/sqlite3-wasm.c 8867f1d41c112fb4a2cfe22ff224eccaf309fcdea266cee0ec554f85db72ef0f
F ext/wasm/api/sqlite3-wasm.c 4f0ff557fa67a4b8da8a68a6736e71265378b9d1a24ac33165a7d58fcad6cda4
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
@@ -518,7 +520,7 @@ F ext/wasm/c-pp.c 6d80d8569d85713effe8b0818a3cf51dc779e3f0bf8dc88771b8998552ee25
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
F ext/wasm/common/whwasmutil.js db6368ee57af90ee6691b6fb3ca97ee8064d12482e06a29113127c67b08f956e
F ext/wasm/common/whwasmutil.js fc923692f51b9a47cb6c3d0ab350db542cf6774c39d9e4ed9e894b55180d6989
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js 38aa8faec4d0ace1c973bc8a7a1533584463ebeecd4c420daa7d9687beeb9cb5
@@ -554,7 +556,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
F ext/wasm/tester1.c-pp.js b88dcad5424a652e8204c44a71bbc3deb22a4922c97ba792aedbabb7a6827b91
F ext/wasm/tester1.c-pp.js 5275b89d3657e1ca1b868f227539136a4642d42474e0202191fff9d7f4f91fff
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -2049,8 +2051,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 c4347e4400e96f932ac12f8f22484a2ebce2a578d1b2181977954c432f117bfd
R a3168d15b44149a629121fdff1d59acd
U drh
Z 37bd268b83df81194e3e4e7be36a0655
P 2666f52e88691201a30dc0f27c294c498bba3f16d5d2dddcadcfac115a3cba20
R 643c955846e19c824f4b95b5e770827f
U stephan
Z 00d6d6bd94d004d3b32085de27313db7
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
ea0b9aecbaca9a8e784fd2bcb50f78cbdcf4c5cfb45a7700bb222e4cc104c644
ce2a65d80f7793ae8183e311fc7284b37caa78d747436f22496284dfa57e3533