]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Move the JS pieces of kvvfs into their own file to facilitate pending feature experim...
authorstephan <stephan@noemail.net>
Fri, 21 Nov 2025 10:49:32 +0000 (10:49 +0000)
committerstephan <stephan@noemail.net>
Fri, 21 Nov 2025 10:49:32 +0000 (10:49 +0000)
FossilOrigin-Name: 3c40614285449df259a3444e36f888cfb5e782ea58287914f97f496ea61e9e9f

ext/wasm/GNUmakefile
ext/wasm/api/sqlite3-api-glue.c-pp.js
ext/wasm/api/sqlite3-api-oo1.c-pp.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/jaccwabyt/jaccwabyt.js
ext/wasm/tester1.c-pp.js
manifest
manifest.uuid

index 481c17d8997bc167a2b23311ff84e631268d2685..82c3913b7b29e9824c82dd831139dcfcd9000477 100644 (file)
@@ -910,6 +910,7 @@ sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js
 ifeq (0,$(wasm-bare-bones))
   sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js
 endif
+sqlite3-api.jses += $(dir.api)/sqlite3-vfs-kvvfs.c-pp.js
 sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
 sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
 
index 1b0f8a0807b451a43d5cde9065c2f29271c68d1f..1fc83377757c65cfa1c01cefc742b46867c0005b 100644 (file)
@@ -988,8 +988,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     const notThese = Object.assign(Object.create(null),{
       // For each struct to NOT register, map its name to true:
       WasmTestStruct: true,
-      /* We unregister the kvvfs VFS from Worker threads below. */
-      sqlite3_kvvfs_methods: !util.isUIThread(),
       /* sqlite3_index_info and friends require int64: */
       sqlite3_index_info: !wasm.bigIntEnabled,
       sqlite3_index_constraint: !wasm.bigIntEnabled,
@@ -1783,106 +1781,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     };
   }/* auto-extension */
 
-  const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
-  if( pKvvfs ){/* kvvfs-specific glue */
-    if(util.isUIThread()){
-      const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
-        wasm.exports.sqlite3__wasm_kvvfs_methods()
-      );
-      delete capi.sqlite3_kvvfs_methods;
-
-      const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack,
-            pstack = wasm.pstack;
-
-      const kvvfsStorage = (zClass)=>
-            ((115/*=='s'*/===wasm.peek(zClass))
-             ? sessionStorage : localStorage);
-
-      /**
-         Implementations for members of the object referred to by
-         sqlite3__wasm_kvvfs_methods(). We swap out the native
-         implementations with these, which use localStorage or
-         sessionStorage for their backing store.
-      */
-      const kvvfsImpls = {
-        xRead: (zClass, zKey, zBuf, nBuf)=>{
-          const stack = pstack.pointer,
-                astack = wasm.scopedAllocPush();
-          try {
-            const zXKey = kvvfsMakeKey(zClass,zKey);
-            if(!zXKey) return -3/*OOM*/;
-            const jKey = wasm.cstrToJs(zXKey);
-            const jV = kvvfsStorage(zClass).getItem(jKey);
-            if(!jV) return -1;
-            const nV = jV.length /* We are relying 100% on v being
-                                    ASCII so that jV.length is equal
-                                    to the C-string's byte length. */;
-            if(nBuf<=0) return nV;
-            else if(1===nBuf){
-              wasm.poke(zBuf, 0);
-              return nV;
-            }
-            const zV = wasm.scopedAllocCString(jV);
-            if(nBuf > nV + 1) nBuf = nV + 1;
-            wasm.heap8u().copyWithin(
-              Number(zBuf), Number(zV), wasm.ptr.addn(zV, nBuf,- 1)
-            );
-            wasm.poke(wasm.ptr.add(zBuf, nBuf, -1), 0);
-            return nBuf - 1;
-          }catch(e){
-            sqlite3.config.error("kvstorageRead()",e);
-            return -2;
-          }finally{
-            pstack.restore(stack);
-            wasm.scopedAllocPop(astack);
-          }
-        },
-        xWrite: (zClass, zKey, zData)=>{
-          const stack = pstack.pointer;
-          try {
-            const zXKey = kvvfsMakeKey(zClass,zKey);
-            if(!zXKey) return 1/*OOM*/;
-            const jKey = wasm.cstrToJs(zXKey);
-            kvvfsStorage(zClass).setItem(jKey, wasm.cstrToJs(zData));
-            return 0;
-          }catch(e){
-            sqlite3.config.error("kvstorageWrite()",e);
-            return capi.SQLITE_IOERR;
-          }finally{
-            pstack.restore(stack);
-          }
-        },
-        xDelete: (zClass, zKey)=>{
-          const stack = pstack.pointer;
-          try {
-            const zXKey = kvvfsMakeKey(zClass,zKey);
-            if(!zXKey) return 1/*OOM*/;
-            kvvfsStorage(zClass).removeItem(wasm.cstrToJs(zXKey));
-            return 0;
-          }catch(e){
-            sqlite3.config.error("kvstorageDelete()",e);
-            return capi.SQLITE_IOERR;
-          }finally{
-            pstack.restore(stack);
-          }
-        }
-      }/*kvvfsImpls*/;
-      for(const k of Object.keys(kvvfsImpls)){
-        kvvfsMethods[kvvfsMethods.memberKey(k)] =
-          wasm.installFunction(
-            kvvfsMethods.memberSignature(k),
-            kvvfsImpls[k]
-          );
-      }
-    }else{
-      /* Worker thread: unregister kvvfs to avoid it being used
-         for anything other than local/sessionStorage. It "can"
-         be used that way but it's not really intended to be. */
-      capi.sqlite3_vfs_unregister(pKvvfs);
-      delete capi.sqlite3_kvvfs_methods;
-    }
-  }/*pKvvfs*/
-
   /* Warn if client-level code makes use of FuncPtrAdapter. */
   wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
 
