summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/etc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit-test/etc
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/etc')
-rw-r--r--js/src/jit-test/etc/generate-lookupswitch-tests.js365
-rw-r--r--js/src/jit-test/etc/generate-nosuchproperty-tests.js78
-rw-r--r--js/src/jit-test/etc/wasm/Makefile26
-rw-r--r--js/src/jit-test/etc/wasm/README.md113
-rw-r--r--js/src/jit-test/etc/wasm/config-lock.toml15
-rw-r--r--js/src/jit-test/etc/wasm/config.toml68
6 files changed, 665 insertions, 0 deletions
diff --git a/js/src/jit-test/etc/generate-lookupswitch-tests.js b/js/src/jit-test/etc/generate-lookupswitch-tests.js
new file mode 100644
index 0000000000..2fd2b73d1c
--- /dev/null
+++ b/js/src/jit-test/etc/generate-lookupswitch-tests.js
@@ -0,0 +1,365 @@
+
+/**
+ * A test case spec is an array of objects of the following kind:
+ * { 'match': Num|Str|Null,
+ * 'body': Num|Str|Null,
+ * 'fallthrough': Boolean }
+ *
+ * If the 'match' is null, then it represents a 'default:'
+ * If the 'match' is not null, it represents a 'case X:' where X is the value.
+ * If the 'body' is null, then it means that the case body is empty. Otherwise,
+ * it means that the case body is a single 'arr.push(V);' where "arr" is an input
+ * array to the function containing the switch statement, and V is the value.
+ * If 'fallthrough' is false, then the body is terminated with a break, otherwise
+ * it is not.
+ *
+ * So a spec: [{'match':3, 'body':null, 'fallthrough':false}, {'match':null, 'body':"foo", 'fallthrough':true}]
+ * Represents a switch function:
+ * function(x, arr) {
+ * switch(x) {
+ * case 3:
+ * break;
+ * default:
+ * arr.push('foo');
+ * }
+ * }
+ *
+ * GenerateSpecPermutes generates a bunch of relevant specs, using the given case match-values,
+ * and appends them to result the array passed into it.
+ *
+ * InterpretSwitch takes a spec, a value, and a result array, and behaves as if the switch specified
+ * by the spec had been called on the value and the result array.
+ *
+ * VerifySwitchSpec is there but not used in the code. I was using it while testing the test case
+ * generator. It verifies that a switch spec is sane.
+ *
+ * RunSpec uses eval to run the test directly. It's not used currently.
+ *
+ * GenerateSwitchCode generates a string of the form "function NAME(x, arg) { .... }" which
+ * contains the switch modeled by its input spec.
+ *
+ * RunTest is there to be used from within the generated script. Its code is dumped out
+ * to the generated script text, and invoked there.
+ *
+ * Hope all of this makes some sort of sense.
+ * -kannan
+ */
+
+/** HELPERS **/
+
+function ASSERT(cond, msg) { assertEq(cond, true, msg); }
+
+function IsUndef(x) { return typeof(x) == 'undefined'; }
+function IsNull(x) { return typeof(x) == 'object' && x == null; }
+function IsNum(x) { return typeof(x) == 'number'; }
+function IsStr(x) { return typeof(x) == 'string'; }
+function IsBool(x) { return typeof(x) == 'boolean'; }
+
+function Repr(x) {
+ ASSERT(IsNum(x) || IsStr(x), "Repr");
+ if(IsNum(x)) { return ""+x; }
+ else { return "'"+x+"'"; }
+}
+
+function RandBool() { return Math.random() >= 0.5; }
+function RandInt(max) {
+ if(IsUndef(max)) { max = 0x7fffffff; }
+ return (Math.random() * max)|0;
+}
+
+var CHARS = "abcdefghijklmnopqrstuvywxyzABCDEFGHIJKLMNOPQRSTUVYWXYZ0123456789~!@#$%^&*()-_=+{}[]";
+function RandStr() {
+ var arr = [];
+ var len = Math.floor(Math.random() * 10) + 1;
+ for(var i = 0; i < len; i++) {
+ var c = Math.floor(Math.random() * CHARS.length);
+ arr.push(CHARS[c]);
+ }
+ return arr.join('');
+}
+
+function RandVal() { return RandBool() ? RandInt() : RandStr(); }
+
+/**
+ * Compare two arrays and ensure they are equal.
+ */
+function ArraysEqual(arr1, arr2) {
+ ASSERT(arr1.length == arr2.length, "Lengths not equal");
+ for(var i = 0; i < arr1.length; i++) {
+ ASSERT(typeof(arr1[i]) == typeof(arr2[i]), "Types not equal for position " + i);
+ ASSERT(arr1[i] == arr2[i], "Values not equal for position " + i);
+ }
+}
+
+function VerifySwitchSpec(spec) {
+ var foundDefault = undefined;
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i],
+ match = caseSpec.match,
+ body = caseSpec.body,
+ fallthrough = caseSpec.fallthrough;
+ ASSERT(IsNum(match) || IsStr(match) || IsNull(match), "Invalid case match for " + i);
+ ASSERT(IsNum(body) || IsStr(body) || IsNull(body), "Invalid case body for " + i);
+ ASSERT(IsBool(fallthrough), "Invalid fallthrough for " + i);
+
+ if(IsNull(match)) {
+ ASSERT(IsUndef(foundDefault), "Duplicate default found at " + i);
+ foundDefault = i;
+ }
+ }
+}
+
+/**
+ * Do a manual interpretation of a particular spec, given an input
+ * and outputting to an output array.
+ */
+function InterpretSwitch(spec, input, outputArray) {
+ var foundMatch = undefined, foundDefault = undefined;
+ // Go through cases, trying to find a matching clause.
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i], match = caseSpec.match;
+
+ if(IsNull(match)) {
+ foundDefault = i;
+ continue;
+ } else if(match === input) {
+ foundMatch = i;
+ break;
+ }
+ }
+ // Select either matching clause or default.
+ var matchI = IsNum(foundMatch) ? foundMatch : foundDefault;
+
+ // If match or default was found, interpret body from that point on.
+ if(IsNum(matchI)) {
+ for(var i = matchI; i < spec.length; i++) {
+ var caseSpec = spec[i],
+ match = caseSpec.match,
+ body = caseSpec.body,
+ fallthrough = caseSpec.fallthrough;
+ if(!IsNull(body)) { outputArray.push(body); }
+ if(!fallthrough) { break; }
+ }
+ }
+}
+
+/**
+ * Generate the code string for a javascript function containing the
+ * switch specified by the spec, in pure JS syntax.
+ */
+function GenerateSwitchCode(spec, name) {
+ var lines = [];
+ if(!name) { name = ""; }
+
+ lines.push("function "+name+"(x, arr) {");
+ lines.push(" switch(x) {");
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i],
+ match = caseSpec.match,
+ body = caseSpec.body,
+ fallthrough = caseSpec.fallthrough;
+
+ if(IsNull(match)) { lines.push(" default:"); }
+ else { lines.push(" case "+Repr(match)+":"); }
+
+ if(!IsNull(body)) { lines.push(" arr.push("+Repr(body)+");"); }
+ if(!fallthrough) { lines.push(" break;"); }
+ }
+ lines.push(" }");
+ lines.push("}");
+ return lines.join("\n");
+}
+
+/**
+ * Generates all possible specs for a given set of case match values.
+ */
+function GenerateSpecPermutes(matchVals, resultArray) {
+ ASSERT((0 < matchVals.length) && (matchVals.length <= 5), "Invalid matchVals");
+ var maxPermuteBody = (1 << matchVals.length) - 1;
+ for(var bod_pm = 0; bod_pm <= maxPermuteBody; bod_pm++) {
+ var maxPermuteFallthrough = (1 << matchVals.length) - 1;
+
+ for(var ft_pm = 0; ft_pm <= maxPermuteFallthrough; ft_pm++) {
+ // use bod_m and ft_pm to decide the placement of null vs value bodies,
+ // and the placement of fallthroughs vs breaks.
+ // Empty bodies always fall through, so fallthrough bitmask 1s must be
+ // a subset of the body bitmask 1s.
+ if((bod_pm | ft_pm) != bod_pm) {
+ continue;
+ }
+
+ var spec = [];
+ for(var k = 0; k < matchVals.length; k++) {
+ var match = matchVals[k];
+ var body = ((bod_pm & (1 << k)) > 0) ? null : RandVal();
+ var fallthrough = ((ft_pm & (1 << k)) > 0) ? true : false;
+ var caseSpec = {'match':match, 'body':body, 'fallthrough':fallthrough};
+ spec.push(caseSpec);
+ }
+
+ // Variant specs for following cases:
+
+ // Default with empty body, fallthrough
+ GenerateDefaultPermutes(spec, null, true, resultArray);
+ // Default with nonempty body, fallthrough
+ GenerateDefaultPermutes(spec, RandVal(), true, resultArray);
+ // Default with nonempty body, break
+ GenerateDefaultPermutes(spec, RandVal(), false, resultArray);
+ }
+ }
+}
+function GenerateDefaultPermutes(spec, body, fallthrough, resultArray) {
+ if(spec.length <= 2) {
+ for(var i = 0; i <= spec.length; i++) {
+ var copy = CopySpec(spec);
+ if(IsNull(body)) {
+ copy.splice(i,0,{'match':null, 'body':null, 'fallthrough':true});
+ } else {
+ copy.splice(i,0,{'match':null, 'body':body, 'fallthrough':fallthrough});
+ }
+ resultArray.push(copy);
+ }
+ } else {
+ var posns = [0, Math.floor(spec.length / 2), spec.length];
+ posns.forEach(function (i) {
+ var copy = CopySpec(spec);
+ if(IsNull(body)) {
+ copy.splice(i,0,{'match':null, 'body':null, 'fallthrough':true});
+ } else {
+ copy.splice(i,0,{'match':null, 'body':body, 'fallthrough':fallthrough});
+ }
+ resultArray.push(copy);
+ });
+ }
+}
+function CopySpec(spec) {
+ var newSpec = [];
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i];
+ newSpec.push({'match':caseSpec.match,
+ 'body':caseSpec.body,
+ 'fallthrough':caseSpec.fallthrough});
+ }
+ return newSpec;
+}
+
+
+function RunSpec(spec, matchVals) {
+ var code = GenerateSwitchCode(spec);
+
+ // Generate roughly 200 inputs for the test case spec, exercising
+ // every match value, as well as 3 randomly generated values for every
+ // iteration of the match values.
+ var inputs = [];
+ while(inputs.length < 500) {
+ for(var i = 0; i < matchVals.length; i++) { inputs.push(matchVals[i]); }
+ for(var i = 0; i < 3; i++) { inputs.push(RandVal()); }
+ }
+
+ // Interpret the lookupswitch with the inputs.
+ var interpResults = [];
+ for(var i = 0; i < inputs.length; i++) {
+ InterpretSwitch(spec, inputs[i], interpResults);
+ }
+
+ // Run compiled lookupswitch with the inputs.
+ var fn = eval("_ = " + code);
+ print("Running spec: " + code);
+ var compileResults = RunCompiled(fn, inputs);
+ print("Done Running spec");
+
+ // Verify that they produce the same output.
+ ASSERT(interpResults.length == compileResults.length, "Length mismatch");
+ for(var i = 0; i < interpResults.length; i++) {
+ ASSERT(interpResults[i] == compileResults[i], "Value mismatch");
+ }
+}
+function RunCompiled(fn, inputs) {
+ var results = [];
+ var len = inputs.length;
+ for(var i = 0; i < len; i++) { fn(inputs[i], results); }
+ return results;
+}
+
+function PrintSpec(spec, inputs, fname) {
+ var code = GenerateSwitchCode(spec, fname);
+ var input_s = fname + ".INPUTS = [" + inputs.map(Repr).join(', ') + "];";
+ var spec_s = fname + ".SPEC = " + JSON.stringify(spec) + ";";
+ print(code + "\n" + input_s + "\n" + spec_s);
+}
+
+function RunTest(test) {
+ // Exercise every test case as well as one case which won't match with any of the
+ // ("But what if it randomly generates a string case match whose value is
+ // UNMATCHED_CASE?!", you ask incredulously. Well, RandStr limits itself to 11 chars.
+ // So there.)
+ var inputs = test.INPUTS;
+ inputs.push("UNMATCHED_CASE");
+ var spec = test.SPEC;
+
+ var results1 = [];
+ for(var i = 0; i < 80; i++) {
+ for(var j = 0; j < inputs.length; j++) {
+ test(inputs[j], results1);
+ }
+ }
+
+ var results2 = [];
+ for(var i = 0; i < 80; i++) {
+ for(var j = 0; j < inputs.length; j++) {
+ InterpretSwitch(spec, inputs[j], results2);
+ }
+ }
+ ArraysEqual(results1, results2);
+}
+
+// NOTES:
+// * RunTest is used within the generated test script.
+// * InterpretSwitch is printed out into the generated test script.
+
+print("/////////////////////////////////////////");
+print("// This is a generated file!");
+print("// See jit-tests/etc/generate-lookupswitch-tests.js for the code");
+print("// that generated this code!");
+print("/////////////////////////////////////////");
+print("");
+print("/////////////////////////////////////////");
+print("// PRELUDE //");
+print("/////////////////////////////////////////");
+print("");
+print("// Avoid eager compilation of the global-scope.");
+print("try{} catch (x) {};");
+print("");
+print(ASSERT);
+print(IsNull);
+print(IsNum);
+print(ArraysEqual);
+print(InterpretSwitch);
+print(RunTest);
+print("");
+print("/////////////////////////////////////////");
+print("// TEST CASES //");
+print("/////////////////////////////////////////");
+print("");
+print("var TESTS = [];");
+var MATCH_SETS = [["foo", "bar", "zing"]];
+var count = 0;
+for(var i = 0; i < MATCH_SETS.length; i++) {
+ var matchSet = MATCH_SETS[i];
+ var specs = [];
+ GenerateSpecPermutes(matchSet, specs);
+ for(var j = 0; j < specs.length; j++) {
+ count++;
+ PrintSpec(specs[j], matchSet.slice(), 'test_'+count);
+ print("TESTS.push(test_"+count+");\n");
+ }
+}
+
+print("");
+print("/////////////////////////////////////////");
+print("// RUNNER //");
+print("/////////////////////////////////////////");
+print("");
+print("for(var i = 0; i < TESTS.length; i++) {");
+print(" RunTest(TESTS[i]);");
+print("}");
diff --git a/js/src/jit-test/etc/generate-nosuchproperty-tests.js b/js/src/jit-test/etc/generate-nosuchproperty-tests.js
new file mode 100644
index 0000000000..a0bb3e47db
--- /dev/null
+++ b/js/src/jit-test/etc/generate-nosuchproperty-tests.js
@@ -0,0 +1,78 @@
+
+// This code generates the test cases jit-test/tests/baseline/no-such-property-getprop.js
+//
+// In particular, it generates the testChain_<N>_<I>() and runChain_<N>_<I>() functions
+// at the tail of the file.
+
+var TEST_CASE_FUNCS = [];
+function runChain_NNNN_DDDD(obj) {
+ var sum = 0;
+ for (var i = 0; i < 100; i++)
+ sum += obj.foo;
+ return sum;
+}
+function testChain_NNNN_DDDD() {
+ var obj = createTower(NNNN);
+ assertEq(runChain_NNNN_DDDD(obj), NaN);
+ updateChain(obj, DDDD, 'foo', 9);
+ assertEq(runChain_NNNN_DDDD(obj), 900);
+}
+function generateTestCase(n, d) {
+ var runFuncName = "runChain_" + n + "_" + d;
+ var testFuncName = "testChain_" + n + "_" + d;
+ TEST_CASE_FUNCS.push(testFuncName);
+
+ print("//// Test chain of length " + n + " with late-property-addition at depth " + d);
+ print(String(runChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
+ print(String(testChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
+ print("");
+}
+
+// Helper function to create an object with a proto-chain of length N.
+function createTower(n) {
+ var result = Object.create(null);
+ for (var i = 0; i < n; i++)
+ result = Object.create(result);
+ return result;
+}
+
+function updateChain(obj, depth, prop, value) {
+ // Walk down the proto chain |depth| iterations and set |prop| to |value|.
+ var cur = obj;
+ for (var i = 0; i < depth; i++)
+ cur = Object.getPrototypeOf(cur);
+
+ var desc = {value:value, writable:true, configurable:true, enumerable:true};
+ Object.defineProperty(cur, prop, desc);
+}
+
+print("/////////////////////////////////////////");
+print("// This is a generated file!");
+print("// See jit-tests/etc/generate-nosuchproperty-tests.js for the code");
+print("// that generated this code!");
+print("/////////////////////////////////////////");
+print("");
+print("/////////////////////////////////////////");
+print("// PRELUDE //");
+print("/////////////////////////////////////////");
+print("");
+print(createTower);
+print(updateChain);
+print("");
+print("/////////////////////////////////////////");
+print("// TEST CASES //");
+print("/////////////////////////////////////////");
+print("");
+for (var n = 0; n <= 10; n++) {
+ for (var d = 0; d <= n; d++) {
+ generateTestCase(n, d);
+ }
+}
+
+print("");
+print("/////////////////////////////////////////");
+print("// RUNNER //");
+print("/////////////////////////////////////////");
+print("");
+for (var i = 0; i < TEST_CASE_FUNCS.length; i++)
+ print(TEST_CASE_FUNCS[i] + "();");
diff --git a/js/src/jit-test/etc/wasm/Makefile b/js/src/jit-test/etc/wasm/Makefile
new file mode 100644
index 0000000000..688aeb83da
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/Makefile
@@ -0,0 +1,26 @@
+.PHONY: update run expectations
+
+warning = '\# Wasm Spec Tests\n\nThese tests are autogenerated using a tool, do not edit.\n\nSee `jit-test/etc/wasm/` for more information.'
+
+update:
+ [ -d ./wasm-generate-testsuite ] || git clone https://github.com/eqrion/wasm-generate-testsuite ./wasm-generate-testsuite
+ cp ./config.toml ./wasm-generate-testsuite/config.toml
+ cp ./config-lock.toml ./wasm-generate-testsuite/config-lock.toml
+ (cd ./wasm-generate-testsuite && RUST_LOG=info cargo run)
+ cp ./wasm-generate-testsuite/config-lock.toml ./config-lock.toml
+ rm -r ../../tests/wasm/spec
+ cp -R wasm-generate-testsuite/tests/js ../../tests/wasm/spec
+ echo $(warning) > ../../tests/wasm/spec/README.md
+ [ ! -d ./spec-tests.patch ] || (cd ../../tests/wasm/spec && patch -u -p7 < ../../../etc/wasm/spec-tests.patch)
+ rm -r ../../../../../testing/web-platform/mozilla/tests/wasm
+ cp -R wasm-generate-testsuite/tests/wpt ../../../../../testing/web-platform/mozilla/tests/wasm
+ echo $(warning) > ../../../../../testing/web-platform/mozilla/tests/wasm/README.md
+
+run:
+ @[ -z $(MOZCONFIG) ] && echo "You need to define the MOZCONFIG env variable first."
+ @[ -z $(MOZCONFIG) ] || ../../../../../mach wpt /_mozilla/wasm
+
+expectations:
+ @[ -z $(MOZCONFIG) ] && echo "You need to define the MOZCONFIG env variable first." || true
+ @[ -z $(MOZCONFIG) ] || ../../../../../mach wpt /_mozilla/wasm --log-raw /tmp/expectations.log || true
+ @[ -z $(MOZCONFIG) ] || ../../../../../mach wpt-update /tmp/expectations.log --no-patch
diff --git a/js/src/jit-test/etc/wasm/README.md b/js/src/jit-test/etc/wasm/README.md
new file mode 100644
index 0000000000..bc1ad8bdac
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/README.md
@@ -0,0 +1,113 @@
+# Wasm Spec Tests
+
+This directory contains scripts and configs to manage the in-tree import of the
+wasm spec testsuite.
+
+## Format of tests
+
+Spec tests are given in `test/core` of the `spec` repository as `.wast` files.
+A `.wast` file is a superset of the `.wat` format with commands for running
+tests.
+
+The spec interpreter can natively consume `.wast` files to run the tests, or
+generate `.js` files which rely on the WebAssembly JS-API plus a harness file
+to implement unit-test functionality.
+
+We rely on the spec interpreter to generate `.js` files for us to run.
+
+## Running tests
+
+Tests are imported to `jit-test` and `wpt` to be run in both the JS shell or the
+browser.
+
+To run under `jit-test`:
+```bash
+cd js/src
+./jit-test.py path_to_build/dist/bin/js wasm/spec/
+```
+
+To run under `wpt` (generally not necessary):
+```bash
+./mach web-platform-test testing/web-platform/mozilla/tests/wasm/
+```
+
+## Test importing
+
+There are many proposals in addition to the canonical spec. Each proposal is a
+fork of the canonical spec and may modify any amount of files.
+
+This causes a challenge for any engine implementing multiple proposals, as any
+proposal may conflict (and often will) with each other. This makes it
+infeasible to merge all spec and proposal repos together.
+
+Testing each proposal separately in full isn't an attractive option either, as
+most tests are unchanged and that would cause a significant amount of wasted
+computation time.
+
+For this reason, we use a tool [1] to generate a set of separate test-suites
+that are 'pruned' to obtain a minimal set of tests. The tool works by merging
+each proposal with the proposal it is based off of and removing tests that
+have not changed.
+
+[1] https://github.com/eqrion/wasm-generate-testsuite
+
+### Configuration
+
+The import tool relies on `config.toml` for the list of proposals to import,
+and `config-lock.toml` for a list of commits to use for each proposal.
+
+The lock file makes test importing deterministic and controllable. This is
+useful as proposals often make inconvenient and breaking changes.
+
+### Operation
+
+```bash
+# Add, remove, or modify proposals
+vim config.toml
+# Remove locks for any proposals you wish to pull the latest changes on
+vim config-lock.toml
+# Import the tests
+make update
+# View the tests that were imported
+hg stat
+# Run the imported tests and note failures
+./jit-test.py dist/bin/js wasm/spec/
+# Exclude test failures
+vim config.toml
+# Re-import the tests to exclude failing tests
+make update
+# Commit the changes
+hg commit
+```
+
+### Debugging test failures
+
+Imported tests use the binary format, which is inconvenient for understanding
+why a test is failing.
+
+Luckily, each assertion in an imported test contains the line of the source file
+that it corresponds with.
+
+Unfortunately, the '.wast' files are not commited in the tree and so you must
+use the import tool to get the original source.
+
+Follow the steps in 'Operation' to get a `wasm-generate-testsuite` repo with
+the '.wast' files of each proposal. All proposals are stored in a single git
+repo named `specs/`. Each proposal is a branch, and you can find all tests
+under `test/core`.
+
+### Debugging import failures
+
+Proposals can often have conflicts with their upstream proposals. This is okay,
+and the test importer will fallback to building tests on an unmerged tree.
+
+This will likely result in extra tests being imported due to spurious
+differences between the proposal and upstream, but generally is okay.
+
+It's also possible that a proposal is 'broken' and fails to generate '.js' test
+files with the spec interpreter. The best way to debug this is to check out the
+proposal repo and debug why `./test/build.py` is failing.
+
+The import tool uses `RUST_LOG` to output debug information. `Makefile`
+automatically uses `RUST_LOG=info`. Change that to `RUST_LOG=debug` to get
+verbose output of all the commands run.
diff --git a/js/src/jit-test/etc/wasm/config-lock.toml b/js/src/jit-test/etc/wasm/config-lock.toml
new file mode 100644
index 0000000000..308f4138af
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/config-lock.toml
@@ -0,0 +1,15 @@
+[[repos]]
+name = 'threads'
+commit = 'dfdcb81a'
+
+[[repos]]
+name = 'bulk-memory-operations'
+commit = '66d90a7d'
+
+[[repos]]
+name = 'reference-types'
+commit = '9df8a3e3'
+
+[[repos]]
+name = 'spec'
+commit = '7c4cbf32'
diff --git a/js/src/jit-test/etc/wasm/config.toml b/js/src/jit-test/etc/wasm/config.toml
new file mode 100644
index 0000000000..4d2667b00b
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/config.toml
@@ -0,0 +1,68 @@
+# Standard 'directives.txt' prologues for jit-tests
+harness_directive = "|jit-test| skip-if: true"
+directive = "|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); include:wasm-testharness.js; local-include:harness/sync_index.js"
+
+# Failing tests across all testsuites
+excluded_tests = [
+ # false-positive windows-specific failures
+ "align.wast",
+ # bulk-memory-operations/issues/133 (data.wast:161)
+ "data.wast",
+ # testing that multiple tables fail (imports.wast:309)
+ "imports.wast",
+ # bulk-memory-operations/issues/133 (linking.wast:206)
+ "linking.wast",
+ # bulk-memory-operations/issues/133 (elem.wast:142)
+ "elem.wast",
+ # test harness doesn't acquire global values correctly
+ "exports.wast",
+ # false-positive windows-specific failure
+ "memory_trap.wast",
+ # false-positive error on duplicate export name (names.wast:19)
+ "names.wast",
+ # false-positive error on invalid UTF-8 custom section name (utf8-custom-section-id.wast:6)
+ "utf8-custom-section-id.wast",
+ # invalid table maximum length for web embeddings
+ "table.wast",
+ # fails after a bottom-type has been added to validation
+ "unreached-invalid.wast",
+]
+
+[[repos]]
+name = "spec"
+url = "https://github.com/WebAssembly/spec"
+excluded_tests = []
+
+[[repos]]
+name = "threads"
+url = "https://github.com/WebAssembly/threads"
+parent = "spec"
+excluded_tests = [
+ "atomic.wast",
+ # testing that multi-value isn't implemented (func.wast:492)
+ "func.wast",
+ # testing that multi-value isn't implemented (type.wast:52)
+ "type.wast"
+]
+# Skip in WPT where we can't guard on features being enabled
+skip_wpt = true
+# Skip in jit-test when it's not enabled
+directive = "; skip-if: !wasmThreadsEnabled()"
+
+[[repos]]
+name = "bulk-memory-operations"
+url = "https://github.com/WebAssembly/bulk-memory-operations"
+parent = "spec"
+excluded_tests = []
+# Skip in WPT where we can't guard on features being enabled
+skip_wpt = true
+
+[[repos]]
+name = "reference-types"
+url = "https://github.com/WebAssembly/reference-types"
+parent = "spec"
+excluded_tests = []
+# Skip in WPT where we can't guard on features being enabled
+skip_wpt = true
+# Skip in jit-test when it's not enabled
+directive = "; skip-if: !wasmReftypesEnabled()"