Skip to content

Commit

Permalink
Merge pull request #434 from nicolo-ribaudo/named-groups-regexp-replace
Browse files Browse the repository at this point in the history
Polyfll named groups in RegExp#@@replace
  • Loading branch information
zloirock authored Sep 26, 2018
2 parents 7b57fe4 + 51833fa commit 17f410d
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 92 deletions.
32 changes: 26 additions & 6 deletions packages/core-js/internals/fix-regexp-well-known-symbol-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ var SPECIES = wellKnownSymbol('species');

module.exports = function (KEY, length, exec, sham) {
var SYMBOL = wellKnownSymbol(KEY);
var methods = exec(requireObjectCoercible, SYMBOL, ''[KEY]);
var stringMethod = methods[0];
var regexMethod = methods[1];
if (fails(function () {

var delegates = !fails(function () {
// String methods call symbol-named RegEp methods
var O = {};
O[SYMBOL] = function () { return 7; };
return ''[KEY](O) != 7;
}) || fails(function () {
}) && !fails(function () {
// Symbol-named RegExp methods call .exec
var execCalled = false;
var re = /a/;
Expand All @@ -32,7 +30,29 @@ module.exports = function (KEY, length, exec, sham) {

re[SYMBOL]('');
return !execCalled;
})) {
});

var replaceSupportsNamedGroups = KEY === 'replace' && !fails(function () {
// #replace needs built-in support for named groups.
// #match works fine because it just return the exec results, even if it has
// a "grops" property.
var re = /./;
re.exec = function () {
var result = [];
result.groups = { a: '7' };
return result;
};
return ''.replace(re, '$<a>') !== '7';
});

if (!delegates || (KEY === 'replace' && !replaceSupportsNamedGroups)) {
var methods = exec(requireObjectCoercible, SYMBOL, ''[KEY], /./[SYMBOL], {
delegates: delegates,
replaceSupportsNamedGroups: replaceSupportsNamedGroups
});
var stringMethod = methods[0];
var regexMethod = methods[1];

redefine(String.prototype, KEY, stringMethod);
redefine(RegExp.prototype, SYMBOL, length == 2
// 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue)
Expand Down
184 changes: 98 additions & 86 deletions packages/core-js/modules/es.string.replace.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,101 +18,113 @@ var maybeToString = function (it) {
};

// @@replace logic
require('../internals/fix-regexp-well-known-symbol-logic')('replace', 2, function (defined, REPLACE, nativeReplace) {
return [
// `String.prototype.replace` method
// https://tc39.github.io/ecma262/#sec-string.prototype.replace
function replace(searchValue, replaceValue) {
var O = defined(this);
var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
return replacer !== undefined
? replacer.call(searchValue, O, replaceValue)
: nativeReplace.call(String(O), searchValue, replaceValue);
},
// `RegExp.prototype[@@replace]` method
// https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
function (regexp, replaceValue) {
if (regexp.exec === nativeExec) return nativeReplace.call(this, regexp, replaceValue);
require('../internals/fix-regexp-well-known-symbol-logic')(
'replace',
2,
function (defined, REPLACE, nativeReplace, nativeRegExpReplace, reason) {
return [
// `String.prototype.replace` method
// https://tc39.github.io/ecma262/#sec-string.prototype.replace
function replace(searchValue, replaceValue) {
var O = defined(this);
var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
return replacer !== undefined
? replacer.call(searchValue, O, replaceValue)
: nativeReplace.call(String(O), searchValue, replaceValue);
},
// `RegExp.prototype[@@replace]` method
// https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
function (regexp, replaceValue) {
if (regexp.exec === nativeExec) {
if (reason.delegates) {
// The native #replaceMethod already delegates to @@replace (this
// polyfilled function, leasing to infinite recursion).
// We avoid it by directly calling the native @@replace method.
return nativeRegExpReplace.call(regexp, this, replaceValue);
}
return nativeReplace.call(this, regexp, replaceValue);
}

var rx = anObject(regexp);
var S = String(this);
var rx = anObject(regexp);
var S = String(this);

var functionalReplace = typeof replaceValue === 'function';
if (!functionalReplace) replaceValue = String(replaceValue);
var functionalReplace = typeof replaceValue === 'function';
if (!functionalReplace) replaceValue = String(replaceValue);

var global = rx.global;
if (global) {
var fullUnicode = rx.unicode;
rx.lastIndex = 0;
}
var results = [];
while (true) {
var result = regExpExec(rx, S);
if (result === null) break;
var global = rx.global;
if (global) {
var fullUnicode = rx.unicode;
rx.lastIndex = 0;
}
var results = [];
while (true) {
var result = regExpExec(rx, S);
if (result === null) break;

results.push(result);
if (!global) break;
results.push(result);
if (!global) break;

var matchStr = String(result[0]);
if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
}
var matchStr = String(result[0]);
if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
}

var accumulatedResult = '';
var nextSourcePosition = 0;
for (var i = 0; i < results.length; i++) {
result = results[i];
var accumulatedResult = '';
var nextSourcePosition = 0;
for (var i = 0; i < results.length; i++) {
result = results[i];

var matched = String(result[0]);
var position = max(min(toInteger(result.index), S.length), 0);
var captures = result.slice(1).map(maybeToString);
var namedCaptures = result.groups;
if (functionalReplace) {
var replacerArgs = [matched].concat(captures, position, S);
if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
var replacement = String(replaceValue.apply(undefined, replacerArgs));
} else {
replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
}
if (position >= nextSourcePosition) {
accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
nextSourcePosition = position + matched.length;
var matched = String(result[0]);
var position = max(min(toInteger(result.index), S.length), 0);
var captures = result.slice(1).map(maybeToString);
var namedCaptures = result.groups;
if (functionalReplace) {
var replacerArgs = [matched].concat(captures, position, S);
if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
var replacement = String(replaceValue.apply(undefined, replacerArgs));
} else {
replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
}
if (position >= nextSourcePosition) {
accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
nextSourcePosition = position + matched.length;
}
}
return accumulatedResult + S.slice(nextSourcePosition);
}
return accumulatedResult + S.slice(nextSourcePosition);
}
];
];

// https://tc39.github.io/ecma262/#sec-getsubstitution
function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
var tailPos = position + matched.length;
var m = captures.length;
var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
if (namedCaptures !== undefined) {
namedCaptures = toObject(namedCaptures);
symbols = SUBSTITUTION_SYMBOLS;
}
return nativeReplace.call(replacement, symbols, function (match, ch) {
var capture;
switch (ch[0]) {
case '$': return '$';
case '&': return matched;
case '`': return str.slice(0, position);
case "'": return str.slice(tailPos);
case '<':
capture = namedCaptures[ch.slice(1, -1)];
break;
default: // \d\d?
var n = +ch;
if (n === 0) return ch;
if (n > m) {
var f = floor(n / 10);
if (f === 0) return ch;
if (f <= m) return captures[f - 1] === undefined ? ch[1] : captures[f - 1] + ch[1];
return ch;
}
capture = captures[n - 1];
// https://tc39.github.io/ecma262/#sec-getsubstitution
function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
var tailPos = position + matched.length;
var m = captures.length;
var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
if (namedCaptures !== undefined) {
namedCaptures = toObject(namedCaptures);
symbols = SUBSTITUTION_SYMBOLS;
}
return capture === undefined ? '' : capture;
});
return nativeReplace.call(replacement, symbols, function (match, ch) {
var capture;
switch (ch[0]) {
case '$': return '$';
case '&': return matched;
case '`': return str.slice(0, position);
case "'": return str.slice(tailPos);
case '<':
capture = namedCaptures[ch.slice(1, -1)];
break;
default: // \d\d?
var n = +ch;
if (n === 0) return ch;
if (n > m) {
var f = floor(n / 10);
if (f === 0) return ch;
if (f <= m) return captures[f - 1] === undefined ? ch[1] : captures[f - 1] + ch[1];
return ch;
}
capture = captures[n - 1];
}
return capture === undefined ? '' : capture;
});
}
}
});
);

0 comments on commit 17f410d

Please sign in to comment.