Add branch for JetStream v2.2

This is a copy of JetStream v2.2 as of https://commits.webkit.org/277529@main
diff --git a/JetStreamDriver.js b/JetStreamDriver.js
index e1a6683..2dca23e 100644
--- a/JetStreamDriver.js
+++ b/JetStreamDriver.js
@@ -42,6 +42,15 @@
 if (typeof dumpJSONResults === "undefined")
     var dumpJSONResults = false;
 
+let shouldReport = false;
+let customTestList = [];
+if (typeof(URLSearchParams) !== "undefined") {
+    const urlParameters = new URLSearchParams(window.location.search);
+    shouldReport = urlParameters.has('report') && urlParameters.get('report').toLowerCase() == 'true';
+    if (urlParameters.has('test'))
+        customTestList = urlParameters.getAll("test");
+}
+
 // Used for the promise representing the current benchmark run.
 this.currentResolve = null;
 this.currentReject = null;
@@ -130,7 +139,7 @@
 }
 
 function toScore(timeValue) {
-    return 5000 / timeValue;
+    return 5000 / Math.max(timeValue, 1);
 }
 
 function toTimeValue(score) {
@@ -323,6 +332,10 @@
                     return Realm.eval(realm, s);
                 };
                 globalObject.readFile = read;
+            } else if (isSpiderMonkey) {
+                globalObject = newGlobal();
+                globalObject.loadString = globalObject.evaluate;
+                globalObject.readFile = globalObject.readRelativeToScript;
             } else
                 globalObject = runString("");
 
@@ -404,7 +417,7 @@
         await this.prefetchResourcesForBrowser();
         await this.fetchResources();
         this.prepareToRun();
-        if (isInBrowser && window.location.search == '?report=true') {
+        if (isInBrowser && shouldReport) {
             setTimeout(() => this.start(), 4000);
         }
     }
@@ -494,7 +507,7 @@
         if (!isInBrowser)
             return;
 
-        if (window.location.search !== '?report=true')
+        if (!shouldReport)
             return;
 
         const content = this.resultsJSON();