index 8c2f35e67789a8ba99008a6e8bffb6a16f6b6afe..f28e376f1a364461f3dc6c766f69672d7a37754c 100644 (file)
@@ -239,7 +239,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       */
       ctor._name2vfs = Object.create(null);
       const isWorkerThread = ('function'===typeof importScripts/*===running in worker thread*/)
-            ? (n)=>toss3("The VFS for",n,"is only available in the main window thread.")
+            ? (n)=>toss3("VFS",n,"is only available in the main window thread.")
             : false;
       ctor._name2vfs[':localStorage:'] = {
         vfs: 'kvvfs', filename: isWorkerThread || (()=>'local')
@@ -2295,55 +2295,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     Stmt
   }/*oo1 object*/;
 
-  if(util.isUIThread()){
-    /**
-       Functionally equivalent to DB(storageName,'c','kvvfs') except
-       that it throws if the given storage name is not one of 'local'
-       or 'session'.
-
-       As of version 3.46, the argument may optionally be an options
-       object in the form:
-
-       {
-         filename: 'session'|'local',
-         ... etc. (all options supported by the DB ctor)
-       }
-
-       noting that the 'vfs' option supported by main DB
-       constructor is ignored here: the vfs is always 'kvvfs'.
-    */
-    sqlite3.oo1.JsStorageDb = function(storageName='session'){
-      const opt = dbCtorHelper.normalizeArgs(...arguments);
-      storageName = opt.filename;
-      if('session'!==storageName && 'local'!==storageName){
-        toss3("JsStorageDb db name must be one of 'session' or 'local'.");
-      }
-      opt.vfs = 'kvvfs';
-      dbCtorHelper.call(this, opt);
-    };
-    const jdb = sqlite3.oo1.JsStorageDb;
-    jdb.prototype = Object.create(DB.prototype);
-    /** Equivalent to sqlite3_js_kvvfs_clear(). */
-    jdb.clearStorage = capi.sqlite3_js_kvvfs_clear;
-    /**
-       Clears this database instance's storage or throws if this
-       instance has been closed. Returns the number of
-       database blocks which were cleaned up.
-    */
-    jdb.prototype.clearStorage = function(){
-      return jdb.clearStorage(affirmDbOpen(this).filename);
-    };
-    /** Equivalent to sqlite3_js_kvvfs_size(). */
-    jdb.storageSize = capi.sqlite3_js_kvvfs_size;
-    /**
-       Returns the _approximate_ number of bytes this database takes
-       up in its storage or throws if this instance has been closed.
-    */
-    jdb.prototype.storageSize = function(){
-      return jdb.storageSize(affirmDbOpen(this).filename);
-    };
-  }/*main-window-only bits*/
-
 });
 //#else
 /* Built with the omit-oo1 flag. */
index d63bd14a8352cc5912605b318299c745868e7da2..065ea532e660222d506a0dad36eae540a57106f9 100644 (file)
@@ -1604,86 +1604,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
     return x===v ? undefined : x;
   }
 
-  if( util.isUIThread() ){
-    /* Features specific to the main window thread... */
-
-    /**
-       Internal helper for sqlite3_js_kvvfs_clear() and friends.
-       Its argument should be one of ('local','session',"").
-    */
-    const __kvvfsInfo = function(which){
-      const rc = Object.create(null);
-      rc.prefix = 'kvvfs-'+which;
-      rc.stores = [];
-      if('session'===which || ""===which) rc.stores.push(globalThis.sessionStorage);
-      if('local'===which || ""===which) rc.stores.push(globalThis.localStorage);
-      return rc;
-    };
-
-    /**
-       Clears all storage used by the kvvfs DB backend, deleting any
-       DB(s) stored there. Its argument must be either 'session',
-       'local', or "". In the first two cases, only sessionStorage
-       resp. localStorage is cleared. If it's an empty string (the
-       default) then both are cleared. Only storage keys which match
-       the pattern used by kvvfs are cleared: any other client-side
-       data are retained.
-
-       This function is only available in the main window thread.
-
-       Returns the number of entries cleared.
-    */
-    capi.sqlite3_js_kvvfs_clear = function(which=""){
-      let rc = 0;
-      const kvinfo = __kvvfsInfo(which);
-      kvinfo.stores.forEach((s)=>{
-        const toRm = [] /* keys to remove */;
-        let i;
-        for( i = 0; i < s.length; ++i ){
-          const k = s.key(i);
-          if(k.startsWith(kvinfo.prefix)) toRm.push(k);
-        }
-        toRm.forEach((kk)=>s.removeItem(kk));
-        rc += toRm.length;
-      });
-      return rc;
-    };
-
-    /**
-       This routine guesses the approximate amount of
-       window.localStorage and/or window.sessionStorage in use by the
-       kvvfs database backend. Its argument must be one of
-       ('session', 'local', ""). In the first two cases, only
-       sessionStorage resp. localStorage is counted. If it's an empty
-       string (the default) then both are counted. Only storage keys
-       which match the pattern used by kvvfs are counted. The returned
-       value is the "length" value of every matching key and value,
-       noting that JavaScript stores each character in 2 bytes.
-
-       Note that the returned size is not authoritative from the
-       perspective of how much data can fit into localStorage and
-       sessionStorage, as the precise algorithms for determining
-       those limits are unspecified and may include per-entry
-       overhead invisible to clients.
-    */
-    capi.sqlite3_js_kvvfs_size = function(which=""){
-      let sz = 0;
-      const kvinfo = __kvvfsInfo(which);
-      kvinfo.stores.forEach((s)=>{
-        let i;
-        for(i = 0; i < s.length; ++i){
-          const k = s.key(i);
-          if(k.startsWith(kvinfo.prefix)){
-            sz += k.length;
-            sz += s.getItem(k).length;
-          }
-        }
-      });
-      return sz * 2 /* because JS uses 2-byte char encoding */;
-    };
-
-  }/* main-window-only bits */
-
   /**
      Wraps all known variants of the C-side variadic
      sqlite3_db_config().
index 20e39b0a9dc59c839a4cd85a3126795b9278dcdc..894c390ca783f9358621286db2c041a751a86afa 100644 (file)
@@ -30,19 +30,32 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
       all args with a space between each. */
   const toss = (...args)=>{throw new Error(args.join(' '))};
 
