1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-24 22:22:08 +03:00

Internal cleanups and minor speed optimizations in the sqlite3.wasm.xWrap() infrastructure.

FossilOrigin-Name: c4dab53b8ea3401abd57671b8f3cb39fa4431b864d4c4e14ae24592f8d4cba0a
This commit is contained in:
stephan
2022-12-23 18:14:36 +00:00
parent 0f29f17bf6
commit b5915699d0
4 changed files with 182 additions and 103 deletions

View File

@ -359,7 +359,8 @@ self.WhWasmUtilInstaller = function(target){
/** /**
Given a function pointer, returns the WASM function table entry Given a function pointer, returns the WASM function table entry
if found, else returns a falsy value. if found, else returns a falsy value: undefined if fptr is out of
range or null if it's in range but the table entry is empty.
*/ */
target.functionEntry = function(fptr){ target.functionEntry = function(fptr){
const ft = target.functionTable(); const ft = target.functionTable();
@ -552,13 +553,12 @@ self.WhWasmUtilInstaller = function(target){
cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr); cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
} }
}catch(e){ }catch(e){
if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen); if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
throw e; throw e;
} }
return ptr; return ptr;
}; };
/** /**
Expects a JS function and signature, exactly as for Expects a JS function and signature, exactly as for
this.jsFuncToWasm(). It uses that function to create a this.jsFuncToWasm(). It uses that function to create a
@ -612,8 +612,13 @@ self.WhWasmUtilInstaller = function(target){
installFunction() has been called and results are undefined if installFunction() has been called and results are undefined if
ptr was not returned by that function. The returned function ptr was not returned by that function. The returned function
may be passed back to installFunction() to reinstall it. may be passed back to installFunction() to reinstall it.
To simplify certain use cases, if passed a falsy non-0 value
(noting that 0 is a valid function table index), this function
has no side effects and returns undefined.
*/ */
target.uninstallFunction = function(ptr){ target.uninstallFunction = function(ptr){
if(!ptr && 0!==ptr) return undefined;
const fi = cache.freeFuncIndexes; const fi = cache.freeFuncIndexes;
const ft = target.functionTable(); const ft = target.functionTable();
fi.push(ptr); fi.push(ptr);
@ -730,7 +735,7 @@ self.WhWasmUtilInstaller = function(target){
? cache : heapWrappers(); ? cache : heapWrappers();
for(const p of (Array.isArray(ptr) ? ptr : [ptr])){ for(const p of (Array.isArray(ptr) ? ptr : [ptr])){
switch (type) { switch (type) {
case 'i1': case 'i1':
case 'i8': c.HEAP8[p>>0] = value; continue; case 'i8': c.HEAP8[p>>0] = value; continue;
case 'i16': c.HEAP16[p>>1] = value; continue; case 'i16': c.HEAP16[p>>1] = value; continue;
case 'i32': c.HEAP32[p>>2] = value; continue; case 'i32': c.HEAP32[p>>2] = value; continue;
@ -803,7 +808,6 @@ self.WhWasmUtilInstaller = function(target){
/** f64 variant of poke8(). */ /** f64 variant of poke8(). */
target.poke64f = (ptr, value)=>target.poke(ptr, value, 'f64'); target.poke64f = (ptr, value)=>target.poke(ptr, value, 'f64');
/** Deprecated alias for getMemValue() */ /** Deprecated alias for getMemValue() */
target.getMemValue = target.peek; target.getMemValue = target.peek;
/** Deprecated alias for peekPtr() */ /** Deprecated alias for peekPtr() */
@ -1351,7 +1355,7 @@ self.WhWasmUtilInstaller = function(target){
const __argcMismatch = const __argcMismatch =
(f,n)=>toss(f+"() requires",n,"argument(s)."); (f,n)=>toss(f+"() requires",n,"argument(s).");
/** /**
Looks up a WASM-exported function named fname from Looks up a WASM-exported function named fname from
target.exports. If found, it is called, passed all remaining target.exports. If found, it is called, passed all remaining
@ -1390,18 +1394,25 @@ self.WhWasmUtilInstaller = function(target){
if(target.bigIntEnabled){ if(target.bigIntEnabled){
xArg.set('i64', (i)=>BigInt(i)); xArg.set('i64', (i)=>BigInt(i));
} }
xArg.set('i32', (i)=>(i | 0)); const __xArgPtr = 'i32' === ptrIR
xArg.set('i16', (i)=>((i | 0) & 0xFFFF)); ? ((i)=>(i | 0)) : ((i)=>(BigInt(i) | BigInt(0)));
xArg.set('i8', (i)=>((i | 0) & 0xFF)); xArg.set('i32', __xArgPtr )
xArg.set('f32', (i)=>Number(i).valueOf()); .set('i16', (i)=>((i | 0) & 0xFFFF))
xArg.set('float', xArg.get('f32')); .set('i8', (i)=>((i | 0) & 0xFF))
xArg.set('f64', xArg.get('f32')); .set('f32', (i)=>Number(i).valueOf())
xArg.set('double', xArg.get('f64')); .set('float', xArg.get('f32'))
xArg.set('int', xArg.get('i32')); .set('f64', xArg.get('f32'))
xResult.set('*', xArg.get(ptrIR)); .set('double', xArg.get('f64'))
xResult.set('pointer', xArg.get(ptrIR)); .set('int', xArg.get('i32'))
xArg.set('**', xArg.get(ptrIR)); .set('null', (i)=>i)
xResult.set('number', (v)=>Number(v)); .set(null, xArg.get('null'))
.set('**', __xArgPtr);
xResult.set('*', __xArgPtr)
.set('pointer', __xArgPtr)
.set('number', (v)=>Number(v))
.set('void', (v)=>undefined)
.set('null', (v)=>v)
.set(null, xResult.get('null'));
{ /* Copy certain xArg[...] handlers to xResult[...] and { /* Copy certain xArg[...] handlers to xResult[...] and
add pointer-style variants of them. */ add pointer-style variants of them. */
@ -1430,15 +1441,17 @@ self.WhWasmUtilInstaller = function(target){
TODO? Permit an Int8Array/Uint8Array and convert it to a string? TODO? Permit an Int8Array/Uint8Array and convert it to a string?
Would that be too much magic concentrated in one place, ready to Would that be too much magic concentrated in one place, ready to
backfire? backfire? We handle that at the client level in sqlite3 with a
custom argument converter.
*/ */
xArg.set('string', function(v){ const __xArgString = function(v){
if('string'===typeof v) return target.scopedAllocCString(v); if('string'===typeof v) return target.scopedAllocCString(v);
return v ? xArg.get(ptrIR)(v) : null; return v ? __xArgPtr(v) : null;
}); };
xArg.set('utf8', xArg.get('string')); xArg.set('string', __xArgString);
xArg.set('pointer', xArg.get('string')); xArg.set('utf8', __xArgString);
xArg.set('*', xArg.get('string')); xArg.set('pointer', __xArgString);
xArg.set('*', __xArgString);
xResult.set('string', (i)=>target.cstrToJs(i)); xResult.set('string', (i)=>target.cstrToJs(i));
xResult.set('utf8', xResult.get('string')); xResult.set('utf8', xResult.get('string'));
@ -1452,25 +1465,6 @@ self.WhWasmUtilInstaller = function(target){
try{ return i ? JSON.parse(target.cstrToJs(i)) : null } try{ return i ? JSON.parse(target.cstrToJs(i)) : null }
finally{ target.dealloc(i) } finally{ target.dealloc(i) }
}); });
xResult.set('void', (v)=>undefined);
xResult.set('null', (v)=>v);
if(0){
/***
This idea can't currently work because we don't know the
signature for the func and don't have a way for the user to
convey it. To do this we likely need to be able to match
arg/result handlers by a regex, but that would incur an O(N)
cost as we check the regex one at a time. Another use case for
such a thing would be pseudotypes like "int:-1" to say that
the value will always be treated like -1 (which has a useful
case in the sqlite3 bindings).
*/
xArg.set('func-ptr', function(v){
if(!(v instanceof Function)) return xArg.get(ptrIR);
const f = target.jsFuncToWasm(v, WHAT_SIGNATURE);
});
}
/** /**
Internal-use-only base class for FuncPtrAdapter and potentially Internal-use-only base class for FuncPtrAdapter and potentially
@ -1537,8 +1531,8 @@ self.WhWasmUtilInstaller = function(target){
value. This is only useful for use with "global" functions value. This is only useful for use with "global" functions
which do not rely on any state other than this function which do not rely on any state other than this function
pointer. If the being-converted function pointer is intended pointer. If the being-converted function pointer is intended
to be mapped to some sort of state object (e.g. an sqlite3*) to be mapped to some sort of state object (e.g. an
then "context" (see below) is the proper mode. `sqlite3*`) then "context" (see below) is the proper mode.
- 'context': similar to singleton mode but for a given - 'context': similar to singleton mode but for a given
"context", where the context is a key provided by the user "context", where the context is a key provided by the user
@ -1698,35 +1692,41 @@ self.WhWasmUtilInstaller = function(target){
(t)=>xResult.get(t) || toss("Result adapter not found:",t); (t)=>xResult.get(t) || toss("Result adapter not found:",t);
cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args); cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);
cache.xWrap.convertResult = cache.xWrap.convertResult =
(t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined)); (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
cache.xWrap.convertResultNoCheck =
(t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));
/** /**
Creates a wrapper for the WASM-exported function fname. Uses Creates a wrapper for another function which converts the arguments
xGet() to fetch the exported function (which throws on of the wrapper to argument types accepted by the wrapped function,
error) and returns either that function or a wrapper for that then converts the wrapped function's result to another form
for the wrapper.
The first argument must be one of:
- A JavaScript function.
- The name of a WASM-exported function. In the latter case xGet()
is used to fetch the exported function, which throws if it's not
found.
- A pointer into the indirect function table. e.g. a pointer
returned from target.installFunction().
It returns either the passed-in function or a wrapper for that
function which converts the JS-side argument types into WASM-side function which converts the JS-side argument types into WASM-side
types and converts the result type. If the function takes no types and converts the result type.
arguments and resultType is `null` then the function is returned
as-is, else a wrapper is created for it to adapt its arguments
and result value, as described below.
(If you're familiar with Emscripten's ccall() and cwrap(), this The second argument, `resultType`, describes the conversion for
function is essentially cwrap() on steroids.) the wrapped functions result. A literal `null` or the string
`'null'` both mean to return the original function's value as-is
This function's arguments are: (mnemonic: there is "null" conversion going on). Literal
`undefined` or the string `"void"` both mean to ignore the
- fname: the exported function's name. xGet() is used to fetch function's result and return `undefined`. Aside from those two
this, so will throw if no exported function is found with that special cases, the `resultType` value may be one of the values
name. described below or any mapping installed by the client using
xWrap.resultAdapter().
- resultType: the name of the result type. A literal `null` means
to return the original function's value as-is (mnemonic: there
is "null" conversion going on). Literal `undefined` or the
string `"void"` mean to ignore the function's result and return
`undefined`. Aside from those two special cases, it may be one
of the values described below or any mapping installed by the
client using xWrap.resultAdapter().
If passed 3 arguments and the final one is an array, that array If passed 3 arguments and the final one is an array, that array
must contain a list of type names (see below) for adapting the must contain a list of type names (see below) for adapting the
@ -1740,6 +1740,12 @@ self.WhWasmUtilInstaller = function(target){
xWrap('funcname', 'i32', ['string', 'f64']); xWrap('funcname', 'i32', ['string', 'f64']);
``` ```
This function enforces that the given list of arguments has the
same arity as the being-wrapped function (as defined by its
`length` property) and it will throw if that is not the case.
Similarly, the created wrapper will throw if passed a differing
argument count.
Type names are symbolic names which map the arguments to an Type names are symbolic names which map the arguments to an
adapter function to convert, if needed, the value before passing adapter function to convert, if needed, the value before passing
it on to WASM or to convert a return result from WASM. The list it on to WASM or to convert a return result from WASM. The list
@ -1776,6 +1782,10 @@ self.WhWasmUtilInstaller = function(target){
Non-numeric conversions include: Non-numeric conversions include:
- `null` literal or `"null"` string (args and results): perform
no translation and pass the arg on as-is. This is primarily
useful for results but may have a use or two for arguments.
- `string` or `utf8` (args): has two different semantics in order - `string` or `utf8` (args): has two different semantics in order
to accommodate various uses of certain C APIs to accommodate various uses of certain C APIs
(e.g. output-style strings)... (e.g. output-style strings)...
@ -1790,9 +1800,9 @@ self.WhWasmUtilInstaller = function(target){
client has already allocated and it's passed on as client has already allocated and it's passed on as
a WASM pointer. a WASM pointer.
- `string` or `utf8` (results): treats the result value as a - `string` or `utf8` (results): treats the result value as a
const C-string, encoded as UTF-8, copies it to a JS string, const C-string, encoded as UTF-8, copies it to a JS string,
and returns that JS string. and returns that JS string.
- `string:dealloc` or `utf8:dealloc) (results): treats the result value - `string:dealloc` or `utf8:dealloc) (results): treats the result value
as a non-const UTF-8 C-string, ownership of which has just been as a non-const UTF-8 C-string, ownership of which has just been
@ -1829,6 +1839,11 @@ self.WhWasmUtilInstaller = function(target){
type conversions are valid for both arguments _and_ result types type conversions are valid for both arguments _and_ result types
as they often have different memory ownership requirements. as they often have different memory ownership requirements.
Design note: the ability to pass in a JS function as the first
argument is of relatively limited use, primarily for testing
argument and result converters. JS functions, by and large, will
not want to deal with C-type arguments.
TODOs: TODOs:
- Figure out how/whether we can (semi-)transparently handle - Figure out how/whether we can (semi-)transparently handle
@ -1849,14 +1864,21 @@ self.WhWasmUtilInstaller = function(target){
abstracting it into this API (and taking on the associated abstracting it into this API (and taking on the associated
costs) may well not make good sense. costs) may well not make good sense.
*/ */
target.xWrap = function(fname, resultType, ...argTypes){ target.xWrap = function(fArg, resultType, ...argTypes){
if(3===arguments.length && Array.isArray(arguments[2])){ if(3===arguments.length && Array.isArray(arguments[2])){
argTypes = arguments[2]; argTypes = arguments[2];
} }
const xf = target.xGet(fname); if(target.isPtr(fArg)){
if(argTypes.length!==xf.length) __argcMismatch(fname, xf.length); fArg = target.functionEntry(fArg)
|| toss("Function pointer not found in WASM function table.");
}
const fIsFunc = (fArg instanceof Function);
const xf = fIsFunc ? fArg : target.xGet(fArg);
if(fIsFunc) fArg = xf.name || 'unnamed function';
if(argTypes.length!==xf.length) __argcMismatch(fArg, xf.length);
if((null===resultType) && 0===xf.length){ if((null===resultType) && 0===xf.length){
/* Func taking no args with an as-is return. We don't need a wrapper. */ /* Func taking no args with an as-is return. We don't need a wrapper.
We forego the argc check here, though. */
return xf; return xf;
} }
/*Verify the arg type conversions are valid...*/; /*Verify the arg type conversions are valid...*/;
@ -1869,11 +1891,11 @@ self.WhWasmUtilInstaller = function(target){
if(0===xf.length){ if(0===xf.length){
// No args to convert, so we can create a simpler wrapper... // No args to convert, so we can create a simpler wrapper...
return (...args)=>(args.length return (...args)=>(args.length
? __argcMismatch(fname, xf.length) ? __argcMismatch(fArg, xf.length)
: cxw.convertResult(resultType, xf.call(null))); : cxw.convertResult(resultType, xf.call(null)));
} }
return function(...args){ return function(...args){
if(args.length!==xf.length) __argcMismatch(fname, xf.length); if(args.length!==xf.length) __argcMismatch(fArg, xf.length);
const scope = target.scopedAllocPush(); const scope = target.scopedAllocPush();
try{ try{
/* /*
@ -1891,8 +1913,8 @@ self.WhWasmUtilInstaller = function(target){
and what those arguments are, is _not_ part of the public and what those arguments are, is _not_ part of the public
interface and is _not_ stable. interface and is _not_ stable.
*/ */
for(const i in args) args[i] = cxw.convertArg(argTypes[i], args[i], i, args); for(const i in args) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], i, args);
return cxw.convertResult(resultType, xf.apply(null,args)); return cxw.convertResultNoCheck(resultType, xf.apply(null,args));
}finally{ }finally{
target.scopedAllocPop(scope); target.scopedAllocPop(scope);
} }
@ -1984,14 +2006,13 @@ self.WhWasmUtilInstaller = function(target){
/** /**
Functions like xCall() but performs argument and result type Functions like xCall() but performs argument and result type
conversions as for xWrap(). The first argument is the name of the conversions as for xWrap(). The first, second, and third
exported function to call. The 2nd its the name of its result arguments are as documented for xWrap(), except that the 3rd
type, as documented for xWrap(). The 3rd is an array of argument argument may be either a falsy value or empty array to represent
type name, as documented for xWrap() (use a falsy value or an nullary functions. The 4th+ arguments are arguments for the call,
empty array for nullary functions). The 4th+ arguments are with the special case that if the 4th argument is an array, it is
arguments for the call, with the special case that if the 4th used as the arguments for the call. Returns the converted result
argument is an array, it is used as the arguments for the of the call.
call. Returns the converted result of the call.
This is just a thin wrapper 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 is to be called more than once, it's more efficient to use
@ -2000,9 +2021,9 @@ self.WhWasmUtilInstaller = function(target){
arguably more efficient because it will hypothetically free the arguably more efficient because it will hypothetically free the
wrapper function quickly. wrapper function quickly.
*/ */
target.xCallWrapped = function(fname, resultType, argTypes, ...args){ target.xCallWrapped = function(fArg, resultType, argTypes, ...args){
if(Array.isArray(arguments[3])) args = arguments[3]; if(Array.isArray(arguments[3])) args = arguments[3];
return target.xWrap(fname, resultType, argTypes||[]).apply(null, args||[]); return target.xWrap(fArg, resultType, argTypes||[]).apply(null, args||[]);
}; };
/** /**

View File

@ -539,7 +539,7 @@ self.sqlite3InitModule = sqlite3InitModule;
} }
w.dealloc(m); w.dealloc(m);
} }
// isPtr32() // isPtr32()
{ {
const ip = w.isPtr32; const ip = w.isPtr32;
@ -743,6 +743,65 @@ self.sqlite3InitModule = sqlite3InitModule;
.assert('HI' === cj(new Uint8Array([72, 73]))); .assert('HI' === cj(new Uint8Array([72, 73])));
}); });
// jsFuncToWasm()
{
const fsum3 = (x,y,z)=>x+y+z;
fw = w.jsFuncToWasm('i(iii)', fsum3);
T.assert(fw instanceof Function)
.assert( fsum3 !== fw )
.assert( 3 === fw.length )
.assert( 6 === fw(1,2,3) );
T.mustThrowMatching( ()=>w.jsFuncToWasm('x()', function(){}),
'Invalid signature letter: x');
}
// xWrap(Function,...)
{
let fp;
try {
const fmy = function fmy(i,s,d){
if(fmy.debug) log("fmy(",...arguments,")");
T.assert( 3 === i )
.assert( w.isPtr(s) )
.assert( w.cstrToJs(s) === 'a string' )
.assert( T.eqApprox(1.2, d) );
return w.allocCString("hi");
};
fmy.debug = false;
const xwArgs = ['string:dealloc', ['i32', 'string', 'f64']];
fw = w.xWrap(fmy, ...xwArgs);
const fmyArgs = [3, 'a string', 1.2];
let rc = fw(...fmyArgs);
T.assert( 'hi' === rc );
if(0){
/* Retain this as a "reminder to self"...
This extra level of indirection does not work: the
string argument is ending up as a null in fmy() but
the numeric arguments are making their ways through
What's happening is: installFunction() is creating a
WASM-compatible function instance. When we pass a JS string
into there it's getting coerced into `null` before being passed
on to the lower-level wrapper.
*/
fmy.debug = true;
fp = wasm.installFunction('i(isd)', fw);
fw = w.functionEntry(fp);
rc = fw(...fmyArgs);
log("rc =",rc);
T.assert( 'hi' === rc );
// Similarly, this does not work:
//let fpw = w.xWrap(fp, null, [null,null,null]);
//rc = fpw(...fmyArgs);
//log("rc =",rc);
//T.assert( 'hi' === rc );
}
}finally{
wasm.uninstallFunction(fp);
}
}
if(haveWasmCTests()){ if(haveWasmCTests()){
if(!sqlite3.config.useStdAlloc){ if(!sqlite3.config.useStdAlloc){
fw = w.xWrap('sqlite3_wasm_test_str_hello', 'utf8:dealloc',['i32']); fw = w.xWrap('sqlite3_wasm_test_str_hello', 'utf8:dealloc',['i32']);
@ -768,7 +827,7 @@ self.sqlite3InitModule = sqlite3InitModule;
}); });
} }
} }
} }/*xWrap()*/
}/*WhWasmUtil*/) }/*WhWasmUtil*/)
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -997,7 +1056,6 @@ self.sqlite3InitModule = sqlite3InitModule;
P.restore(stack); P.restore(stack);
} }
}/*pstack tests*/) }/*pstack tests*/)
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
;/*end of C/WASM utils checks*/ ;/*end of C/WASM utils checks*/

