Merge pull request #30 from danleh/sqlite-scoring

Move sqlite Wasm benchmark to first, worst, average scoring
diff --git a/JetStreamDriver.js b/JetStreamDriver.js
index e6abc30..35bb018 100644
--- a/JetStreamDriver.js
+++ b/JetStreamDriver.js
@@ -2001,17 +2001,17 @@
         worstCaseCount: 2,
         testGroup: WasmGroup
     }),
-    new WasmLegacyBenchmark({
+    new WasmEMCCBenchmark({
         name: "sqlite3-wasm",
         files: [
-            "./sqlite3/polyfills.js",
-            "./sqlite3/build/jswasm/speedtest1.js",
             "./sqlite3/benchmark.js",
+            "./sqlite3/build/jswasm/speedtest1.js",
         ],
         preload: {
             wasmBinary: "./sqlite3/build/jswasm/speedtest1.wasm"
         },
-        benchmarkClass: WasmLegacyBenchmark,
+        iterations: 15,
+        worstCaseCount: 2,
         testGroup: WasmGroup
     }),
     new WasmLegacyBenchmark({
diff --git a/sqlite3/benchmark.js b/sqlite3/benchmark.js
index 1ac0a45..9de15dd 100644
--- a/sqlite3/benchmark.js
+++ b/sqlite3/benchmark.js
@@ -2,18 +2,45 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-const inJetStreamRunner = typeof globalThis.benchmarkTime !== "undefined";
-if (!inJetStreamRunner) {
-  load("polyfills.js");
+// First, some polyfills for missing browser APIs in JavaScript shells.
+// Since the generated JavaScript code of SQLite immediately uses some of them,
+// we need to load and run this code before the generated `speedtest1.js` in the
+// JetStream driver.
 
-  // Exports `sqlite3InitModule()` and contains the main code.
-  load("build/jswasm/speedtest1.js");
+// Empty `URLSearchParams` has just the same interface as a `Map`.
+globalThis.URLSearchParams = Map;
 
-  // Load Wasm binary from disk.
-  globalThis.Module = {
-    wasmBinary: read("build/jswasm/speedtest1.wasm", "binary"),
-  };
+// `TextEncoder` and `TextDecoder`. These are called only a few times with short
+// ASCII strings, so this is sufficient and not performance-critical.
+class TextEncoder {
+  encode(string) {
+    return Uint8Array.from(string, (char) => {
+      let byte = char.codePointAt(0);
+      if (byte > 0x7f)
+        throw new Error("TextEncoder polyfill only supports ASCII");
+      return byte;
+    });
+  }
 }
+class TextDecoder {
+  decode(array) {
+    for (let byte of array) {
+      if (byte > 0x7f)
+        throw new Error("TextDecoder polyfill only supports ASCII");
+    }
+    return String.fromCharCode.apply(null, array);
+  }
+}
+
+// Now, some configuration options for when we initialize SQLite.
+
+// Use JetStream functions instead of `console.log` and friends.
+globalThis.sqlite3ApiConfig = {
+  log: print,
+  debug: print,
+  warn: print,
+  error: print,
+};
 
 // Make sure we never initialize OPFS by removing one of it's APIs (see
 // `installOpfsVfs` in the generated JavaScript code of sqlite).
@@ -21,53 +48,51 @@
 // waste cycles on the browser runner to initialize it.
 delete globalThis.FileSystemHandle;
 
-// Simplified from inline JavaScript in `speedtest1.html`.
-function runTests(sqlite3Module) {
-  // Configure the VFS to use.
-  // Don't use OPFS, WASMFS (which is on top of OPFS), or kvvfs, since they all
-  // use persistent browser storage (localStorage or OPFS), which is not
-  // available in JavaScript shells.
-  // Also don't use memfs, since that crashes with a NULL function pointer.
-  // Instead, make the default VFS explicit.
-  const capi = sqlite3Module.capi
-  console.log("Available SQLite VFS:", capi.sqlite3_js_vfs_list());
-  const vfs = "unix";
-  console.log("Using VFS:", vfs);
-  const pVfs = capi.sqlite3_vfs_find(vfs);
-  if (!pVfs) {
-    console.error("Error: Unknown VFS:", vfs);
-    return;
+class Benchmark {
+  sqlite3Module;
+
+  async runIteration() {
+    if (!this.sqlite3Module) {
+      // Defined in the generated SQLite JavaScript code.
+      // Different in details but seemingly related/inspired by Emscripten code.
+      this.sqlite3Module = await sqlite3InitModule(Module);
+    }
+
+    // The following is simplified from inline JavaScript in `speedtest1.html`.
+    
+    // Configure the VFS to use.
+    // Don't use OPFS, WASMFS (which is on top of OPFS), or kvvfs, since they
+    // all use persistent browser storage (localStorage or OPFS), which is not
+    // available in JavaScript shells.
+    // Also don't use memfs, since that crashes with a NULL function pointer.
+    // Instead, make the default VFS explicit.
+    const capi = this.sqlite3Module.capi
+    print("Available SQLite VFS:", capi.sqlite3_js_vfs_list());
+    const vfs = "unix";
+    print("Using VFS:", vfs);
+    const pVfs = capi.sqlite3_vfs_find(vfs);
+    if (!pVfs) {
+      throw new Error("Unknown VFS:", vfs);
+    }
+
+    // These arguments should match the upstream browser runner in 
+    // `speedtest1.html`, except for the --size parameter.
+    let argv = [
+      "speedtest1",
+      "--singlethread",
+      //"--nomutex",
+      //"--nosync",
+      //"--memdb", // note that memdb trumps the filename arg
+      "--nomemstat",
+      "--big-transactions" /*important for tests 410 and 510!*/,
+      "--size", "10", // To speedup, default is 100 (and takes about 4s).
+      "--vfs", vfs, // See VFS comment above.
+    ];
+
+    print("Calling main with argv:", argv);
+    const wasm = this.sqlite3Module.wasm;
+    wasm.scopedAllocPush();  // Required for `scopedAllocMainArgv()`.
+    wasm.xCall("wasm_main", argv.length, wasm.scopedAllocMainArgv(argv));
+    wasm.scopedAllocPop();
   }
-
-  // These arguments should match the upstream browser runner `speedtest1.html`.
-  let argv = [
-    "speedtest1",
-    "--singlethread",
-    //"--nomutex",
-    //"--nosync",
-    //"--memdb", // note that memdb trumps the filename arg
-    "--nomemstat",
-    "--big-transactions" /*important for tests 410 and 510!*/,
-    "--size", "20", // To speedup, default is 100 (and takes about 4s).
-    "--vfs", vfs, // See VFS comment above.
-  ];
-
-  console.log("Calling main with argv:", argv);
-  const wasm = sqlite3Module.wasm;
-  wasm.scopedAllocPush();  // Required for `scopedAllocMainArgv()`.
-  wasm.xCall("wasm_main", argv.length, wasm.scopedAllocMainArgv(argv));
-  wasm.scopedAllocPop();
-}
-
-async function doRun() {
-  let start = benchmarkTime();
-  const sqliteModule = await sqlite3InitModule(Module);
-  reportCompileTime(benchmarkTime() - start);
-
-  start = benchmarkTime();
-  runTests(sqliteModule);
-  reportRunTime(benchmarkTime() - start);
-}
-if (!inJetStreamRunner) {
-  sqlite3InitModule(Module).then(runTests);
 }
diff --git a/sqlite3/build.log b/sqlite3/build.log
index 8c57c22..c32b220 100644
--- a/sqlite3/build.log
+++ b/sqlite3/build.log
@@ -1,10 +1,10 @@
-Built on 2024-12-05 14:24:19+01:00
+Built on 2025-01-21T15:10:24Z
 
 Toolchain versions
-emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.71 (4171ae200b77a6c266b0e1ebb507d61d1ade3501)
+emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.73 (ac676d5e437525d15df5fd46bc2c208ec6d376a3)
 wasm-strip 1.0.34
 
-Getting sources from https://sqlite.org/2024/sqlite-src-3470100.zip
+Getting sources from https://sqlite.org/2025/sqlite-src-3480000.zip
 
 Building...
 Copying files from sqlite-src-*/ext/wasm/ into build/
diff --git a/sqlite3/build.sh b/sqlite3/build.sh
index ff7b067..27c6142 100755
--- a/sqlite3/build.sh
+++ b/sqlite3/build.sh
@@ -1,10 +1,9 @@
 #!/bin/bash
 
-set -e
-set -o pipefail
+set -eo pipefail
 
 # Cleanup old files.
-rm sqlite-src-*.zip
+rm -f sqlite-src-*.zip
 rm -rf sqlite-src-*/
 rm -rf build/
 
@@ -17,7 +16,7 @@
 echo -e "wasm-strip $(wasm-strip --version)\n" | tee -a "$BUILD_LOG"
 
 # Check https://sqlite.org/download.html and update the source link, if needed.
-SQLITE_SRC_URL="https://sqlite.org/2024/sqlite-src-3470100.zip"
+SQLITE_SRC_URL="https://sqlite.org/2025/sqlite-src-3480000.zip"
 echo -e "Getting sources from $SQLITE_SRC_URL\n" | tee -a "$BUILD_LOG"
 SQLITE_SRC_FILE="$(basename $SQLITE_SRC_URL)"
 curl -o "$SQLITE_SRC_FILE" $SQLITE_SRC_URL
diff --git a/sqlite3/build/jswasm/speedtest1.js b/sqlite3/build/jswasm/speedtest1.js
index ef21275..3d592ba 100644
--- a/sqlite3/build/jswasm/speedtest1.js
+++ b/sqlite3/build/jswasm/speedtest1.js
@@ -26,11 +26,11 @@
 /*
 ** This code was built from sqlite3 version...
 **
-** SQLITE_VERSION "3.47.1"
-** SQLITE_VERSION_NUMBER 3047001
-** SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
+** SQLITE_VERSION "3.48.0"
+** SQLITE_VERSION_NUMBER 3048000
+** SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
 **
-** Using the Emscripten SDK version 3.1.71.
+** Using the Emscripten SDK version 3.1.73.
 */
 
 var sqlite3InitModule = (() => {
@@ -73,7 +73,7 @@
 
 // --pre-jses are emitted after the Module integration code, so that they can
 // refer to Module (if they choose; they can also define Module)
-// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/pre-js.speedtest1-vanilla.js
+// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/pre-js.speedtest1-vanilla.js
 /**
    BEGIN FILE: api/pre-js.js
 
@@ -132,7 +132,7 @@
 /* END FILE: api/pre-js.js, noting that the build process may add a
    line after this one to change the above .uri to a build-specific
    one. */
-// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/pre-js.speedtest1-vanilla.js
+// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/pre-js.speedtest1-vanilla.js
 
 
 // Sometimes an existing Module object exists with properties
@@ -558,7 +558,6 @@
 // Create the wasm instance.
 // Receives the wasm imports, returns the exports.
 function createWasm() {
-  var info = getWasmImports();
   // Load the wasm module and create an instance of using native support in the JS engine.
   // handle a generated wasm instance, receiving its exports and
   // performing other necessary setup
@@ -589,6 +588,8 @@
     receiveInstance(result['instance']);
   }
 
+  var info = getWasmImports();
+
   // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback
   // to manually instantiate the Wasm module themselves. This allows pages to
   // run the instantiation parallel to any other async startup actions they are
@@ -751,9 +752,8 @@
   var UTF8ToString = (ptr, maxBytesToRead) => {
       return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '';
     };
-  var ___assert_fail = (condition, filename, line, func) => {
+  var ___assert_fail = (condition, filename, line, func) =>
       abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']);
-    };
 
   var PATH = {
   isAbs:(path) => path.charAt(0) === '/',
@@ -1149,7 +1149,7 @@
   var MEMFS = {
   ops_table:null,
   mount(mount) {
-        return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0);
+        return MEMFS.createNode(null, '/', 16895, 0);
       },
   createNode(parent, name, mode, dev) {
         if (FS.isBlkdev(mode) || FS.isFIFO(mode)) {
@@ -1304,7 +1304,7 @@
           }
         },
   lookup(parent, name) {
-          throw FS.genericErrors[44];
+          throw MEMFS.doesNotExistError;
         },
   mknod(parent, name, mode, dev) {
           return MEMFS.createNode(parent, name, mode, dev);
@@ -1350,7 +1350,7 @@
           return entries;
         },
   symlink(parent, newname, oldpath) {
-          var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0);
+          var node = MEMFS.createNode(parent, newname, 0o777 | 40960, 0);
           node.link = oldpath;
           return node;
         },
@@ -1590,8 +1590,6 @@
           this.errno = errno;
         }
       },
-  genericErrors:{
-  },
   filesystems:null,
   syncFSRequests:0,
   readFiles:{
@@ -2098,14 +2096,35 @@
         }
         return parent.node_ops.mknod(parent, name, mode, dev);
       },
-  create(path, mode) {
-        mode = mode !== undefined ? mode : 438 /* 0666 */;
+  statfs(path) {
+  
+        // NOTE: None of the defaults here are true. We're just returning safe and
+        //       sane values.
+        var rtn = {
+          bsize: 4096,
+          frsize: 4096,
+          blocks: 1e6,
+          bfree: 5e5,
+          bavail: 5e5,
+          files: FS.nextInode,
+          ffree: FS.nextInode - 1,
+          fsid: 42,
+          flags: 2,
+          namelen: 255,
+        };
+  
+        var parent = FS.lookupPath(path, {follow: true}).node;
+        if (parent?.node_ops.statfs) {
+          Object.assign(rtn, parent.node_ops.statfs(parent.mount.opts.root));
+        }
+        return rtn;
+      },
+  create(path, mode = 0o666) {
         mode &= 4095;
         mode |= 32768;
         return FS.mknod(path, mode, 0);
       },
-  mkdir(path, mode) {
-        mode = mode !== undefined ? mode : 511 /* 0777 */;
+  mkdir(path, mode = 0o777) {
         mode &= 511 | 512;
         mode |= 16384;
         return FS.mknod(path, mode, 0);
@@ -2126,7 +2145,7 @@
   mkdev(path, mode, dev) {
         if (typeof dev == 'undefined') {
           dev = mode;
-          mode = 438 /* 0666 */;
+          mode = 0o666;
         }
         mode |= 8192;
         return FS.mknod(path, mode, dev);
@@ -2224,7 +2243,7 @@
         // do the underlying fs rename
         try {
           old_dir.node_ops.rename(old_node, new_dir, new_name);
-          // update old node (we do this here to avoid each backend 
+          // update old node (we do this here to avoid each backend
           // needing to)
           old_node.parent = new_dir;
         } catch (e) {
@@ -2294,7 +2313,7 @@
         if (!link.node_ops.readlink) {
           throw new FS.ErrnoError(28);
         }
-        return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link));
+        return link.node_ops.readlink(link);
       },
   stat(path, dontFollow) {
         var lookup = FS.lookupPath(path, { follow: !dontFollow });
@@ -2399,13 +2418,12 @@
           timestamp: Math.max(atime, mtime)
         });
       },
-  open(path, flags, mode) {
+  open(path, flags, mode = 0o666) {
         if (path === "") {
           throw new FS.ErrnoError(44);
         }
         flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags;
         if ((flags & 64)) {
-          mode = typeof mode == 'undefined' ? 438 /* 0666 */ : mode;
           mode = (mode & 4095) | 32768;
         } else {
           mode = 0;
@@ -2691,6 +2709,7 @@
         FS.registerDevice(FS.makedev(1, 3), {
           read: () => 0,
           write: (stream, buffer, offset, length, pos) => length,
+          llseek: () => 0,
         });
         FS.mkdev('/dev/null', FS.makedev(1, 3));
         // setup /dev/tty and /dev/tty1
@@ -2724,7 +2743,7 @@
         FS.mkdir('/proc/self/fd');
         FS.mount({
           mount() {
-            var node = FS.createNode(proc_self, 'fd', 16384 | 511 /* 0777 */, 73);
+            var node = FS.createNode(proc_self, 'fd', 16895, 73);
             node.node_ops = {
               lookup(parent, name) {
                 var fd = +name;
@@ -2773,12 +2792,6 @@
         var stderr = FS.open('/dev/stderr', 1);
       },
   staticInit() {
-        // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info)
-        [44].forEach((code) => {
-          FS.genericErrors[code] = new FS.ErrnoError(code);
-          FS.genericErrors[code].stack = '<generic error, no stack>';
-        });
-  
         FS.nameTable = new Array(4096);
   
         FS.mount(MEMFS, {}, '/');
@@ -3837,9 +3850,7 @@
   var ENV = {
   };
   
-  var getExecutableName = () => {
-      return thisProgram || './this.program';
-    };
+  var getExecutableName = () => thisProgram || './this.program';
   var getEnvStrings = () => {
       if (!getEnvStrings.strings) {
         // Default values.
@@ -4059,6 +4070,13 @@
   FS.staticInit();
   // Set module methods based on EXPORTED_RUNTIME_METHODS
   ;
+
+      // This error may happen quite a bit. To avoid overhead we reuse it (and
+      // suffer a lack of stack info).
+      MEMFS.doesNotExistError = new FS.ErrnoError(44);
+      /** @suppress {checkTypes} */
+      MEMFS.doesNotExistError.stack = '<generic error, no stack>';
+      ;
 var wasmImports = {
   /** @export */
   __assert_fail: ___assert_fail,
@@ -4400,7 +4418,7 @@
 
 // end include: postamble.js
 
-// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/post-js.speedtest1-vanilla.js
+// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/post-js.speedtest1-vanilla.js
 /* BEGIN FILE: api/post-js-header.js */
 /**
    post-js-header.js is to be prepended to other code to create
@@ -4461,11 +4479,11 @@
 /*
 ** This code was built from sqlite3 version...
 **
-** SQLITE_VERSION "3.47.1"
-** SQLITE_VERSION_NUMBER 3047001
-** SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
+** SQLITE_VERSION "3.48.0"
+** SQLITE_VERSION_NUMBER 3048000
+** SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
 **
-** Using the Emscripten SDK version 3.1.71.
+** Using the Emscripten SDK version 3.1.73.
 */
 /* END FILE: ./bld/sqlite3-license-version.js */
 /* BEGIN FILE: api/sqlite3-api-prologue.js */
@@ -8723,7 +8741,7 @@
       if(1===argc) return xcvPart.get(typeName);
       else if(2===argc){
         if(!adapter){
-          delete xcvPart.get(typeName);
+          xcvPart.delete(typeName);
           return func;
         }else if(!(adapter instanceof Function)){
           toss(modeName,"requires a function argument.");
@@ -11561,7 +11579,7 @@
 /* END FILE: api/sqlite3-api-glue.c-pp.js */
 /* BEGIN FILE: ./bld/sqlite3-api-build-version.js */
 globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
-  sqlite3.version = {"libVersion": "3.47.1", "libVersionNumber": 3047001, "sourceId": "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e","downloadVersion": 3470100};
+  sqlite3.version = {"libVersion": "3.48.0", "libVersionNumber": 3048000, "sourceId": "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010","downloadVersion": 3480000};
 });
 /* END FILE: ./bld/sqlite3-api-build-version.js */
 /* BEGIN FILE: api/sqlite3-api-oo1.c-pp.js */
@@ -17606,7 +17624,7 @@
    scope. */
 })/*postRun.push(...)*/;
 /* END FILE: api/post-js-footer.js */
-// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/post-js.speedtest1-vanilla.js
+// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/post-js.speedtest1-vanilla.js
 
 // include: postamble_modularize.js
 // In MODULARIZE mode we wrap the generated code in a factory function
diff --git a/sqlite3/build/jswasm/speedtest1.wasm b/sqlite3/build/jswasm/speedtest1.wasm
index 6a6e16a..94c755b 100644
--- a/sqlite3/build/jswasm/speedtest1.wasm
+++ b/sqlite3/build/jswasm/speedtest1.wasm
Binary files differ
diff --git a/sqlite3/polyfills.js b/sqlite3/polyfills.js
deleted file mode 100644
index a972f8f..0000000
--- a/sqlite3/polyfills.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2024 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Polyfills for missing browser APIs in JavaScript shells.
-
-// `TextEncoder` and `TextDecoder`. These are called only a few times with short
-// ASCII strings, so this is sufficient and not performance-critical.
-class TextEncoder {
-  encode(string) {
-    return Uint8Array.from(string, (char) => {
-      let byte = char.codePointAt(0);
-      if (byte > 0x7f)
-        throw new Error("TextEncoder polyfill only supports ASCII");
-      return byte;
-    });
-  }
-}
-class TextDecoder {
-  decode(array) {
-    for (let byte of array) {
-      if (byte > 0x7f)
-        throw new Error("TextDecoder polyfill only supports ASCII");
-    }
-    return String.fromCharCode.apply(null, array);
-  }
-}
-
-// `crypto.getRandomValues`. This is called only once during setup.
-// The implementation is copied from an Emscripten error message proposing this.
-globalThis.crypto = {
-  getRandomValues: (array) => {
-    for (var i = 0; i < array.length; i++) array[i] = (Math.random() * 256) | 0;
-  },
-};
-
-// Empty `URLSearchParams` has just the same interface as a `Map`.
-globalThis.URLSearchParams = Map;
-
-// `self` global object.
-globalThis.self = this;
-
-globalThis.console = {
-  log: print,
-  debug: print,
-  warn: print,
-  error: print,
-};