-  if(!(config.heap instanceof WebAssembly.Memory)
-     && !(config.heap instanceof Function)){
-    toss("config.heap must be WebAssembly.Memory instance or a function which returns one.");
+  {
+    let h = config.heap;
+    if( h instanceof WebAssembly.Memory ){
+      h = function(){return new Uint8Array(this.buffer)}.bind(h);
+    }else if( !(h instanceof Function) ){
+      //console.warn("The bothersome StructBinderFactory config:",config);
+      toss("config.heap must be WebAssembly.Memory instance or",
+           "a function which returns one.");
+    }
+    config.heap = h;
   }
   ['alloc','dealloc'].forEach(function(k){
     (config[k] instanceof Function) ||
       toss("Config option '"+k+"' must be a function.");
   });
-  const __heap = config.heap;
   const SBF = StructBinderFactory;
-  const heap = __heap ? __heap : ()=>new Uint8Array(__heap.buffer),
+  const heap = config.heap,
         alloc = config.alloc,
         dealloc = config.dealloc,
+        realloc = (config.realloc || function(){
+          toss("This StructBinderFactory was configured without realloc()");
+          /* We can't know the original memory's size from here unless
+             we internally proxy alloc()/dealloc() to track all
+             pointers (not going to happen), so we can't fall back to
+             doing alloc()/copy/dealloc(). */
+        }),
         log = config.log || console.debug.bind(console),
         memberPrefix = (config.memberPrefix || ""),
         memberSuffix = (config.memberSuffix || ""),
@@ -51,10 +64,10 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
         bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array;
 
   //console.warn("config",config);
-  let ptr = alloc(1);
+  let ptr;
   const ptrIR = config.pointerIR
-    || config.ptrIR/*deprecated*/
-    || ('bigint'===typeof ptr ? 'i64' : 'i32');
+        || config.ptrIR/*deprecated*/
+        || ('bigint'===typeof (ptr = alloc(1)) ? 'i64' : 'i32');
   /* Undocumented (on purpose) config options: */
   const ptrSize = config.ptrSize/*deprecated*/
         || ('i32'===ptrIR ? 4 : 8);
@@ -91,6 +104,10 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     return rc;
   };
 
