From 254a22794ef1d795e543d4265d2fd3f433e10dcd Mon Sep 17 00:00:00 2001 From: Matt Clough Date: Fri, 10 Jan 2020 07:23:01 -0500 Subject: [PATCH] fixes #471 (#745) * checks if RegExp[Symbol.replace] substitutes undefined capture groups * use wellKnownSymbol util to get symbol key for replace method * fixes REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE conditional logic * caches Symbol.replace as constant, uses regexp literal, swaps default for guard in fix-regexp-well-known-symbol-logic --- .../fix-regexp-well-known-symbol-logic.js | 20 +++++++++++++++++-- packages/core-js/modules/es.string.replace.js | 5 ++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js b/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js index 05de94193514..932469f546ad 100644 --- a/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js +++ b/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js @@ -28,6 +28,15 @@ var REPLACE_KEEPS_$0 = (function () { return 'a'.replace(/./, '$0') === '$0'; })(); +var REPLACE = wellKnownSymbol('replace'); +// Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string +var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () { + if (/./[REPLACE]) { + return /./[REPLACE]('a', '$0') === ''; + } + return false; +})(); + // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec // Weex JS has frozen built-in prototypes, so use try / catch wrapper var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () { @@ -75,7 +84,11 @@ module.exports = function (KEY, length, exec, sham) { if ( !DELEGATES_TO_SYMBOL || !DELEGATES_TO_EXEC || - (KEY === 'replace' && !(REPLACE_SUPPORTS_NAMED_GROUPS && REPLACE_KEEPS_$0)) || + (KEY === 'replace' && !( + REPLACE_SUPPORTS_NAMED_GROUPS && + REPLACE_KEEPS_$0 && + REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE + )) || (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC) ) { var nativeRegExpMethod = /./[SYMBOL]; @@ -90,7 +103,10 @@ module.exports = function (KEY, length, exec, sham) { return { done: true, value: nativeMethod.call(str, regexp, arg2) }; } return { done: false }; - }, { REPLACE_KEEPS_$0: REPLACE_KEEPS_$0 }); + }, { + REPLACE_KEEPS_$0: REPLACE_KEEPS_$0, + REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE: REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE + }); var stringMethod = methods[0]; var regexMethod = methods[1]; diff --git a/packages/core-js/modules/es.string.replace.js b/packages/core-js/modules/es.string.replace.js index 6eeb2abdeea6..f0e0933ced7f 100644 --- a/packages/core-js/modules/es.string.replace.js +++ b/packages/core-js/modules/es.string.replace.js @@ -33,7 +33,10 @@ fixRegExpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, ma // `RegExp.prototype[@@replace]` method // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace function (regexp, replaceValue) { - if (reason.REPLACE_KEEPS_$0 || (typeof replaceValue === 'string' && replaceValue.indexOf('$0') === -1)) { + if ( + !reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && + (reason.REPLACE_KEEPS_$0 || (typeof replaceValue === 'string' && replaceValue.indexOf('$0') === -1)) + ) { var res = maybeCallNative(nativeReplace, regexp, this, replaceValue); if (res.done) return res.value; }