@@ -1844,6 +1857,8 @@
 
 if (typeof testList !== "undefined") {
     processTestList(testList);
+} else if (customTestList.length) {
+    processTestList(customTestList);
 } else {
     if (runARES)
         addTestsByGroup(ARESGroup);
diff --git a/RAMification.py b/RAMification.py
index 51edf1c..bac0e70 100644
--- a/RAMification.py
+++ b/RAMification.py
@@ -29,6 +29,7 @@
 import re
 import subprocess
 import sys
+import time
 
 jitTests = ["3d-cube-SP", "3d-raytrace-SP", "acorn-wtb", "ai-astar", "Air", "async-fs", "Babylon", "babylon-wtb", "base64-SP", "Basic", "Box2D", "cdjs", "chai-wtb", "coffeescript-wtb", "crypto", "crypto-aes-SP", "crypto-md5-SP", "crypto-sha1-SP", "date-format-tofte-SP", "date-format-xparb-SP", "delta-blue", "earley-boyer", "espree-wtb", "first-inspector-code-load", "FlightPlanner", "float-mm.c", "gaussian-blur", "gbemu", "gcc-loops-wasm", "hash-map", "HashSet-wasm", "jshint-wtb", "json-parse-inspector", "json-stringify-inspector", "lebab-wtb", "mandreel", "ML", "multi-inspector-code-load", "n-body-SP", "navier-stokes", "octane-code-load", "octane-zlib", "OfflineAssembler", "pdfjs", "prepack-wtb", "quicksort-wasm", "raytrace", "regex-dna-SP", "regexp", "richards", "richards-wasm", "splay", "stanford-crypto-aes", "stanford-crypto-pbkdf2", "stanford-crypto-sha256", "string-unpack-code-SP", "tagcloud-SP", "tsf-wasm", "typescript", "uglify-js-wtb", "UniPoker", "WSL"]
 
@@ -120,7 +121,10 @@
     parser.add_argument("-l", "--lua", dest="runLuaTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run Lua comparison tests [default]")
     parser.add_argument("-n", "--run-no-jit", dest="runNoJITTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run no JIT tests [default]")
     parser.add_argument("-o", "--output", dest="jsonFilename", type=str, default=None, metavar="JSON-output-file", help="Path to JSON output")
+    parser.add_argument("--diagnostics-dir", dest="diagnosticDir", type=str, default="/tmp/RAMification-diagnostics/", metavar="diagnostic-dir", help="Path to a directory to dump diagnostic output.")
     parser.add_argument("-m", "--vmmap", dest="takeVmmap", action="store_true", default=False, help="Take a vmmap after each test")
+    parser.add_argument("--detailed-vmmap", dest="takeDetailedVmmap", action="store_true", default=False, help="Take a vmmap after each test including regions")
+    parser.add_argument("--memgraph", dest="takeMemgraph", action="store_true", default=False, help="Take a memgraph after each test")
     parser.add_argument("--smaps", dest="takeSmaps", action="store_true", default=False, help="Take a smaps rollup after each test")
 
     args = parser.parse_args()
@@ -149,7 +153,15 @@
         self.rootDir = args.testDir
         self.environmentVars = {}
         self.vmmapOutput = "" if args.takeVmmap else None
+        self.takeDetailedVmmap = args.takeDetailedVmmap
         self.smapsOutput = "" if args.takeSmaps else None
+        self.takeMemgraph = args.takeMemgraph
+        self.diagnosticDir = args.diagnosticDir
+
+        if (self.takeDetailedVmmap or self.takeMemgraph) and not os.path.exists(self.diagnosticDir):
+            os.makedirs(self.diagnosticDir)
+            if not os.path.exists(self.diagnosticDir):
+                raise Exception("Couldn't create diagnostic dir {}".format(self.diagnosticDir))
 
     def setup(self):
         pass
@@ -190,9 +202,14 @@
         BaseRunner.__init__(self, args)
         self.jscCommand = args.jscCommand
 
+
     def runOneTest(self, test, extraOptions=None, useJetStream2Harness=True):
         self.resetForTest(test)
 
+        if self.takeMemgraph:
+            self.environmentVars["MallocStackLogging"] = "1"
+            self.environmentVars["__XPC_MallocStackLogging"] = "1"
+
         args = [self.jscCommand]
         if extraOptions:
             args.extend(extraOptions)
@@ -202,6 +219,11 @@
         else:
             args.extend(["--footprint", "{test}".format(test=test)])
 
+        if self.takeDetailedVmmap or self.takeMemgraph:
+            test_diagnostic_dir = os.path.dirname(os.path.join(self.diagnosticDir, test))
+            if not os.path.exists(test_diagnostic_dir):
+                os.makedirs(test_diagnostic_dir)
+
         self.resetForTest(test)
 
         proc = subprocess.Popen(args, cwd=self.rootDir, env=self.environmentVars, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=None, shell=False)
@@ -217,8 +239,18 @@
                     self.vmmapOutput = subprocess.Popen(['vmmap', '--summary', '{}'.format(proc.pid)], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE).stdout.read()
                     if sys.version_info[0] >= 3:
                         self.vmmapOutput = str(self.vmmapOutput, "utf-8")
+                if self.takeDetailedVmmap:
+                    vmmap_filename = os.path.join(self.diagnosticDir, "{}-{}.vmmap".format(test, int(time.time())))
+                    print("Collecting detailed vmmap at {}".format(vmmap_filename))
+                    self.vmmapDetailedOutput = subprocess.Popen(['vmmap', '{}'.format(proc.pid)], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE).stdout.read()
+                    with open(vmmap_filename, 'wb') as f:
+                        f.write(self.vmmapDetailedOutput)
                 if self.smapsOutput is not None:
                     self.smapsOutput = subprocess.Popen(['cat', '/proc/{}/smaps_rollup'.format(proc.pid)], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE).stdout.read()
+                if self.takeMemgraph:
+                    memgraph_filename = os.path.join(self.diagnosticDir, "{}-{}.memgraph".format(test, int(time.time())))
+                    print("Collecting memgraph at {}".format(memgraph_filename))
+                    subprocess.call(['/usr/bin/leaks', str(proc.pid), '--fullContent', '--forkCorpse', "--outputGraph={}".format(memgraph_filename)], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
                 proc.stdin.write(b"done\n")
                 proc.stdin.flush()
 
@@ -240,7 +272,7 @@
 
     testRunner = args.runner(args)
 
-    if args.takeVmmap or args.takeSmaps:
+    if args.takeVmmap or args.takeDetailedVmmap or args.takeMemgraph or args.takeSmaps:
         testRunner.setEnv("JS_SHELL_WAIT_FOR_INPUT_TO_EXIT", "1")
 
     dyldFrameworkPath = frameworkPathFromExecutablePath(args.jscCommand)
diff --git a/cli.js b/cli.js
index df7676a..8c76669 100644
--- a/cli.js
+++ b/cli.js
@@ -30,7 +30,10 @@
 
 const isD8 = typeof Realm !== "undefined";
 if (isD8)
-    readFile = read;
+    globalThis.readFile = read;
+const isSpiderMonkey = typeof newGlobal !== "undefined";
+if (isSpiderMonkey)
+    globalThis.readFile = readRelativeToScript;
 
 if (typeof testList === "undefined")
     testList = undefined;
diff --git a/in-depth.html b/in-depth.html
index 9def091..be3d365 100644
--- a/in-depth.html
+++ b/in-depth.html
@@ -1,5 +1,5 @@
 <!--
- Copyright (C) 2019-2022 Apple Inc. All rights reserved.
+ Copyright (C) 2019-2024 Apple Inc. All rights reserved.
 
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
@@ -27,7 +27,7 @@
 <head>
     <meta charset="utf-8" />
 
-    <title>JetStream 2.1 In-Depth Analysis</title>
+    <title>JetStream 2.2 In-Depth Analysis</title>
 
     <link rel="stylesheet" href="JetStream.css">
 
@@ -35,7 +35,7 @@
 <body>
 <h1 class="logo">
     <div id="jetstreams">
-        <a href="index.html" class="logo-image">JetStream 2.1</a>
+        <a href="index.html" class="logo-image">JetStream 2.2</a>
     </div>
 </h1>
 <main>
@@ -113,19 +113,20 @@
         </p>
 
         <p>
-        JetStream 2.1 runs the same benchmarks as JetStream 2, but updates the benchmark driver to
-        improve score stability. This is achieved by pre-fetching network resources prior to running
-        the benchmarks. This can reduce perturbations on the measurement of JavaScript execution
-        time due to second order effects of pause times induced by network latency.
+        JetStream 2.2 runs the same benchmarks as JetStream 2.1, but includes fixes for several minor issues.
+        One such issue happened when running on fast hardware where the timer resolution, as returned by the VM, was occasionally 0.
+        This causes a problem when calculating a scores.  There were two issues with the sub-test segmentation, where the code didn’t
+        create the initial Float32Array for the test, and the second a race condition in the task queue code.
+        The command line scripts cli.js and wasm-cli.js had issues with the D8 and SpiderMonkey shell programs.
         </p>
 
         <p>
-        Note that scores from JetStream 2.1 are not comparable to scores to other versions
+        Note that scores from JetStream 2.2 are not comparable to scores to other versions
         of any JetStream benchmark.
         </p>
 
         <h3>
-        JetStream 2.1 has 64 subtests:
+        JetStream 2.2 has 64 subtests:
         </h3>
 
         <dl>
diff --git a/index.html b/index.html
index 54cfffa..5b40123 100644
--- a/index.html
+++ b/index.html
@@ -1,5 +1,5 @@
 <!--
- Copyright (C) 2019-2022 Apple Inc. All rights reserved.
+ Copyright (C) 2019-2024 Apple Inc. All rights reserved.
 
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
@@ -27,7 +27,7 @@
 <head>
     <meta charset="utf-8" />
 
-    <title>JetStream 2.1</title>
+    <title>JetStream 2.2</title>
 
     <link rel="stylesheet" href="JetStream.css">
     <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes, viewport-fit=cover">
@@ -74,7 +74,7 @@
 </h1>
 
 <main>
-    <p class="summary">JetStream 2.1 is a JavaScript and WebAssembly benchmark suite focused on the most advanced web applications. It rewards browsers that start up quickly, execute code quickly, and run smoothly. For more information, read the <a href="in-depth.html">in-depth analysis</a>. Bigger scores are better.</p>
+    <p class="summary">JetStream 2.2 is a JavaScript and WebAssembly benchmark suite focused on the most advanced web applications. It rewards browsers that start up quickly, execute code quickly, and run smoothly. For more information, read the <a href="in-depth.html">in-depth analysis</a>. Bigger scores are better.</p>
     <p class="summary" id="mode-description"></p>
 
     <div id="result-summary"></div>
diff --git a/wasm-cli.js b/wasm-cli.js
index 8020303..f39b818 100644
--- a/wasm-cli.js
+++ b/wasm-cli.js
@@ -24,6 +24,8 @@
 */
 
 const isInBrowser = false;
+const isD8 = false;
+const isSpiderMonkey = false;
 console = {
     log: () => { }
 }
diff --git a/worker/async-task.js b/worker/async-task.js
index e80e703..9ca2675 100644
--- a/worker/async-task.js
+++ b/worker/async-task.js
@@ -390,7 +390,7 @@
 
     function findOptimalSegmentationInternal(cost, previousNode, values, costMatrix, segmentCount)
     {
-        cost[0] = [0]; // The cost of segmenting single value is always 0.
+        cost[0] = new Float32Array([0]); // The cost of segmenting single value is always 0.
         previousNode[0] = [-1];
         for (var segmentStart = 0; segmentStart < values.length; segmentStart++) {
             var costOfOptimalSegmentationThatEndAtCurrentStart = cost[segmentStart];
@@ -522,7 +522,8 @@
         var worker = this._makeWorkerEventuallyAvailable();
         if (worker)
             callback(worker);
-        this._queue.push(callback);
+        else
+            this._queue.push(callback);
     }
 
     static _makeWorkerEventuallyAvailable()
@@ -537,6 +538,8 @@
 
         if (this._latestStartTime > Date.now() - 50) {
             setTimeout(function () {
+                if (!AsyncTaskWorker._queue.length)
+                    return;
                 var worker = AsyncTaskWorker._findAvailableWorker();
                 if (worker)
                     AsyncTaskWorker._queue.pop()(worker);
diff --git a/worker/segmentation.js b/worker/segmentation.js
index 0b2955d..51fbdce 100644
--- a/worker/segmentation.js
+++ b/worker/segmentation.js
@@ -392,7 +392,7 @@
 
     function findOptimalSegmentationInternal(cost, previousNode, values, costMatrix, segmentCount)
     {
-        cost[0] = [0]; // The cost of segmenting single value is always 0.
+        cost[0] = new Float32Array([0]); // The cost of segmenting single value is always 0.
         previousNode[0] = [-1];
         for (var segmentStart = 0; segmentStart < values.length; segmentStart++) {
             var costOfOptimalSegmentationThatEndAtCurrentStart = cost[segmentStart];
@@ -522,7 +522,8 @@
         var worker = this._makeWorkerEventuallyAvailable();
         if (worker)
             callback(worker);
-        this._queue.push(callback);
+        else
+            this._queue.push(callback);
     }
 
     static _makeWorkerEventuallyAvailable()
@@ -537,6 +538,8 @@
 
         if (this._latestStartTime > Date.now() - 50) {
             setTimeout(function () {
+                if (!AsyncTaskWorker._queue.length)
+                    return;
                 var worker = AsyncTaskWorker._findAvailableWorker();
                 if (worker)
                     AsyncTaskWorker._queue.pop()(worker);