]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
wasm binding: consolidated the two sqlite3_prepare_v2() bindings behind a single...
authorstephan <stephan@noemail.net>
Sat, 25 Jun 2022 02:37:57 +0000 (02:37 +0000)
committerstephan <stephan@noemail.net>
Sat, 25 Jun 2022 02:37:57 +0000 (02:37 +0000)
FossilOrigin-Name: ab3e50dab4d71557ab5d179bbd6caf7fb61ab7c51dffc8e4714441c189ce3e5c

ext/fiddle/sqlite3-api.js
ext/fiddle/testing1.js
manifest
manifest.uuid

index 5057eb53f817ae01682121463b1856dfa775d956..3ee57f68181c50442980d8d1786375f2138aceb1 100644 (file)
@@ -166,9 +166,29 @@ Module.postRun.push(function(namespace/*the module object, the target for
         SQLITE_INNOCUOUS: 0x000200000,
         /* sqlite encodings, used for creating UDFs, noting that we
            will only support UTF8. */
-        SQLITE_UTF8: 1
+        SQLITE_UTF8: 1,
+        /* Values for the final argument of sqlite3_result_blob(),
+           noting that these are interpreted in WASM as pointer
+           values. */
+        SQLITE_TRANSIENT: -1,
+        SQLITE_STATIC: 0,
+
+        /**
+           Holds state which are specific to the WASM-related
+           infrastructure and glue code. It is not expected that client
+           code will normally need these, but they're exposed here in case it
+           does.
+        */
+        wasm: {
+            /**
+               Proxy for SQM.allocate(). TODO: remove deprecated allocate(),
+               use _malloc(). The kicker is that allocate() uses
+               module-init-internal state which isn't normally visible to
+               us.
+            */
+            allocate: (slab, allocator=SQM.ALLOC_NORMAL)=>SQM.allocate(slab, allocator)
+        }
     };
-    const cwrap = SQM.cwrap;
     [/* C-side functions to bind. Each entry is an array with 3 or 4
         elements:
         
@@ -219,14 +239,8 @@ Module.postRun.push(function(namespace/*the module object, the target for
         ["sqlite3_open", "number", ["string", "number"]],
         //["sqlite3_open_v2", "number", ["string", "number", "number", "string"]],
         //^^^^ TODO: add the flags needed for the 3rd arg
-        ["sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"]],
-        ["sqlite3_prepare_v2_sqlptr", "sqlite3_prepare_v2",
-         /* Impl which requires that the 2nd argument be a pointer to
-            the SQL string, instead of being converted to a
-            string. This is used for cases where we require a non-NULL
-            value for the final argument (exec()'ing multiple
-            statements from one input string). */
-         "number", ["number", "number", "number", "number", "number"]],
+        /* sqlite3_prepare_v2() is handled separately due to us requiring two
+           different sets of semantics for that function. */
         ["sqlite3_reset", "number", ["number"]],
         ["sqlite3_result_blob",null,["number", "number", "number", "number"]],
         ["sqlite3_result_double",null,["number", "number"]],
@@ -245,7 +259,104 @@ Module.postRun.push(function(namespace/*the module object, the target for
         //["sqlite3_normalized_sql", "string", ["number"]]
     ].forEach(function(a){
         const k = (4==a.length) ? a.shift() : a[0];
-        api[k] = cwrap.apply(this, a);
+        api[k] = SQM.cwrap.apply(this, a);
+    });
+
+    /**
+       Proxies for variants of sqlite3_prepare_v2() which have
+       differing JS/WASM binding semantics.
+    */
+    const prepareMethods = {
+        /**
+           This binding expects a JS string as its 2nd argument and
+           null as its final argument. In order to compile multiple
+           statements from a single string, the "full" impl (see
+           below) must be used.
+        */
+        basic: SQM.cwrap('sqlite3_prepare_v2',
+                         "number", ["number", "string", "number"/*MUST always be negative*/,
+                                    "number", "number"/*MUST be 0 or null or undefined!*/]),
+         /* Impl which requires that the 2nd argument be a pointer to
+            the SQL string, instead of being converted to a
+            string. This variant is necessary for cases where we
+            require a non-NULL value for the final argument
+            (exec()'ing multiple statements from one input
+            string). For simpler cases, where only the first statement
+            in the SQL string is required, the wrapper named
+            sqlite3_prepare_v2() is sufficient and easier to use
+            because it doesn't require dealing with pointers.
+
+            TODO: hide both of these methods behind a single hand-written 
+            sqlite3_prepare_v2() wrapper which dispatches to the appropriate impl.
+         */
+        full: SQM.cwrap('sqlite3_prepare_v2',
+                        "number", ["number", "number", "number"/*MUST always be negative*/,
+                                   "number", "number"]),
+    };
+
+    const uint8ToString = (str)=>new TextDecoder('utf-8').decode(str);
+    //const stringToUint8 = (sql)=>new TextEncoder('utf-8').encode(sql);
+
+    /**
+       sqlite3_prepare_v2() binding which handles two different uses
+       with differing JS/WASM semantics:
+
+       1) sqlite3_prepare_v2(pDb, sqlString, -1, ppStmt [, null])
+
+       2) sqlite3_prepare_v2(pDb, sqlPointer, -1, ppStmt, sqlPointerToPointer)
+
+       Note that the SQL length argument (the 3rd argument) must
+       always be negative because it must be a byte length and that
+       value is expensive to calculate from JS (where we get the
+       character length of strings). It is retained in this API's
+       interface for code/documentation compatibility reasons but is
+       currently _always_ ignored. When using the 2nd form of this
+       call, it is critical that the custom-allocated string be
+       terminated with a 0 byte. (Potential TODO: if this value is >0,
+       assume the caller knows precisely what they're doing and pass
+       it on as-is. That approach currently seems fraught with peril.)
+
+       In usage (1), the 2nd argument must be of type string or
+       Uint8Array (which is assumed to hold SQL). If it is, this
+       function assumes case (1) and calls the underling C function
+       with:
+
+       (pDb, sql, -1, ppStmt, null)
+
+       If sql is not a string or Uint8Array, it must be a _pointer_ to
+       a string which was allocated via api.wasm.allocateUTF8OnStack()
+       or equivalent (TODO: define "or equivalent"). In that case, the
+       final argument may be 0/null/undefined or must be a pointer to
+       which the "tail" of the compiled SQL is written, as documented
+       for the C-side sqlite3_prepare_v2(). In case (2), the
+       underlying C function is called with:
+
+       (pDb, sql, -1, ppStmt, pzTail)
+
+       It returns its result and compiled statement as documented in
+       the C API. Fetching the output pointer (4th argument) requires using
+       api.wasm.getValue().
+    */
+    api.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){
+        if(sql instanceof Uint8Array) sql = uint8ToString(sql);
+        /* ^^^ TODO: confirm whether this conversion is really
+           necessary or whether passing on the array as-is will work
+           as if it were a string. */
+        switch(typeof sql){
+            case 'string': return prepareMethods.basic(pDb, sql, -1, ppStmt, null);
+            case 'number': return prepareMethods.full(pDb, sql, -1, ppStmt, pzTail);
+            default: toss("Invalid SQL argument type for sqlite3_prepare_v2().");
+        }
+    };
+
+    /** Populate api.wasm... */
+    ['getValue','setValue', 'stackSave', 'stackRestore', 'stackAlloc',
+     'allocateUTF8OnStack', '_malloc', '_free',
+     'addFunction', 'removeFunction'     
+    ].forEach(function(m){
+        if(undefined === (api.wasm[m] = SQM[m])){
+            toss("Internal init error: Module."+m+" not found.");
+        }
     });
 
     /* What follows is colloquially known as "OO API #1". It is a
@@ -255,8 +366,6 @@ Module.postRun.push(function(namespace/*the module object, the target for
        the sqlite3 binding if, e.g., the wrapper is in the main thread
        and the sqlite3 API is in a worker. */
 
-    /** Memory for use in some pointer-to-pointer-passing routines. */
-    const pPtrArg = stackAlloc(4);
     /** Throws a new error, concatenating all args with a space between
         each. */
     const toss = function(){
@@ -322,9 +431,12 @@ Module.postRun.push(function(namespace/*the module object, the target for
             }
             FS.createDataFile("/", fn, buffer, true, true);
         }
-        setValue(pPtrArg, 0, "i32");
-        this.checkRc(api.sqlite3_open(fn, pPtrArg));
-        this._pDb = getValue(pPtrArg, "i32");
+        const stack = api.wasm.stackSave();
+        const ppDb  = api.wasm.stackAlloc(4) /* output (sqlite3**) arg */;
+        api.wasm.setValue(ppDb, 0, "i32");
+        try {this.checkRc(api.sqlite3_open(fn, ppDb));}
+        finally{api.wasm.stackRestore(stack);}
+        this._pDb = api.wasm.getValue(ppDb, "i32");
         this.filename = fn;
         this._statements = {/*map of open Stmt _pointers_ to Stmt*/};
         this._udfs = {/*map of UDF names to wasm function _pointers_*/};
@@ -378,7 +490,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
        DB.execMulti(). Does the argument processing/validation, throws
        on error, and returns a new object on success:
 
-       { sql: the SQL, obt: optionsObj, cbArg: function}
+       { sql: the SQL, opt: optionsObj, cbArg: function}
 
        cbArg is only set if the opt.callback is set, in which case
        it's a function which expects to be passed the current Stmt
@@ -386,12 +498,13 @@ Module.postRun.push(function(namespace/*the module object, the target for
        the input arguments.
     */
     const parseExecArgs = function(args){
-        const out = {};
+        const out = {opt:{}};
         switch(args.length){
             case 1:
                 if('string'===typeof args[0]){
                     out.sql = args[0];
-                    out.opt = {};
+                }else if(args[0] instanceof Uint8Array){
+                    out.sql = args[0];
                 }else if(args[0] && 'object'===typeof args[0]){
                     out.opt = args[0];
                     out.sql = out.opt.sql;
@@ -403,7 +516,11 @@ Module.postRun.push(function(namespace/*the module object, the target for
             break;
             default: toss("Invalid argument count for exec().");
         };
-        if('string'!==typeof out.sql) toss("Missing SQL argument.");
+        if(out.sql instanceof Uint8Array){
+            out.sql = uint8ToString(out.sql);
+        }else if('string'!==typeof out.sql){
+            toss("Missing SQL argument.");
+        }
         if(out.opt.callback || out.opt.resultRows){
             switch((undefined===out.opt.rowMode)
                    ? 'stmt' : out.opt.rowMode) {
@@ -453,7 +570,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
                     delete that._statements[k];
                     if(s && s._pStmt) s.finalize();
                 });
-                Object.values(this._udfs).forEach(SQM.removeFunction);
+                Object.values(this._udfs).forEach(api.wasm.removeFunction);
                 delete this._udfs;
                 delete this._statements;
                 api.sqlite3_close_v2(this._pDb);
@@ -481,13 +598,21 @@ Module.postRun.push(function(namespace/*the module object, the target for
         /**
            Compiles the given SQL and returns a prepared Stmt. This is
            the only way to create new Stmt objects. Throws on error.
+
+           The given SQL must be a string, a Uint8Array holding SQL,
+           or a WASM pointer to memory allocated using
+           api.wasm.allocateUTF8OnStack() (or equivalent (a term which
+           is yet to be defined precisely)).
         */
         prepare: function(sql){
             affirmDbOpen(this);
-            setValue(pPtrArg,0,"i32");
-            this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null));
-            const pStmt = getValue(pPtrArg, "i32");
-            if(!pStmt) toss("Empty SQL is not permitted.");
+            const stack = api.wasm.stackSave();
+            const ppStmt  = api.wasm.stackAlloc(4)/* output (sqlite3_stmt**) arg */;
+            api.wasm.setValue(ppStmt, 0, "i32");
+            try {this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, ppStmt, null));}
+            finally {api.wasm.stackRestore(stack);}
+            const pStmt = api.wasm.getValue(ppStmt, "i32");
+            if(!pStmt) toss("Cannot prepare empty SQL.");
             const stmt = new Stmt(this, pStmt, BindTypes);
             this._statements[pStmt] = stmt;
             return stmt;
@@ -585,7 +710,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
            properties:
 
            - .sql = the SQL to run (unless it's provided as the first
-             argument).
+             argument). This must be of type string or Uint8Array.
 
            - .bind = a single value valid as an argument for
              Stmt.bind(). This is ONLY applied to the FIRST non-empty
@@ -638,23 +763,27 @@ Module.postRun.push(function(namespace/*the module object, the target for
                          ? arguments[0] : parseExecArgs(arguments));
             if(!arg.sql) return this;
             const opt = arg.opt;
-            const stack = stackSave();
+            const stack = api.wasm.stackSave();
             let stmt;
             let bind = opt.bind;
             let rowMode = (
                 (opt.callback && opt.rowMode)
                     ? opt.rowMode : false);
             try{
-                let pSql = SQM.allocateUTF8OnStack(arg.sql)
-                const pzTail = stackAlloc(4);
-                while(getValue(pSql, "i8")){
-                    setValue(pPtrArg, 0, "i32");
-                    setValue(pzTail, 0, "i32");
-                    this.checkRc(api.sqlite3_prepare_v2_sqlptr(
-                        this._pDb, pSql, -1, pPtrArg, pzTail
+                const sql = (arg.sql instanceof Uint8Array)
+                      ? uint8ToString(arg.sql)
+                      : arg.sql;
+                let pSql = api.wasm.allocateUTF8OnStack(sql)
+                const ppStmt  = api.wasm.stackAlloc(8) /* output (sqlite3_stmt**) arg */;
+                const pzTail = ppStmt + 4 /* final arg to sqlite3_prepare_v2_sqlptr() */;
+                while(api.wasm.getValue(pSql, "i8")){
+                    api.wasm.setValue(ppStmt, 0, "i32");
+                    api.wasm.setValue(pzTail, 0, "i32");
+                    this.checkRc(api.sqlite3_prepare_v2(
+                        this._pDb, pSql, -1, ppStmt, pzTail
                     ));
-                    const pStmt = getValue(pPtrArg, "i32");
-                    pSql = getValue(pzTail, "i32");
+                    const pStmt = api.wasm.getValue(ppStmt, "i32");
+                    pSql = api.wasm.getValue(pzTail, "i32");
                     if(!pStmt) continue;
                     if(opt.saveSql){
                         opt.saveSql.push(api.sqlite3_sql(pStmt).trim());
@@ -683,7 +812,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
                     delete stmt._isLocked;
                     stmt.finalize();
                 }
-                stackRestore(stack);
+                api.wasm.stackRestore(stack);
             }
             return this;
         }/*execMulti()*/,
@@ -736,7 +865,6 @@ Module.postRun.push(function(namespace/*the module object, the target for
            - .directOnly = SQLITE_DIRECTONLY
            - .innocuous = SQLITE_INNOCUOUS
 
-
            Maintenance reminder: the ability to add new
            WASM-accessible functions to the runtime requires that the
            WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
@@ -769,7 +897,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
                     let i, pVal, valType, arg;
                     const tgt = [];
                     for(i = 0; i < argc; ++i){
-                        pVal = getValue(pArgv + (4 * i), "i32");
+                        pVal = api.wasm.getValue(pArgv + (4 * i), "i32");
                         valType = api.sqlite3_value_type(pVal);
                         switch(valType){
                             case api.SQLITE_INTEGER:
@@ -806,8 +934,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
                             break;
                         }
                         case 'string':
-                            api.sqlite3_result_text(pCx, val, -1,
-                                                  -1/*==SQLITE_TRANSIENT*/);
+                            api.sqlite3_result_text(pCx, val, -1, api.SQLITE_TRANSIENT);
                             break;
                         case 'object':
                             if(null===val) {
@@ -815,9 +942,10 @@ Module.postRun.push(function(namespace/*the module object, the target for
                                 break;
                             }else if(undefined!==val.length){
                                 const pBlob =
-                                      SQM.allocate(val, SQM.ALLOC_NORMAL);
-                                api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/);
-                                SQM._free(blobptr);
+                                      api.wasm.allocate(val);
+                                api.sqlite3_result_blob(pCx, pBlob, val.length,
+                                                        api.SQLITE_TRANSIENT);
+                                api.wasm._free(blobptr);
                                 break;
                             }
                             // else fall through
@@ -833,7 +961,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
                     api.sqlite3_result_error(pCx, e.message, -1);
                 }
             };
-            const pUdf = SQM.addFunction(wrapper, "viii");
+            const pUdf = api.wasm.addFunction(wrapper, "viii");
             let fFlags = 0;
             if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC;
             if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY;
@@ -846,11 +974,11 @@ Module.postRun.push(function(namespace/*the module object, the target for
                     api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
                     null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
             }catch(e){
-                SQM.removeFunction(pUdf);
+                api.wasm.removeFunction(pUdf);
                 throw e;
             }
             if(this._udfs.hasOwnProperty(name)){
-                SQM.removeFunction(this._udfs[name]);
+                api.wasm.removeFunction(this._udfs[name]);
             }
             this._udfs[name] = pUdf;
             return this;
@@ -999,7 +1127,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
             f._ = {
                 string: function(stmt, ndx, val, asBlob){
                     const bytes = intArrayFromString(val,true);
-                    const pStr = SQM.allocate(bytes, ALLOC_NORMAL);
+                    const pStr = api.wasm.allocate(bytes);
                     stmt._allocs.push(pStr);
                     const func =  asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text;
                     return func(stmt._pStmt, ndx, pStr, bytes.length, 0);
@@ -1038,7 +1166,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
                         toss("Binding a value as a blob requires",
                              "that it have a length member.");
                     }
-                    const pBlob = SQM.allocate(val, ALLOC_NORMAL);
+                    const pBlob = api.wasm.allocate(val);
                     stmt._allocs.push(pBlob);
                     rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
                 }
@@ -1054,7 +1182,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
     const freeBindMemory = function(stmt){
         let m;
         while(undefined !== (m = stmt._allocs.pop())){
-            SQM._free(m);
+            api.wasm._free(m);
         }
         return stmt;
     };
@@ -1481,7 +1609,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
 
     if(self === self.window){
         /* This is running in the main window thread, so we're done. */
-        setTimeout(()=>postMessage({type:'sqlite3-api',data:'loaded'}), 0);
+        postMessage({type:'sqlite3-api',data:'loaded'});
         return;
     }
     /******************************************************************
@@ -1489,14 +1617,15 @@ Module.postRun.push(function(namespace/*the module object, the target for
      in Worker threads.
     ******************************************************************/
 
-    /*
+    /**
       UNDER CONSTRUCTION
 
       We need an API which can proxy the DB API via a Worker message
       interface. The primary quirky factor in such an API is that we
       cannot pass callback functions between the window thread and a
       worker thread, so we have to receive all db results via
-      asynchronous message-passing.
+      asynchronous message-passing. That requires an asychronous API
+      with a distinctly different shape that the main OO API.
 
       Certain important considerations here include:
 
@@ -1777,5 +1906,5 @@ Module.postRun.push(function(namespace/*the module object, the target for
         wState.post(evType, response, wMsgHandler.xfer);
     };
 
-    setTimeout(()=>postMessage({type:'sqlite3-api',data:'loaded'}), 0);
-});
+    postMessage({type:'sqlite3-api',data:'loaded'});
+})/*postRun.push(...)*/;
index a6f0062dcc18051df6dba76903184737d8c42cee..ceea0e62000a7c2a13a8a1ccd48769272fe798e5 100644 (file)
         const api = sqlite3.api;
         log("Basic sanity tests...");
         T.assert(db._pDb);
-        let st = db.prepare("select 3 as a");
+        let st = db.prepare(
+            new TextEncoder('utf-8').encode("select 3 as a")
+            /* Testing handling of Uint8Array input */
+        );
         //log("statement =",st);
         T.assert(st._pStmt)
             .assert(!st._mayGet)
@@ -47,6 +50,7 @@
             .assert(3.0 === st.get(0,api.SQLITE_FLOAT))
             .assert(3.0 === st.getFloat(0))
             .assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array)
+            .assert(1===st.get(0,api.SQLITE_BLOB).length)
             .assert(st.getBlob(0) instanceof Uint8Array)
             .assert(3 === st.get([])[0])
             .assert(3 === st.get({}).a)
@@ -72,7 +76,7 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
         //log("Exec'd SQL:", list);
         let counter = 0, colNames = [];
         list.length = 0;
-        db.exec("SELECT a a, b b FROM t",{
+        db.exec(new TextEncoder('utf-8').encode("SELECT a a, b b FROM t"),{
             rowMode: 'object',
             resultRows: list,
             columnNames: colNames,
index 621dd03662f344d8ff99bad4bff7afea233c0902..dffd8233a84f23186c8de10454d70bda32963987 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sharmless\sUBSAN\swarning\sassociated\swith\sPRAGMA\sschema_version\nfound\sby\sOSSFuzz.
-D 2022-06-24T12:56:48.588
+C wasm\sbinding:\sconsolidated\sthe\stwo\ssqlite3_prepare_v2()\sbindings\sbehind\sa\ssingle\sdispathcer.\sVarious\sinternal\scleanups\sand\srefactoring.\sBranched\sbecause\strunk\sis\sin\spencils-down\smode\sfor\spending\s3.39\srelease.
+D 2022-06-25T02:37:57.122
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -65,11 +65,11 @@ F ext/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba83
 F ext/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
 F ext/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8
 F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
-F ext/fiddle/sqlite3-api.js ccf4bd0c1c5bbb3be3469573423d6c53991941bec497eac63e9f17ea13bf8952
+F ext/fiddle/sqlite3-api.js aff4fe5583d02cb7207cc27a92d2cc708fca8a0eba14339f519059298f6bbc5e
 F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421
 F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
 F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a
-F ext/fiddle/testing1.js e2fa02ac8adbd21c69bc50cfcb79bfc26af0d30a8d6b95ac473a17e0dc9de733
+F ext/fiddle/testing1.js 2ba8d14b335cdfc694757c25a3ffcfa76f4696bb21187db5c12ea6e505c44af0
 F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4
 F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee
 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@@ -1978,8 +1978,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P db5266dec601a9513bc8dd09a9f8bb4aef55b780d22610946099e8edd4836587
-R 09efcead2b1be4144ef279312bf3e194
-U drh
-Z 1fc91f4cca35906afdee86eebc313610
+P e93fd170ce4ae91d572c46d03f68f55d00091d0188030517455017d90d212587
+R 3edce7228a2d729bf332a89ed3af6305
+T *branch * wasm-cleanups
+T *sym-wasm-cleanups *
+T -sym-trunk * Cancelled\sby\sbranch.
+U stephan
+Z e1adc8db1d84e5f34db3da1c68e91f04
 # Remove this line to create a well-formed Fossil manifest.
index 1a1f8000667182639455b3450030496b6edf9cfd..e24c1ef14ba6ac8e834be5f754044021b0e8ca97 100644 (file)
@@ -1 +1 @@
-e93fd170ce4ae91d572c46d03f68f55d00091d0188030517455017d90d212587
\ No newline at end of file
+ab3e50dab4d71557ab5d179bbd6caf7fb61ab7c51dffc8e4714441c189ce3e5c
\ No newline at end of file