+  const __ptrAddSelf = function(...args){
+    return __ptrAdd(this.pointer,...args);
+  };
+
   if(!SBF.debugFlags){
     SBF.__makeDebugFlags = function(deriveFrom=null){
       /* This is disgustingly overengineered. :/ */
@@ -120,12 +137,12 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     SBF.debugFlags = SBF.__makeDebugFlags();
   }/*static init*/
 
-  const isLittleEndian = (function() {
+  const isLittleEndian = true || (function() {
     const buffer = new ArrayBuffer(2);
     new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
     // Int16Array uses the platform's endianness.
     return new Int16Array(buffer)[0] === 256;
-  })();
+  })() /* WASM is, by definition, Little Endian */;
 
   /**
      Some terms used in the internal docs:
@@ -146,8 +163,9 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
   const isAutoPtrSig = (s)=>'P'===s /*EXPERIMENTAL*/;
   /** Returns p if SIG s is a function SIG, else returns s[0]. */
   const sigLetter = (s)=>s ? (isFuncSig(s) ? 'p' : s[0]) : undefined;
-  /** Returns the WASM IR form of the Emscripten-conventional letter
-      at SIG s[0]. Throws for an unknown SIG. */
+
+  /** Returns the WASM IR form of the letter at SIG s[0]. Throws for
+      an unknown SIG. */
   const sigIR = function(s){
     switch(sigLetter(s)){
         case 'c': case 'C': return 'i8';
@@ -159,8 +177,9 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     }
     toss("Unhandled signature IR:",s);
   };
-  /** Returns the WASM sizeof of the Emscripten-conventional letter
-      at SIG s[0]. Throws for an unknown SIG. */
+
+  /** Returns the WASM sizeof of the letter at SIG s[0]. Throws for an
+      unknown SIG. */
   const sigSize = function(s){
     switch(sigLetter(s)){
         case 'c': case 'C': return 1;
@@ -175,6 +194,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
 
   const affirmBigIntArray = BigInt64Array
         ? ()=>true : ()=>toss('BigInt64Array is not available.');
+
   /** Returns the name of a DataView getter method corresponding
       to the given SIG. */
   const sigDVGetter = function(s){
@@ -195,6 +215,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     }
     toss("Unhandled DataView getter for signature:",s);
   };
+
   /** Returns the name of a DataView setter method corresponding
       to the given SIG. */
   const sigDVSetter = function(s){
@@ -246,26 +267,44 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
   /**
      In order to completely hide StructBinder-bound struct pointers
      from JS code, we store them in a scope-local WeakMap which maps
-     the struct-bound objects to their WASM pointers. The pointers are
-     accessible via boundObject.pointer, which is gated behind a
-     property interceptor, but are not exposed anywhere else in the
-     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.
+     the struct-bound objects to an object with their metadata:
+
+     {
+       .p = the native pointer,
+       .o = self (for an eventual reverse-mapping),
+       .xb = extra bytes allocated for p,
+       .zod = zeroOnDispose,
+       .ownsPointer = true if this object owns p
+     }
+
+     The .p data are accessible via obj.pointer, which is gated behind
+     a property interceptor, but are not exposed anywhere else in the
+     public API.
   */
-  const __instancePointerMap = new WeakMap();
-
-  const getInstancePtr = (obj)=>__instancePointerMap.get(obj);
+  const getInstanceHandle = function f(obj, create=true){
+    let ii = f.map.get(obj);
+    if( !ii && create ){
+      f.map.set(obj, (ii=f.create(obj)));
+    }
+    return ii;
+  };
+  getInstanceHandle.map = new WeakMap;
+  getInstanceHandle.create = (forObj)=>{
+    return Object.assign(Object.create(null),{
+      o: forObj,
+      p: undefined/*native ptr*/,
+      ownsPointer: false,
+      zod: false/*zeroOnDispose*/,
+      xb: 0/*extraBytes*/
+    });
+  };
 
-  /** Property name for the pointer-is-external marker. */
-  const xPtrPropName = Symbol('(pointer-is-external)');
+  /**
+     Remove the getInstanceHandle() mapping for obj.
+  */
+  const rmInstanceHandle = (obj)=>getInstanceHandle.map.delete(obj)
+    /* If/when we have a reverse map of ptr-to-objects, we need to
+       clean that here. */;
 
   const __isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0);
   const __isPtr64 = (ptr)=>(
@@ -278,85 +317,127 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
   */
   const __isPtr = (4===ptrSize) ? __isPtr32 : __isPtr64;
 
-  /** Frees the obj.pointer memory and clears the pointer
-      property. */
+  const __isNonNullPtr = (v)=>__isPtr(v) && (v>0);
+
+  /** Frees the obj.pointer memory (a.k.a. m), handles obj.ondispose,
+      and unmaps obj from its native resources. */
   const __freeStruct = function(ctor, obj, m){
-    if(!m) m = getInstancePtr(obj);
-    if(m) {
-      __instancePointerMap.delete(obj);
-      if(Array.isArray(obj.ondispose)){
-        let x;
-        while((x = obj.ondispose.shift())){
-          try{
-            if(x instanceof Function) x.call(obj);
-            else if(x instanceof StructType) x.dispose();
-            else if(__isPtr(x)) dealloc(x);
-            // else ignore. Strings are permitted to annotate entries
-            // to assist in debugging.
-          }catch(e){
-            console.warn("ondispose() for",ctor.structName,'@',
-                         m,'threw. NOT propagating it.',e);
-          }
-        }
-      }else if(obj.ondispose instanceof Function){
-        try{obj.ondispose()}
-        catch(e){
-          /*do not rethrow: destructors must not throw*/
+    const ii = getInstanceHandle(obj, false);
+    if( !ii ) return;
+    rmInstanceHandle(obj);
+    if( !m && !(m = ii.p) ){
+      console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo");
+      return;
+    }
+    if(Array.isArray(obj.ondispose)){
+      let x;
+      while((x = obj.ondispose.pop())){
+        try{
+          if(x instanceof Function) x.call(obj);
+          else if(x instanceof StructType) x.dispose();
+          else if(__isPtr(x)) dealloc(x);
+          // else ignore. Strings are permitted to annotate entries
+          // to assist in debugging.
+        }catch(e){
           console.warn("ondispose() for",ctor.structName,'@',
                        m,'threw. NOT propagating it.',e);
         }
       }
-      delete obj.ondispose;
-      if(ctor.debugFlags.__flags.dealloc){
-        log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
-            ctor.structName,"instance:",
-            ctor.structInfo.sizeof,"bytes @"+m);
+    }else if(obj.ondispose instanceof Function){
+      try{obj.ondispose()}
+      catch(e){
+        /*do not rethrow: destructors must not throw*/
+        console.warn("ondispose() for",ctor.structName,'@',
+                     m,'threw. NOT propagating it.',e);
       }
-      if(!obj[xPtrPropName]){
-        if( ctor.structInfo.zeroOnFree ){
-          heap().fill(0, Number(m), Number(m)+ctor.structInfo.sizeof);
-        }
-        dealloc(m);
+    }
+    delete obj.ondispose;
+    if(ctor.debugFlags.__flags.dealloc){
+      log("debug.dealloc:",(ii.ownsPointer?"":"EXTERNAL"),
+          ctor.structName,"instance:",
+          ctor.structInfo.sizeof,"bytes @"+m);
+    }
+    if(ii.ownsPointer){
+      if( ii.zod || ctor.structInfo.zeroOnDispose ){
+        heap().fill(0, Number(m),
+                    Number(m) + ctor.structInfo.sizeof + ii.xb);
       }
+      dealloc(m);
     }
   };
 
+  /** Returns a skeleton for a read-only, non-iterable property
+   * descriptor. */
+  const rop0 = ()=>{return {configurable: false, writable: false,
+                            iterable: false}};
+
   /** Returns a skeleton for a read-only property accessor wrapping
       value v. */
-  const rop = (v)=>{return {configurable: false, writable: false,
-                            iterable: false, value: v}};
+  const rop = (v)=>{return {...rop0(), value: v}};
 
   /** Allocates obj's memory buffer based on the size defined in
       ctor.structInfo.sizeof. */
-  const __allocStruct = function(ctor, obj, m){
-    let fill = !m;
-    if(m) Object.defineProperty(obj, xPtrPropName, rop(m));
-    else{
-      m = alloc(ctor.structInfo.sizeof);
-      if(!m) toss("Allocation of",ctor.structName,"structure failed.");
+  const __allocStruct = function f(ctor, obj, xm){
+    let opt;
+    const checkPtr = (ptr)=>{
+      __isNonNullPtr(ptr) ||
+        toss("Invalid pointer value",arguments[0],"for",ctor.structName,"constructor.");
+    };
+    if( arguments.length>=3 ){
+      if( xm && ('object'===typeof xm) ){
+        opt = xm;
+        xm = opt?.wrap;
+      }else{
+        checkPtr(xm);
+        opt = {wrap: xm};
+      }
+    }else{
+      opt = {}
+    }
+
+    const fill = !xm /* true if we need to zero the memory */;
+    let nAlloc = 0;
+    let ownsPointer = false;
+    if(xm){
+      /* Externally-allocated memory. */
+      checkPtr(xm);
+      ownsPointer = !!opt?.takeOwnership;
+    }else{
+      const nX = opt?.extraBytes ?? 0;
+      if( nX<0 || (nX!==(nX|0)) ){
+        toss("Invalid extraBytes value:",opt?.extraBytes);
+      }
+      nAlloc = ctor.structInfo.sizeof + nX;
+      xm = alloc(nAlloc)
+        || toss("Allocation of",ctor.structName,"structure failed.");
+      ownsPointer = true;
     }
     try {
-      if(ctor.debugFlags.__flags.alloc){
+      if( opt?.debugFlags ){
+        /* specifically undocumented */
+        obj.debugFlags(opt.debugFlags);
+      }
+      if(ctor./*prototype.???*/debugFlags.__flags.alloc){
         log("debug.alloc:",(fill?"":"EXTERNAL"),
             ctor.structName,"instance:",
-            ctor.structInfo.sizeof,"bytes @"+m);
+            ctor.structInfo.sizeof,"bytes @"+xm);
       }
       if(fill){
-        heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof);
+        heap().fill(0, Number(xm), Number(xm) + nAlloc);
+      }
+      const ii = getInstanceHandle(obj);
+      ii.p = xm;
+      ii.ownsPointer = ownsPointer;
+      ii.xb = nAlloc ? (nAlloc-ctor.structInfo.sizeof) : 0;
+      ii.zod = !!opt?.zeroOnDispose;
+      if( opt?.ondispose && opt.ondispose!==xm ){
+        obj.addOnDispose( opt.ondispose );
       }
-      __instancePointerMap.set(obj, m);
     }catch(e){
-      __freeStruct(ctor, obj, m);
+      __freeStruct(ctor, obj, xm);
       throw e;
     }
   };
-  /** Gets installed as the memoryDump() method of all structs. */
-  const __memoryDump = function(){
-    const p = this.pointer;
-    return p
-      ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof))
-      : null;
-  };
 
   /** True if sig looks like an emscripten/jaccwabyt
       type signature, else false. */
@@ -390,25 +471,33 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
 
   // 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);
   };
 
+  const __affirmNotASig = function(ctx,key){
+    looksLikeASig(key) &&
+      toss(ctx,"(",key,") collides with a data type signature.");
+  };
+
   // StructBinder::adaptSet()
   const __adaptSet = function(key, ...args){
-    if( looksLikeASig(key) ){
-      toss("Setter adaptor's name (",key,") collides with a data type signature.");
-    }
+    __affirmNotASig('Setter adaptor',key);
     return __adaptor(this, 1, key, ...args);
   };
 
   // StructBinder::adaptStruct()
   const __adaptStruct = function(key, ...args){
+    __affirmNotASig('Struct adaptor',key);
     return __adaptor(this, 2, key, ...args);
   };
 
+  /**
+     An internal counterpart of __adaptStruct().  If key is-a string,
+     uses __adaptor(who) to fetch the struct-adaptor entry for key,
+     else key is assumed to be a struct description object. If it
+     resolves to an object, that's returned, else an exception is
+     thrown.
+  */
   const __adaptStruct2 = function(who,key){
     const si = ('string'===typeof key)
           ? __adaptor(who, 2, key) : key;
@@ -455,15 +544,6 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     return emscriptenFormat ? f._(m.signature) : m.signature;
   };
 
-  const __ptrPropDescriptor = {
-    configurable: false, enumerable: false,
-    get: function(){return getInstancePtr(this)},
-    set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
-    // Reminder: leaving `set` undefined makes assignments
-    // to the property _silently_ do nothing. Current unit tests
-    // rely on it throwing, though.
-  };
-
   /** Impl of X.memberKeys() for StructType and struct ctors. */
   const __structMemberKeys = rop(function(){
     const a = [];
@@ -597,13 +677,13 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
      Prototype for all StructFactory instances (the constructors
      returned from StructBinder).
   */
-  const StructType = function ctor(structName, structInfo){
-    if(arguments[2]!==rop){
+  const StructType = function StructType(structName, structInfo){
+    if(arguments[2]!==rop/*internal sentinel value*/){
       toss("Do not call the StructType constructor",
            "from client-level code.");
     }
     Object.defineProperties(this,{
-      //isA: rop((v)=>v instanceof ctor),
+      //isA: rop((v)=>v instanceof StructType),
       structName: rop(structName),
       structInfo: rop(structInfo)
     });
@@ -629,8 +709,31 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     memberSignature: rop(function(memberName, emscriptenFormat=false){
       return __memberSignature(this, memberName, emscriptenFormat);
     }),
-    memoryDump: rop(__memoryDump),
-    pointer: __ptrPropDescriptor,
+    memoryDump: rop(function(){
+      const p = this.pointer;
+      return p
+        ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof))
+        : null;
+    }),
+    extraBytes: {
+      configurable: false, enumerable: false,
+      get: function(){return getInstanceHandle(this, false)?.xb ?? 0;}
+    },
+    zeroOnDispose: {
+      configurable: false, enumerable: false,
+      get: function(){
+        return getInstanceHandle(this, false)?.zod
+          ?? !!this.structInfo.zeroOnDispose;
+      }
+    },
+    pointer: {
+      configurable: false, enumerable: false,
+      get: function(){return getInstanceHandle(this, false)?.p},
+      set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
+      // Reminder: leaving `set` undefined makes assignments
+      // to the property _silently_ do nothing. Current unit tests
+      // rely on it throwing, though.
+    },
     setMemberCString: rop(function(memberName, str){
       return __setMemberCString(this, memberName, str);
     })
@@ -649,8 +752,12 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
   Object.defineProperties(StructType, {
     allocCString: rop(__allocCString),
     isA: rop((v)=>v instanceof StructType),
-    hasExternalPointer: rop((v)=>(v instanceof StructType) && !!v[xPtrPropName]),
+    hasExternalPointer: rop((v)=>{
+      const ii = getInstanceHandle(v, false);
+      return !!(ii?.p && !ii?.ownsPointer);
+    }),
     memberKey: __memberKeyProp
+    //ptrAdd = rop(__ptrAdd) no b/c one might think that it adds based on this.pointer.
   });
 
   /**
@@ -693,7 +800,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
      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
+     The si.zeroOnDispose flag has no effect on embedded structs because
      they wrap "external" memory, so do not own it, and are thus never
      freed, as such.
   */
@@ -725,7 +832,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
       enumerable: false,
       set: __propThrowOnSet(ctor/*not si.constructor*/.structName, key),
       get: function(){
-        const dbg = si.constructor.prototype.debugFlags.__flags;
+        const dbg = this.debugFlags.__flags;
         const p = this.pointer;
         const k = p+'.'+key;
         let s = __innerStructs.get(k);
@@ -758,16 +865,16 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
       return makeMemberStructWrapper.call(this, ctor, name, si);
     }
 
-    if(!f._){
+    if(!f.cache){
       /* Cache all available getters/setters/set-wrappers for
          direct reuse in each accessor function. */
-      f._ = {getters: {}, setters: {}, sw:{}};
+      f.cache = {getters: {}, setters: {}, sw:{}};
       const a = ['i','c','C','p','P','s','f','d','v()'];
       if(bigIntEnabled) a.push('j');
       a.forEach(function(v){
-        f._.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
-        f._.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
-        f._.sw[v] = sigDVSetWrapper(v)  /* BigInt or Number ctor to wrap around values
+        f.cache.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
+        f.cache.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
+        f.cache.sw[v] = sigDVSetWrapper(v)  /* BigInt or Number ctor to wrap around values
                                            for conversion */;
       });
       f.sigCheck = function(obj, name, key,sig){
@@ -784,8 +891,8 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     si.key = key;
     si.name = name;
     const sigGlyph = sigLetter(si.signature);
-    const xPropName = sPropName(ctor.prototype.structName,key);
-    const dbg = ctor.prototype.debugFlags.__flags;
+    const xPropName = sPropName(ctor.structName,key);
+    const dbg = ctor.debugFlags.__flags;
     /*
       TODO?: set prototype of si to an object which can set/fetch
       its preferred representation, e.g. conversion to string or mapped
@@ -797,14 +904,20 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     prop.configurable = false;
     prop.enumerable = false;
     prop.get = function(){
+      /**
+         This getter proxy reads its value from the appropriate pointer
+         address in the heap. It knows where and how much to read based on
+         this.pointer, si.offset, and si.sizeof.
+      */
       if(dbg.getter){
-        log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
+        log("debug.getter:",f.cache.getters[sigGlyph],"for", sigIR(sigGlyph),
             xPropName,'@', this.pointer,'+',si.offset,'sz',si.sizeof);
       }
       let rc = (
         new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)
-      )[f._.getters[sigGlyph]](0, isLittleEndian);
-      if(getterProxy) rc = getterProxy.apply(this,[rc,key]);
+      )[f.cache.getters[sigGlyph]](0, isLittleEndian);
+
+      if(getterProxy) rc = getterProxy.apply(this,[key,rc]);
       if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
       return rc;
     };
@@ -813,28 +926,33 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     }else{
       const setterProxy = memberSetterProxy(si);
       prop.set = function(v){
+        /**
+           The converse of prop.get(), this encodes v into the appropriate
+           spot in the WASM heap.
+        */
         if(dbg.setter){
-          log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
+          log("debug.setter:",f.cache.setters[sigGlyph],"for", sigIR(sigGlyph),
               xPropName,'@', this.pointer,'+',si.offset,'sz',si.sizeof, v);
         }
         if(!this.pointer){
-          toss("Cannot set native property on a disposed struct instance.");
+          toss("Cannot set native property on a disposed",
+               this.structSame,"instance.");
         }
-        if( setterProxy ) v = setterProxy.apply(this,[v]);
+        if( setterProxy ) v = setterProxy.apply(this,[key,v]);
         if( null===v || undefined===v ) v = __NullPtr;
-        else while( isPtrSig(si.signature) && !__isPtr(v) ){
+        else if( 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: store its pointer value
             v = v.pointer || __NullPtr;
             if(dbg.setter) log("debug.setter:",xPropName,"resolved to",v);
-            break;
+          }else{
+            toss("Invalid value for pointer-type",xPropName+'.');
           }
-          toss("Invalid value for pointer-type",xPropName+'.');
         }
         (
           new DataView(heap().buffer, Number(this.pointer) + si.offset,
                        si.sizeof)
-        )[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
+        )[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian);
       };
     }
     Object.defineProperty(ctor.prototype, key, prop);
@@ -863,25 +981,59 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
        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.
+
+       Uses:
+
+       // heap-allocated:
+       const x = new StructCtor;
+       // externally-managed memory:
+       const y = new StructCtor( aPtrToACompatibleCStruct );
+
+       or, more recently:
+
+       const z = new StructCtor({
+         extraBytes: [int=0] extra bytes to allocate after the struct
+
+         wrap: [aPtrToACompatibleCStruct=undefined]. If provided, this
+         instance waps, but does not (by default) own the memory, else
+         a new instance is allocated from the WASM heap.
+
+         ownsPointer: true if this object takes over ownership of
+         wrap.
+
+         zeroOnDispose: [bool=StructCtor.structInfo.zeroOnDispose]
+
+         autoCalcSizeOffset: [bool=false] Automatically calculate
+         sizeof an offset. This is fine for pure-JS structs (which
+         probably aren't useful beyond testing of Jaccwabyt) but it's
+         dangerous to use with actual WASM objects because we cannot
+         be guaranteed to have the same memory layout as an ostensibly
+         matching C struct. This applies recursively to all children
+         of the struct description.
+
+         // TODO? Per-instance overrides of the struct-level flags?
+
+         get: (k,v)=>v,
+         set: (k,v)=>v,
+         adaptGet: string,
+         adaptSet: string
+
+         // That wouldn't fit really well right now, apparently.
+       });
+
     */
-    const StructCtor = function StructCtor(externalMemory){
-      externalMemory = __asPtrType(externalMemory);
-      //console.warn("externalMemory",externalMemory,arguments[0]);
+    const StructCtor = function StructCtor(arg){
+      //console.warn("opt",opt,arguments[0]);
       if(!(this instanceof StructCtor)){
         toss("The",structName,"constructor may only be called via 'new'.");
-      }else if(arguments.length){
-        if( !__isPtr(externalMemory) ){
-          toss("Invalid pointer value",arguments[0],"for",structName,"constructor.");
-        }
-        __allocStruct(StructCtor, this, externalMemory);
-      }else{
-        __allocStruct(StructCtor, this);
       }
+      __allocStruct(StructCtor, this, ...arguments);
     };
     const self = this;
     /**
       "Convert" struct description x to a struct description, if
-      needed.
+      needed. This expands adaptStruct() mappings and transforms
+      {memberName:signatureString} signature syntax to object form.
     */
     const ads = (x)=>{
       //console.warn("looksLikeASig(",x,") =",looksLikeASig(x));
@@ -902,6 +1054,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
     structName ??= opt.structName;
     if( !structName ) toss("One of 'name' or 'structName' are required.");
     if( si.adapt ){
+      /* Install adaptGet(), adaptSet(), and adaptStruct() proxies. */
       Object.keys(si.adapt.struct||{}).forEach((k)=>{
         __adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]);
       });
@@ -924,21 +1077,32 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
       memberKeys: __structMemberKeys,
       //methodInfoForKey: rop(function(mKey){/*???*/}),
       structInfo: rop(si),
-      structName: rop(structName)
+      structName: rop(structName),
+      ptrAdd: rop(__ptrAdd)
     });
     StructCtor.prototype = new StructType(structName, si, rop);
     Object.defineProperties(StructCtor.prototype,{
       debugFlags: debugFlags,
       constructor: rop(StructCtor)
       /*if we assign StructCtor.prototype and don't do
-        this then StructCtor!==instance.constructor*/
+        this then StructCtor!==instance.constructor*/,
+      ptrAdd: rop(__ptrAddSelf)
     });
-
-
     let lastMember = false;
     let offset = 0;
+    const autoCalc = !!si.autoCalcSizeOffset;
     //console.warn(structName,"si =",si);
-    si.offset ??= 0;
+    if( !autoCalc ){
+      if( !si.sizeof ){
+        toss(structName,"description is missing its sizeof property.");
+      }
+      /*if( undefined===si.offset ){
+        toss(structName,"description is missing its offset property.");
+      }*/
+      si.offset ??= 0;
+    }else{
+      si.offset ??= 0;
+    }
     Object.keys(si.members || {}).forEach((k)=>{
       // Sanity checks of sizeof/offset info...
       let m = ads(si.members[k]);
@@ -951,11 +1115,23 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
         }
       }
       if( undefined===m.offset ){
-        m.offset = offset;
+        if( autoCalc ) m.offset = offset;
+        else{
+          toss(sPropName(structName,k),"is missing its offset.",
+               JSON.stringify(m));
+        }
+        /* A missing offset on the initial child is okay (it's always
+           zero), but we don't know for sure that the members are
+           their natural order, so we don't know, at this point, which
+           one is "first". */
       }
-      si.members[k] = m;
+      si.members[k] = m /* in case ads() resolved it to something else */;
       if(!lastMember || lastMember.offset < m.offset) lastMember = m;
-      makeMemberWrapper.call(self, StructCtor, k, m)
+      const oldAutoCalc = !!m.autoCalc;
+      if( autoCalc ) m.autoCalcSizeOffset = true;
+      makeMemberWrapper.call(self, StructCtor, k, m);
+      if( oldAutoCalc ) m.autoCalcSizeOffset = true;
+      else delete m.autoCalcSizeOffset;
       offset += m.sizeof;
       //console.warn("offset",sPropName(structName,k),offset);
     });
@@ -979,12 +1155,15 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
         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);
+      /* It is legal for the native struct to be larger, so long as
+         we're pointing to all the right offsets for the members
+         exposed here. */
     }
+    delete si.autoCalcSizeOffset;
     return StructCtor;
   }/*StructBinderImpl*/;
 
@@ -999,6 +1178,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
   StructBinder.adaptGet = __adaptGet;
   StructBinder.adaptSet = __adaptSet;
   StructBinder.adaptStruct = __adaptStruct;
+  StructBinder.ptrAdd = __ptrAdd;
   if(!StructBinder.debugFlags){
     StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags);
   }
index a146a871d8bfc3af403ce3ce8bcf4aa595811d87..33bf733e9cc2885e6fb9fd9ed597263d0500f1d3 100644 (file)
@@ -1027,7 +1027,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
           let ptr = k1.pointer;
           k1.dispose();
           T.assert(undefined === k1.pointer).
-            mustThrowMatching(()=>{k1.$pP=1}, /disposed struct instance/);
+            mustThrowMatching(()=>{k1.$pP=1}, /disposed/);
         }finally{
           k1.dispose();
           k2.dispose();
@@ -2907,11 +2907,12 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
           ]);
           T.assert(3 === db.selectValue('select count(*) from kvvfs'));
           db.close();
