From b01a420cc9ce19e90568af26e31855d6535e45be Mon Sep 17 00:00:00 2001 From: alazarev Date: Mon, 1 Jul 2024 17:24:34 +0300 Subject: [PATCH 1/3] fix - consider captured groups inside non-capturing groups --- packages/core-js/modules/es.regexp.constructor.js | 2 +- tests/unit-global/es.regexp.constructor.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core-js/modules/es.regexp.constructor.js b/packages/core-js/modules/es.regexp.constructor.js index a12a5a3e0291..e0724229cbb5 100644 --- a/packages/core-js/modules/es.regexp.constructor.js +++ b/packages/core-js/modules/es.regexp.constructor.js @@ -98,9 +98,9 @@ var handleNCG = function (string) { if (exec(IS_NCG, stringSlice(string, index + 1))) { index += 2; ncg = true; + groupid++; } result += chr; - groupid++; continue; case chr === '>' && ncg: if (groupname === '' || hasOwn(names, groupname)) { diff --git a/tests/unit-global/es.regexp.constructor.js b/tests/unit-global/es.regexp.constructor.js index 9a376d1dafc7..46df437d1bdc 100644 --- a/tests/unit-global/es.regexp.constructor.js +++ b/tests/unit-global/es.regexp.constructor.js @@ -84,6 +84,10 @@ if (DESCRIPTORS) { const { groups } = RegExp('foo:(?\\w+),bar:(?\\w+)').exec('foo:abc,bar:def'); assert.same(getPrototypeOf(groups), null, 'null prototype'); assert.deepEqual(groups, { foo: 'abc', bar: 'def' }, 'NCG #3'); + const { groups: nonCaptured, length } = RegExp('foo:(?:value=(?\\w+)),bar:(?:value=(?\\w+))').exec('foo:value=abc,bar:value=def'); + assert.deepEqual(nonCaptured, { foo: 'abc', bar: 'def' }, 'NCG #4'); + assert.same(length, 3, 'incorrect number of matched entries #1') + // fails in Safari // assert.same(Object.getPrototypeOf(groups), null, 'NCG #4'); assert.same('foo:abc,bar:def'.replace(RegExp('foo:(?\\w+),bar:(?\\w+)'), '$,$'), 'def,abc', 'replace #1'); From 60f054260b00017cf45f551cff8bb48c3bdf5acd Mon Sep 17 00:00:00 2001 From: alazarev Date: Mon, 1 Jul 2024 17:24:34 +0300 Subject: [PATCH 2/3] fix es.regexp.constructor - consider captured groups inside non-capturing groups --- packages/core-js/modules/es.regexp.constructor.js | 6 ++++-- tests/unit-global/es.regexp.constructor.js | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/core-js/modules/es.regexp.constructor.js b/packages/core-js/modules/es.regexp.constructor.js index a12a5a3e0291..08ce6e4e69b5 100644 --- a/packages/core-js/modules/es.regexp.constructor.js +++ b/packages/core-js/modules/es.regexp.constructor.js @@ -95,11 +95,13 @@ var handleNCG = function (string) { brackets = true; break; case chr === '(': - if (exec(IS_NCG, stringSlice(string, index + 1))) { + result += chr; + if (stringSlice(string, index + 1, index + 3) == "?:") { // avoid groupid increment in non-capturing group + continue; + } if (exec(IS_NCG, stringSlice(string, index + 1))) { index += 2; ncg = true; } - result += chr; groupid++; continue; case chr === '>' && ncg: diff --git a/tests/unit-global/es.regexp.constructor.js b/tests/unit-global/es.regexp.constructor.js index 9a376d1dafc7..8847627e1048 100644 --- a/tests/unit-global/es.regexp.constructor.js +++ b/tests/unit-global/es.regexp.constructor.js @@ -84,6 +84,13 @@ if (DESCRIPTORS) { const { groups } = RegExp('foo:(?\\w+),bar:(?\\w+)').exec('foo:abc,bar:def'); assert.same(getPrototypeOf(groups), null, 'null prototype'); assert.deepEqual(groups, { foo: 'abc', bar: 'def' }, 'NCG #3'); + const { groups: nonCaptured, length } = RegExp('foo:(?:value=(?\\w+)),bar:(?:value=(?\\w+))').exec('foo:value=abc,bar:value=def'); + assert.deepEqual(nonCaptured, { foo: 'abc', bar: 'def' }, 'NCG #4'); + assert.same(length, 3, 'incorrect number of matched entries #1') + + const { groups: skipBar } = RegExp('foo:(?\\w+),bar:(\\w+),buz:(?\\w+)').exec('foo:abc,bar:def,buz:ghi'); + assert.deepEqual(skipBar, { foo: 'abc', buz: 'ghi' }, 'NCG #5'); + // fails in Safari // assert.same(Object.getPrototypeOf(groups), null, 'NCG #4'); assert.same('foo:abc,bar:def'.replace(RegExp('foo:(?\\w+),bar:(?\\w+)'), '$,$'), 'def,abc', 'replace #1'); From f3b0335aaad0bd75ae861818903e4353a7b598fc Mon Sep 17 00:00:00 2001 From: alazarev Date: Tue, 2 Jul 2024 13:19:01 +0300 Subject: [PATCH 3/3] fix eslint warnings --- packages/core-js/modules/es.regexp.constructor.js | 4 ++-- tests/unit-global/es.regexp.constructor.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core-js/modules/es.regexp.constructor.js b/packages/core-js/modules/es.regexp.constructor.js index 08ce6e4e69b5..b9e5ca1b8a0b 100644 --- a/packages/core-js/modules/es.regexp.constructor.js +++ b/packages/core-js/modules/es.regexp.constructor.js @@ -96,9 +96,9 @@ var handleNCG = function (string) { break; case chr === '(': result += chr; - if (stringSlice(string, index + 1, index + 3) == "?:") { // avoid groupid increment in non-capturing group + if (stringSlice(string, index + 1, index + 3) === '?:') { // avoid groupid increment in non-capturing group continue; - } if (exec(IS_NCG, stringSlice(string, index + 1))) { + } else if (exec(IS_NCG, stringSlice(string, index + 1))) { index += 2; ncg = true; } diff --git a/tests/unit-global/es.regexp.constructor.js b/tests/unit-global/es.regexp.constructor.js index 8847627e1048..d22e0ce562f6 100644 --- a/tests/unit-global/es.regexp.constructor.js +++ b/tests/unit-global/es.regexp.constructor.js @@ -84,10 +84,12 @@ if (DESCRIPTORS) { const { groups } = RegExp('foo:(?\\w+),bar:(?\\w+)').exec('foo:abc,bar:def'); assert.same(getPrototypeOf(groups), null, 'null prototype'); assert.deepEqual(groups, { foo: 'abc', bar: 'def' }, 'NCG #3'); + // eslint-disable-next-line regexp/no-useless-non-capturing-group -- required for testing const { groups: nonCaptured, length } = RegExp('foo:(?:value=(?\\w+)),bar:(?:value=(?\\w+))').exec('foo:value=abc,bar:value=def'); assert.deepEqual(nonCaptured, { foo: 'abc', bar: 'def' }, 'NCG #4'); - assert.same(length, 3, 'incorrect number of matched entries #1') + assert.same(length, 3, 'incorrect number of matched entries #1'); + // eslint-disable-next-line regexp/no-unused-capturing-group -- required for testing const { groups: skipBar } = RegExp('foo:(?\\w+),bar:(\\w+),buz:(?\\w+)').exec('foo:abc,bar:def,buz:ghi'); assert.deepEqual(skipBar, { foo: 'abc', buz: 'ghi' }, 'NCG #5');