]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Work on an alternate (slightly simpler) approach to binding JS vtabs. Non-eponymous...
authorstephan <stephan@noemail.net>
Wed, 7 Dec 2022 07:22:34 +0000 (07:22 +0000)
committerstephan <stephan@noemail.net>
Wed, 7 Dec 2022 07:22:34 +0000 (07:22 +0000)
FossilOrigin-Name: 6a0fefb93bcccd950df211cf5c2f49660c7b92115dd01b2b508a4ab9e3ab3d23

ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/api/sqlite3-v-helper.js
ext/wasm/jaccwabyt/jaccwabyt.js
ext/wasm/tester1.c-pp.js
manifest
manifest.uuid

index bccae8b19919ae8e29ac67b27887157c1a3768c1..e6a8c0fc3cc9e2415e54b47763c551fa76ed5d03 100644 (file)
@@ -185,28 +185,49 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     /**
        Constructs this object with a message depending on its arguments:
 
-       - If it's passed only a single integer argument, it is assumed
-       to be an sqlite3 C API result code. The message becomes the
-       result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns
-       falsy) a synthesized string which contains that integer.
-
-       - If passed 2 arguments and the 2nd is a object, it behaves
-       like the Error(string,object) constructor except that the first
-       argument is subject to the is-integer semantics from the
-       previous point.
-
-       - Else all arguments are concatenated with a space between each
-       one, using args.join(' '), to create the error message.
+       If its first argument is an integer, it is assumed to be
+       an SQLITE_... result code and it is passed to
+       sqlite3.capi.sqlite3_js_rc_str() to stringify it.
+
+       If called with exactly 2 arguments and the 2nd is an object,
+       that object is treated as the 2nd argument to the parent
+       constructor.
+
+       The exception's message is created by concatenating its
+       arguments with a space between each, except for the
+       two-args-with-an-objec form and that the first argument will
+       get coerced to a string, as described above, if it's an
+       integer.
+
+       If passed an integer first argument, the error object's
+       `resultCode` member will be set to the given integer value,
+       else it will be set to capi.SQLITE_ERROR.
     */
     constructor(...args){
-      if(1===args.length && __isInt(args[0])){
-        super(__rcStr(args[0]));
-      }else if(2===args.length && 'object'===typeof args[1]){
-        if(__isInt(args[0])) super(__rcStr(args[0]), args[1]);
-        else super(...args);
-      }else{
-        super(args.join(' '));
+      let rc;
+      if(args.length){
+        if(__isInt(args[0])){
+          rc = args[0];
+          if(1===args.length){
+            super(__rcStr(args[0]));
+          }else{
+            const rcStr = __rcStr(rc);
+            if('object'===typeof args[1]){
+              super(rcStr,args[1]);
+            }else{
+              args[0] = rcStr+':';
+              super(args.join(' '));
+            }
+          }
+        }else{
+          if(2===args.length && 'object'===typeof args[1]){
+            super(...args);
+          }else{
+            super(args.join(' '));
+          }
+        }
       }
+      this.resultCode = rc || capi.SQLITE_ERROR;
       this.name = 'SQLite3Error';
     }
   };
index 1f2950f74981c2e6ee3df14e5b015cc246a1279b..73ba8cc49e83842a06ae3c25aa53f74e36d2aadf 100644 (file)
@@ -16,7 +16,7 @@
 */
 'use strict';
 self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
-  const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss;
+  const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
   const vh = Object.create(null), vt = Object.create(null);
 
   sqlite3.VfsHelper = vh;