+          db = undefined;
           db = new JDb(filename);
           db.exec('insert into kvvfs(a) values(4),(5),(6)');
           T.assert(6 === db.selectValue('select count(*) from kvvfs'));
         }finally{
-          db.close();
+          if( db ) db.close();
         }
       }
     }/*kvvfs sanity checks*/)
index 957b28b1a3f5d6c49c9171d9d115e5a1fc5b0a8c..4122d483d3de6da2598c4918266ab3109f8037b0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Test\scases\sfor\sthe\sfix\sin\sthe\sprior\scheck-in.
-D 2025-11-20T22:46:27.556
+C Move\sthe\sJS\spieces\sof\skvvfs\sinto\stheir\sown\sfile\sto\sfacilitate\spending\sfeature\sexperimentation.
+D 2025-11-21T10:49:32.462
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -578,7 +578,7 @@ F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009e
 F ext/session/sqlite3session.c b3de195ce668cace9b324599bf6255a70290cbfb5451e826e946f3aee6e64c54
 F ext/session/sqlite3session.h 7404723606074fcb2afdc6b72c206072cdb2b7d8ba097ca1559174a80bc26f7a
 F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb
-F ext/wasm/GNUmakefile 01f5e1bc688911b0c0accb0b152dccb19818cb301a90ba9772e3b329a7f625b0
+F ext/wasm/GNUmakefile bff7f432a65bc5152ae5aaf19f480dd16fa7fdd11a451773b7dbcac544c703b3
 F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
 F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
 F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -593,9 +593,9 @@ F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e
 F ext/wasm/api/post-js-footer.js a50c1a2c4d008aede7b2aa1f18891a7ee71437c2f415b8aeb3db237ddce2935b
 F ext/wasm/api/post-js-header.js d24bd0d065f3489c8b78ddf3ead6321e5d047187a162cd503c41700e03dd1f06
 F ext/wasm/api/pre-js.c-pp.js ad2546290e0c8ce5ca2081bff6e85cc25eeb904a3303921f1184290a7ff1b32f
