summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/RegExp/named-groups
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/test262/built-ins/RegExp/named-groups')
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-exec.js38
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-group-property-enumeration-order.js27
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match-indices.js18
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match.js38
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-matchall.js31
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replace.js20
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replaceall.js18
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-search.js14
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-split.js15
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-test.js23
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-global.js58
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-non-global.js45
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass-sans.js38
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass.js36
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-undefined.js35
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-unmatched.js37
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/groups-object.js41
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/groups-properties.js34
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/lookbehind.js47
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-match.js44
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-invalid.js57
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js96
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names.js25
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-references.js36
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/shell.js37
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-escaped.js26
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-get.js31
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-missing.js26
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-nocaptures.js34
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-numbered.js31
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-unclosed.js22
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/unicode-match.js48
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-invalid.js57
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-valid.js102
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names.js32
-rw-r--r--js/src/tests/test262/built-ins/RegExp/named-groups/unicode-references.js49
38 files changed, 1396 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/browser.js b/js/src/tests/test262/built-ins/RegExp/named-groups/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/browser.js
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-exec.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-exec.js
new file mode 100644
index 0000000000..9624cc6545
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-exec.js
@@ -0,0 +1,38 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Matching behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+includes: [compareArray.js]
+---*/
+
+assert.compareArray(/(?<x>a)|(?<x>b)/.exec("bab"), ["b", undefined, "b"]);
+assert.compareArray(/(?<x>b)|(?<x>a)/.exec("bab"), ["b", "b", undefined]);
+
+assert.compareArray(/(?:(?<x>a)|(?<x>b))\k<x>/.exec("aa"), ["aa", "a", undefined]);
+assert.compareArray(/(?:(?<x>a)|(?<x>b))\k<x>/.exec("bb"), ["bb", undefined, "b"]);
+
+let matchResult = /(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/.exec("aabb");
+assert.compareArray(matchResult, ["aabb", undefined, "b"]);
+assert.sameValue(matchResult.groups.x, "b");
+
+assert.sameValue(/(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/.exec("abab"), null);
+
+assert.sameValue(/(?:(?<x>a)|(?<x>b))\k<x>/.exec("abab"), null);
+
+assert.sameValue(/(?:(?<x>a)|(?<x>b))\k<x>/.exec("cdef"), null);
+
+assert.compareArray(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/.exec("xx"), ["xx", "x", undefined]);
+assert.compareArray(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/.exec("z"), ["z", undefined, undefined]);
+assert.sameValue(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/.exec("zz"), null);
+assert.compareArray(/(?<a>x)|(?:zy\k<a>)/.exec("zy"), ["zy", undefined]);
+
+assert.compareArray(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/.exec("xz"), ["xz", undefined, undefined]);
+assert.compareArray(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/.exec("yz"), ["yz", undefined, undefined]);
+assert.sameValue(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/.exec("xzx"), null);
+assert.sameValue(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/.exec("yzy"), null);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-group-property-enumeration-order.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-group-property-enumeration-order.js
new file mode 100644
index 0000000000..33a21cbd2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-group-property-enumeration-order.js
@@ -0,0 +1,27 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Kevin Gibbons. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Enumeration order of the groups object with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+includes: [compareArray.js]
+---*/
+
+
+let regexp = /(?<y>a)(?<x>a)|(?<x>b)(?<y>b)/;
+
+assert.compareArray(
+ Object.keys(regexp.exec("aa").groups),
+ ["y", "x"],
+ "property enumeration order of the groups object is based on source order, not match order"
+);
+
+assert.compareArray(
+ Object.keys(regexp.exec("bb").groups),
+ ["y", "x"],
+ "property enumeration order of the groups object is based on source order, not match order"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match-indices.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match-indices.js
new file mode 100644
index 0000000000..3135fd75a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match-indices.js
@@ -0,0 +1,18 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Kevin Gibbons. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: match indices with duplicate named capture groups
+esid: sec-makematchindicesindexpairarray
+features: [regexp-duplicate-named-groups, regexp-match-indices]
+includes: [compareArray.js]
+---*/
+
+let indices = "..ab".match(/(?<x>a)|(?<x>b)/d).indices;
+assert.compareArray(indices.groups.x, [2, 3]);
+
+indices = "..ba".match(/(?<x>a)|(?<x>b)/d).indices;
+assert.compareArray(indices.groups.x, [2, 3]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match.js
new file mode 100644
index 0000000000..dee2d62455
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-match.js
@@ -0,0 +1,38 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Kevin Gibbons. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Matching behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+includes: [compareArray.js]
+---*/
+
+assert.compareArray("bab".match(/(?<x>a)|(?<x>b)/), ["b", undefined, "b"]);
+assert.compareArray("bab".match(/(?<x>b)|(?<x>a)/), ["b", "b", undefined]);
+
+assert.compareArray("aa".match(/(?:(?<x>a)|(?<x>b))\k<x>/), ["aa", "a", undefined]);
+assert.compareArray("bb".match(/(?:(?<x>a)|(?<x>b))\k<x>/), ["bb", undefined, "b"]);
+
+let matchResult = "aabb".match(/(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/);
+assert.compareArray(matchResult, ["aabb", undefined, "b"]);
+assert.sameValue(matchResult.groups.x, "b");
+
+assert.sameValue("abab".match(/(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/), null);
+
+assert.sameValue("abab".match(/(?:(?<x>a)|(?<x>b))\k<x>/), null);
+
+assert.sameValue("cdef".match(/(?:(?<x>a)|(?<x>b))\k<x>/), null);
+
+assert.compareArray("xx".match(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/), ["xx", "x", undefined]);
+assert.compareArray("z".match(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/), ["z", undefined, undefined]);
+assert.sameValue("zz".match(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/), null);
+assert.compareArray("zy".match(/(?<a>x)|(?:zy\k<a>)/), ["zy", undefined]);
+
+assert.compareArray("xz".match(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/), ["xz", undefined, undefined]);
+assert.compareArray("yz".match(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/), ["yz", undefined, undefined]);
+assert.sameValue("xzx".match(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/), null);
+assert.sameValue("yzy".match(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/), null);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-matchall.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-matchall.js
new file mode 100644
index 0000000000..fea434f83d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-matchall.js
@@ -0,0 +1,31 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: String.prototype.search behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+includes: [compareArray.js,compareIterator.js]
+features: [regexp-duplicate-named-groups]
+---*/
+
+function matchesIterator(iterator, expected) {
+ assert.compareIterator(iterator, expected.map(e => {
+ return v => assert.compareArray(v, e);
+ }));
+}
+
+matchesIterator("bab".matchAll(/(?<x>a)|(?<x>b)/g),
+ [
+ ["b", undefined, "b"],
+ ["a", "a", undefined],
+ ["b", undefined, "b"],
+ ]);
+matchesIterator("bab".matchAll(/(?<x>b)|(?<x>a)/g),
+ [
+ ["b", "b", undefined],
+ ["a", undefined, "a"],
+ ["b", "b", undefined],
+ ]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replace.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replace.js
new file mode 100644
index 0000000000..76893704af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replace.js
@@ -0,0 +1,20 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Kevin Gibbons. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: String.prototype.replace behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+---*/
+
+assert.sameValue("ab".replace(/(?<x>a)|(?<x>b)/, "[$<x>]"), "[a]b");
+assert.sameValue("ba".replace(/(?<x>a)|(?<x>b)/, "[$<x>]"), "[b]a");
+
+assert.sameValue("ab".replace(/(?<x>a)|(?<x>b)/, "[$<x>][$1][$2]"), "[a][a][]b");
+assert.sameValue("ba".replace(/(?<x>a)|(?<x>b)/, "[$<x>][$1][$2]"), "[b][][b]a");
+
+assert.sameValue("ab".replace(/(?<x>a)|(?<x>b)/g, "[$<x>]"), "[a][b]");
+assert.sameValue("ba".replace(/(?<x>a)|(?<x>b)/g, "[$<x>]"), "[b][a]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replaceall.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replaceall.js
new file mode 100644
index 0000000000..1cd45805a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-replaceall.js
@@ -0,0 +1,18 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: String.prototype.replaceAll behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+---*/
+
+assert.sameValue("abxab".replaceAll(/(?<x>a)|(?<x>b)/g, "[$<x>]"), "[a][b]x[a][b]");
+assert.sameValue("baxba".replaceAll(/(?<x>a)|(?<x>b)/g, "[$<x>]"), "[b][a]x[b][a]");
+
+assert.sameValue("abxab".replaceAll(/(?<x>a)|(?<x>b)/g, "[$<x>][$1][$2]"), "[a][a][][b][][b]x[a][a][][b][][b]");
+assert.sameValue("baxba".replaceAll(/(?<x>a)|(?<x>b)/g, "[$<x>][$1][$2]"), "[b][][b][a][a][]x[b][][b][a][a][]");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-search.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-search.js
new file mode 100644
index 0000000000..db6f8b7534
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-search.js
@@ -0,0 +1,14 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: String.prototype.search behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+---*/
+
+assert.sameValue("xab".search(/(?<x>a)|(?<x>b)/), 1);
+assert.sameValue("xba".search(/(?<x>a)|(?<x>b)/), 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-split.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-split.js
new file mode 100644
index 0000000000..ccd9c77993
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-split.js
@@ -0,0 +1,15 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: String.prototype.search behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+includes: [compareArray.js]
+features: [regexp-duplicate-named-groups]
+---*/
+
+assert.compareArray("xab".split(/(?<x>a)|(?<x>b)/), ["x", "a", undefined, "", undefined, "b", ""]);
+assert.compareArray("xba".split(/(?<x>a)|(?<x>b)/), ["x", undefined, "b", "", "a", undefined, ""]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-test.js b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-test.js
new file mode 100644
index 0000000000..77a8dd4391
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/duplicate-names-test.js
@@ -0,0 +1,23 @@
+// |reftest| skip -- regexp-duplicate-named-groups is not supported
+// Copyright 2022 Igalia S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Matching behavior with duplicate named capture groups
+esid: prod-GroupSpecifier
+features: [regexp-duplicate-named-groups]
+---*/
+
+assert(/(?<x>a)|(?<x>b)/.test("bab"));
+assert(/(?<x>b)|(?<x>a)/.test("bab"));
+
+assert(/(?:(?<x>a)|(?<x>b))\k<x>/.test("aa"));
+assert(/(?:(?<x>a)|(?<x>b))\k<x>/.test("bb"));
+
+let matchResult = /(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/.test("aabb");
+assert(matchResult);
+
+let notMatched = /(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/.test("abab");
+assert.sameValue(notMatched, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-global.js b/js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-global.js
new file mode 100644
index 0000000000..3285bf9cf0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-global.js
@@ -0,0 +1,58 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+ Function argument to String.prototype.replace gets groups as the last argument
+esid: sec-regexp.prototype-@@replace
+features: [regexp-named-groups]
+info: |
+ RegExp.prototype [ @@replace ] ( string, replaceValue )
+ 14. Repeat, for each result in results,
+ j. Let namedCaptures be ? Get(result, "groups").
+ k. If functionalReplace is true, then
+ iv. If namedCaptures is not undefined,
+ 1. Append namedCaptures as the last element of replacerArgs.
+---*/
+
+let source = "(?<fst>.)(?<snd>.)";
+let alternateSource = "(?<fst>.)|(?<snd>.)";
+
+for (let flags of ["g", "gu"]) {
+ let i = 0;
+ let re = new RegExp(source, flags);
+ let result = "abcd".replace(re,
+ (match, fst, snd, offset, str, groups) => {
+ if (i == 0) {
+ assert.sameValue("ab", match);
+ assert.sameValue("a", groups.fst);
+ assert.sameValue("b", groups.snd);
+ assert.sameValue("a", fst);
+ assert.sameValue("b", snd);
+ assert.sameValue(0, offset);
+ assert.sameValue("abcd", str);
+ } else if (i == 1) {
+ assert.sameValue("cd", match);
+ assert.sameValue("c", groups.fst);
+ assert.sameValue("d", groups.snd);
+ assert.sameValue("c", fst);
+ assert.sameValue("d", snd);
+ assert.sameValue(2, offset);
+ assert.sameValue("abcd", str);
+ } else {
+ assertUnreachable();
+ }
+ i++;
+ return `${groups.snd}${groups.fst}`;
+ });
+ assert.sameValue("badc", result);
+ assert.sameValue(i, 2);
+
+ let re2 = new RegExp(alternateSource, flags);
+ assert.sameValue("undefinedundefinedundefinedundefined",
+ "abcd".replace(re2,
+ (match, fst, snd, offset, str, groups) => groups.snd));
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-non-global.js b/js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-non-global.js
new file mode 100644
index 0000000000..c2631d892d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/functional-replace-non-global.js
@@ -0,0 +1,45 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+ Function argument to String.prototype.replace gets groups as the last argument
+esid: sec-regexp.prototype-@@replace
+features: [regexp-named-groups]
+info: |
+ RegExp.prototype [ @@replace ] ( string, replaceValue )
+ 14. Repeat, for each result in results,
+ j. Let namedCaptures be ? Get(result, "groups").
+ k. If functionalReplace is true, then
+ iv. If namedCaptures is not undefined,
+ 1. Append namedCaptures as the last element of replacerArgs.
+---*/
+
+let source = "(?<fst>.)(?<snd>.)";
+let alternateSource = "(?<fst>.)|(?<snd>.)";
+
+for (let flags of ["", "u"]) {
+ let i = 0;
+ let re = new RegExp(source, flags);
+ let result = "abcd".replace(re,
+ (match, fst, snd, offset, str, groups) => {
+ assert.sameValue(i++, 0);
+ assert.sameValue("ab", match);
+ assert.sameValue("a", groups.fst);
+ assert.sameValue("b", groups.snd);
+ assert.sameValue("a", fst);
+ assert.sameValue("b", snd);
+ assert.sameValue(0, offset);
+ assert.sameValue("abcd", str);
+ return `${groups.snd}${groups.fst}`;
+ });
+ assert.sameValue("bacd", result);
+ assert.sameValue(i, 1);
+
+ let re2 = new RegExp(alternateSource, flags);
+ assert.sameValue("undefinedbcd",
+ "abcd".replace(re2,
+ (match, fst, snd, offset, str, groups) => groups.snd));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass-sans.js b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass-sans.js
new file mode 100644
index 0000000000..a6674a6fb1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass-sans.js
@@ -0,0 +1,38 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+ Test the groups object on RegExp subclass results that do not have their own.
+esid: sec-regexpbuiltinexec
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: RegExpBuiltinExec ( R, S )
+ 24. If _R_ contains any |GroupName|, then
+ a. Let _groups_ be ObjectCreate(*null*).
+ 25. Else,
+ a. Let _groups_ be *undefined*.
+ 26. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
+---*/
+
+class FakeRegExp extends RegExp {
+ exec(subject) {
+ const fakeResult = ["ab", "a"];
+ fakeResult.index = 0;
+ // `groups` is not set, triggering prototype lookup.
+ return fakeResult;
+ }
+};
+
+const re = new FakeRegExp();
+const result = re.exec("ab");
+assert.sameValue(Object.getPrototypeOf(result), Array.prototype);
+assert.sameValue(false, result.hasOwnProperty("groups"));
+
+Array.prototype.groups = { a: "b" };
+Object.getPrototypeOf(Array.prototype.groups).b = "c";
+assert.sameValue("b", "ab".replace(re, "$<a>"));
+assert.sameValue("c", "ab".replace(re, "$<b>"));
+Array.prototype.groups = undefined;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass.js b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass.js
new file mode 100644
index 0000000000..a833d1f10f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-subclass.js
@@ -0,0 +1,36 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+ Test the groups object on RegExp subclass results that have their own.
+esid: sec-regexpbuiltinexec
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: RegExpBuiltinExec ( R, S )
+ 24. If _R_ contains any |GroupName|, then
+ a. Let _groups_ be ObjectCreate(*null*).
+ 25. Else,
+ a. Let _groups_ be *undefined*.
+ 26. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
+---*/
+
+class FakeRegExp extends RegExp {
+ exec(subject) {
+ const fakeResult = ["ab", "a"];
+ fakeResult.index = 0;
+ fakeResult.groups = { a: "b" };
+ Object.getPrototypeOf(fakeResult.groups).b = "c";
+ return fakeResult;
+ }
+};
+
+const re = new FakeRegExp();
+const result = re.exec("ab");
+assert.sameValue(Object.getPrototypeOf(result), Array.prototype);
+assert(result.hasOwnProperty("groups"));
+assert.sameValue("b", result.groups.a);
+assert.sameValue("b", "ab".replace(re, "$<a>"));
+assert.sameValue("c", "ab".replace(re, "$<b>"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-undefined.js b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-undefined.js
new file mode 100644
index 0000000000..06f5e49454
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-undefined.js
@@ -0,0 +1,35 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: The groups object is created unconditionally.
+includes: [propertyHelper.js]
+esid: sec-regexpbuiltinexec
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: RegExpBuiltinExec ( R, S )
+ 24. If _R_ contains any |GroupName|, then
+ a. Let _groups_ be ObjectCreate(*null*).
+ 25. Else,
+ a. Let _groups_ be *undefined*.
+ 26. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
+---*/
+
+const re = /./;
+const result = re.exec("a");
+assert.sameValue(Object.getPrototypeOf(result), Array.prototype);
+assert(result.hasOwnProperty("groups"));
+assert.sameValue("a", result[0]);
+assert.sameValue(0, result.index);
+assert.sameValue(undefined, result.groups);
+verifyProperty(result, "groups", {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+});
+
+Array.prototype.groups = { a: "b" };
+assert.sameValue("$<a>", "a".replace(re, "$<a>"));
+Array.prototype.groups = undefined;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-unmatched.js b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-unmatched.js
new file mode 100644
index 0000000000..d43732eb06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object-unmatched.js
@@ -0,0 +1,37 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+ Test the groups object with matched and unmatched named captures.
+esid: sec-regexpbuiltinexec
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: RegExpBuiltinExec ( R, S )
+ 24. If _R_ contains any |GroupName|, then
+ a. Let _groups_ be ObjectCreate(*null*).
+ 25. Else,
+ a. Let _groups_ be *undefined*.
+ 26. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
+---*/
+
+const re = /(?<a>a).|(?<x>x)/;
+const result = re.exec("ab");
+assert.sameValue(Object.getPrototypeOf(result), Array.prototype);
+assert(result.hasOwnProperty("groups"));
+assert.sameValue("ab", result[0]);
+assert.sameValue("a", result[1]);
+assert.sameValue(undefined, result[2]);
+assert.sameValue(0, result.index);
+assert.sameValue("a", result.groups.a);
+assert.sameValue(undefined, result.groups.x);
+
+// `a` is a matched named capture, `b` is an unmatched named capture, and `z`
+// is not a named capture.
+Array.prototype.groups = { a: "b", x: "y", z: "z" };
+assert.sameValue("a", "ab".replace(re, "$<a>"));
+assert.sameValue("", "ab".replace(re, "$<x>"));
+assert.sameValue("", "ab".replace(re, "$<z>"));
+Array.prototype.groups = undefined;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object.js b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object.js
new file mode 100644
index 0000000000..bec4ae8d59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-object.js
@@ -0,0 +1,41 @@
+// Copyright 2017 Aleksey Shvayka. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Properties of the groups object are created with CreateDataProperty
+includes: [propertyHelper.js]
+esid: sec-regexpbuiltinexec
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: RegExpBuiltinExec ( R, S )
+ 24. If _R_ contains any |GroupName|, then
+ a. Let _groups_ be ObjectCreate(*null*).
+ 25. Else,
+ a. Let _groups_ be *undefined*.
+ 26. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
+---*/
+
+// `groups` is created with Define, not Set.
+let counter = 0;
+Object.defineProperty(Array.prototype, "groups", {
+ set() { counter++; }
+});
+
+let match = /(?<x>.)/.exec("a");
+assert.sameValue(counter, 0);
+
+// `groups` is writable, enumerable and configurable
+// (from CreateDataProperty).
+verifyProperty(match, "groups", {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+});
+
+// The `__proto__` property on the groups object is not special,
+// and does not affect the [[Prototype]] of the resulting groups object.
+let {groups} = /(?<__proto__>.)/.exec("a");
+assert.sameValue("a", groups.__proto__);
+assert.sameValue(null, Object.getPrototypeOf(groups));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/groups-properties.js b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-properties.js
new file mode 100644
index 0000000000..ce971cf0b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/groups-properties.js
@@ -0,0 +1,34 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Properties of the groups object are created with CreateDataProperty
+includes: [compareArray.js, propertyHelper.js]
+esid: sec-regexpbuiltinexec
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: RegExpBuiltinExec ( R, S )
+ 25. For each integer i such that i > 0 and i ≀ n
+ f. If the ith capture of R was defined with a GroupName,
+ i. Let s be the StringValue of the corresponding RegExpIdentifierName.
+ ii. Perform ! CreateDataProperty(groups, s, capturedValue).
+---*/
+
+// Properties created on result.groups in textual order.
+assert.compareArray(["fst", "snd"], Object.getOwnPropertyNames(
+ /(?<fst>.)|(?<snd>.)/u.exec("abcd").groups));
+
+// Properties are created with Define, not Set
+let counter = 0;
+Object.defineProperty(Object.prototype, 'x', {set() { counter++; }});
+let match = /(?<x>.)/.exec('a');
+let groups = match.groups;
+assert.sameValue(counter, 0);
+
+// Properties are writable, enumerable and configurable
+// (from CreateDataProperty)
+verifyWritable(groups, "x");
+verifyEnumerable(groups, "x");
+verifyConfigurable(groups, "x");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/lookbehind.js b/js/src/tests/test262/built-ins/RegExp/named-groups/lookbehind.js
new file mode 100644
index 0000000000..470ff342cd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/lookbehind.js
@@ -0,0 +1,47 @@
+// Copyright 2018 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Named groups can be used in conjunction with lookbehind
+esid: prod-GroupSpecifier
+features: [regexp-named-groups, regexp-lookbehind]
+includes: [compareArray.js]
+---*/
+
+// Unicode mode
+assert.compareArray(["f", "c"], "abcdef".match(/(?<=(?<a>\w){3})f/u));
+assert.sameValue("c", "abcdef".match(/(?<=(?<a>\w){3})f/u).groups.a);
+assert.sameValue("b", "abcdef".match(/(?<=(?<a>\w){4})f/u).groups.a);
+assert.sameValue("a", "abcdef".match(/(?<=(?<a>\w)+)f/u).groups.a);
+assert.sameValue(null, "abcdef".match(/(?<=(?<a>\w){6})f/u));
+
+assert.compareArray(["f", ""], "abcdef".match(/((?<=\w{3}))f/u));
+assert.compareArray(["f", ""], "abcdef".match(/(?<a>(?<=\w{3}))f/u));
+
+assert.compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\d){3})f/u));
+assert.sameValue(null, "abcdef".match(/(?<!(?<a>\D){3})f/u));
+
+assert.compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\D){3})f|f/u));
+assert.compareArray(["f", undefined], "abcdef".match(/(?<a>(?<!\D{3}))f|f/u));
+
+// Non-Unicode mode
+assert.compareArray(["f", "c"], "abcdef".match(/(?<=(?<a>\w){3})f/));
+assert.sameValue("c", "abcdef".match(/(?<=(?<a>\w){3})f/).groups.a);
+assert.sameValue("b", "abcdef".match(/(?<=(?<a>\w){4})f/).groups.a);
+assert.sameValue("a", "abcdef".match(/(?<=(?<a>\w)+)f/).groups.a);
+assert.sameValue(null, "abcdef".match(/(?<=(?<a>\w){6})f/));
+
+assert.compareArray(["f", ""], "abcdef".match(/((?<=\w{3}))f/));
+assert.compareArray(["f", ""], "abcdef".match(/(?<a>(?<=\w{3}))f/));
+
+assert.compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\d){3})f/));
+assert.sameValue(null, "abcdef".match(/(?<!(?<a>\D){3})f/));
+
+assert.compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\D){3})f|f/));
+assert.compareArray(["f", undefined], "abcdef".match(/(?<a>(?<!\D{3}))f|f/));
+
+// Even within a lookbehind, properties are created in left to right order
+assert.compareArray(["fst", "snd"], Object.getOwnPropertyNames(
+ /(?<=(?<fst>.)|(?<snd>.))/u.exec("abcd").groups));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-match.js b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-match.js
new file mode 100644
index 0000000000..bc68c12bf8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-match.js
@@ -0,0 +1,44 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Basic matching cases with non-Unicode groups
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+includes: [compareArray.js]
+---*/
+
+assert.compareArray(["a", "a"], "bab".match(/(?<a>a)/));
+assert.compareArray(["a", "a"], "bab".match(/(?<a42>a)/));
+assert.compareArray(["a", "a"], "bab".match(/(?<_>a)/));
+assert.compareArray(["a", "a"], "bab".match(/(?<$>a)/));
+assert.compareArray(["bab", "a"], "bab".match(/.(?<$>a)./));
+assert.compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(.)/));
+assert.compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(?<b>.)/));
+assert.compareArray(["bab", "ab"], "bab".match(/.(?<a>\w\w)/));
+assert.compareArray(["bab", "bab"], "bab".match(/(?<a>\w\w\w)/));
+assert.compareArray(["bab", "ba", "b"], "bab".match(/(?<a>\w\w)(?<b>\w)/));
+
+let {a, b, c} = /(?<a>.)(?<b>.)(?<c>.)\k<c>\k<b>\k<a>/.exec("abccba").groups;
+assert.sameValue(a, "a");
+assert.sameValue(b, "b");
+assert.sameValue(c, "c");
+
+assert.compareArray("bab".match(/(a)/), "bab".match(/(?<a>a)/));
+assert.compareArray("bab".match(/(a)/), "bab".match(/(?<a42>a)/));
+assert.compareArray("bab".match(/(a)/), "bab".match(/(?<_>a)/));
+assert.compareArray("bab".match(/(a)/), "bab".match(/(?<$>a)/));
+assert.compareArray("bab".match(/.(a)./), "bab".match(/.(?<$>a)./));
+assert.compareArray("bab".match(/.(a)(.)/), "bab".match(/.(?<a>a)(.)/));
+assert.compareArray("bab".match(/.(a)(.)/), "bab".match(/.(?<a>a)(?<b>.)/));
+assert.compareArray("bab".match(/.(\w\w)/), "bab".match(/.(?<a>\w\w)/));
+assert.compareArray("bab".match(/(\w\w\w)/), "bab".match(/(?<a>\w\w\w)/));
+assert.compareArray("bab".match(/(\w\w)(\w)/), "bab".match(/(?<a>\w\w)(?<b>\w)/));
+
+assert.compareArray(["bab", "b"], "bab".match(/(?<b>b).\1/));
+assert.compareArray(["baba", "b", "a"], "baba".match(/(.)(?<a>a)\1\2/));
+assert.compareArray(["baba", "b", "a", "b", "a"], "baba".match(/(.)(?<a>a)(?<b>\1)(\2)/));
+assert.compareArray(["<a", "<"], "<a".match(/(?<lt><)a/));
+assert.compareArray([">a", ">"], ">a".match(/(?<gt>>)a/));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-invalid.js b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-invalid.js
new file mode 100644
index 0000000000..b2523652af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-invalid.js
@@ -0,0 +1,57 @@
+// Copyright (C) 2020 Apple Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Michael Saboff
+description: Invalid exotic named group names in non-Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+---*/
+
+/*
+ Valid ID_Continue Unicode characters (Can't be first identifier character.)
+
+ 𝟚 \u{1d7da} \ud835 \udfda
+
+ Invalid ID_Start / ID_Continue
+
+ (fox face emoji) 🦊 \u{1f98a} \ud83e \udd8a
+ (dog emoji) πŸ• \u{1f415} \ud83d \udc15
+*/
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<🦊>fox)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\u{1f98a}>fox)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\ud83e\udd8a>fox)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<πŸ•>dog)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\u{1f415}>dog)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\ud83d \udc15>dog)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<𝟚the>the)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\u{1d7da}the>the)");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\ud835\udfdathe>the)");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js
new file mode 100644
index 0000000000..eb568a1ee0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js
@@ -0,0 +1,96 @@
+// Copyright (C) 2020 Apple Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Michael Saboff
+description: Exotic named group names in non-Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+---*/
+
+/*
+ Valid ID_Start / ID_Continue Unicode characters
+
+ 𝑓 \u{1d453} \ud835 \udc53
+ π‘œ \u{1d45c} \ud835 \udc5c
+ π‘₯ \u{id465} \ud835 \udc65
+
+ 𝓓 \u{1d4d3} \ud835 \udcd3
+ 𝓸 \u{1d4f8} \ud835 \udcf8
+ 𝓰 \u{1d4f0} \ud835 \udcf0
+
+ 𝓑 \u{1d4d1} \ud835 \udcd1
+ 𝓻 \u{1d4fb} \ud835 \udcfb
+ 𝓸 \u{1d4f8} \ud835 \udcf8
+ 𝔀 \u{1d500} \ud835 \udd00
+ 𝓷 \u{1d4f7} \ud835 \udcf7
+
+ 𝖰 \u{1d5b0} \ud835 \uddb0
+ 𝖑 \u{1d5a1} \ud835 \udda1
+ π–₯ \u{1d5a5} \ud835 \udda5
+
+ (fox) η‹Έ \u{72f8} \u72f8
+ (dog) η‹— \u{72d7} \u72d7
+
+ Valid ID_Continue Unicode characters (Can't be first identifier character.)
+
+ 𝟚 \u{1d7da} \ud835 \udfda
+*/
+
+var string = "The quick brown fox jumped over the lazy dog's back";
+var string2 = "It is a dog eat dog world.";
+
+let match = null;
+
+assert.sameValue(string.match(/(?<animal>fox|dog)/).groups.animal, "fox");
+
+match = string.match(/(?<π‘“π‘œπ‘₯>fox).*(?<𝓓𝓸𝓰>dog)/);
+assert.sameValue(match.groups.π‘“π‘œπ‘₯, "fox");
+assert.sameValue(match.groups.𝓓𝓸𝓰, "dog");
+assert.sameValue(match[1], "fox");
+assert.sameValue(match[2], "dog");
+
+match = string.match(/(?<η‹Έ>fox).*(?<η‹—>dog)/);
+assert.sameValue(match.groups.η‹Έ, "fox");
+assert.sameValue(match.groups.η‹—, "dog");
+assert.sameValue(match[1], "fox");
+assert.sameValue(match[2], "dog");
+
+assert.sameValue(string.match(/(?<𝓑𝓻𝓸𝔀𝓷>brown)/).groups.𝓑𝓻𝓸𝔀𝓷, "brown");
+assert.sameValue(string.match(/(?<𝓑𝓻𝓸𝔀𝓷>brown)/).groups.\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}, "brown");
+assert.sameValue(string.match(/(?<\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}>brown)/).groups.𝓑𝓻𝓸𝔀𝓷, "brown");
+assert.sameValue(string.match(/(?<\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}>brown)/).groups.\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}, "brown");
+assert.sameValue(string.match(/(?<\ud835\udcd1\ud835\udcfb\ud835\udcf8\ud835\udd00\ud835\udcf7>brown)/).groups.𝓑𝓻𝓸𝔀𝓷, "brown");
+assert.sameValue(string.match(/(?<\ud835\udcd1\ud835\udcfb\ud835\udcf8\ud835\udd00\ud835\udcf7>brown)/).groups.\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}, "brown");
+
+assert.sameValue(string.match(/(?<𝖰𝖑π–₯>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<𝖰𝖑\u{1d5a5}>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<𝖰\u{1d5a1}π–₯>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<𝖰\u{1d5a1}\u{1d5a5}>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}𝖑π–₯>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}𝖑\u{1d5a5}>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}\u{1d5a1}π–₯>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}\u{1d5a1}\u{1d5a5}>q\w*\W\w*\W\w*)/).groups.𝖰𝖑π–₯, "quick brown fox");
+
+assert.sameValue(string.match(/(?<the𝟚>the)/).groups.the𝟚, "the");
+assert.sameValue(string.match(/(?<the\u{1d7da}>the)/).groups.the𝟚, "the");
+assert.sameValue(string.match(/(?<the\ud835\udfda>the)/).groups.the𝟚, "the");
+
+match = string2.match(/(?<dog>dog)(.*?)(\k<dog>)/);
+assert.sameValue(match.groups.dog, "dog");
+assert.sameValue(match[1], "dog");
+assert.sameValue(match[2], " eat ");
+assert.sameValue(match[3], "dog");
+
+match = string2.match(/(?<𝓓𝓸𝓰>dog)(.*?)(\k<𝓓𝓸𝓰>)/);
+assert.sameValue(match.groups.𝓓𝓸𝓰, "dog");
+assert.sameValue(match[1], "dog");
+assert.sameValue(match[2], " eat ");
+assert.sameValue(match[3], "dog");
+
+match = string2.match(/(?<η‹—>dog)(.*?)(\k<η‹—>)/);
+assert.sameValue(match.groups.η‹—, "dog");
+assert.sameValue(match[1], "dog");
+assert.sameValue(match[2], " eat ");
+assert.sameValue(match[3], "dog");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names.js b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names.js
new file mode 100644
index 0000000000..e9b3cabb4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-property-names.js
@@ -0,0 +1,25 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Exotic named group names in non-Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+---*/
+
+assert.sameValue("a", /(?<Ο€>a)/.exec("bab").groups.Ο€);
+assert.sameValue("a", /(?<Ο€>a)/.exec("bab").groups.\u03C0);
+assert.sameValue("a", /(?<$>a)/.exec("bab").groups.$);
+assert.sameValue("a", /(?<_>a)/.exec("bab").groups._);
+assert.sameValue("a", /(?<_\u200C>a)/.exec("bab").groups._\u200C);
+assert.sameValue("a", /(?<_\u200D>a)/.exec("bab").groups._\u200D);
+assert.sameValue("a", /(?<ΰ² _ΰ² >a)/.exec("bab").groups.ΰ² _ΰ² );
+
+// Unicode escapes in capture names.
+assert(/(?<\u0041>.)/.test("a"));
+assert(RegExp("(?<\u{0041}>.)").test("a"), "Non-surrogate");
+
+// 4-char escapes must be the proper ID_Start/ID_Continue
+assert(RegExp("(?<\\u0041>.)").test("a"), "Non-surrogate");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-references.js b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-references.js
new file mode 100644
index 0000000000..3f8f8c4ab3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/non-unicode-references.js
@@ -0,0 +1,36 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Named backreferences in non-Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+includes: [compareArray.js]
+---*/
+
+// Named references.
+assert.compareArray(["bab", "b"], "bab".match(/(?<b>.).\k<b>/));
+assert.sameValue(null, "baa".match(/(?<b>.).\k<b>/));
+
+// Reference inside group.
+assert.compareArray(["bab", "b"], "bab".match(/(?<a>\k<a>\w)../));
+assert.sameValue("b", "bab".match(/(?<a>\k<a>\w)../).groups.a);
+
+// Reference before group.
+assert.compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/));
+assert.sameValue("b", "bab".match(/\k<a>(?<a>b)\w\k<a>/).groups.a);
+assert.compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/));
+let {a, b} = "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/).groups;
+assert.sameValue(a, "a");
+assert.sameValue(b, "b");
+
+assert.compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/));
+assert.compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/));
+
+// Reference properties.
+assert.sameValue("a", /(?<a>a)(?<b>b)\k<a>/.exec("aba").groups.a);
+assert.sameValue("b", /(?<a>a)(?<b>b)\k<a>/.exec("aba").groups.b);
+assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>/.exec("aba").groups.c);
+assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>|(?<c>c)/.exec("aba").groups.c);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/shell.js b/js/src/tests/test262/built-ins/RegExp/named-groups/shell.js
new file mode 100644
index 0000000000..c752bae293
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/shell.js
@@ -0,0 +1,37 @@
+// GENERATED, DO NOT EDIT
+// file: compareIterator.js
+// Copyright (C) 2018 Peter Wong. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: Compare the values of an iterator with an array of expected values
+defines: [assert.compareIterator]
+---*/
+
+// Example:
+//
+// function* numbers() {
+// yield 1;
+// yield 2;
+// yield 3;
+// }
+//
+// assert.compareIterator(numbers(), [
+// v => assert.sameValue(v, 1),
+// v => assert.sameValue(v, 2),
+// v => assert.sameValue(v, 3),
+// ]);
+//
+assert.compareIterator = function(iter, validators, message) {
+ message = message || '';
+
+ var i, result;
+ for (i = 0; i < validators.length; i++) {
+ result = iter.next();
+ assert(!result.done, 'Expected ' + i + ' values(s). Instead iterator only produced ' + (i - 1) + ' value(s). ' + message);
+ validators[i](result.value);
+ }
+
+ result = iter.next();
+ assert(result.done, 'Expected only ' + i + ' values(s). Instead iterator produced more. ' + message);
+ assert.sameValue(result.value, undefined, 'Expected value of `undefined` when iterator completes. ' + message);
+}
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-escaped.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-escaped.js
new file mode 100644
index 0000000000..8a4f4b96d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-escaped.js
@@ -0,0 +1,26 @@
+// Copyright 2017 Aleksey Shvayka. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Do not replace $<> preceded with $
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
+
+ 12. These $ replacements are done left-to-right, and, once such a replacement is performed,
+ the new replacement text is not subject to further replacements.
+
+ Table: Replacement Text Symbol Substitutions
+
+ Unicode Characters: $$
+ Replacement text: $
+---*/
+
+let source = "(?<fst>.)";
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("$<fst>bc", "abc".replace(re, "$$<fst>"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-get.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-get.js
new file mode 100644
index 0000000000..a765c9d30a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-get.js
@@ -0,0 +1,31 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Named substitutions are found by getting the property from the groups object
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
+
+ Table: Replacement Text Symbol Substitutions
+
+ Unicode Characters: $<
+ Replacement text:
+ 2. Otherwise,
+ c. Let capture be ? Get(namedCaptures, groupName).
+ d. If capture is undefined, replace the text through > with the empty string.
+ e. Otherwise, replace the text through this following > with ? ToString(capture).
+---*/
+
+let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
+for (let flags of ["g", "gu"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("badc", "abcd".replace(re, "$<snd>$<fst>"));
+}
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("bacd", "abcd".replace(re, "$<snd>$<fst>"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-missing.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-missing.js
new file mode 100644
index 0000000000..defb9e358c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-missing.js
@@ -0,0 +1,26 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: If the group doesn't exist, replace with the empty string
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+---*/
+
+let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("cd", "abcd".replace(re, "$<42$1>"));
+ assert.sameValue("cd", "abcd".replace(re, "$<fth>"));
+ assert.sameValue("cd", "abcd".replace(re, "$<$1>"));
+ assert.sameValue("cd", "abcd".replace(re, "$<>"));
+}
+for (let flags of ["g", "gu"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("", "abcd".replace(re, "$<42$1>"));
+ assert.sameValue("", "abcd".replace(re, "$<fth>"));
+ assert.sameValue("", "abcd".replace(re, "$<$1>"));
+ assert.sameValue("", "abcd".replace(re, "$<>"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-nocaptures.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-nocaptures.js
new file mode 100644
index 0000000000..bfd98fa38d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-nocaptures.js
@@ -0,0 +1,34 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: If there are no named captures, don't replace $<>
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
+
+ Table: Replacement Text Symbol Substitutions
+
+ Unicode Characters: $<
+ Replacement text:
+ 1. If namedCaptures is undefined, the replacement text is the literal string $<.
+---*/
+
+// @@replace with a string replacement argument (no named captures).
+
+let source = "(.)(.)|(x)";
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("$<snd>$<fst>cd", "abcd".replace(re, "$<snd>$<fst>"));
+ assert.sameValue("bacd", "abcd".replace(re, "$2$1"));
+ assert.sameValue("cd", "abcd".replace(re, "$3"));
+ assert.sameValue("$<sndcd", "abcd".replace(re, "$<snd"));
+ assert.sameValue("$<sndacd", "abcd".replace(re, "$<snd$1"));
+ assert.sameValue("$<42a>cd", "abcd".replace(re, "$<42$1>"));
+ assert.sameValue("$<fth>cd", "abcd".replace(re, "$<fth>"));
+ assert.sameValue("$<a>cd", "abcd".replace(re, "$<$1>"));
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-numbered.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-numbered.js
new file mode 100644
index 0000000000..a9943dc7d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-numbered.js
@@ -0,0 +1,31 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Named groups may be accessed in their replacement string by number
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
+
+ Table: Replacement Text Symbol Substitutions
+
+ Unicode Characters: $n
+ Replacement text:
+ The nth element of captures, where n is a single digit in the range 1 to 9. If
+ n≀m and the nth element of captures is undefined, use the empty String instead.
+ If n>m, the result is implementation-defined.
+---*/
+
+let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
+for (let flags of ["g", "gu"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("badc", "abcd".replace(re, "$2$1"));
+}
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("bacd", "abcd".replace(re, "$2$1"));
+}
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-unclosed.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-unclosed.js
new file mode 100644
index 0000000000..1c1e927dcc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-unclosed.js
@@ -0,0 +1,22 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+ A missing > following $< means that the $< is taken literally
+ in a replacement string in the context of named capture substitution.
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+---*/
+
+let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("$<sndcd", "abcd".replace(re, "$<snd"));
+}
+for (let flags of ["g", "gu"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("$<snd$<snd", "abcd".replace(re, "$<snd"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-undefined.js b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-undefined.js
new file mode 100644
index 0000000000..5c4d0e06f1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/string-replace-undefined.js
@@ -0,0 +1,30 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: If a named group was not reached, it is replaced by the empty string
+esid: sec-getsubstitution
+features: [regexp-named-groups]
+info: |
+ Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
+
+ Table: Replacement Text Symbol Substitutions
+
+ Unicode Characters: $<
+ Replacement text:
+ 2. Otherwise,
+ c. Let capture be ? Get(namedCaptures, groupName).
+ d. If capture is undefined, replace the text through > with the empty string.
+---*/
+
+let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
+for (let flags of ["g", "gu"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("", "abcd".replace(re, "$<thd>"));
+}
+for (let flags of ["", "u"]) {
+ let re = new RegExp(source, flags);
+ assert.sameValue("cd", "abcd".replace(re, "$<thd>"));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-match.js b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-match.js
new file mode 100644
index 0000000000..0e87178091
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-match.js
@@ -0,0 +1,48 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Basic matching cases with Unicode groups
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+includes: [compareArray.js]
+---*/
+
+assert.compareArray(["a", "a"], "bab".match(/(?<a>a)/u));
+assert.compareArray(["a", "a"], "bab".match(/(?<a42>a)/u));
+assert.compareArray(["a", "a"], "bab".match(/(?<_>a)/u));
+assert.compareArray(["a", "a"], "bab".match(/(?<$>a)/u));
+assert.compareArray(["bab", "a"], "bab".match(/.(?<$>a)./u));
+assert.compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(.)/u));
+assert.compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(?<b>.)/u));
+assert.compareArray(["bab", "ab"], "bab".match(/.(?<a>\w\w)/u));
+assert.compareArray(["bab", "bab"], "bab".match(/(?<a>\w\w\w)/u));
+assert.compareArray(["bab", "ba", "b"], "bab".match(/(?<a>\w\w)(?<b>\w)/u));
+
+let {a, b, c} = /(?<a>.)(?<b>.)(?<c>.)\k<c>\k<b>\k<a>/u.exec("abccba").groups;
+assert.sameValue(a, "a");
+assert.sameValue(b, "b");
+assert.sameValue(c, "c");
+
+assert.compareArray("bab".match(/(a)/u), "bab".match(/(?<a>a)/u));
+assert.compareArray("bab".match(/(a)/u), "bab".match(/(?<a42>a)/u));
+assert.compareArray("bab".match(/(a)/u), "bab".match(/(?<_>a)/u));
+assert.compareArray("bab".match(/(a)/u), "bab".match(/(?<$>a)/u));
+assert.compareArray("bab".match(/.(a)./u), "bab".match(/.(?<$>a)./u));
+assert.compareArray("bab".match(/.(a)(.)/u), "bab".match(/.(?<a>a)(.)/u));
+assert.compareArray("bab".match(/.(a)(.)/u), "bab".match(/.(?<a>a)(?<b>.)/u));
+assert.compareArray("bab".match(/.(\w\w)/u), "bab".match(/.(?<a>\w\w)/u));
+assert.compareArray("bab".match(/(\w\w\w)/u), "bab".match(/(?<a>\w\w\w)/u));
+assert.compareArray("bab".match(/(\w\w)(\w)/u), "bab".match(/(?<a>\w\w)(?<b>\w)/u));
+
+assert.compareArray(["bab", "b"], "bab".match(/(?<b>b).\1/u));
+assert.compareArray(["baba", "b", "a"], "baba".match(/(.)(?<a>a)\1\2/u));
+assert.compareArray(["baba", "b", "a", "b", "a"], "baba".match(/(.)(?<a>a)(?<b>\1)(\2)/u));
+assert.compareArray(["<a", "<"], "<a".match(/(?<lt><)a/u));
+assert.compareArray([">a", ">"], ">a".match(/(?<gt>>)a/u));
+
+// Nested groups.
+assert.compareArray(["bab", "bab", "ab", "b"], "bab".match(/(?<a>.(?<b>.(?<c>.)))/u));
+assert.compareArray({a: "bab", b: "ab", c: "b"}, "bab".match(/(?<a>.(?<b>.(?<c>.)))/u).groups);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-invalid.js b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-invalid.js
new file mode 100644
index 0000000000..9b0ef33496
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-invalid.js
@@ -0,0 +1,57 @@
+// Copyright (C) 2020 Apple Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Michael Saboff
+description: Invalid exotic named group names in Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+---*/
+
+/*
+ Valid ID_Continue Unicode characters (Can't be first identifier character.)
+
+ 𝟚 \u{1d7da} \ud835 \udfda
+
+ Invalid ID_Start / ID_Continue
+
+ (fox face emoji) 🦊 \u{1f98a} \ud83e \udd8a
+ (dog emoji) πŸ• \u{1f415} \ud83d \udc15
+*/
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<🦊>fox)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\u{1f98a}>fox)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\ud83e\udd8a>fox)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<πŸ•>dog)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\u{1f415}>dog)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\ud83d \udc15>dog)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<𝟚the>the)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\u{1d7da}the>the)", "u");
+});
+
+assert.throws(SyntaxError, function() {
+ return new RegExp("(?<\ud835\udfdathe>the)", "u");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-valid.js b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-valid.js
new file mode 100644
index 0000000000..d26f3c626b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names-valid.js
@@ -0,0 +1,102 @@
+// Copyright (C) 2020 Apple Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Michael Saboff
+description: Exotic named group names in Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+---*/
+
+/*
+ Valid ID_Start / ID_Continue Unicode characters
+
+ 𝑓 \u{1d453} \ud835 \udc53
+ π‘œ \u{1d45c} \ud835 \udc5c
+ π‘₯ \u{id465} \ud835 \udc65
+
+ 𝓓 \u{1d4d3} \ud835 \udcd3
+ 𝓸 \u{1d4f8} \ud835 \udcf8
+ 𝓰 \u{1d4f0} \ud835 \udcf0
+
+ 𝓑 \u{1d4d1} \ud835 \udcd1
+ 𝓻 \u{1d4fb} \ud835 \udcfb
+ 𝓸 \u{1d4f8} \ud835 \udcf8
+ 𝔀 \u{1d500} \ud835 \udd00
+ 𝓷 \u{1d4f7} \ud835 \udcf7
+
+ 𝖰 \u{1d5b0} \ud835 \uddb0
+ 𝖑 \u{1d5a1} \ud835 \udda1
+ π–₯ \u{1d5a5} \ud835 \udda5
+
+ (fox) η‹Έ \u{72f8} \u72f8
+ (dog) η‹— \u{72d7} \u72d7
+
+ Valid ID_Continue Unicode characters (Can't be first identifier character.)
+
+ 𝟚 \u{1d7da} \ud835 \udfda
+
+ Invalid ID_Start / ID_Continue
+
+ (fox face emoji) 🦊 \u{1f98a} \ud83e \udd8a
+ (dog emoji) πŸ• \u{1f415} \ud83d \udc15
+*/
+
+var string = "The quick brown fox jumped over the lazy dog's back";
+var string2 = "It is a dog eat dog world.";
+
+let match = null;
+
+assert.sameValue(string.match(/(?<animal>fox|dog)/u).groups.animal, "fox");
+assert.sameValue(string.match(/(?<the2>the)/u).groups.the2, "the");
+
+match = string.match(/(?<π‘“π‘œπ‘₯>fox).*(?<𝓓𝓸𝓰>dog)/u);
+assert.sameValue(match.groups.π‘“π‘œπ‘₯, "fox");
+assert.sameValue(match.groups.𝓓𝓸𝓰, "dog");
+assert.sameValue(match[1], "fox");
+assert.sameValue(match[2], "dog");
+
+match = string.match(/(?<η‹Έ>fox).*(?<η‹—>dog)/u);
+assert.sameValue(match.groups.η‹Έ, "fox");
+assert.sameValue(match.groups.η‹—, "dog");
+assert.sameValue(match[1], "fox");
+assert.sameValue(match[2], "dog");
+
+assert.sameValue(string.match(/(?<𝓑𝓻𝓸𝔀𝓷>brown)/u).groups.𝓑𝓻𝓸𝔀𝓷, "brown");
+assert.sameValue(string.match(/(?<𝓑𝓻𝓸𝔀𝓷>brown)/u).groups.\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}, "brown");
+assert.sameValue(string.match(/(?<\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}>brown)/u).groups.𝓑𝓻𝓸𝔀𝓷, "brown");
+assert.sameValue(string.match(/(?<\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}>brown)/u).groups.\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}, "brown");
+assert.sameValue(string.match(/(?<\ud835\udcd1\ud835\udcfb\ud835\udcf8\ud835\udd00\ud835\udcf7>brown)/u).groups.𝓑𝓻𝓸𝔀𝓷, "brown");
+assert.sameValue(string.match(/(?<\ud835\udcd1\ud835\udcfb\ud835\udcf8\ud835\udd00\ud835\udcf7>brown)/u).groups.\u{1d4d1}\u{1d4fb}\u{1d4f8}\u{1d500}\u{1d4f7}, "brown");
+
+assert.sameValue(string.match(/(?<𝖰𝖑π–₯>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<𝖰𝖑\u{1d5a5}>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<𝖰\u{1d5a1}π–₯>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<𝖰\u{1d5a1}\u{1d5a5}>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}𝖑π–₯>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}𝖑\u{1d5a5}>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}\u{1d5a1}π–₯>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+assert.sameValue(string.match(/(?<\u{1d5b0}\u{1d5a1}\u{1d5a5}>q\w*\W\w*\W\w*)/u).groups.𝖰𝖑π–₯, "quick brown fox");
+
+assert.sameValue(string.match(/(?<the𝟚>the)/u).groups.the𝟚, "the");
+assert.sameValue(string.match(/(?<the\u{1d7da}>the)/u).groups.the𝟚, "the");
+assert.sameValue(string.match(/(?<the\ud835\udfda>the)/u).groups.the𝟚, "the");
+
+match = string2.match(/(?<dog>dog)(.*?)(\k<dog>)/u);
+assert.sameValue(match.groups.dog, "dog");
+assert.sameValue(match[1], "dog");
+assert.sameValue(match[2], " eat ");
+assert.sameValue(match[3], "dog");
+
+match = string2.match(/(?<𝓓𝓸𝓰>dog)(.*?)(\k<𝓓𝓸𝓰>)/u);
+assert.sameValue(match.groups.𝓓𝓸𝓰, "dog");
+assert.sameValue(match[1], "dog");
+assert.sameValue(match[2], " eat ");
+assert.sameValue(match[3], "dog");
+
+match = string2.match(/(?<η‹—>dog)(.*?)(\k<η‹—>)/u);
+assert.sameValue(match.groups.η‹—, "dog");
+assert.sameValue(match[1], "dog");
+assert.sameValue(match[2], " eat ");
+assert.sameValue(match[3], "dog");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names.js b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names.js
new file mode 100644
index 0000000000..bb321dd6ff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-property-names.js
@@ -0,0 +1,32 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Exotic named group names in Unicode RegExps
+esid: prod-GroupSpecifier
+features: [regexp-named-groups]
+---*/
+
+assert.sameValue("a", /(?<Ο€>a)/u.exec("bab").groups.Ο€);
+assert.sameValue("a", /(?<\u{03C0}>a)/u.exec("bab").groups.Ο€);
+assert.sameValue("a", /(?<Ο€>a)/u.exec("bab").groups.\u03C0);
+assert.sameValue("a", /(?<\u{03C0}>a)/u.exec("bab").groups.\u03C0);
+assert.sameValue("a", /(?<$>a)/u.exec("bab").groups.$);
+assert.sameValue("a", /(?<_>a)/u.exec("bab").groups._);
+assert.sameValue("a", /(?<$𐒀>a)/u.exec("bab").groups.$𐒀);
+assert.sameValue("a", /(?<_\u200C>a)/u.exec("bab").groups._\u200C);
+assert.sameValue("a", /(?<_\u200D>a)/u.exec("bab").groups._\u200D);
+assert.sameValue("a", /(?<ΰ² _ΰ² >a)/u.exec("bab").groups.ΰ² _ΰ² );
+
+// Unicode escapes in capture names.
+assert(/(?<a\uD801\uDCA4>.)/u.test("a"), "\\u Lead \\u Trail");
+assert(/(?<\u0041>.)/u.test("a"), "\\u NonSurrogate");
+assert(/(?<\u{0041}>.)/u.test("a"), "\\u{ Non-surrogate }");
+assert(/(?<a\u{104A4}>.)/u.test("a"), "\\u{ Surrogate, ID_Continue }");
+assert(RegExp("(?<\u{0041}>.)", "u").test("a"), "Non-surrogate");
+assert(RegExp("(?<a\u{104A4}>.)", "u").test("a"), "Surrogate,ID_Continue");
+assert((/(?<\u{0041}>.)/u).test("a"), "Non-surrogate");
+assert(/(?<a\u{104A4}>.)/u.test("a"), "Surrogate, ID_Continue");
+assert(RegExp("(?<\\u0041>.)", "u").test("a"), "Non-surrogate");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-references.js b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-references.js
new file mode 100644
index 0000000000..30d70ad112
--- /dev/null
+++ b/js/src/tests/test262/built-ins/RegExp/named-groups/unicode-references.js
@@ -0,0 +1,49 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Named backreferences in Unicode RegExps
+esid: sec-atomescape
+info: |
+ The production AtomEscape :: [+N] k GroupName evaluates as follows:
+
+ 1. Search the enclosing RegExp for an instance of a GroupSpecifier for an
+ RegExpIdentifierName which has a StringValue equal to the StringValue
+ of the RegExpIdentifierName contained in GroupName.
+ 2. Assert: A unique such GroupSpecifier is found.
+ 3. Let parenIndex be the number of left capturing parentheses in the entire
+ regular expression that occur to the left of the located GroupSpecifier.
+ This is the total number of times the Atom::(GroupSpecifierDisjunction)
+ production is expanded prior to that production's Term plus the total
+ number of Atom :: (GroupSpecifierDisjunction) productions enclosing this Term.
+ 4. Call BackreferenceMatcher(parenIndex) and return its Matcher result.
+features: [regexp-named-groups]
+includes: [compareArray.js]
+---*/
+
+// Named references.
+assert.compareArray(["bab", "b"], "bab".match(/(?<b>.).\k<b>/u));
+assert.sameValue(null, "baa".match(/(?<b>.).\k<b>/u));
+
+// Reference inside group.
+assert.compareArray(["bab", "b"], "bab".match(/(?<a>\k<a>\w)../u));
+assert.sameValue("b", "bab".match(/(?<a>\k<a>\w)../u).groups.a);
+
+// Reference before group.
+assert.compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/u));
+assert.sameValue("b", "bab".match(/\k<a>(?<a>b)\w\k<a>/u).groups.a);
+assert.compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/u));
+let {a, b} = "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/u).groups;
+assert.sameValue(a, "a");
+assert.sameValue(b, "b");
+
+assert.compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/));
+assert.compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/));
+
+// Reference properties.
+assert.sameValue("a", /(?<a>a)(?<b>b)\k<a>/u.exec("aba").groups.a);
+assert.sameValue("b", /(?<a>a)(?<b>b)\k<a>/u.exec("aba").groups.b);
+assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>/u.exec("aba").groups.c);
+assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>|(?<c>c)/u.exec("aba").groups.c);
+
+reportCompare(0, 0);