View File

@ -1,5 +1,5 @@
C Add\ssqlite3.capi\sJS\sbindings\sfor\sthe\ssqlite3session_...(),\ssqlite3changeset_...()\sand\ssqlite3changegroup_...()\sAPIs,\snoting\sthat\sthey\sare\scompletely\suntested.\sAside\sfrom\smissing\stests,\sthese\sbindings\sreveal\sa\sslight\sstring-argument-type\sshortcoming\sin\sthe\scallback\sfunction\spointer\s"reverse\sbinding"\swhich\sshould\sideally\sbe\sresolved\sbefore\spublishing\sthem. C Internal\scleanups\sand\sminor\sspeed\soptimizations\sin\sthe\ssqlite3.wasm.xWrap()\sinfrastructure.
D 2022-12-23T14:11:54.508 D 2022-12-23T18:14:36.766
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f
F ext/wasm/common/whwasmutil.js af85d9a09fa79847d08279be0f61978ecc3bed893c88003f4a85d8bd204e6b5a F ext/wasm/common/whwasmutil.js ccfd4addd60f0c3f02ee1669c4e5c8935ca770ed7b737ea3e9e82cc6505576c8
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9
F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406
F ext/wasm/tester1.c-pp.js c45c46cdae1949d426ee12a736087ab180beacc2a20cd829f9052b957adf9ac9 F ext/wasm/tester1.c-pp.js 4202e7ec525445386f29612ddf1365348edd1f6002b8b21721c954b9569b756a
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P cd8c100808da1043fcf63555f48f30c90272c48c6627321ceb0a0995b34733d1 P 0a39172ee134816f5ce17a403b960e9c22bb56efd5bcf77ecde465efe0d88b1d
R 042a575edf17a004d3f3655705629da7 R 01a5a3416aa00dbf975dac1dad0558b8
U stephan U stephan
Z 81bf3460fe50c1e6a01aac07ed8bed30 Z fd750e2a8255c013dd9faba040bbeb23
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
0a39172ee134816f5ce17a403b960e9c22bb56efd5bcf77ecde465efe0d88b1d c4dab53b8ea3401abd57671b8f3cb39fa4431b864d4c4e14ae24592f8d4cba0a