-F ext/wasm/api/sqlite3-api-glue.c-pp.js 6ff61b7a7a8fb4367ebb2262d9f95a4b8c43eb6964890af99fd322f6cb1b4f00
-F ext/wasm/api/sqlite3-api-oo1.c-pp.js 31dbfd470c91ffd96d77399b749bab6b69e3ba9074188833f97ac13f087cf07b
-F ext/wasm/api/sqlite3-api-prologue.js a7cab3e8f78d5a93d7173657677fef18265f48d4e0b1e1755740f734a69cae6d
+F ext/wasm/api/sqlite3-api-glue.c-pp.js 9eaed1801be392f6687aa7da8e3a5a41d03de19993d8fe62ee6c52617eab4985
+F ext/wasm/api/sqlite3-api-oo1.c-pp.js 8ce38bd4b22aa2b0311c7a8e87e748e06213766fe2141de7574672d103ece255
+F ext/wasm/api/sqlite3-api-prologue.js 7004b569624765c5132984bfecee2305bef928a6adf44e0202dacc9cbc5c8e2a
 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-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
@@ -627,7 +627,7 @@ F ext/wasm/fiddle/fiddle.js 84fd75967e0af8b69d3dd849818342227d0f81d13db92e0dcbc6
 F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9
 F ext/wasm/index-dist.html db23748044e286773f2768eec287669501703b5d5f72755e8db73607dc54d290
 F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b287da1