@@ -72,21 +72,29 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
   /**
      Installs a StructBinder-bound function pointer member of the
      given name and function in the given StructType target object.
+
      It creates a WASM proxy for the given function and arranges for
-     that proxy to be cleaned up when tgt.dispose() is called.  Throws
+     that proxy to be cleaned up when tgt.dispose() is called. Throws
      on the slightest hint of error, e.g. tgt is-not-a StructType,
      name does not map to a struct-bound member, etc.
 
-     Returns a proxy for this function which is bound to tgt and takes
-     2 args (name,func). That function returns the same thing,
-     permitting calls to be chained.
+     As a special case, if the given function is a pointer, it is
+     assumed to be an existing WASM-bound function pointer and is used
+     as-is with no extra level of proxying or cleanup. Results are
+     undefined if it's a pointer and it's _not_ a function pointer.
+     It is legal to pass a value of 0, indicating a NULL pointer, with
+     the caveat that 0 _is_ a legal function pointer in WASM but it
+     will not be accepted as such _here_. (Justification: the function
+     at address zero must be one which initially came from the WASM
+     module, not a method we want to bind to a virtual table or VFS.)
+
+     This function returns a proxy for itself which is bound to tgt
+     and takes 2 args (name,func). That function returns the same
+     thing as this one, permitting calls to be chained.
 
      If called with only 1 arg, it has no side effects but returns a
      func with the same signature as described above.
 
-     If tgt.ondispose is set before this is called then it _must_
-     be an array, to which this function will append entries.
-
      ACHTUNG: because we cannot generically know how to transform JS
      exceptions into result codes, the installed functions do no
      automatic catching of exceptions. It is critical, to avoid 
@@ -94,20 +102,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      this function do not throw. The exception, as it were, to that
      rule is...
 
-     If applyArgcCheck is true then each method gets wrapped in a
-     proxy which asserts that it is passed the expected number of
-     arguments, throwing if the argument count does not match
-     expectations. That is only intended for dev-time usage for sanity
-     checking, and will leave the C environment in an undefined
-     state. For non-dev-time use, it is a given that the C API will
-     never call one of the generated function wrappers with the wrong
-     argument count.
+     If applyArgcCheck is true then each JS function (as opposed to
+     function pointers) gets wrapped in a proxy which asserts that it
+     is passed the expected number of arguments, throwing if the
+     argument count does not match expectations. That is only intended
+     for dev-time usage for sanity checking, and will leave the C
+     environment in an undefined state.
   */
   vh.installMethod = vt.installMethod = function callee(
     tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
   ){
     if(!(tgt instanceof sqlite3.StructBinder.StructType)){
       toss("Usage error: target object is-not-a StructType.");
+    }else if(!(func instanceof Function) && !wasm.isPtr(func)){
+      toss("Usage errror: expecting a Function or WASM pointer to one.");
     }
     if(1===arguments.length){
       return (n,f)=>callee(tgt, n, f, applyArgcCheck);
@@ -143,26 +151,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }/*static init*/
     const sigN = tgt.memberSignature(name);
     if(sigN.length<2){
-      toss("Member",name," is not a function pointer. Signature =",sigN);
+      toss("Member",name,"does not have a function pointer signature:",sigN);
     }
     const memKey = tgt.memberKey(name);
-    const fProxy = applyArgcCheck
+    const fProxy = (applyArgcCheck && !wasm.isPtr(func))
     /** This middle-man proxy is only for use during development, to
         confirm that we always pass the proper number of
         arguments. We know that the C-level code will always use the
         correct argument count. */
           ? callee.argcProxy(tgt, memKey, func, sigN)
           : func;
-    const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
-    tgt[memKey] = pFunc;
-    if(!tgt.ondispose) tgt.ondispose = [];
-    else if(!Array.isArray(tgt.ondispose)) tgt.ondispose = [tgt.ondispose];
-    if(!tgt.ondispose.__removeFuncList){
-      tgt.ondispose.push('ondispose.__removeFuncList handler',
+    if(wasm.isPtr(fProxy)){
+      if(fProxy && !wasm.functionEntry(fProxy)){
+        toss("Pointer",fProxy,"is not a WASM function table entry.");
+      }
+      tgt[memKey] = fProxy;
+    }else{
+      const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
+      tgt[memKey] = pFunc;
+      if(!tgt.ondispose) tgt.ondispose = [];
+      else if(!Array.isArray(tgt.ondispose)) tgt.ondispose = [tgt.ondispose];
+      if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){
+        tgt.addOnDispose('ondispose.__removeFuncList handler',
                          callee.removeFuncList);
-      tgt.ondispose.__removeFuncList = [];
+        tgt.ondispose.__removeFuncList = [];
+      }
+      tgt.ondispose.__removeFuncList.push(memKey, pFunc);
     }
-    tgt.ondispose.__removeFuncList.push(memKey, pFunc);
     return (n,f)=>callee(tgt, n, f, applyArgcCheck);
   }/*installMethod*/;
   vh.installMethod.installMethodArgcCheck = false;
@@ -269,8 +284,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         this.installMethods(o.struct, o.methods, !!o.applyArgcCheck);
         if('vfs'===key){
           if(!o.struct.$zName && 'string'===typeof o.name){
-            o.struct.$zName = wasm.allocCString(o.name);
-            /* Note that we leak that C-string. */
+            o.struct.addOnDispose(
+              o.struct.$zName = wasm.allocCString(o.name)
+            );
           }
           this.registerVfs(o.struct, !!o.asDefault);
         }
@@ -292,24 +308,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
   vt.sqlite3ValuesToJs = capi.sqlite3_create_function_v2.udfConvertArgs;
 
   /**
-     Factory function for wrapXyz() impls.
+     Factory function for xAbc() impls.
   */
-  const __xWrapFactory = function(structType){
-    return function(ptr,remove=false){
+  const __xWrapFactory = function(methodName,structType){
+    return function(ptr,removeMapping=false){
       if(0===arguments.length) ptr = new structType;
       if(ptr instanceof structType){
         //T.assert(!this.has(ptr.pointer));
         this.set(ptr.pointer, ptr);
         return ptr;
       }else if(!wasm.isPtr(ptr)){
-        sqlite3.SQLite3Error.toss("Invalid argument to xWrapFactory");
+        sqlite3.SQLite3Error.toss("Invalid argument to",methodName+"()");
       }
       let rc = this.get(ptr);
-      if(remove) this.delete(ptr);
-      /*arguable else if(!rc){
-        rc = new structType(ptr);
-        this.set(ptr, rc);
-      }*/
+      if(removeMapping) this.delete(ptr);
       return rc;
     }.bind(new Map);
   };
@@ -336,7 +348,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        called from sqlite3_module::xDisconnect() implementations or
        in error handling of a failed xCreate() or xConnect().
  */
-  vt.xWrapVtab = __xWrapFactory(capi.sqlite3_vtab);
+  vt.xVtab = __xWrapFactory('xVtab',capi.sqlite3_vtab);
 
   /**
      EXPERIMENTAL. DO NOT USE IN CLIENT CODE.
@@ -356,7 +368,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        intended to be called from xClose() or in error handling of a
        failed xOpen().
  */
-  vt.xWrapCursor = __xWrapFactory(capi.sqlite3_vtab_cursor);
+  vt.xCursor = __xWrapFactory('xCursor',capi.sqlite3_vtab_cursor);
+
+  /**
+     Convenience form of creating an sqlite3_index_info wrapper,
+     intended for use in xBestIndex implementations. Note that the
+     caller is expected to call dispose() on the returned object
+     before returning. Though not _strictly_ required, as that object
+     does not own the pIdxInfo memory, it is nonetheless good form.
+  */
+  vt.xIndexInfo = (pIdxInfo)=>new capi.sqlite3_index_info(pIdxInfo);
 
   /**
      Given an error object, this function returns
@@ -394,27 +415,38 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       let rc = ...
       return rc;
      }catch(e){
-       return sqlite3.VtabHelper.xMethodError(
+       return sqlite3.VtabHelper.xError(
                 'xColumn', e, sqlite3.capi.SQLITE_XYZ);
-       // where SQLITE_XYZ is some call-appropriate result code
-       // defaulting to SQLITE_ERROR.
+       // where SQLITE_XYZ is some call-appropriate result code.
      }
      ```
 
-     If xMethodError.errorReporter is a function, it is called in
+     If no 3rd argument is provided, its default depends on
+     the error type:
+
+     - An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM.
+
+     - If err is an SQLite3Error then its `resultCode` property
+       is used.
+
+     - If all else fails, capi.SQLITE_ERROR is used.
+
+     If xError.errorReporter is a function, it is called in
      order to report the error, else the error is not reported.
      If that function throws, that exception is ignored.
   */
-  vt.xMethodError = function f(methodName, err, defaultRc=capi.SQLITE_ERROR){
+  vt.xError = function f(methodName, err, defaultRc){
     if(f.errorReporter instanceof Function){
       try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);}
       catch(e){/*ignored*/}
     }
-    return (err instanceof sqlite3.WasmAllocError)
-      ? capi.SQLITE_NOMEM
-      : defaultRc;
+    let rc;
+    if(err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM;
+    else if(arguments.length>2) rc = defaultRc;
+    else if(err instanceof sqlite3.SQLite3Error) rc = err.resultCode;
+    return rc || capi.SQLITE_ERROR;
   };
-  vt.xMethodError.errorReporter = 1 ? console.error.bind(console) : false;
+  vt.xError.errorReporter = 1 ? console.error.bind(console) : false;
 
   /**
      "The problem" with this is that it introduces an outer function with
@@ -426,15 +458,139 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
   /** vt.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){
     return function(...args){
       try { method(...args); }
-      }catch(e){ return vt.xMethodError(methodName, e, defaultRc) }
+      }catch(e){ return vt.xError(methodName, e, defaultRc) }
   };
   */
 
   /**
-     A helper for sqlite3_vtab::xRow() implementations. It must be
+     A helper for sqlite3_vtab::xRowid() implementations. It must be
      passed that function's 2nd argument and the value for that
      pointer.  Returns the same as wasm.setMemValue() and will throw
      if the 1st or 2nd arguments are invalid for that function.
   */
-  vt.setRowId = (ppRowid64, value)=>wasm.setMemValue(ppRowid64, value, 'i64');
+  vt.xRowid = (ppRowid64, value)=>wasm.setMemValue(ppRowid64, value, 'i64');
+
+  /**
+     Sets up an sqlite3_module() object for later installation into
+     individual databases using sqlite3_create_module(). Requires an
+     object with the following properties:
+
+     - `methods`: an object containing a mapping of properties with
+       the C-side names of the sqlite3_module methods, e.g. xCreate,
+       xBestIndex, etc., to JS implementations for those functions.
+       Certain special-case handling is performed, as described below.
+
+     - `catchExceptions` (default=false): if truthy, the given methods
+       are not mapped as-is, but are instead wrapped inside wrappers
+       which translate exceptions into result codes of SQLITE_ERROR or
+       SQLITE_NOMEM, depending on whether the exception is an
+       sqlite3.WasmAllocError. In the case of the xConnect and xCreate
+       methods, the exception handler also sets the output error
+       string to the exception's error string.
+
+     - OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If
+       not set, one will be created automatically and (on success)
+       added to the object.
+
+     - OPTIONAL `iVersion`: if set, it must be an integer value and it
+       gets assigned to the `$iVersion` member of the struct object.
+       If it's _not_ set, and the passed-in `struct` object's `$iVersion`
+       is 0 (the default) then this function attempts to define a value
+       for that property based on the list of methods it has.
+
+     If `catchExceptions` is false, it is up to the client to ensure
+     that no exceptions escape the methods, as doing so would move
+     them through the C API, leading to undefined
+     behavior. (VtabHelper.xError() is intended to assist in reporting
+     such exceptions.)
+
+     If `methods.xConnect` is `true` then the value of
+     `methods.xCreate` is used in its place, and vice versa. This is
+     to facilitate creation of those methods inline in the passed-in
+     object without requiring the client to explicitly get a reference
+     to one of them in order to assign it to the other one. Note that
+     sqlite treats those two functions specially if they are exactly
+     the same function (same pointer value).  The
+     `catchExceptions`-installed handlers will account for identical
+     references to those two functions and will install the same
+     wrapper function for both.
+
+     The given methods are expected to return integer values, as
+     expected by the C API. If `catchExceptions` is truthy, the return
+     value of the wrapped function will be used as-is and will be
+     translated to 0 if the function returns a falsy value (e.g. if it
+     does not have an explicit return). If `catchExceptions` is _not_
+     active, the method implementations must explicitly return integer
+     values.
+
+     Throws on error. Returns the sqlite3_module object on success.
+  */
+  vt.setupModule = function(opt){
+    const mod = opt.struct || new capi.sqlite3_module();
+    try{
+      const methods = opt.methods || toss("Missing 'methods' object.");
+      if(true===methods.xConnect) methods.xConnect = methods.xCreate;
+      else if(true===methods.xCreate) methods.xCreate = methods.xConnect;
+      if(opt.catchExceptions){
+        const fwrap = function(methodName, func){
+          if(['xConnect','xCreate'].indexOf(methodName) >= 0){
+            return function(pDb, pAux, argc, argv, ppVtab, pzErr){
+              try{return func(...arguments) || 0;}
+              catch(e){
+                if(!(e instanceof sqlite3.WasmAllocError)){
+                  wasm.setPtrValue(pzErr, wasm.allocCString(e.message));
+                }
+                return vt.xError(methodName, e);
+              }
+            };
+          }else{
+            return function(...args){
+              try{return func(...args) || 0;}
+              catch(e){
+                return vt.xError(methodName, e);
+              }
+            };
+          }
+        };
+        const mnames = [
+          'xCreate', 'xConnect', 'xBestIndex', 'xDisconnect',
+          'xDestroy', 'xOpen', 'xClose', 'xFilter', 'xNext',
+          'xEof', 'xColumn', 'xRowid', 'xUpdate',
+          'xBegin', 'xSync', 'xCommit', 'xRollback',
+          'xFindFunction', 'xRename', 'xSavepoint', 'xRelease',
+          'xRollbackTo', 'xShadowName'
+        ];
+        const remethods = Object.create(null);
+        for(const k of mnames){
+          const m = methods[k];
+          if(!(m instanceof Function)) continue;
+          else if('xConnect'===k && methods.xCreate===m){
+            remethods[k] = methods.xCreate;
+          }else if('xCreate'===k && methods.xConnect===m){
+            remethods[k] = methods.xConnect;
+          }else{
+            remethods[k] = fwrap(k, m);
+          }
+        }
+        this.installMethods(mod, remethods, false);
+      }else{
+        this.installMethods(
+          mod, methods, !!opt.applyArgcCheck/*undocumented option*/
+        );
+      }
+      if(0===mod.$iVersion){
+        let v;
+        if('number'===typeof opt.iVersion) v = opt.iVersion;
+        else if(mod.$xShadowName) v = 3;
+        else if(mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2;
+        else v = 1;
+        mod.$iVersion = v;
+      }
+    }catch(e){
+      if(!opt.struct) mod.dispose();
+      throw e;
+    }
+    if(!opt.struct) opt.struct = mod;
+    return mod;
+  }/*setupModule()*/;
 }/*sqlite3ApiBootstrap.initializers.push()*/);
index f02c8bbdefb7e1c2a9841caddcdff5f9931901dd..d4ec719fb56e372d408da3054e63f179a16e4a1b 100644 (file)
@@ -222,6 +222,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
   const __freeStruct = function(ctor, obj, m){
     if(!m) m = __instancePointerMap.get(obj);
     if(m) {
+      __instancePointerMap.delete(obj);
       if(Array.isArray(obj.ondispose)){
         let x;
         while((x = obj.ondispose.shift())){
@@ -245,7 +246,6 @@ self.Jaccwabyt = function StructBinderFactory(config){
         }
       }
       delete obj.ondispose;
-      __instancePointerMap.delete(obj);
       if(ctor.debugFlags.__flags.dealloc){
         log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
             ctor.structName,"instance:",
index 910bbc94e6d42087f73d2a955b538a301cb7bf44..83b02522e4197114523bc8b63b47740eb28fc6b5 100644 (file)
@@ -301,14 +301,12 @@ self.sqlite3InitModule = sqlite3InitModule;
     addTest: function(name, callback){
       let predicate;
       if(1===arguments.length){
-        const opt = arguments[0];
-        predicate = opt.predicate;
-        name = opt.name;
-        callback = opt.test;
+        this.currentTestGroup.addTest(arguments[0]);
+      }else{
+        this.currentTestGroup.addTest({
+          name, predicate, test: callback
+        });
       }
-      this.currentTestGroup.addTest({
-        name, predicate, test: callback
-      });
       return this;
     },
     runTests: async function(sqlite3){
@@ -399,12 +397,21 @@ self.sqlite3InitModule = sqlite3InitModule;
       catch(e){T.assert("test ing ." === e.message)}
 
       try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
-      catch(e){ T.assert('SQLITE_SCHEMA' === e.message) }
+      catch(e){
+        T.assert('SQLITE_SCHEMA' === e.message)
+          .assert(capi.SQLITE_SCHEMA === e.resultCode);
+      }
       try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) }
       catch(e){
-        T.assert('SQLITE_CORRUPT'===e.message)
+        T.assert('SQLITE_CORRUPT' === e.message)
+          .assert(capi.SQLITE_CORRUPT === e.resultCode)
           .assert(true===e.cause);
       }
+      try{ sqlite3.SQLite3Error.toss("resultCode check") }
+      catch(e){
+        T.assert(capi.SQLITE_ERROR === e.resultCode)
+          .assert('resultCode check' === e.message);        
+      }
     })
   ////////////////////////////////////////////////////////////////////
     .t('strglob/strlike', function(sqlite3){
@@ -988,6 +995,20 @@ self.sqlite3InitModule = sqlite3InitModule;
       const dbFile = '/tester1.db';
       wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
       const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c');
+      db.onclose = {
+        disposeThese: [],
+        after: function(){
+          while(this.disposeThese.length){
+            const v = this.disposeThese.shift();
+            console.debug("db.onclose cleaning up:",v);
+            if(wasm.isPtr(v)) wasm.dealloc(v);
+            else if(v instanceof sqlite3.StructBinder.StructType){
+              v.dispose();
+            }
+          }
+        }
+      };
+      
       T.assert(Number.isInteger(db.pointer))
         .mustThrowMatching(()=>db.pointer=1, /read-only/)
         .assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1))
@@ -1539,10 +1560,96 @@ self.sqlite3InitModule = sqlite3InitModule;
       T.mustThrow(()=>db.exec("select * from foo.bar"));
     })
 
+  ////////////////////////////////////////////////////////////////////
+    .t({
+      name: 'C-side WASM tests',
+      predicate: ()=>(haveWasmCTests() || "Not compiled in."),
+      test: function(){
+        const w = wasm, db = this.db;
+        const stack = w.scopedAllocPush();
+        let ptrInt;
+        const origValue = 512;
+        const ptrValType = 'i32';
+        try{
+          ptrInt = w.scopedAlloc(4);
+          w.setMemValue(ptrInt,origValue, ptrValType);
+          const cf = w.xGet('sqlite3_wasm_test_intptr');
+          const oldPtrInt = ptrInt;
+          //log('ptrInt',ptrInt);
+          //log('getMemValue(ptrInt)',w.getMemValue(ptrInt));
+          T.assert(origValue === w.getMemValue(ptrInt, ptrValType));
+          const rc = cf(ptrInt);
+          //log('cf(ptrInt)',rc);
+          //log('ptrInt',ptrInt);
+          //log('getMemValue(ptrInt)',w.getMemValue(ptrInt,ptrValType));
+          T.assert(2*origValue === rc).
+            assert(rc === w.getMemValue(ptrInt,ptrValType)).
+            assert(oldPtrInt === ptrInt);
+          const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/;
+          const o64 = 0x010203040506/*>32-bit integer*/;
+          const ptrType64 = 'i64';
+          if(w.bigIntEnabled){
+            w.setMemValue(pi64, o64, ptrType64);
+            //log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64);
+            const v64 = ()=>w.getMemValue(pi64,ptrType64)
+            //log("getMemValue(pi64)",v64());
+            T.assert(v64() == o64);
+            //T.assert(o64 === w.getMemValue(pi64, ptrType64));
+            const cf64w = w.xGet('sqlite3_wasm_test_int64ptr');
+            cf64w(pi64);
+            //log("getMemValue(pi64)",v64());
+            T.assert(v64() == BigInt(2 * o64));
+            cf64w(pi64);
+            T.assert(v64() == BigInt(4 * o64));
+
+            const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2');
+            T.assert(BigInt(2 * o64) ===
+                     biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError
+                                           in the call :/ */));
+
+            const pMin = w.scopedAlloc(16);
+            const pMax = pMin + 8;
+            const g64 = (p)=>w.getMemValue(p,ptrType64);
+            w.setMemValue(pMin, 0, ptrType64);
+            w.setMemValue(pMax, 0, ptrType64);
+            const minMaxI64 = [
+              w.xCall('sqlite3_wasm_test_int64_min'),
+              w.xCall('sqlite3_wasm_test_int64_max')
+            ];
+            T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)).
+              assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER));
+            //log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]);
+            w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax);
+            T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch").
+              assert(g64(pMax) === minMaxI64[1], "int64 mismatch");
+            //log("pMin",g64(pMin), "pMax",g64(pMax));
+            w.setMemValue(pMin, minMaxI64[0], ptrType64);
+            T.assert(g64(pMin) === minMaxI64[0]).
+              assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))).
+              assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax)));
+            const rxRange = /too big/;
+            T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))},
+                                rxRange).
+              mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))},
+                                (e)=>rxRange.test(e.message));
+          }else{
+            log("No BigInt support. Skipping related tests.");
+            log("\"The problem\" here is that we can manipulate, at the byte level,",
+                "heap memory to set 64-bit values, but we can't get those values",
+                "back into JS because of the lack of 64-bit integer support.");
+          }
+        }finally{
+          const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
+          //log("x=",x,"y=",y,"z=",z); // just looking at the alignment
+          w.scopedAllocPop(stack);
+        }
+      }
+    }/* jaccwabyt-specific tests */)
+
   ////////////////////////////////////////////////////////////////////////
     .t({
-      name: 'Custom virtual tables',
-      predicate: ()=>wasm.bigIntEnabled,
+      name: 'virtual table #1',
+      predicate: ()=>!!capi.sqlite3_index_info,
       test: function(sqlite3){
         warn("The vtab/module JS bindings are experimental and subject to change.");
         const vth = sqlite3.VtabHelper;
@@ -1566,63 +1673,63 @@ self.sqlite3InitModule = sqlite3InitModule;
                 pDb, "CREATE TABLE ignored(a,b)"
               );
               if(0===rc){
-                const t = vth.xWrapVtab();
+                const t = vth.xVtab();
                 wasm.setPtrValue(ppVtab, t.pointer);
-                T.assert(t === vth.xWrapVtab(wasm.getPtrValue(ppVtab)));
+                T.assert(t === vth.xVtab(wasm.getPtrValue(ppVtab)));
               }
               return rc;
             }catch(e){
               if(!(e instanceof sqlite3.WasmAllocError)){
                 wasm.setPtrValue(pzErr, wasm.allocCString(e.message));
               }
-              return vth.xMethodError('xConnect',e);
+              return vth.xError('xConnect',e);
             }
           },
           xDisconnect: function(pVtab){
             try {
-              const t = vth.xWrapVtab(pVtab, true);
+              const t = vth.xVtab(pVtab, true);
               t.dispose();
               return 0;
             }catch(e){
-              return vth.xMethodError('xDisconnect',e);
+              return vth.xError('xDisconnect',e);
             }
           },
           xOpen: function(pVtab, ppCursor){
             try{
-              const t = vth.xWrapVtab(pVtab), c = vth.xWrapCursor();
+              const t = vth.xVtab(pVtab), c = vth.xCursor();
               T.assert(t instanceof capi.sqlite3_vtab)
                 .assert(c instanceof capi.sqlite3_vtab_cursor);
               wasm.setPtrValue(ppCursor, c.pointer);
               c._rowId = 0;
               return 0;
             }catch(e){
-              return vth.xMethodError('xOpen',e);
+              return vth.xError('xOpen',e);
             }
           },
           xClose: function(pCursor){
             try{
-              const c = vth.xWrapCursor(pCursor,true);
+              const c = vth.xCursor(pCursor,true);
               T.assert(c instanceof capi.sqlite3_vtab_cursor)
-                .assert(!vth.xWrapCursor(pCursor));
+                .assert(!vth.xCursor(pCursor));
               c.dispose();
               return 0;
             }catch(e){
-              return vth.xMethodError('xClose',e);
+              return vth.xError('xClose',e);
             }
           },
           xNext: function(pCursor){
             try{
-              const c = vth.xWrapCursor(pCursor);
+              const c = vth.xCursor(pCursor);
               ++c._rowId;
               return 0;
             }catch(e){
-              return vth.xMethodError('xNext',e);
+              return vth.xError('xNext',e);
             }
 
           },
           xColumn: function(pCursor, pCtx, iCol){
             try{
-              const c = vth.xWrapCursor(pCursor);
+              const c = vth.xCursor(pCursor);
               switch(iCol){
                   case tmplCols.A:
                     capi.sqlite3_result_int(pCtx, 1000 + c._rowId);
@@ -1634,38 +1741,41 @@ self.sqlite3InitModule = sqlite3InitModule;
               }
               return 0;
             }catch(e){
-              return vth.xMethodError('xColumn',e);
+              return vth.xError('xColumn',e);
             }
           },
           xRowid: function(pCursor, ppRowid64){
             try{
-              const c = vth.xWrapCursor(pCursor);
-              vth.setRowId(ppRowid64, c._rowId);
+              const c = vth.xCursor(pCursor);
+              vth.xRowid(ppRowid64, c._rowId);
               return 0;
             }catch(e){
-              return vth.xMethodError('xRowid',e);
+              return vth.xError('xRowid',e);
             }
           },
           xEof: function(pCursor){
-            const c = vth.xWrapCursor(pCursor);
-            return c._rowId>=10;
+            const c = vth.xCursor(pCursor),
+                  rc = c._rowId>=10;
+            c.dispose();
+            return rc;
           },
           xFilter: function(pCursor, idxNum, idxCStr,
                             argc, argv/* [sqlite3_value* ...] */){
             try{
-              const c = vth.xWrapCursor(pCursor);
+              const c = vth.xCursor(pCursor);
               c._rowId = 0;
               const list = vth.sqlite3ValuesToJs(argc, argv);
               T.assert(argc === list.length);
               //log(argc,"xFilter value(s):",list);
+              c.dispose();
               return 0;
             }catch(e){
-              return vth.xMethodError('xFilter',e);
+              return vth.xError('xFilter',e);
             }
           },
           xBestIndex: function(pVtab, pIdxInfo){
             try{
-              //const t = vth.xWrapVtab(pVtab);
+              //const t = vth.xVtab(pVtab);
               const sii = capi.sqlite3_index_info;
               const pii = new sii(pIdxInfo);
               pii.$estimatedRows = 10;
@@ -1706,7 +1816,7 @@ self.sqlite3InitModule = sqlite3InitModule;
               pii.dispose();
               return 0;
             }catch(e){
-              return vth.xMethodError('xBestIndex',e);
+              return vth.xError('xBestIndex',e);
             }
           }
         };
