diff --git a/test/built-ins/RegExp/named-groups/groups-object.js b/test/built-ins/RegExp/named-groups/groups-object.js index a7d2b5433cc..5479db3d8a4 100644 --- a/test/built-ins/RegExp/named-groups/groups-object.js +++ b/test/built-ins/RegExp/named-groups/groups-object.js @@ -1,4 +1,5 @@ // Copyright 2017 Aleksey Shvayka. All rights reserved. +// Copyright 2017 Mathias Bynens. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- @@ -8,26 +9,112 @@ esid: sec-regexpbuiltinexec features: [regexp-named-groups] info: > Runtime Semantics: RegExpBuiltinExec ( R, S ) - 24. If R contains any GroupName, - a. Let groups be ObjectCreate(null). - b. Perform ! CreateDataProperty(A, "groups", groups). + 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 +// The groups property must be created unconditionally. +// https://github.com/tc39/proposal-regexp-named-groups/pull/40 +{ + const re = /./; + const result = re.exec("a"); + assert.sameValue(result.__proto__, Array.prototype); + assert(result.hasOwnProperty("groups")); + assert.sameValue("a", result[0]); + assert.sameValue(0, result.index); + assert.sameValue(undefined, result.groups); + + Array.prototype.groups = { a: "b" }; + assert.sameValue("$", "a".replace(re, "$")); + Array.prototype.groups = undefined; +} + +{ + const re = /(?a).|(?x)/; + const result = re.exec("ab"); + assert.sameValue(result.__proto__, 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, "$")); + assert.sameValue("", "ab".replace(re, "$")); + assert.sameValue("", "ab".replace(re, "$")); + Array.prototype.groups = undefined; +} + +{ + 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(result.__proto__, Array.prototype); + assert.sameValue(false, result.hasOwnProperty("groups")); + + Array.prototype.groups = { a: "b" }; + Array.prototype.groups.__proto__.b = "c"; + assert.sameValue("b", "ab".replace(re, "$")); + assert.sameValue("c", "ab".replace(re, "$")); + Array.prototype.groups = undefined; +} + +{ + class FakeRegExp extends RegExp { + exec(subject) { + const fakeResult = ["ab", "a"]; + fakeResult.index = 0; + fakeResult.groups = { a: "b" }; + fakeResult.groups.__proto__.b = "c"; + return fakeResult; + } + }; + + const re = new FakeRegExp(); + const result = re.exec("ab"); + assert.sameValue(result.__proto__, Array.prototype); + assert(result.hasOwnProperty("groups")); + assert.sameValue("b", result.groups.a); + assert.sameValue("b", "ab".replace(re, "$")); + assert.sameValue("c", "ab".replace(re, "$")); +} + +// `groups` is created with Define, not Set. +// Note: This must be at the bottom of the file, to not interfere with the +// tests above. let counter = 0; -Object.defineProperty(Array.prototype, "groups", {set() { counter++; }}); +Object.defineProperty(Array.prototype, "groups", { + set() { counter++; } +}); + let match = /(?.)/.exec("a"); assert.sameValue(counter, 0); -// groups is writable, enumerable and configurable -// (from CreateDataProperty) +// `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, +// 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__);