summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/regexp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/regexp')
-rw-r--r--js/src/jit-test/tests/regexp/CheckRegExpSyntax.js16
-rw-r--r--js/src/jit-test/tests/regexp/RegExpExec-errors.js6
-rw-r--r--js/src/jit-test/tests/regexp/atom-match-unicode-split-surrogate.js25
-rw-r--r--js/src/jit-test/tests/regexp/bug-1841771.js76
-rw-r--r--js/src/jit-test/tests/regexp/bug-1845715.js2
-rw-r--r--js/src/jit-test/tests/regexp/bug1419785.js3
-rw-r--r--js/src/jit-test/tests/regexp/bug1445907.js14
-rw-r--r--js/src/jit-test/tests/regexp/bug1600272.js6
-rw-r--r--js/src/jit-test/tests/regexp/bug1640473.js20
-rw-r--r--js/src/jit-test/tests/regexp/bug1640475.js9
-rw-r--r--js/src/jit-test/tests/regexp/bug1640479.js15
-rw-r--r--js/src/jit-test/tests/regexp/bug1640487.js8
-rw-r--r--js/src/jit-test/tests/regexp/bug1640592.js2
-rw-r--r--js/src/jit-test/tests/regexp/bug1697077.js13
-rw-r--r--js/src/jit-test/tests/regexp/bug1703750.js7
-rw-r--r--js/src/jit-test/tests/regexp/bug1718842-1.js7
-rw-r--r--js/src/jit-test/tests/regexp/bug1718842-2.js9
-rw-r--r--js/src/jit-test/tests/regexp/bug1783555.js4
-rw-r--r--js/src/jit-test/tests/regexp/bug1783830.js7
-rw-r--r--js/src/jit-test/tests/regexp/bug1786012.js8
-rw-r--r--js/src/jit-test/tests/regexp/bug1794317.js7
-rw-r--r--js/src/jit-test/tests/regexp/builtin-exec-wrapper.js7
-rw-r--r--js/src/jit-test/tests/regexp/clone-statics.js21
-rw-r--r--js/src/jit-test/tests/regexp/flag-getters.js61
-rw-r--r--js/src/jit-test/tests/regexp/has-capture-groups-intrinsic.js20
-rw-r--r--js/src/jit-test/tests/regexp/huge-01.js21
-rw-r--r--js/src/jit-test/tests/regexp/huge-02.js13
-rw-r--r--js/src/jit-test/tests/regexp/lastIndex-negative.js14
-rw-r--r--js/src/jit-test/tests/regexp/lastIndex-non-writable.js71
-rw-r--r--js/src/jit-test/tests/regexp/lastIndex-too-large.js13
-rw-r--r--js/src/jit-test/tests/regexp/lastIndex-valueOf.js16
-rw-r--r--js/src/jit-test/tests/regexp/match-indices-dictionary.js24
-rw-r--r--js/src/jit-test/tests/regexp/match-indices-warp.js8
-rw-r--r--js/src/jit-test/tests/regexp/match-stub-realms.js19
-rw-r--r--js/src/jit-test/tests/regexp/named-capture-proxy.js21
-rw-r--r--js/src/jit-test/tests/regexp/negated-set-expression.js1
-rw-r--r--js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js67
-rw-r--r--js/src/jit-test/tests/regexp/non-unicode-case-folding.js68
-rw-r--r--js/src/jit-test/tests/regexp/replace-exec.js12
-rw-r--r--js/src/jit-test/tests/regexp/replace-global-lambda.js56
-rw-r--r--js/src/jit-test/tests/regexp/rope-inputs.js10
-rw-r--r--js/src/jit-test/tests/regexp/unicode-back-reference.js3
42 files changed, 810 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js b/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js
new file mode 100644
index 0000000000..19471fdf50
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/CheckRegExpSyntax.js
@@ -0,0 +1,16 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+load(libdir + "asserts.js");
+
+assertEq(checkRegExpSyntax("correct[reg]exp"), undefined);
+let err = checkRegExpSyntax("regex[withSyntaxError");
+assertEq(err instanceof SyntaxError, true);
+
+oomTest(() => checkRegExpSyntax("correct(re)gexp"))
+
+var checkReturnedSyntaxError = true;
+oomTest(() => {
+ let err = checkRegExpSyntax("regex[withSyntaxError");
+ if (!(err instanceof SyntaxError)) { checkReturnedSyntaxError = false; }
+})
+assertEq(checkReturnedSyntaxError, true);
diff --git a/js/src/jit-test/tests/regexp/RegExpExec-errors.js b/js/src/jit-test/tests/regexp/RegExpExec-errors.js
new file mode 100644
index 0000000000..bee579859f
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/RegExpExec-errors.js
@@ -0,0 +1,6 @@
+load(libdir + "asserts.js");
+
+assertErrorMessage(() => RegExp.prototype.test.call({}), TypeError,
+ /test method called on incompatible Object/);
+assertErrorMessage(() => RegExp.prototype[Symbol.match].call([]), TypeError,
+ /\[Symbol\.match\] method called on incompatible Array/);
diff --git a/js/src/jit-test/tests/regexp/atom-match-unicode-split-surrogate.js b/js/src/jit-test/tests/regexp/atom-match-unicode-split-surrogate.js
new file mode 100644
index 0000000000..3c1909fcf2
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/atom-match-unicode-split-surrogate.js
@@ -0,0 +1,25 @@
+function test(flags) {
+ // RegExp with a simple atom matcher.
+ // - Global flag to enable setting 'lastIndex'.
+ let s = "\u{10000}";
+ let re = RegExp(s, flags + "g");
+
+ for (let i = 0; i < 200; ++i) {
+ // Set lastIndex in the middle of the surrogate pair.
+ re.lastIndex = 1;
+
+ // |exec| will reset lastIndex to the start of the surrogate pair.
+ let r = re.exec(s);
+
+ // Atom match should succeed.
+ assertEq(r[0], s);
+ assertEq(r.index, 0);
+ assertEq(re.lastIndex, 2);
+ }
+}
+
+// Unicode flag to enable surrogate pairs support.
+test("u");
+
+// Unicode-Sets flag to enable surrogate pairs support.
+test("v");
diff --git a/js/src/jit-test/tests/regexp/bug-1841771.js b/js/src/jit-test/tests/regexp/bug-1841771.js
new file mode 100644
index 0000000000..ae13aa86f5
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug-1841771.js
@@ -0,0 +1,76 @@
+var lfLogBuffer = `
+let hasFunction ;
+for (const name of [, "" ])
+ g55 = newGlobal();
+gcparam('maxBytes', gcparam('gcBytes') );
+//corefuzz-dcd-endofdata
+/*
+//corefuzz-dcd-endofdata
+/**/
+
+
+
+let hasFunction ;
+for (const name of [ "", "", "", ""]) {
+
+
+
+
+
+ const present = name in this;
+ if (!present)
+ thisname = function() {};
+}
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 1089924061
+//corefuzz-dcd-endofdata
+oomTest(function() {
+ let m14 = parseModule('a'.replace(/a/, assertEq.toString));
+});
+`;
+lfLogBuffer = lfLogBuffer.split('\n');
+let lfPreamble = `
+ Object.defineProperty(this, "fuzzutils", {
+ value:{
+ untemplate: function(s) {
+ return s.replace(/\\\\/g, '\\\\\\\\').replace(/\`/g, '\\\\\`').replace(/\\\$/g, '\\\\\$');
+ }
+ }
+ });
+function lfFixRedeclaration(lfSrc, lfExc, lfRewriteSet) {
+ let varName;
+ let srcParts = lfSrc.split("\\n");
+ let regReplace = new RegExp;
+ for (let lfIdx = 0; lfIdx < srcParts.length; ++lfIdx)
+ srcParts[lfExc.lineNumber - 1] = srcParts[lfExc.lineNumber - 1].replace(regReplace, varName);
+ return srcParts.join();
+}
+`;
+evaluate(lfPreamble);
+let lfRunTypeId = -1;
+let lfCodeBuffer = "";
+while (true) {
+ let line = lfLogBuffer.shift();
+ if (line == null) break;
+ else if (line == "//corefuzz-dcd-endofdata") {
+ loadFile(lfCodeBuffer);
+ lfCodeBuffer = "";
+ } else lfCodeBuffer += line + "\n";
+}
+loadFile(lfCodeBuffer);
+function loadFile(lfVarx, lfForceRunType = 0, lfPatchSets = []) {
+ try {
+ if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+ if (lfGCStress);
+ } else {
+ evaluate(lfVarx);
+ }
+ } catch (lfVare) {
+ if (lfVare.toString.indexOf0 >= 0);
+ else if (lfVare.toString().indexOf("redeclaration of ") >= 0) {
+ let lfNewSrc = lfFixRedeclaration(lfVarx, lfVare, lfPatchSets);
+ loadFile(lfNewSrc, lfRunTypeId, lfPatchSets);
+ }
+ lfVarx = fuzzutils.untemplate(lfVarx);
+ }
+}
diff --git a/js/src/jit-test/tests/regexp/bug-1845715.js b/js/src/jit-test/tests/regexp/bug-1845715.js
new file mode 100644
index 0000000000..992a5a8d8a
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug-1845715.js
@@ -0,0 +1,2 @@
+// |jit-test| skip-if: !('oomTest' in this)
+oomTest(() => { gc(); /./d.exec(); });
diff --git a/js/src/jit-test/tests/regexp/bug1419785.js b/js/src/jit-test/tests/regexp/bug1419785.js
new file mode 100644
index 0000000000..b40eca9f8c
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1419785.js
@@ -0,0 +1,3 @@
+// |jit-test| error:character class
+Object.defineProperty(RegExp.prototype, Symbol.search, {get: () => { throw "wrong"; }});
+"abc".search("[[");
diff --git a/js/src/jit-test/tests/regexp/bug1445907.js b/js/src/jit-test/tests/regexp/bug1445907.js
new file mode 100644
index 0000000000..a0e0dca57b
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1445907.js
@@ -0,0 +1,14 @@
+// |jit-test| skip-if: !wasmDebuggingEnabled()
+
+// On ARM64, we failed to save x28 properly when generating code for the regexp
+// matcher.
+//
+// There's wasm and Debugger code here because the combination forces the use of
+// x28 and exposes the bug when running on the simulator.
+
+var g = newGlobal({newCompartment: true});
+var dbg = new Debugger(g);
+g.eval(`var m = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func (export "test")))')))`);
+var re = /./;
+dbg.onEnterFrame = function(frame) { re.exec("x") };
+result = g.eval("m.exports.test()");
diff --git a/js/src/jit-test/tests/regexp/bug1600272.js b/js/src/jit-test/tests/regexp/bug1600272.js
new file mode 100644
index 0000000000..08f154f771
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1600272.js
@@ -0,0 +1,6 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+evalInWorker(`
+ Array.prototype[1] = 1;
+ var source = Array(50000).join("(") + "a" + Array(50000).join(")");
+ var r70 = RegExp(source);
+`);
diff --git a/js/src/jit-test/tests/regexp/bug1640473.js b/js/src/jit-test/tests/regexp/bug1640473.js
new file mode 100644
index 0000000000..d2ba97043d
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1640473.js
@@ -0,0 +1,20 @@
+// |jit-test| skip-if: getBuildConfiguration("wasi")
+var s = "";
+var input = "";
+for (var i = 0; i < 500; ++i) {
+ s += "(?<a" + i + ">a)";
+ s += "(?<b" + i + ">b)?";
+ input += "a";
+}
+
+try {
+ var r = RegExp(s);
+ var e = r.exec(input);
+
+ for (var i = 0; i < 500; i++) {
+ assertEq(e.groups["a" + i], "a");
+ assertEq(e.groups["b" + i], undefined);
+ }
+} catch (err) {
+ assertEq(err.message, "too much recursion");
+}
diff --git a/js/src/jit-test/tests/regexp/bug1640475.js b/js/src/jit-test/tests/regexp/bug1640475.js
new file mode 100644
index 0000000000..58c092ec1d
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1640475.js
@@ -0,0 +1,9 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var i = 0;
+oomTest(function() {
+ for (var j = 0; j < 10; ++j) {
+ var r = RegExp(`(?<_${(i++).toString(32)}>a)`);
+ r.exec("a");
+ }
+});
diff --git a/js/src/jit-test/tests/regexp/bug1640479.js b/js/src/jit-test/tests/regexp/bug1640479.js
new file mode 100644
index 0000000000..ff166d6451
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1640479.js
@@ -0,0 +1,15 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+var failures = 0;
+var i = 0;
+
+function foo() {
+ var e;
+ var r = RegExp("(?<_" + (i++) + "a>)");
+ try { e = r.exec("a"); } catch {}
+ e = r.exec("a");
+ if (e.groups === undefined) failures++;
+}
+
+oomTest(foo);
+assertEq(failures, 0);
diff --git a/js/src/jit-test/tests/regexp/bug1640487.js b/js/src/jit-test/tests/regexp/bug1640487.js
new file mode 100644
index 0000000000..7ee3756075
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1640487.js
@@ -0,0 +1,8 @@
+var re = /(?<x>a)|b/;
+
+function f(j) {
+ var s = String.fromCharCode(0x61 + (j === 1));
+ var e = re.exec(s);
+ if (e.groups.x !== "a") print(i,j);
+}
+for (var i = 0; i < 2; ++i) f(i);
diff --git a/js/src/jit-test/tests/regexp/bug1640592.js b/js/src/jit-test/tests/regexp/bug1640592.js
new file mode 100644
index 0000000000..e00fd81e99
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1640592.js
@@ -0,0 +1,2 @@
+gczeal(14, 16);
+"a".match(/(?<a>a)/);
diff --git a/js/src/jit-test/tests/regexp/bug1697077.js b/js/src/jit-test/tests/regexp/bug1697077.js
new file mode 100644
index 0000000000..5551b98655
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1697077.js
@@ -0,0 +1,13 @@
+// |jit-test| skip-if: !('interruptRegexp' in this) || getBuildConfiguration('pbl')
+// skip testing under PBL because interruption of regex engine here seems to
+// depend on GC behavior and is hard to reproduce reliably.
+var s0 = "A".repeat(10*1024);
+var interrupted = false;
+gczeal(0);
+setInterruptCallback(() => {
+ interrupted = true;
+ startgc(7,'shrinking');
+ return true;
+});
+assertEq(interruptRegexp(/a(bc|bd)/, s0), null);
+assertEq(interrupted, true);
diff --git a/js/src/jit-test/tests/regexp/bug1703750.js b/js/src/jit-test/tests/regexp/bug1703750.js
new file mode 100644
index 0000000000..4be2d7748a
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1703750.js
@@ -0,0 +1,7 @@
+function foo() {
+ const b = "".match();
+ try {
+ foo();
+ } catch {}
+}
+foo()
diff --git a/js/src/jit-test/tests/regexp/bug1718842-1.js b/js/src/jit-test/tests/regexp/bug1718842-1.js
new file mode 100644
index 0000000000..35b557b6b4
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1718842-1.js
@@ -0,0 +1,7 @@
+var g = newGlobal({sameZoneAs: this});
+
+var re1 = RegExp("(?<a>a)");
+var re2 = g.RegExp("(?<a>a)");
+
+re1.exec("a");
+re2.exec("a");
diff --git a/js/src/jit-test/tests/regexp/bug1718842-2.js b/js/src/jit-test/tests/regexp/bug1718842-2.js
new file mode 100644
index 0000000000..88f76046f7
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1718842-2.js
@@ -0,0 +1,9 @@
+var g = newGlobal();
+
+g.enableTrackAllocations();
+
+var re1 = RegExp("(?<a>a)");
+var re2 = g.RegExp("(?<a>a)");
+
+re1.exec("a");
+re2.exec("a");
diff --git a/js/src/jit-test/tests/regexp/bug1783555.js b/js/src/jit-test/tests/regexp/bug1783555.js
new file mode 100644
index 0000000000..60978910a2
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1783555.js
@@ -0,0 +1,4 @@
+var src = "(.?)".repeat(65536);
+try {
+ "".match(src);
+} catch {}
diff --git a/js/src/jit-test/tests/regexp/bug1783830.js b/js/src/jit-test/tests/regexp/bug1783830.js
new file mode 100644
index 0000000000..cd41be7f66
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1783830.js
@@ -0,0 +1,7 @@
+assertEq(/[^!]/u.exec('\u{1F4A9}')[0], "\u{1F4A9}");
+assertEq(/^[^!]/u.exec('\u{1F4A9}')[0], "\u{1F4A9}");
+assertEq(/[^!]$/u.exec('\u{1F4A9}')[0], "\u{1F4A9}");
+assertEq(/![^!]/u.exec('!\u{1F4A9}')[0], "!\u{1F4A9}");
+assertEq(/[^!]!/u.exec('\u{1F4A9}!')[0], "\u{1F4A9}!");
+assertEq(/![^!]/ui.exec('!\u{1F4A9}')[0], "!\u{1F4A9}");
+assertEq(/[^!]!/ui.exec('\u{1F4A9}!')[0], "\u{1F4A9}!");
diff --git a/js/src/jit-test/tests/regexp/bug1786012.js b/js/src/jit-test/tests/regexp/bug1786012.js
new file mode 100644
index 0000000000..f806fd22bb
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1786012.js
@@ -0,0 +1,8 @@
+var repeated = "A".repeat(65536);
+var src = "^(?:" + repeated + ")\$";
+
+for (var i = 0; i < 100; i++) {
+ try {
+ RegExp(src).test();
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/regexp/bug1794317.js b/js/src/jit-test/tests/regexp/bug1794317.js
new file mode 100644
index 0000000000..1ecb21eb64
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/bug1794317.js
@@ -0,0 +1,7 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
+for (let i = 0; i < 2; i++) {
+ oomTest(function () {
+ RegExp("(?<name" + i + ">)").exec();
+ })
+}
diff --git a/js/src/jit-test/tests/regexp/builtin-exec-wrapper.js b/js/src/jit-test/tests/regexp/builtin-exec-wrapper.js
new file mode 100644
index 0000000000..63660de643
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/builtin-exec-wrapper.js
@@ -0,0 +1,7 @@
+var g = newGlobal({newCompartment: true});
+g.evaluate(`RegExp.prototype.exec = {};`);
+var wrapper = g.evaluate(`/abc.+def/`);
+assertEq(RegExp.prototype.test.call(wrapper, "abc"), false);
+assertEq(RegExp.prototype.test.call(wrapper, "abcXdef"), true);
+assertEq(RegExp.prototype[Symbol.match].call(wrapper, "abc"), null);
+assertEq(RegExp.prototype[Symbol.match].call(wrapper, "abcXdef")[0], "abcXdef");
diff --git a/js/src/jit-test/tests/regexp/clone-statics.js b/js/src/jit-test/tests/regexp/clone-statics.js
new file mode 100644
index 0000000000..d2f0ce1cac
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/clone-statics.js
@@ -0,0 +1,21 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+offThreadCompileToStencil(`
+ function foo(x, {}) {
+ do {
+ re = /erwe/;
+ if (x === 1)
+ re.x = 1;
+ else
+ re.x = "a";
+ assertEq(re.x.length, (x === 1) ? undefined : 1);
+ } while (!inIon());
+ }
+
+ foo(0, 0);
+ RegExp.multiline = 1;
+ foo(1, 0);
+`);
+
+var stencil = finishOffThreadStencil();
+evalStencil(stencil);
diff --git a/js/src/jit-test/tests/regexp/flag-getters.js b/js/src/jit-test/tests/regexp/flag-getters.js
new file mode 100644
index 0000000000..fbc1ad7274
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/flag-getters.js
@@ -0,0 +1,61 @@
+// Test inlining for RegExp flag getters.
+
+function testGlobal() {
+ const xs = [/a/, /b/g];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.global, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testGlobal();
+
+function testIgnoreCase() {
+ const xs = [/a/, /b/i];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.ignoreCase, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testIgnoreCase();
+
+function testMultiline() {
+ const xs = [/a/, /b/m];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.multiline, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testMultiline();
+
+function testDotAll() {
+ const xs = [/a/, /b/s];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.dotAll, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testDotAll();
+
+function testUnicode() {
+ const xs = [/a/, /b/u];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.unicode, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testUnicode();
+
+function testSticky() {
+ const xs = [/a/, /b/y];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.sticky, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testSticky();
diff --git a/js/src/jit-test/tests/regexp/has-capture-groups-intrinsic.js b/js/src/jit-test/tests/regexp/has-capture-groups-intrinsic.js
new file mode 100644
index 0000000000..2dfb450ea9
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/has-capture-groups-intrinsic.js
@@ -0,0 +1,20 @@
+function test() {
+ var RegExpHasCaptureGroups = getSelfHostedValue("RegExpHasCaptureGroups");
+ var cases = [
+ [/a.+/, false],
+ [/abc/, false],
+ [/\r\n?|\n/, false],
+ [/(abc)/, true],
+ [/a(.+)/, true],
+ [/a(b)(c)(d)/, true],
+ [/a(?:b)/, false],
+ [/((?:a))/, true],
+ [/(?<name>a)/, true],
+ ];
+ for (var i = 0; i < 10; i++) {
+ for (var [re, expected] of cases) {
+ assertEq(RegExpHasCaptureGroups(re, "abcdef"), expected);
+ }
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/huge-01.js b/js/src/jit-test/tests/regexp/huge-01.js
new file mode 100644
index 0000000000..bc1529f0c6
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/huge-01.js
@@ -0,0 +1,21 @@
+
+function g(N, p) {
+ var prefix = p.repeat(N);
+ var str = prefix + "[AB]";
+
+ try {
+ var re = new RegExp(str);
+ re.exec(prefix + "A");
+ } catch(e) {
+ // 1. Regexp too big is raised by the lack of the 64k virtual registers
+ // reserved for the regexp evaluation.
+ // 2. The stack overflow can occur during the analysis of the regexp
+ assertEq(e.message.includes("regexp too big") ||
+ e.message.includes("Stack overflow") ||
+ e.message.includes("too much recursion"), true);
+ }
+}
+
+var prefix = "/(?=k)ok/";
+for (var i = 0; i < 18; i++)
+ g(Math.pow(2, i), prefix)
diff --git a/js/src/jit-test/tests/regexp/huge-02.js b/js/src/jit-test/tests/regexp/huge-02.js
new file mode 100644
index 0000000000..1d0d94e10c
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/huge-02.js
@@ -0,0 +1,13 @@
+var interestingCaptureNums = [(1 << 14),
+ (1 << 15) - 1,
+ (1 << 15),
+ (1 << 15) + 1,
+ (1 << 16)]
+
+for (let i of interestingCaptureNums) {
+ print(i);
+ try {
+ var source = Array(i).join("(") + "a" + Array(i).join(")");
+ RegExp(source).exec("a");
+ } catch {}
+}
diff --git a/js/src/jit-test/tests/regexp/lastIndex-negative.js b/js/src/jit-test/tests/regexp/lastIndex-negative.js
new file mode 100644
index 0000000000..536b3d2233
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/lastIndex-negative.js
@@ -0,0 +1,14 @@
+// A negative lastIndex value must be treated as 0.
+function test() {
+ var re = /abc.+de/g;
+ for (var i = 0; i < 100; i++) {
+ re.lastIndex = (i > 60) ? -1 : 0;
+ assertEq(typeof re.exec("abcXdef"), "object");
+ assertEq(re.lastIndex, 6);
+
+ re.lastIndex = (i > 60) ? -1 : 0;
+ assertEq(re.test("abcXdef"), true);
+ assertEq(re.lastIndex, 6);
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/lastIndex-non-writable.js b/js/src/jit-test/tests/regexp/lastIndex-non-writable.js
new file mode 100644
index 0000000000..74839762f5
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/lastIndex-non-writable.js
@@ -0,0 +1,71 @@
+function testGlobalExec() {
+ var re = /abc.+de/g;
+ var c = 0;
+ for (var i = 0; i < 100; i++) {
+ re.lastIndex = 0;
+ if (i === 60) {
+ Object.freeze(re);
+ }
+ try {
+ re.exec("abcXdef");
+ } catch (e) {
+ assertEq(e.toString().includes("lastIndex"), true);
+ c++;
+ }
+ assertEq(re.lastIndex, i >= 60 ? 0 : 6);
+ }
+ assertEq(c, 40);
+}
+testGlobalExec();
+
+function testStickyTest() {
+ var re = /abc.+de/y;
+ var c = 0;
+ for (var i = 0; i < 100; i++) {
+ re.lastIndex = 0;
+ if (i === 60) {
+ Object.freeze(re);
+ }
+ try {
+ re.test("abcXdef");
+ } catch (e) {
+ assertEq(e.toString().includes("lastIndex"), true);
+ c++;
+ }
+ assertEq(re.lastIndex, i >= 60 ? 0 : 6);
+ }
+ assertEq(c, 40);
+}
+testStickyTest();
+
+// Must not reset too-large .lastIndex to 0 if non-writable.
+function testLastIndexOutOfRange() {
+ var re = /abc.+de/g;
+ re.lastIndex = 123;
+ Object.freeze(re);
+ for (var i = 0; i < 100; i++) {
+ var ex = null;
+ try {
+ re.exec("abcXdef");
+ } catch (e) {
+ ex = e;
+ }
+ assertEq(ex.toString().includes("lastIndex"), true);
+ assertEq(re.lastIndex, 123);
+ }
+}
+testLastIndexOutOfRange();
+
+// .lastIndex is ignored for non-global/non-sticky regexps.
+function testPlainExec() {
+ var re = /abc.+de/;
+ re.lastIndex = 1234;
+ for (var i = 0; i < 100; i++) {
+ if (i === 60) {
+ Object.freeze(re);
+ }
+ assertEq(re.exec("abcXdef")[0], "abcXde");
+ assertEq(re.lastIndex, 1234);
+ }
+}
+testPlainExec();
diff --git a/js/src/jit-test/tests/regexp/lastIndex-too-large.js b/js/src/jit-test/tests/regexp/lastIndex-too-large.js
new file mode 100644
index 0000000000..3e04042dd0
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/lastIndex-too-large.js
@@ -0,0 +1,13 @@
+function test() {
+ var re = /abc.+de/y;
+ for (var i = 0; i < 100; i++) {
+ re.lastIndex = 10;
+ assertEq(re.exec("abcXdef"), null);
+ assertEq(re.lastIndex, 0);
+
+ re.lastIndex = 10;
+ assertEq(re.test("abcXdef"), false);
+ assertEq(re.lastIndex, 0);
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/lastIndex-valueOf.js b/js/src/jit-test/tests/regexp/lastIndex-valueOf.js
new file mode 100644
index 0000000000..3794d20c2e
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/lastIndex-valueOf.js
@@ -0,0 +1,16 @@
+function test() {
+ var re = /abc.+de/g;
+ var c = 0;
+ var weird = {valueOf() { c++; return 0; }};
+ for (var i = 0; i < 100; i++) {
+ re.lastIndex = (i > 60) ? weird : 0;
+ assertEq(typeof re.exec("abcXdef"), "object");
+ assertEq(re.lastIndex, 6);
+
+ re.lastIndex = (i > 60) ? weird : 0;
+ assertEq(re.test("abcXdef"), true);
+ assertEq(re.lastIndex, 6);
+ }
+ assertEq(c, 78);
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/match-indices-dictionary.js b/js/src/jit-test/tests/regexp/match-indices-dictionary.js
new file mode 100644
index 0000000000..14b7fd0630
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/match-indices-dictionary.js
@@ -0,0 +1,24 @@
+// |jit-test| skip-if: getBuildConfiguration("wasi")
+var s = "";
+var input = "";
+for (var i = 0; i < 500; ++i) {
+ s += "(?<a" + i + ">a)";
+ s += "(?<b" + i + ">b)?";
+ input += "a";
+}
+
+try {
+ var r = RegExp(s, "d");
+ var e = r.exec(input);
+
+ for (var i = 0; i < 500; i++) {
+ assertEq(e.groups["a" + i], "a");
+ assertEq(e.groups["b" + i], undefined);
+
+ assertEq(e.indices.groups["a" + i][0], i)
+ assertEq(e.indices.groups["a" + i][1], i + 1)
+ assertEq(e.indices.groups["b" + i], undefined)
+ }
+} catch (err) {
+ assertEq(err.message, "too much recursion");
+}
diff --git a/js/src/jit-test/tests/regexp/match-indices-warp.js b/js/src/jit-test/tests/regexp/match-indices-warp.js
new file mode 100644
index 0000000000..634d368aee
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/match-indices-warp.js
@@ -0,0 +1,8 @@
+var re = /A*(B)A*/d;
+for (var i = 0; i < 100; i++) {
+ var match = re.exec("xxxxxAAABAxxxxx");
+ assertEq(match.indices[0][0], 5);
+ assertEq(match.indices[0][1], 10);
+ assertEq(match.indices[1][0], 8);
+ assertEq(match.indices[1][1], 9);
+}
diff --git a/js/src/jit-test/tests/regexp/match-stub-realms.js b/js/src/jit-test/tests/regexp/match-stub-realms.js
new file mode 100644
index 0000000000..9cf082f015
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/match-stub-realms.js
@@ -0,0 +1,19 @@
+// Ensure regexp match stub uses the correct realm for the match object and
+// the regexp statics.
+function test() {
+ var g1 = newGlobal({sameCompartmentAs: this});
+ var g2 = newGlobal({sameCompartmentAs: this});
+ g1.evaluate("function match(s) { return /(.)([\\d]+)/.exec(s); }");
+ g2.evaluate("function match(s) { return /(.)([\\d]+)/.exec(s); }");
+ for (var i = 0; i < 25; i++) {
+ var res1 = g1.match(`A${i}`);
+ var res2 = g2.match(`B${i}`);
+ assertEq(objectGlobal(res1), g1);
+ assertEq(objectGlobal(res2), g2);
+ assertEq(g1.RegExp.$1, "A");
+ assertEq(g1.RegExp.$2, String(i));
+ assertEq(g2.RegExp.$1, "B");
+ assertEq(g2.RegExp.$2, String(i));
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/named-capture-proxy.js b/js/src/jit-test/tests/regexp/named-capture-proxy.js
new file mode 100644
index 0000000000..e7cc933850
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/named-capture-proxy.js
@@ -0,0 +1,21 @@
+var access_log = "";
+const handler = {
+ get: function(obj, prop) {
+ access_log += prop + ",";
+ return prop in obj ? obj[prop] : "<filled by proxy>";
+ }
+};
+
+class ProxiedGroupRegExp extends RegExp {
+ exec(s) {
+ var result = super.exec(s);
+ if (result) {
+ result.groups = new Proxy(result.groups, handler);
+ }
+ return result;
+ }
+}
+
+let re = new ProxiedGroupRegExp("(?<x>.)");
+assertEq("a".replace(re, "$<x> $<y>"), "a <filled by proxy>");
+assertEq(access_log, "x,y,")
diff --git a/js/src/jit-test/tests/regexp/negated-set-expression.js b/js/src/jit-test/tests/regexp/negated-set-expression.js
new file mode 100644
index 0000000000..70be08f680
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/negated-set-expression.js
@@ -0,0 +1 @@
+assertEq("ab".match(/^(?:a[^a])+$/v)[0], "ab");
diff --git a/js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js b/js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js
new file mode 100644
index 0000000000..bfea5f89a9
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js
@@ -0,0 +1,67 @@
+// |jit-test| skip-if: typeof Intl === 'undefined'
+
+// See https://tc39.es/ecma262/#sec-runtime-semantics-canonicalize-ch
+function Canonicalize(ch) {
+ var u = ch.toUpperCase();
+ if (u.length > 1) return ch;
+ var cu = u.charCodeAt(0);
+ if (ch.charCodeAt(0) >= 128 && cu < 128) return ch;
+ return cu;
+}
+
+function TestEquivalenceClass(eclass) {
+ var backref = /(.)\1/i;
+
+ for (var i = 0; i < eclass.length; i++) {
+ for (var j = 0; j < eclass.length; j++) {
+ if (i == j) continue;
+ var c1 = eclass[i];
+ var c2 = eclass[j];
+ var cc = c1 + c2;
+ var shouldMatch = Canonicalize(c1) === Canonicalize(c2);
+
+ assertEq(backref.test(cc), shouldMatch);
+ }
+ }
+}
+
+function TestAll() {
+ for (var eclass of equivalence_classes) {
+ TestEquivalenceClass(eclass);
+ }
+}
+
+// Interesting case-folding equivalence classes (as determined by
+// ICU's UnicodeSet::closeOver). A class is interesting if it contains
+// more than two characters, or if it contains any characters in
+// IgnoreSet or SpecialAddSet as defined in new-regexp/special-case.h.
+var equivalence_classes = [
+ '\u0041\u0061', // Aa (sanity check)
+ '\u004b\u006b\u212a', // KkK
+ '\u0053\u0073\u017f', // Ssſ
+ '\u00b5\u039c\u03bc', // µΜμ
+ '\u00c5\u00e5\u212b', // ÅåÅ
+ '\u00df\u1e9e', // ßẞ
+ '\u03a9\u03c9\u2126', // ΩωΩ
+ '\u0390\u1fd3', // ΐΐ
+ '\u0398\u03b8\u03d1\u03f4', // Θθϑϴ
+ '\u03b0\u1fe3', // ΰΰ
+ '\u1f80\u1f88', // ᾀᾈ
+ '\u1fb3\u1fbc', // ᾳᾼ
+ '\u1fc3\u1fcc', // ῃῌ
+ '\u1ff3\u1ffc', // ῳῼ
+ '\ufb05\ufb06', // ſtst
+
+ // Everything below this line is a well-behaved case-folding
+ // equivalence class with more than two characters but only one
+ // canonical case-folded character
+ '\u01c4\u01c5\u01c6', '\u01c7\u01c8\u01c9', '\u01ca\u01cb\u01cc',
+ '\u01f1\u01f2\u01f3', '\u0345\u0399\u03b9\u1fbe', '\u0392\u03b2\u03d0',
+ '\u0395\u03b5\u03f5', '\u039a\u03ba\u03f0', '\u03a0\u03c0\u03d6',
+ '\u03a1\u03c1\u03f1', '\u03a3\u03c2\u03c3', '\u03a6\u03c6\u03d5',
+ '\u0412\u0432\u1c80', '\u0414\u0434\u1c81', '\u041e\u043e\u1c82',
+ '\u0421\u0441\u1c83', '\u0422\u0442\u1c84\u1c85', '\u042a\u044a\u1c86',
+ '\u0462\u0463\u1c87', '\u1c88\ua64a\ua64b', '\u1e60\u1e61\u1e9b'
+];
+
+TestAll();
diff --git a/js/src/jit-test/tests/regexp/non-unicode-case-folding.js b/js/src/jit-test/tests/regexp/non-unicode-case-folding.js
new file mode 100644
index 0000000000..52a799238e
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/non-unicode-case-folding.js
@@ -0,0 +1,68 @@
+// |jit-test| skip-if: typeof Intl === 'undefined'
+
+// See https://tc39.es/ecma262/#sec-runtime-semantics-canonicalize-ch
+function Canonicalize(ch) {
+ var u = ch.toUpperCase();
+ if (u.length > 1) return ch;
+ var cu = u.charCodeAt(0);
+ if (ch.charCodeAt(0) >= 128 && cu < 128) return ch;
+ return cu;
+}
+
+function TestEquivalenceClass(eclass) {
+ for (var i = 0; i < eclass.length; i++) {
+ for (var j = 0; j < eclass.length; j++) {
+ if (i == j) continue;
+ var c1 = eclass[i];
+ var c2 = eclass[j];
+ var shouldMatch = Canonicalize(c1) === Canonicalize(c2);
+
+ var re1 = new RegExp(c1, 'i');
+ var re2 = new RegExp('[' + c1 + ']', 'i');
+
+ assertEq(re1.test(c2), shouldMatch);
+ assertEq(re2.test(c2), shouldMatch);
+ }
+ }
+}
+
+function TestAll() {
+ for (var eclass of equivalence_classes) {
+ TestEquivalenceClass(eclass);
+ }
+}
+
+// Interesting case-folding equivalence classes (as determined by
+// ICU's UnicodeSet::closeOver). A class is interesting if it contains
+// more than two characters, or if it contains any characters in
+// IgnoreSet or SpecialAddSet as defined in new-regexp/special-case.h.
+var equivalence_classes = [
+ '\u0041\u0061', // Aa (sanity check)
+ '\u004b\u006b\u212a', // KkK
+ '\u0053\u0073\u017f', // Ssſ
+ '\u00b5\u039c\u03bc', // µΜμ
+ '\u00c5\u00e5\u212b', // ÅåÅ
+ '\u00df\u1e9e', // ßẞ
+ '\u03a9\u03c9\u2126', // ΩωΩ
+ '\u0390\u1fd3', // ΐΐ
+ '\u0398\u03b8\u03d1\u03f4', // Θθϑϴ
+ '\u03b0\u1fe3', // ΰΰ
+ '\u1f80\u1f88', // ᾀᾈ
+ '\u1fb3\u1fbc', // ᾳᾼ
+ '\u1fc3\u1fcc', // ῃῌ
+ '\u1ff3\u1ffc', // ῳῼ
+ '\ufb05\ufb06', // ſtst
+
+ // Everything below this line is a well-behaved case-folding
+ // equivalence class with more than two characters but only one
+ // canonical case-folded character
+ '\u01c4\u01c5\u01c6', '\u01c7\u01c8\u01c9', '\u01ca\u01cb\u01cc',
+ '\u01f1\u01f2\u01f3', '\u0345\u0399\u03b9\u1fbe', '\u0392\u03b2\u03d0',
+ '\u0395\u03b5\u03f5', '\u039a\u03ba\u03f0', '\u03a0\u03c0\u03d6',
+ '\u03a1\u03c1\u03f1', '\u03a3\u03c2\u03c3', '\u03a6\u03c6\u03d5',
+ '\u0412\u0432\u1c80', '\u0414\u0434\u1c81', '\u041e\u043e\u1c82',
+ '\u0421\u0441\u1c83', '\u0422\u0442\u1c84\u1c85', '\u042a\u044a\u1c86',
+ '\u0462\u0463\u1c87', '\u1c88\ua64a\ua64b', '\u1e60\u1e61\u1e9b'
+];
+
+TestAll();
diff --git a/js/src/jit-test/tests/regexp/replace-exec.js b/js/src/jit-test/tests/regexp/replace-exec.js
new file mode 100644
index 0000000000..b6488ec678
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/replace-exec.js
@@ -0,0 +1,12 @@
+function test() {
+ var re = /abc.+de/;
+ var c = 0;
+ for (var i = 0; i < 100; i++) {
+ assertEq(re.test("abcXdef"), true);
+ if (i === 60) {
+ RegExp.prototype.exec = () => { c++; return {}; };
+ }
+ }
+ assertEq(c, 39);
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/replace-global-lambda.js b/js/src/jit-test/tests/regexp/replace-global-lambda.js
new file mode 100644
index 0000000000..ca102c8d62
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/replace-global-lambda.js
@@ -0,0 +1,56 @@
+"use strict";
+
+function testNoCaptureGroups() {
+ var s = "a..bb.cba.";
+ for (var i = 0; i < 20; i++) {
+ var log = "";
+ var res = s.replace(/a|b/g, function(...args) {
+ assertEq(this, undefined);
+ assertEq(args.length, 3);
+ assertEq(args[2], s);
+ log += "(" + args[0] + args[1] + ")";
+ return "X";
+ });
+ assertEq(res, "X..XX.cXX.");
+ assertEq(log, "(a0)(b3)(b4)(b7)(a8)");
+ }
+}
+testNoCaptureGroups();
+
+function testCaptureGroups() {
+ var s = "a..bb.cba.";
+ for (var i = 0; i < 20; i++) {
+ var log = "";
+ var res = s.replace(/(a)|(b)/g, function(...args) {
+ assertEq(this, undefined);
+ assertEq(args.length, 5);
+ assertEq(args[4], s);
+ log += "(" + args[0] + args[1] + args[2] + args[3] + ")";
+ return "X";
+ });
+ assertEq(res, "X..XX.cXX.");
+ assertEq(log, "(aaundefined0)(bundefinedb3)(bundefinedb4)(bundefinedb7)(aaundefined8)");
+ }
+}
+testCaptureGroups();
+
+// Calling RegExp#compile in the callback has no effect, because |replace| will
+// create a new RegExp object from the original source/flags if this happens.
+function testCaptureGroupsChanging() {
+ var s = "a..bb.cba.";
+ for (var i = 0; i < 20; i++) {
+ var log = "";
+ var re = /a|b/g;
+ var res = s.replace(re, function(...args) {
+ assertEq(this, undefined);
+ assertEq(args.length, 3);
+ assertEq(args[2], s);
+ log += "(" + args[0] + args[1] + ")";
+ re.compile("(a)|(b)", "g");
+ return "X";
+ });
+ assertEq(res, "X..XX.cXX.");
+ assertEq(log, "(a0)(b3)(b4)(b7)(a8)");
+ }
+}
+testCaptureGroupsChanging();
diff --git a/js/src/jit-test/tests/regexp/rope-inputs.js b/js/src/jit-test/tests/regexp/rope-inputs.js
new file mode 100644
index 0000000000..ca10fa3a4f
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/rope-inputs.js
@@ -0,0 +1,10 @@
+function test() {
+ var re = /abc(.+)z/;
+ for (var i = 0; i < 100; i++) {
+ assertEq(re.exec(newRope("abcdefghijklmnopqrstuvw", "xyz"))[1], "defghijklmnopqrstuvwxy");
+ assertEq(re.test(newRope("abcdefghijklmnopqrstuvw", "xyz")), true);
+ assertEq(re[Symbol.search](newRope(".abcdefghijklmnopqrstuvw", "xyz")), 1);
+ assertEq(re[Symbol.match](newRope("abcdefghijklmnopqrstuvw", "xyz"))[1], "defghijklmnopqrstuvwxy");
+ }
+}
+test();
diff --git a/js/src/jit-test/tests/regexp/unicode-back-reference.js b/js/src/jit-test/tests/regexp/unicode-back-reference.js
new file mode 100644
index 0000000000..90eef410a5
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/unicode-back-reference.js
@@ -0,0 +1,3 @@
+for (var t = 0; t < 10000; t++) {
+ (t + "\u1234").split(/(.)\1/i);
+}