@@ -1727,8 +1837,8 @@ self.sqlite3InitModule = sqlite3InitModule;
         }
 
         const tmplMod = new sqlite3.capi.sqlite3_module();
-        tmplMod.ondispose = [];
         tmplMod.$iVersion = 0;
+        this.db.onclose.disposeThese.push(tmplMod);
         vth.installMethods(tmplMod, tmplMethods, true);
         if(tmplMethods.xCreate){
           T.assert(tmplMod.$xCreate)
@@ -1750,94 +1860,165 @@ self.sqlite3InitModule = sqlite3InitModule;
           .assert(1000===list[0][0])
           .assert(2009===list[list.length-1][1])
       }
-    })/*vtab sanity checks*/
+    })/*custom vtab #1*/
 
-  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////////
     .t({
-      name: 'C-side WASM tests',
-      predicate: ()=>(haveWasmCTests() || "Not compiled in."),
-      test: function(){
-        const w = wasm, db = this.db;
-        const stack = w.scopedAllocPush();
-        let ptrInt;
-        const origValue = 512;
-        const ptrValType = 'i32';
-        try{
-          ptrInt = w.scopedAlloc(4);
-          w.setMemValue(ptrInt,origValue, ptrValType);
-          const cf = w.xGet('sqlite3_wasm_test_intptr');
-          const oldPtrInt = ptrInt;
-          //log('ptrInt',ptrInt);
-          //log('getMemValue(ptrInt)',w.getMemValue(ptrInt));
-          T.assert(origValue === w.getMemValue(ptrInt, ptrValType));
-          const rc = cf(ptrInt);
-          //log('cf(ptrInt)',rc);
-          //log('ptrInt',ptrInt);
-          //log('getMemValue(ptrInt)',w.getMemValue(ptrInt,ptrValType));
-          T.assert(2*origValue === rc).
-            assert(rc === w.getMemValue(ptrInt,ptrValType)).
-            assert(oldPtrInt === ptrInt);
-          const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/;
-          const o64 = 0x010203040506/*>32-bit integer*/;
-          const ptrType64 = 'i64';
-          if(w.bigIntEnabled){
-            w.setMemValue(pi64, o64, ptrType64);
-            //log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64);
-            const v64 = ()=>w.getMemValue(pi64,ptrType64)
-            //log("getMemValue(pi64)",v64());
-            T.assert(v64() == o64);
-            //T.assert(o64 === w.getMemValue(pi64, ptrType64));
-            const cf64w = w.xGet('sqlite3_wasm_test_int64ptr');
-            cf64w(pi64);
-            //log("getMemValue(pi64)",v64());
-            T.assert(v64() == BigInt(2 * o64));
-            cf64w(pi64);
-            T.assert(v64() == BigInt(4 * o64));
-
-            const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2');
-            T.assert(BigInt(2 * o64) ===
-                     biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError
-                                           in the call :/ */));
-
-            const pMin = w.scopedAlloc(16);
-            const pMax = pMin + 8;
-            const g64 = (p)=>w.getMemValue(p,ptrType64);
-            w.setMemValue(pMin, 0, ptrType64);
-            w.setMemValue(pMax, 0, ptrType64);
-            const minMaxI64 = [
-              w.xCall('sqlite3_wasm_test_int64_min'),
-              w.xCall('sqlite3_wasm_test_int64_max')
-            ];
-            T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)).
-              assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER));
-            //log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]);
-            w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax);
-            T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch").
-              assert(g64(pMax) === minMaxI64[1], "int64 mismatch");
-            //log("pMin",g64(pMin), "pMax",g64(pMax));
-            w.setMemValue(pMin, minMaxI64[0], ptrType64);
-            T.assert(g64(pMin) === minMaxI64[0]).
-              assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))).
-              assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax)));
-            const rxRange = /too big/;
-            T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))},
-                                rxRange).
-              mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))},
-                                (e)=>rxRange.test(e.message));
+      name: 'virtual table #2 (w/ automated exception wrapping)',
+      predicate: ()=>!!capi.sqlite3_index_info,
+      test: function(sqlite3){
+        warn("The vtab/module JS bindings are experimental and subject to change.");
+        const vth = sqlite3.VtabHelper;
+        const tmplCols = Object.assign(Object.create(null),{
+          A: 0, B: 1
+        });
+        /**
+           The vtab demonstrated here is a JS-ification of
+           ext/misc/templatevtab.c.
+        */
+        let throwOnConnect = 1 ? 0 : capi.SQLITE_CANTOPEN
+        /* ^^^ just for testing exception wrapping. Note that sqlite
+           always translates errors from a vtable to a generic
+           SQLITE_ERROR unless it's from xConnect()/xCreate() and that
+           callback sets an error string. */;
+        const modConfig = {
+          /* catchExceptions changes how the methods are wrapped */
+          catchExceptions: false,
+          name: "vtab2test",
+          methods:{
+            xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr){
+              if(throwOnConnect){
+                sqlite3.SQLite3Error.toss(
+                  throwOnConnect,
+                  "Throwing a test exception."
+                );
+              }
+              const args = wasm.cArgvToJs(argc, argv);
+              console.debug("xCreate/xConnect args:",args);
+              T.assert(args.length>=3);
+              const rc = capi.sqlite3_declare_vtab(
+                pDb, "CREATE TABLE ignored(a,b)"
+              );
+              if(0===rc){
+                const t = vth.xVtab();
+                wasm.setPtrValue(ppVtab, t.pointer);
+                T.assert(t === vth.xVtab(wasm.getPtrValue(ppVtab)));
+              }
+              return rc;
+            },
+            xDisconnect: function(pVtab){
+              const t = vth.xVtab(pVtab, true);
+              t.dispose();
+            },
+            xOpen: function(pVtab, ppCursor){
+              const t = vth.xVtab(pVtab), c = vth.xCursor();
+              T.assert(t instanceof capi.sqlite3_vtab)
+                .assert(c instanceof capi.sqlite3_vtab_cursor);
+              wasm.setPtrValue(ppCursor, c.pointer);
+              c._rowId = 0;
+            },
+            xClose: function(pCursor){
+              const c = vth.xCursor(pCursor,true);
+              T.assert(c instanceof capi.sqlite3_vtab_cursor)
+                .assert(!vth.xCursor(pCursor));
+              c.dispose();
+            },
+            xNext: function(pCursor){
+              const c = vth.xCursor(pCursor);
+              ++c._rowId;
+            },
+            xColumn: function(pCursor, pCtx, iCol){
+              const c = vth.xCursor(pCursor);
+              switch(iCol){
+                  case tmplCols.A:
+                    capi.sqlite3_result_int(pCtx, 1000 + c._rowId);
+                    break;
+                  case tmplCols.B:
+                    capi.sqlite3_result_int(pCtx, 2000 + c._rowId);
+                    break;
+                  default: sqlite3.SQLite3Error.toss("Invalid column id",iCol);
+              }
+            },
+            xRowid: function(pCursor, ppRowid64){
+              const c = vth.xCursor(pCursor);
+              vth.xRowid(ppRowid64, c._rowId);
+              c.dispose();
+            },
+            xEof: function(pCursor){
+              const c = vth.xCursor(pCursor),
+                    rc = c._rowId>=10;
+              c.dispose();
+              return rc;
+            },
+            xFilter: function(pCursor, idxNum, idxCStr,
+                              argc, argv/* [sqlite3_value* ...] */){
+              const c = vth.xCursor(pCursor);
+              c._rowId = 0;
+              const list = vth.sqlite3ValuesToJs(argc, argv);
+              T.assert(argc === list.length);
+              c.dispose();
+            },
+            xBestIndex: function(pVtab, pIdxInfo){
+              //const t = vth.xVtab(pVtab);
+              const pii = vth.xIndexInfo(pIdxInfo);
+              pii.$estimatedRows = 10;
+              pii.$estimatedCost = 10.0;
+              pii.dispose();
+            }
+          }/*methods*/
+        };
+        const doEponymous =
+        /* Bug (somewhere): non-eponymous is behaving as is
+           the call to sqlite3_create_module() is missing
+           or failed: 
+
+           SQL TRACE #63 create virtual table testvtab2 using vtab2test(arg1, arg2)
+
+           => sqlite3 result code 1: no such module: vtab2test
+        */ true;
+        if(doEponymous){
+          warn("Reminder: non-eponymous mode is still not working here.",
+              "Details are in the code comments.");
+          modConfig.methods.xCreate = 0;
+        }else{
+          modConfig.methods.xCreate = (...args)=>0;
+        }
+        const tmplMod = vth.setupModule(modConfig);
+        T.assert(tmplMod instanceof capi.sqlite3_module)
+          .assert(1===tmplMod.$iVersion);
+        if(doEponymous){
+          if(modConfig.methods.xCreate !== 0){
+            T.assert(modConfig.methods.xCreate === modConfig.methods.xConnect)
+              .assert(tmplMod.$xCreate === tmplMod.$xConnect);
           }else{
-            log("No BigInt support. Skipping related tests.");
-            log("\"The problem\" here is that we can manipulate, at the byte level,",
-                "heap memory to set 64-bit values, but we can't get those values",
-                "back into JS because of the lack of 64-bit integer support.");
+            T.assert(0 === tmplMod.$xCreate);
           }
-        }finally{
-          const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
-          //log("x=",x,"y=",y,"z=",z); // just looking at the alignment
-          w.scopedAllocPop(stack);
         }
