1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-12-20 01:22:32 +03:00

Reworking of JS internals to support binding of nested C structs (like sqlite3_index_constraint and friends) and allow some of the automated JS/C conversions to be plugged in at the struct-binding level, simplifying how struct members, in particular function pointers, can be used from JS.

FossilOrigin-Name: bb4fd5b789cebf2b224c29023fea3e620a86fb36730c36c0d85d9f35880bf643
This commit is contained in:
stephan
2025-11-10 07:41:54 +00:00
parent 998ebf1de7
commit 73caea1764
8 changed files with 415 additions and 136 deletions

View File

@@ -55,12 +55,7 @@ try{
} }
); );
/** Figure out if this is a 32- or 64-bit WASM build. */ sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig);
bootstrapConfig.wasmPtrIR =
'number'===(typeof bootstrapConfig.exports.sqlite3_libversion())
? 'i32' :'i64';
const sIMS = sqlite3InitScriptInfo;
sIMS.debugModule("Bootstrapping lib config", sIMS);
/** /**
For purposes of the Emscripten build, call sqlite3ApiBootstrap(). For purposes of the Emscripten build, call sqlite3ApiBootstrap().

View File

@@ -799,22 +799,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
environment via whwashutil.js. environment via whwashutil.js.
*/ */
Object.assign(wasm, { Object.assign(wasm, {
/**
The WASM IR (Intermediate Representation) value for
pointer-type values. If set then it MUST be one of 'i32' or
'i64' (else an exception will be thrown). If it's not set, it
will default to 'i32'.
*/
pointerIR: config.wasmPtrIR,
/**
True if BigInt support was enabled via (e.g.) the
Emscripten -sWASM_BIGINT flag, else false. When
enabled, certain 64-bit sqlite3 APIs are enabled which
are not otherwise enabled due to JS/WASM int64
impedance mismatches.
*/
bigIntEnabled: !!config.bigIntEnabled,
/** /**
The symbols exported by the WASM environment. The symbols exported by the WASM environment.
@@ -834,6 +818,29 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
"in either config.exports.memory (exported)", "in either config.exports.memory (exported)",
"or config.memory (imported)."), "or config.memory (imported)."),
/**
The WASM pointer size. If set then it MUST be one of 4 or 8 and
it MUST correspond to the WASM environment's pointer size. We
figure out the size by calling some un-JS-wrapped WASM function
which returns a pointer-type value. If that value is a BigInt,
it's 64-bit, else it's 32-bit. The pieces which populate
sqlite3.wasm (whwasmutil.js) can figure this out _if_ they can
allocate, but we have a chicken/egg situation there which makes
it illegal for that code to invoke wasm.dealloc() at the time
it would be needed. So we need to configure it ahead of time
(here) instead.
*/
pointerSize: ('number'===typeof config.exports.sqlite3_libversion()) ? 4 : 8,
/**
True if BigInt support was enabled via (e.g.) the
Emscripten -sWASM_BIGINT flag, else false. When
enabled, certain 64-bit sqlite3 APIs are enabled which
are not otherwise enabled due to JS/WASM int64
impedance mismatches.
*/
bigIntEnabled: !!config.bigIntEnabled,
/** /**
WebAssembly.Table object holding the indirect function call WebAssembly.Table object holding the indirect function call
table. Defaults to exports.__indirect_function_table. table. Defaults to exports.__indirect_function_table.

View File

@@ -227,6 +227,14 @@ globalThis.WhWasmUtilInstaller = function(target){
all args with a space between each. */ all args with a space between each. */
const toss = (...args)=>{throw new Error(args.join(' '))}; const toss = (...args)=>{throw new Error(args.join(' '))};
if( !target.pointerSize && !target.pointerIR
&& target.alloc && target.dealloc ){
/* Try to determine the pointer size by allocating. */
const ptr = target.alloc(1);
target.pointerSize = ('bigint'===typeof ptr ? 8 : 4);
target.dealloc(ptr);
}
/** /**
As of 2025-09-21, this library works with 64-bit WASM modules As of 2025-09-21, this library works with 64-bit WASM modules
built with Emscripten's -sMEMORY64=1. built with Emscripten's -sMEMORY64=1.
@@ -996,12 +1004,12 @@ globalThis.WhWasmUtilInstaller = function(target){
target.heap8u(). target.heap8u().
*/ */
target.cstrlen = function(ptr){ target.cstrlen = function(ptr){
if(!ptr || !target.isPtr(ptr)) return null; if(!ptr || !target.isPtr/*64*/(ptr)) return null;
ptr = Number(ptr) /*tag:64bit*/; ptr = Number(ptr) /*tag:64bit*/;
const h = heapWrappers().HEAP8U; const h = heapWrappers().HEAP8U;
let pos = ptr; let pos = ptr;
for( ; h[pos] !== 0; ++pos ){} for( ; h[pos] !== 0; ++pos ){}
return Number(pos - ptr); return pos - ptr;
}; };
/** Internal helper to use in operations which need to distinguish /** Internal helper to use in operations which need to distinguish

View File

@@ -32,7 +32,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
if(!(config.heap instanceof WebAssembly.Memory) if(!(config.heap instanceof WebAssembly.Memory)
&& !(config.heap instanceof Function)){ && !(config.heap instanceof Function)){
toss("config.heap must be WebAssembly.Memory instance or a function."); toss("config.heap must be WebAssembly.Memory instance or a function which returns one.");
} }
['alloc','dealloc'].forEach(function(k){ ['alloc','dealloc'].forEach(function(k){
(config[k] instanceof Function) || (config[k] instanceof Function) ||
@@ -48,14 +48,19 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
memberSuffix = (config.memberSuffix || ""), memberSuffix = (config.memberSuffix || ""),
BigInt = globalThis['BigInt'], BigInt = globalThis['BigInt'],
BigInt64Array = globalThis['BigInt64Array'], BigInt64Array = globalThis['BigInt64Array'],
bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array, bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array;
ptrIR = config.pointerIR
//console.warn("config",config);
let ptr = alloc(1);
const ptrIR = config.pointerIR
|| config.ptrIR/*deprecated*/ || config.ptrIR/*deprecated*/
|| 'i32', || ('bigint'===typeof ptr ? 'i64' : 'i32');
/* Undocumented (on purpose) config options: */ /* Undocumented (on purpose) config options: */
ptrSize = config.ptrSize/*deprecated*/ const ptrSize = config.ptrSize/*deprecated*/
|| ('i32'===ptrIR ? 4 : 8) || ('i32'===ptrIR ? 4 : 8);
; dealloc(ptr);
ptr = undefined;
//console.warn("ptrIR =",ptrIR,"ptrSize =",ptrSize);
if(ptrIR!=='i32' && ptrIR!=='i64') toss("Invalid pointer representation:",ptrIR); if(ptrIR!=='i32' && ptrIR!=='i64') toss("Invalid pointer representation:",ptrIR);
if(ptrSize!==4 && ptrSize!==8) toss("Invalid pointer size:",ptrSize); if(ptrSize!==4 && ptrSize!==8) toss("Invalid pointer size:",ptrSize);
@@ -136,11 +141,11 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
/** True if SIG s looks like a function signature, else /** True if SIG s looks like a function signature, else
false. */ false. */
const isFuncSig = (s)=>'('===s[1]; const isFuncSig = (s)=>'('===s[1];
/** True if SIG s is-a pointer signature. */ /** True if SIG s is-a pointer-type signature. */
const isPtrSig = (s)=>'p'===s || 'P'===s; const isPtrSig = (s)=>'p'===s || 'P'===s || 's'===s;
const isAutoPtrSig = (s)=>'P'===s /*EXPERIMENTAL*/; const isAutoPtrSig = (s)=>'P'===s /*EXPERIMENTAL*/;
/** Returns p if SIG s is a function SIG, else returns s[0]. */ /** Returns p if SIG s is a function SIG, else returns s[0]. */
const sigLetter = (s)=>isFuncSig(s) ? 'p' : s[0]; const sigLetter = (s)=>s ? (isFuncSig(s) ? 'p' : s[0]) : undefined;
/** Returns the WASM IR form of the Emscripten-conventional letter /** Returns the WASM IR form of the Emscripten-conventional letter
at SIG s[0]. Throws for an unknown SIG. */ at SIG s[0]. Throws for an unknown SIG. */
const sigIR = function(s){ const sigIR = function(s){
@@ -154,6 +159,19 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
} }
toss("Unhandled signature IR:",s); toss("Unhandled signature IR:",s);
}; };
/** Returns the WASM sizeof of the Emscripten-conventional letter
at SIG s[0]. Throws for an unknown SIG. */
const sigSize = function(s){
switch(sigLetter(s)){
case 'c': case 'C': return 1;
case 'i': return 4;
case 'p': case 'P': case 's': return ptrSize;
case 'j': return 8;
case 'f': return 4;
case 'd': return 8;
}
toss("Unhandled signature sizeof:",s);
};
const affirmBigIntArray = BigInt64Array const affirmBigIntArray = BigInt64Array
? ()=>true : ()=>toss('BigInt64Array is not available.'); ? ()=>true : ()=>toss('BigInt64Array is not available.');
@@ -232,11 +250,22 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
accessible via boundObject.pointer, which is gated behind a accessible via boundObject.pointer, which is gated behind a
property interceptor, but are not exposed anywhere else in the property interceptor, but are not exposed anywhere else in the
object. object.
This approach means we cannot proxy arrays, or any type which
might be realloced, as that pointer could change out from under
us. That's not an issue for nested structs, but it might be for a
struct they're embedded in. In the case of nested structs we
"could" record their top-most parent object and their offset into
that object, instead of storing the pointer itself. We could that
by allowing a function instead of a pointer in this map, that
function returning the (lazily-calculated) address. Hmm.
*/ */
const __instancePointerMap = new WeakMap(); const __instancePointerMap = new WeakMap();
const getInstancePtr = (obj)=>__instancePointerMap.get(obj);
/** Property name for the pointer-is-external marker. */ /** Property name for the pointer-is-external marker. */
const xPtrPropName = '(pointer-is-external)'; const xPtrPropName = Symbol('(pointer-is-external)');
const __isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0); const __isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0);
const __isPtr64 = (ptr)=>( const __isPtr64 = (ptr)=>(
@@ -252,7 +281,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
/** Frees the obj.pointer memory and clears the pointer /** Frees the obj.pointer memory and clears the pointer
property. */ property. */
const __freeStruct = function(ctor, obj, m){ const __freeStruct = function(ctor, obj, m){
if(!m) m = __instancePointerMap.get(obj); if(!m) m = getInstancePtr(obj);
if(m) { if(m) {
__instancePointerMap.delete(obj); __instancePointerMap.delete(obj);
if(Array.isArray(obj.ondispose)){ if(Array.isArray(obj.ondispose)){
@@ -283,7 +312,12 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
ctor.structName,"instance:", ctor.structName,"instance:",
ctor.structInfo.sizeof,"bytes @"+m); ctor.structInfo.sizeof,"bytes @"+m);
} }
if(!obj[xPtrPropName]) dealloc(m); if(!obj[xPtrPropName]){
if( ctor.structInfo.zeroOnFree ){
heap().fill(0, Number(m), Number(m)+ctor.structInfo.sizeof);
}
dealloc(m);
}
} }
}; };
@@ -324,8 +358,69 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
: null; : null;
}; };
/** True if sig looks like an emscripten/jaccwabyt
type signature, else false. */
const looksLikeASig = function f(sig){
f.rxSig1 ??= /^[ipPsjfdcC]$/;
f.rxSig2 ??= /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/;
return f.rxSig1.test(sig) || f.rxSig2.test(sig);
};
/** Returns a pair of adaptor maps (objects) in a length-3
array specific to the given object. */
const __adaptorsFor = function(who){
let x = this.get(who);
if( !x ){
x = [ Object.create(null), Object.create(null), Object.create(null) ];
this.set(who, x);
}
return x;
}.bind(new WeakMap);
/** Code de-duplifier for __adaptGet(), __adaptSet(), and
__adaptStruct(). */
const __adaptor = function(who, which, key, proxy){
const a = __adaptorsFor(who)[which];
if(3===arguments.length) return a[key];
if( proxy ) return a[key] = proxy;
return delete a[key];
};
const noopAdapter = (x)=>x;
// StructBinder::adaptGet()
const __adaptGet = function(key, ...args){
/*if( looksLikeASig(key) ){
toss("Getter adaptor's name (",key,") collides with a data type signature.");
}*/
return __adaptor(this, 0, key, ...args);
};
// StructBinder::adaptSet()
const __adaptSet = function(key, ...args){
if( looksLikeASig(key) ){
toss("Setter adaptor's name (",key,") collides with a data type signature.");
}
return __adaptor(this, 1, key, ...args);
};
// StructBinder::adaptStruct()
const __adaptStruct = function(key, ...args){
return __adaptor(this, 2, key, ...args);
};
const __adaptStruct2 = function(who,key){
const si = ('string'===typeof key)
? __adaptor(who, 2, key) : key;
if( 'object'!==typeof si ){
toss("Invalid struct mapping object. Arg =",key,JSON.stringify(si));
}
return si;
};
const __memberKey = (k)=>memberPrefix + k + memberSuffix; const __memberKey = (k)=>memberPrefix + k + memberSuffix;
const __memberKeyProp = rop(__memberKey); const __memberKeyProp = rop(__memberKey);
//const __adaptGetProp = rop(__adaptGet);
/** /**
Looks up a struct member in structInfo.members. Throws if found Looks up a struct member in structInfo.members. Throws if found
@@ -342,7 +437,8 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
if(v.key===memberName){ m = v; break; } if(v.key===memberName){ m = v; break; }
} }
if(!m && tossIfNotFound){ if(!m && tossIfNotFound){
toss(sPropName(structInfo.name,memberName),'is not a mapped struct member.'); toss(sPropName(structInfo.name || structInfo.structName, memberName),
'is not a mapped struct member.');
} }
} }
return m; return m;
@@ -361,7 +457,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
const __ptrPropDescriptor = { const __ptrPropDescriptor = {
configurable: false, enumerable: false, configurable: false, enumerable: false,
get: function(){return __instancePointerMap.get(this)}, get: function(){return getInstancePtr(this)},
set: ()=>toss("Cannot assign the 'pointer' property of a struct.") set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
// Reminder: leaving `set` undefined makes assignments // Reminder: leaving `set` undefined makes assignments
// to the property _silently_ do nothing. Current unit tests // to the property _silently_ do nothing. Current unit tests
@@ -558,78 +654,176 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
}); });
/** /**
Pass this a StructBinder-generated prototype, and the struct If struct description object si has a getter proxy, return it (a
member description object. It will define property accessors for function), else return undefined.
proto[memberKey] which read from/write to memory in
this.pointer. It modifies descr to make certain downstream
operations much simpler.
*/ */
const makeMemberWrapper = function f(ctor,name, descr){ const memberGetterProxy = function(si){
return si.get || (si.adaptGet
? StructBinder.adaptGet(si.adaptGet)
: undefined);
};
/**
If struct description object si has a setter proxy, return it (a
function), else return undefined.
*/
const memberSetterProxy = function(si){
return si.set || (si.adaptSet
? StructBinder.adaptSet(si.adaptSet)
: undefined);
};
/**
To be called by makeMemberWrapper() when si has a 'members'
member, i.e. is an embedded struct. This function sets up that
struct like any other and also sets up property accessor for
ctor.memberKey(name) which returns an instance of that new
StructType when the member is accessed. That instance wraps the
memory of the member's part of the containing C struct instance.
That is, if struct Foo has member bar which is an inner struct
then:
const f = new Foo;
const b = f.bar;
assert( b is-a StructType object );
assert( b.pointer === f.b.pointer );
b will be disposed of when f() is. Calling b.dispose() will not
do any permanent harm, as the wrapper object will be recreated
when accessing f.bar, pointing to the same memory in f.
The si.zeroOnFree flag has no effect on embedded structs because
they wrap "external" memory, so do not own it, and are thus never
freed, as such.
*/
const makeMemberStructWrapper = function callee(ctor, name, si){
/**
Where we store inner-struct member proxies. Keys are a
combination of the parent object's pointer address and the
property's name. The values are StructType instances.
*/
const __innerStructs = (callee.innerStructs ??= new Map());
const key = ctor.memberKey(name);
if( undefined!==si.signature ){
toss("'signature' cannot be used on an embedded struct (",
ctor.structName,".",key,").");
}
if( memberSetterProxy(si) ){
toss("'set' and 'adaptSet' are not permitted for nested struct members.");
}
//console.warn("si =",ctor.structName, name, JSON.stringify(si,' '));
si.structName ??= ctor.structName+'::'+name;
si.key = key;
si.name = name;
si.constructor = this.call(this, si.structName, si);
//console.warn("si.constructor =",si.constructor);
//console.warn("si =",si,"ctor=",ctor);
const getterProxy = memberGetterProxy(si);
const prop = Object.assign(Object.create(null),{
configurable: false,
enumerable: false,
set: __propThrowOnSet(ctor/*not si.constructor*/.structName, key),
get: function(){
const dbg = si.constructor.prototype.debugFlags.__flags;
const p = this.pointer;
const k = p+'.'+key;
let s = __innerStructs.get(k);
if(dbg.getter){ log("debug.getter: k =",k); }
if( !s ){
s = new si.constructor(__ptrAdd(p, si.offset));
__innerStructs.set(k, s);
this.addOnDispose(()=>s.dispose());
s.addOnDispose(()=>__innerStructs.delete(k));
//console.warn("Created",k,"proxy");
}
if(getterProxy) s = getterProxy.apply(this,[s,key]);
if(dbg.getter) log("debug.getter: result =",s);
return s;
}
});
Object.defineProperty(ctor.prototype, key, prop);
}/*makeMemberStructWrapper()*/;
/**
Pass this a StructBinderImpl-generated constructor, a member
property name, and the struct member description object. It will
define property accessors for proto[memberKey] which read
from/write to memory in this.pointer. It modifies si to make
certain downstream operations simpler.
*/
const makeMemberWrapper = function f(ctor, name, si){
si = __adaptStruct2(this, si);
if( si.members ){
return makeMemberStructWrapper.call(this, ctor, name, si);
}
if(!f._){ if(!f._){
/*cache all available getters/setters/set-wrappers for /* Cache all available getters/setters/set-wrappers for
direct reuse in each accessor function. */ direct reuse in each accessor function. */
f._ = {getters: {}, setters: {}, sw:{}}; f._ = {getters: {}, setters: {}, sw:{}};
const a = ['i','c','C','p','P','s','f','d','v()']; const a = ['i','c','C','p','P','s','f','d','v()'];
if(bigIntEnabled) a.push('j'); if(bigIntEnabled) a.push('j');
a.forEach(function(v){ a.forEach(function(v){
//const ir = sigIR(v);
f._.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */; f._.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
f._.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */; f._.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
for conversion */; for conversion */;
}); });
const rxSig1 = /^[ipPsjfdcC]$/,
rxSig2 = /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/;
f.sigCheck = function(obj, name, key,sig){ f.sigCheck = function(obj, name, key,sig){
if(Object.prototype.hasOwnProperty.call(obj, key)){ if(Object.prototype.hasOwnProperty.call(obj, key)){
toss(obj.structName,'already has a property named',key+'.'); toss(obj.structName,'already has a property named',key+'.');
} }
rxSig1.test(sig) || rxSig2.test(sig) looksLikeASig(sig)
|| toss("Malformed signature for", || toss("Malformed signature for",
sPropName(obj.structName,name)+":",sig); sPropName(obj.structName,name)+":",sig);
}; };
} }
const key = ctor.memberKey(name); const key = ctor.memberKey(name);
f.sigCheck(ctor.prototype, name, key, descr.signature); f.sigCheck(ctor.prototype, name, key, si.signature);
descr.key = key; si.key = key;
descr.name = name; si.name = name;
const sigGlyph = sigLetter(descr.signature); const sigGlyph = sigLetter(si.signature);
const xPropName = sPropName(ctor.prototype.structName,key); const xPropName = sPropName(ctor.prototype.structName,key);
const dbg = ctor.prototype.debugFlags.__flags; const dbg = ctor.prototype.debugFlags.__flags;
/* /*
TODO?: set prototype of descr to an object which can set/fetch TODO?: set prototype of si to an object which can set/fetch
its preferred representation, e.g. conversion to string or mapped its preferred representation, e.g. conversion to string or mapped
function. Advantage: we can avoid doing that via if/else if/else function. Advantage: we can avoid doing that via if/else if/else
in the get/set methods. in the get/set methods.
*/ */
const getterProxy = memberGetterProxy(si);
const prop = Object.create(null); const prop = Object.create(null);
prop.configurable = false; prop.configurable = false;
prop.enumerable = false; prop.enumerable = false;
prop.get = function(){ prop.get = function(){
if(dbg.getter){ if(dbg.getter){
log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph), log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
xPropName,'@', this.pointer,'+',descr.offset,'sz',descr.sizeof); xPropName,'@', this.pointer,'+',si.offset,'sz',si.sizeof);
} }
let rc = ( let rc = (
new DataView(heap().buffer, Number(this.pointer) + descr.offset, descr.sizeof) new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)
)[f._.getters[sigGlyph]](0, isLittleEndian); )[f._.getters[sigGlyph]](0, isLittleEndian);
if(getterProxy) rc = getterProxy.apply(this,[rc,key]);
if(dbg.getter) log("debug.getter:",xPropName,"result =",rc); if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
return rc; return rc;
}; };
if(descr.readOnly){ if(si.readOnly){
prop.set = __propThrowOnSet(ctor.prototype.structName,key); prop.set = __propThrowOnSet(ctor.prototype.structName,key);
}else{ }else{
const setterProxy = memberSetterProxy(si);
prop.set = function(v){ prop.set = function(v){
if(dbg.setter){ if(dbg.setter){
log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph), log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
xPropName,'@', this.pointer,'+',descr.offset,'sz',descr.sizeof, v); xPropName,'@', this.pointer,'+',si.offset,'sz',si.sizeof, v);
} }
if(!this.pointer){ if(!this.pointer){
toss("Cannot set struct property on disposed instance."); toss("Cannot set native property on a disposed struct instance.");
} }
if(null===v) v = __NullPtr; if( setterProxy ) v = setterProxy.apply(this,[v]);
else while(!__isPtr(v)){ if( null===v || undefined===v ) v = __NullPtr;
if(isAutoPtrSig(descr.signature) && (v instanceof StructType)){ else while( isPtrSig(si.signature) && !__isPtr(v) ){
if(isAutoPtrSig(si.signature) && (v instanceof StructType)){
// It's a struct instance: let's store its pointer value! // It's a struct instance: let's store its pointer value!
v = v.pointer || __NullPtr; v = v.pointer || __NullPtr;
if(dbg.setter) log("debug.setter:",xPropName,"resolved to",v); if(dbg.setter) log("debug.setter:",xPropName,"resolved to",v);
@@ -638,66 +832,45 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
toss("Invalid value for pointer-type",xPropName+'.'); toss("Invalid value for pointer-type",xPropName+'.');
} }
( (
new DataView(heap().buffer, Number(this.pointer) + descr.offset, new DataView(heap().buffer, Number(this.pointer) + si.offset,
descr.sizeof) si.sizeof)
)[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian); )[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
}; };
} }
Object.defineProperty(ctor.prototype, key, prop); Object.defineProperty(ctor.prototype, key, prop);
}/*makeMemberWrapper*/; }/*makeMemberWrapper()*/;
/** /**
The main factory function which will be returned to the The main factory function which will be returned to the
caller. caller. The third argument is structly for internal use.
This level of indirection is to avoid that clients can pass a
third argument to this, as that's only for internal use.
internalOpt options:
- None right now. This is for potential use in recursion.
Usages:
StructBinder(string, obj [,optObj]);
StructBinder(obj);
*/
const StructBinderImpl = function StructBinderImpl(
structName, si, opt = Object.create(null)
){
/**
StructCtor is the eventual return value of this function. We
need to populate this early on so that we can do some trickery
in feeding it through recursion.
*/ */
const StructBinder = function StructBinder(structName, structInfo){
if(1===arguments.length){
structInfo = structName;
structName = structInfo.name;
}else if(!structInfo.name){
structInfo.name = structName;
}
if(!structName) toss("Struct name is required.");
let lastMember = false;
Object.keys(structInfo.members).forEach((k)=>{
// Sanity checks of sizeof/offset info...
const m = structInfo.members[k];
if(!m.sizeof) toss(structName,"member",k,"is missing sizeof.");
else if(m.sizeof===1){
(m.signature === 'c' || m.signature === 'C') ||
toss("Unexpected sizeof==1 member",
sPropName(structInfo.name,k),
"with signature",m.signature);
}else{
// sizes and offsets of size-1 members may be odd values, but
// others may not.
if(0!==(m.sizeof%4)){
console.warn("Invalid struct member description =",m,"from",structInfo);
toss(structName,"member",k,"sizeof is not aligned. sizeof="+m.sizeof);
}
if(0!==(m.offset%4)){
console.warn("Invalid struct member description =",m,"from",structInfo);
toss(structName,"member",k,"offset is not aligned. offset="+m.offset);
}
}
if(!lastMember || lastMember.offset < m.offset) lastMember = m;
});
if(!lastMember) toss("No member property descriptions found.");
else if(structInfo.sizeof < lastMember.offset+lastMember.sizeof){
toss("Invalid struct config:",structName,
"max member offset ("+lastMember.offset+") ",
"extends past end of struct (sizeof="+structInfo.sizeof+").");
}
const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags));
/** Constructor for the StructCtor. */
const zeroAsPtr = __asPtrType(0);
const StructCtor = function StructCtor(externalMemory){ const StructCtor = function StructCtor(externalMemory){
externalMemory = __asPtrType(externalMemory); externalMemory = __asPtrType(externalMemory);
//console.warn("externalMemory",externalMemory,arguments[0]); //console.warn("externalMemory",externalMemory,arguments[0]);
if(!(this instanceof StructCtor)){ if(!(this instanceof StructCtor)){
toss("The",structName,"constructor may only be called via 'new'."); toss("The",structName,"constructor may only be called via 'new'.");
}else if(arguments.length){ }else if(arguments.length){
if(Number.isNaN(externalMemory) || externalMemory<=zeroAsPtr){ if( !__isPtr(externalMemory) ){
toss("Invalid pointer value",arguments[0],"for",structName,"constructor."); toss("Invalid pointer value",arguments[0],"for",structName,"constructor.");
} }
__allocStruct(StructCtor, this, externalMemory); __allocStruct(StructCtor, this, externalMemory);
@@ -705,31 +878,127 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
__allocStruct(StructCtor, this); __allocStruct(StructCtor, this);
} }
}; };
const self = this;
/**
"Convert" struct description x to a struct description, if
needed.
*/
const ads = (x)=>{
//console.warn("looksLikeASig(",x,") =",looksLikeASig(x));
return (('string'===typeof x) && looksLikeASig(x))
? {signature: x} : __adaptStruct2(self,x);
};
if(1===arguments.length){
si = ads(structName);
structName = si.structName || si.name;
}else if(2===arguments.length){
si = ads(si);
si.name ??= structName;
}else{
si = ads(si);
}
structName ??= si.structName;
//console.warn("arguments =",JSON.stringify(arguments));
structName ??= opt.structName;
if( !structName ) toss("One of 'name' or 'structName' are required.");
if( si.adapt ){
Object.keys(si.adapt.struct||{}).forEach((k)=>{
__adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]);
});
Object.keys(si.adapt.set||{}).forEach((k)=>{
__adaptSet.call(StructBinderImpl, k, si.adapt.set[k]);
});
Object.keys(si.adapt.get||{}).forEach((k)=>{
__adaptGet.call(StructBinderImpl, k, si.adapt.get[k]);
});
}
if(!si.members && !si.sizeof){
si.sizeof = sigSize(si.signature);
}
const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags));
Object.defineProperties(StructCtor,{ Object.defineProperties(StructCtor,{
debugFlags: debugFlags, debugFlags: debugFlags,
isA: rop((v)=>v instanceof StructCtor), isA: rop((v)=>v instanceof StructCtor),
memberKey: __memberKeyProp, memberKey: __memberKeyProp,
memberKeys: __structMemberKeys, memberKeys: __structMemberKeys,
methodInfoForKey: rop(function(mKey){ //methodInfoForKey: rop(function(mKey){/*???*/}),
}), structInfo: rop(si),
structInfo: rop(structInfo),
structName: rop(structName) structName: rop(structName)
}); });
StructCtor.prototype = new StructType(structName, structInfo, rop); StructCtor.prototype = new StructType(structName, si, rop);
Object.defineProperties(StructCtor.prototype,{ Object.defineProperties(StructCtor.prototype,{
debugFlags: debugFlags, debugFlags: debugFlags,
constructor: rop(StructCtor) constructor: rop(StructCtor)
/*if we assign StructCtor.prototype and don't do /*if we assign StructCtor.prototype and don't do
this then StructCtor!==instance.constructor!*/ this then StructCtor!==instance.constructor*/
}); });
Object.keys(structInfo.members).forEach(
(name)=>makeMemberWrapper(StructCtor, name, structInfo.members[name])
); let lastMember = false;
let offset = 0;
//console.warn(structName,"si =",si);
si.offset ??= 0;
Object.keys(si.members || {}).forEach((k)=>{
// Sanity checks of sizeof/offset info...
let m = ads(si.members[k]);
if(!m.members && !m.sizeof){
/* ^^^^ fixme: we need to recursively collect all sizeofs
before updating that. */
m.sizeof = sigSize(m.signature);
if(!m.sizeof){
toss(sPropName(structName,k), "is missing a sizeof property.",m);
}
}
if( undefined===m.offset ){
m.offset = offset;
}
si.members[k] = m;
if(!lastMember || lastMember.offset < m.offset) lastMember = m;
makeMemberWrapper.call(self, StructCtor, k, m)
offset += m.sizeof;
//console.warn("offset",sPropName(structName,k),offset);
});
if( !lastMember ) toss("No member property descriptions found.");
if( !si.sizeof ) si.sizeof = offset;
if(si.sizeof===1){
(si.signature === 'c' || si.signature === 'C') ||
toss("Unexpected sizeof==1 member",
sPropName(structName,k),
"with signature",si.signature);
}else{
// sizes and offsets of size-1 members may be odd values, but
// others may not.
if(0!==(si.sizeof%4)){
console.warn("Invalid struct member description",si);
toss(structName,"sizeof is not aligned. sizeof="+si.sizeof);
}
if(0!==(si.offset%4)){
console.warn("Invalid struct member description",si);
toss(structName,"offset is not aligned. offset="+si.offset);
}
}
if( si.sizeof < offset ){
console.warn("Suspect struct description:",si,"offset =",offset);
toss("Mismatch in the calculated vs. the provided sizeof/offset info.",
"Expected sizeof",offset,"but got",si.sizeof,"for",si);
}
return StructCtor; return StructCtor;
}/*StructBinderImpl*/;
const StructBinder = function StructBinder(structName, structInfo){
return (1==arguments.length)
? StructBinderImpl.call(StructBinder, structName)
: StructBinderImpl.call(StructBinder, structName, structInfo);
}; };
StructBinder.StructType = StructType; StructBinder.StructType = StructType;
StructBinder.config = config; StructBinder.config = config;
StructBinder.allocCString = __allocCString; StructBinder.allocCString = __allocCString;
StructBinder.adaptGet = __adaptGet;
StructBinder.adaptSet = __adaptSet;
StructBinder.adaptStruct = __adaptStruct;
if(!StructBinder.debugFlags){ if(!StructBinder.debugFlags){
StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags); StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags);
} }