-F ext/wasm/jaccwabyt/jaccwabyt.js 1e734c624205cdf621f322972dfb0fc8013d573a5882f57492a6830e5ec23e17
+F ext/wasm/jaccwabyt/jaccwabyt.js 236464b7c8e2d2540a296ba049b3e812e2697ef8439ac96f0d20716e46ba6809
 F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f
 F ext/wasm/mkdist.sh 64d53f469c823ed311f6696f69cec9093f745e467334b34f5ceabdf9de3c5b28 x
 F ext/wasm/mkwasmbuilds.c ef42e404236dd98cedb6ecea47b6d2474e3c593633ce6d992d316289dfc442b6
@@ -646,7 +646,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
 F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
 F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a
 F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb
-F ext/wasm/tester1.c-pp.js 2c255093205a0dac9dae7475030665c2c9d6dccc857de68ee7daf49aa82e6de8
+F ext/wasm/tester1.c-pp.js 015b4133cc3a5fb41d6236a6b39d23d996cc2d61a4877acde31a1f69574d4ce3
 F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
 F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -2175,8 +2175,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 8896185ae0b0af8918aa8ce449f18759e6ae60358afbac1019397bae307b15d5
-R 254d017f2dd051b92cbdd62f2ef390cd
-U drh
-Z 1247e56565ac3eb588c6a5672754d68c
+P 4d41bee75eda51251121c8e3903f47941116e5182238a03f41a593c47efb6fcf
+R 89e12ac8833b73cf56a70154881e6f51
+U stephan
+Z 653a6aeee27ed8b2d1b86bc8d2dfe977
 # Remove this line to create a well-formed Fossil manifest.
index ccec045e5534d999754e5beccc56f0aeff9fed5c..17a24daa49ac5aad522c7787125b003944b398b1 100644 (file)
@@ -1 +1 @@
-4d41bee75eda51251121c8e3903f47941116e5182238a03f41a593c47efb6fcf
+3c40614285449df259a3444e36f888cfb5e782ea58287914f97f496ea61e9e9f