+        this.db.onclose.disposeThese.push(tmplMod);
+        this.db.checkRc(capi.sqlite3_create_module(
+          this.db, modConfig.name, tmplMod, 0
+        ));
+        if(!doEponymous){
+          this.db.exec([
+            "create virtual table testvtab2 using ",
+            modConfig.name,
+            "(arg1, arg2)"
+          ]);
+        }
+        const list = this.db.selectArrays(
+          ["SELECT a,b FROM ",
+           (doEponymous ? modConfig.name : "testvtab2"),
+           " where a<9999 and b>1 order by a, b"
+          ]/* Query is shaped so that it will ensure that some
+              constraints end up in xBestIndex(). */
+        );
+        T.assert(10===list.length)
+          .assert(1000===list[0][0])
+          .assert(2009===list[list.length-1][1])
       }
-    }/* jaccwabyt-specific tests */)
+    })/*custom vtab #2*/
 
+  ////////////////////////////////////////////////////////////////////////
     .t('Close db', function(){
       T.assert(this.db).assert(wasm.isPtr(this.db.pointer));
       wasm.sqlite3_wasm_db_reset(this.db);
index e691430191bf42e90b64b9adb23870a5408a37cb..8786729db6d30545b563a387330f933199b84f5d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\saddOnDispose()\smethod\sto\sJaccwabyt\sand\scode-adjacent\sminor\sinternal\scleanups.
-D 2022-12-07T03:42:39.134
+C Work\son\san\salternate\s(slightly\ssimpler)\sapproach\sto\sbinding\sJS\svtabs.\sNon-eponymous\svtabs\sare\snot\sworking,\sfor\sreasons\sas\syet\sunknown.
+D 2022-12-07T07:22:34.835
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -505,11 +505,11 @@ F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a9578430388
 F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
 F ext/wasm/api/sqlite3-api-glue.js 8fa55af37c9880f94a803f32591dc0304750cc2f750048daf41fe942757bee64
 F ext/wasm/api/sqlite3-api-oo1.js 416e6398721a4cbb80ddfa3d7b303216790f1d344efdbbc36239d39abc66aa27
-F ext/wasm/api/sqlite3-api-prologue.js a7596c30392d9ca8f5b7c14feb4e6788107d1159fd5f90eb26708d653d36c9bc
+F ext/wasm/api/sqlite3-api-prologue.js 1380e933325c11786b2afc93fc8ff88c2fd1ffeac3e0081da35e5a7317f20e09
 F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
 F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
 F ext/wasm/api/sqlite3-opfs-async-proxy.js f79dd8d98ef3e0b55c10bb2bee7a3840fa967318e1f577c156aafc34664271d1
-F ext/wasm/api/sqlite3-v-helper.js 2125255f102ab07a2653d74d4931d716b0d27c5a89bebc188ad828de0dd1dcef
+F ext/wasm/api/sqlite3-v-helper.js c0c56d4fb1272140629ac858297a825e0a8e04005df92c70ef4a4aa75d4d4645
 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 8ec510fee735c646fb18a3b99f0ca5ca461f9e066c43cdc404d7144f12ae6ed6
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
 F ext/wasm/api/sqlite3-wasm.c 723522a6c2a2463884a83fa1cc7ae5770deaaf0856a1058cc1023b2bfa1c898b
@@ -539,7 +539,7 @@ F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5
 F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
 F ext/wasm/index-dist.html c806b6005145b71d64240606e9c6e0bf56878ee8829c66fe7486cebf34b0e6b1
 F ext/wasm/index.html f151b7c7b5cfdc066567d556acd168e769efd4e982286dc5f849a5ee69ecd0ff
-F ext/wasm/jaccwabyt/jaccwabyt.js b7efd07ea3927e9d0bc75b3819c6d40bdfb0a03cbc8d93331be16799f35261c3
+F ext/wasm/jaccwabyt/jaccwabyt.js 06f2ef1ad640c26c593def3d960336e9bb789819b920516480895c38ed5f58fa
 F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb
 F ext/wasm/module-symbols.html 980680c8acfa3c8ae6a5aa223512d1b8e78040ced20f8ba2c382129bc73ec028
 F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
@@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
 F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
 F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9
 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406
-F ext/wasm/tester1.c-pp.js 8144e0e0f76b14fcd5223fad190bd94a50caf19b2419848b782448cda3ccbc78
+F ext/wasm/tester1.c-pp.js 661c9461fa104f231ff9e767c89ba1fc4a4af6a61db076772ca634d562afd35d
 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
 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.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 0d77c348039926c24e0fb50a7dc7e4b62895cd201c021f8e29832917e4b8b09f
-R c60d510be7a6225cada64eb18dd4b778
+P 6a2723fe3f28dd94328d901e64e1e9ee9a1b2e9eeaed6c54038a5b83c914db78
+R 8623f61e63ac3c807b096f8d7028ccc0
 U stephan
-Z 9a43ff51fca7fb341935fe3d300c9a6e
+Z 80cda1da5b829b2851b20f834a26edf8
 # Remove this line to create a well-formed Fossil manifest.
index ae8e40499be2117296225eb6ebeef30c24befe02..c832b9405dfe956db14b51c317d836bb5ddd7093 100644 (file)
@@ -1 +1 @@
-6a2723fe3f28dd94328d901e64e1e9ee9a1b2e9eeaed6c54038a5b83c914db78
\ No newline at end of file
+6a0fefb93bcccd950df211cf5c2f49660c7b92115dd01b2b508a4ab9e3ab3d23
\ No newline at end of file