View File

@@ -284,7 +284,7 @@ const BuildDefs oBuildDefs = {
" -DSQLITE_SPEEDTEST1_WASM" " -DSQLITE_SPEEDTEST1_WASM"
" $(SQLITE_OPT)" " $(SQLITE_OPT)"
" -USQLITE_WASM_BARE_BONES" " -USQLITE_WASM_BARE_BONES"
" -USQLITE_C -DSQLITE_C=$(sqlite3.c)" " -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c)"
" $(speedtest1.exit-runtime0)" " $(speedtest1.exit-runtime0)"
" $(speedtest1.c.in)" " $(speedtest1.c.in)"
" -lm", " -lm",
@@ -312,7 +312,7 @@ const BuildDefs oBuildDefs = {
" -DSQLITE_SPEEDTEST1_WASM" " -DSQLITE_SPEEDTEST1_WASM"
" $(SQLITE_OPT)" " $(SQLITE_OPT)"
" -USQLITE_WASM_BARE_BONES" " -USQLITE_WASM_BARE_BONES"
" -USQLITE_C -DSQLITE_C=$(sqlite3.c)" " -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c)"
" $(speedtest1.exit-runtime0)" " $(speedtest1.exit-runtime0)"
" $(speedtest1.c.in)" " $(speedtest1.c.in)"
" -lm", " -lm",

View File

@@ -1027,7 +1027,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
let ptr = k1.pointer; let ptr = k1.pointer;
k1.dispose(); k1.dispose();
T.assert(undefined === k1.pointer). T.assert(undefined === k1.pointer).
mustThrowMatching(()=>{k1.$pP=1}, /disposed instance/); mustThrowMatching(()=>{k1.$pP=1}, /disposed struct instance/);
}finally{ }finally{
k1.dispose(); k1.dispose();
k2.dispose(); k2.dispose();

View File

@@ -1,5 +1,5 @@
C Fix\sthe\s".www"\scommand\sof\sthe\sCLI\sso\sthat\sit\sworks\son\sunix\ssystems\swith\nnewer\sweb\sbrowsers\sthat\sdo\snot\sallow\saccess\sto\sfiles\sin\s/tmp. C Reworking\sof\sJS\sinternals\sto\ssupport\sbinding\sof\snested\sC\sstructs\s(like\ssqlite3_index_constraint\sand\sfriends)\sand\sallow\ssome\sof\sthe\sautomated\sJS/C\sconversions\sto\sbe\splugged\sin\sat\sthe\sstruct-binding\slevel,\ssimplifying\show\sstruct\smembers,\sin\sparticular\sfunction\spointers,\scan\sbe\sused\sfrom\sJS.
D 2025-11-10T01:46:06.786 D 2025-11-10T07:41:54.187
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -590,10 +590,10 @@ F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e
F ext/wasm/api/post-js-footer.js 5bd7170b5e8ce7b62102702bbcf47ef7b3b49cd56ed40c043fd990aa715b74ee F ext/wasm/api/post-js-footer.js 5bd7170b5e8ce7b62102702bbcf47ef7b3b49cd56ed40c043fd990aa715b74ee
F ext/wasm/api/post-js-header.js 79d078aec33d93b640a19c574b504d88bb2446432f38e2fbb3bb8e36da436e70 F ext/wasm/api/post-js-header.js 79d078aec33d93b640a19c574b504d88bb2446432f38e2fbb3bb8e36da436e70
F ext/wasm/api/pre-js.c-pp.js a876c6399dff29b6fe9e434036beb89889164cc872334e184291723ecc7cb072 F ext/wasm/api/pre-js.c-pp.js a876c6399dff29b6fe9e434036beb89889164cc872334e184291723ecc7cb072
F ext/wasm/api/sqlite3-api-cleanup.js a3d6b9e449aefbb8bba283c2ba9477e2333a0eeb94a7a26b5bf952736f65a6dd F ext/wasm/api/sqlite3-api-cleanup.js 79b54a566291e17c0c3e165c6c4969c48ec17cd297755180151af65ac616dfa0
F ext/wasm/api/sqlite3-api-glue.c-pp.js 79a54b54ca6324d28e31e19b56bbaebb7d2cc4b3079066e7e901333fa5047c53 F ext/wasm/api/sqlite3-api-glue.c-pp.js 79a54b54ca6324d28e31e19b56bbaebb7d2cc4b3079066e7e901333fa5047c53
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 31dbfd470c91ffd96d77399b749bab6b69e3ba9074188833f97ac13f087cf07b F ext/wasm/api/sqlite3-api-oo1.c-pp.js 31dbfd470c91ffd96d77399b749bab6b69e3ba9074188833f97ac13f087cf07b
F ext/wasm/api/sqlite3-api-prologue.js 307583ff39a978c897c4ef4ce53fe231dce5c73dc84785969c81c1ab5960a293 F ext/wasm/api/sqlite3-api-prologue.js b6b2fd1720c484e168705909862442b4524a1e61e16b4549a5725dd28c3cecc2
F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938 F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966 F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
@@ -608,7 +608,7 @@ F ext/wasm/c-pp-lite.c 8fa0148e73782a86274db688c4730e2962cd675af329490493adddaf3
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
F ext/wasm/common/whwasmutil.js 0d539324097fc83b953e9844267359ba0fd02286caa784ea2f597ced279ea640 F ext/wasm/common/whwasmutil.js b4fd73fac162406e67f276647ff8bae51881e15eebdf4b725aeb2b10ab1e056a
F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a
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
@@ -625,10 +625,10 @@ F ext/wasm/fiddle/fiddle.js 84fd75967e0af8b69d3dd849818342227d0f81d13db92e0dcbc6
F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9 F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9
F ext/wasm/index-dist.html db23748044e286773f2768eec287669501703b5d5f72755e8db73607dc54d290 F ext/wasm/index-dist.html db23748044e286773f2768eec287669501703b5d5f72755e8db73607dc54d290
F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b287da1 F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b287da1
F ext/wasm/jaccwabyt/jaccwabyt.js bbac67bc7a79dca34afe6215fd16b27768d84e22273507206f888c117e2ede7d F ext/wasm/jaccwabyt/jaccwabyt.js 1e734c624205cdf621f322972dfb0fc8013d573a5882f57492a6830e5ec23e17
F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f
F ext/wasm/mkdist.sh 64d53f469c823ed311f6696f69cec9093f745e467334b34f5ceabdf9de3c5b28 x F ext/wasm/mkdist.sh 64d53f469c823ed311f6696f69cec9093f745e467334b34f5ceabdf9de3c5b28 x
F ext/wasm/mkwasmbuilds.c 5e194df8763c8e5b2de070575a5f1bc7d7fb862f03c09d3cb9c56e0fa57b7e77 F ext/wasm/mkwasmbuilds.c 1b53c4d2a1350c19a96a8cdfbda6a39baea9d2142bfe0cbef0ccb0e898787f47
F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2 F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2
F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96 F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63 F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
@@ -644,7 +644,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a
F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb
F ext/wasm/tester1.c-pp.js 56a7889415b996f684765aff07d35ac8a31343201f887ac61d7dd14678d9f0f0 F ext/wasm/tester1.c-pp.js 2c255093205a0dac9dae7475030665c2c9d6dccc857de68ee7daf49aa82e6de8
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88 F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -2167,8 +2167,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P a1f9c977b83fab11c54710070dbedfaea47195050946db74075bdd3ade97a4c8 P 2f918c14bac28c567cc854b3d41dcdd59191a118bb5fdea9373945fe860161f5
R 433947701e1e8d1dbfecf3626f6f1737 R ec4a247d32a2beb55a1852fe7b3a7b07
U drh U stephan
Z bf0d9abc0630918a2bdb7897b1798077 Z 6d1537ab535678201b3302bce4da7f8d
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
2f918c14bac28c567cc854b3d41dcdd59191a118bb5fdea9373945fe860161f5 bb4fd5b789cebf2b224c29023fea3e620a86fb36730c36c0d85d9f35880bf643