Skip to content

Commit

Permalink
Fix #lastIndex in IE8 and move NPCG check from #split to #exec
Browse files Browse the repository at this point in the history
IE8 is not fully fixed yet
  • Loading branch information
nicolo-ribaudo committed Nov 12, 2018
1 parent 62ade11 commit 5bd11fc
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/core-js-builder/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ module.exports = {
'es.string.sub',
'es.string.sup',
'es.regexp.constructor',
'es.regexp.exec',
'es.regexp.flags',
'es.regexp.to-string',
'es.parse-int',
Expand Down
1 change: 1 addition & 0 deletions packages/core-js/es/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ require('../modules/es.string.strike');
require('../modules/es.string.sub');
require('../modules/es.string.sup');
require('../modules/es.regexp.constructor');
require('../modules/es.regexp.exec');
require('../modules/es.regexp.flags');
require('../modules/es.regexp.to-string');
require('../modules/es.parse-int');
Expand Down
1 change: 1 addition & 0 deletions packages/core-js/es/regexp/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require('../../modules/es.regexp.constructor');
require('../../modules/es.regexp.to-string');
require('../../modules/es.regexp.exec');
require('../../modules/es.regexp.flags');
require('../../modules/es.string.match');
require('../../modules/es.string.replace');
Expand Down
1 change: 1 addition & 0 deletions packages/core-js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ require('./modules/es.string.strike');
require('./modules/es.string.sub');
require('./modules/es.string.sup');
require('./modules/es.regexp.constructor');
require('./modules/es.regexp.exec');
require('./modules/es.regexp.flags');
require('./modules/es.regexp.to-string');
require('./modules/es.parse-int');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ var redefine = require('../internals/redefine');
var fails = require('../internals/fails');
var requireObjectCoercible = require('../internals/require-object-coercible');
var wellKnownSymbol = require('../internals/well-known-symbol');
var regexpExec = require('../internals/regexp-exec');

var nativeExec = RegExp.prototype.exec;
var SPECIES = wellKnownSymbol('species');

module.exports = function (KEY, length, exec, sham) {
Expand Down Expand Up @@ -69,7 +69,7 @@ module.exports = function (KEY, length, exec, sham) {
SYMBOL,
''[KEY],
function maybeCallNative(nativeMethod, regexp, str, arg2, forceStringMethod) {
if (regexp.exec === nativeExec) {
if (regexp.exec === regexpExec.impl) {
if (delegatesToSymbol && !forceStringMethod) {
// The native String method already delegates to @@method (this
// polyfilled function), leasing to infinite recursion.
Expand Down
4 changes: 2 additions & 2 deletions packages/core-js/internals/regexp-exec-abstract.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var classof = require('./classof-raw');
var builtinExec = RegExp.prototype.exec;
var regexpExec = require('./regexp-exec');

// `RegExpExec` abstract operation
// https://tc39.github.io/ecma262/#sec-regexpexec
Expand All @@ -17,6 +17,6 @@ module.exports = function (R, S) {
throw new TypeError('RegExp#exec called on incompatible receiver');
}

return builtinExec.call(R, S);
return regexpExec.impl.call(R, S);
};

58 changes: 58 additions & 0 deletions packages/core-js/internals/regexp-exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict';

var regexpFlags = require('./regexp-flags');

var nativeExec = RegExp.prototype.exec;
// This always refers to the native implementation, because the
// String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js,
// which loads this file before patching the method.
var nativeReplace = String.prototype.replace;

var patchedExec = nativeExec;

var LAST_INDEX = 'lastIndex';

var UPDATES_LAST_INDEX_NON_GLOBAL = (function () {
var re = /a/;
nativeExec.call(re, 'a');
return re[LAST_INDEX] !== 0;
})();

// nonparticipating capturing group, copied from es5-shim's String#split patch.
var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;

var patch = UPDATES_LAST_INDEX_NON_GLOBAL || NPCG_INCLUDED;

if (patch) {
patchedExec = function exec(str) {
var re = this;
var lastIndex, reCopy, match, i;

if (NPCG_INCLUDED) {
reCopy = new RegExp('^' + re.source + '$(?!\\s)', regexpFlags.call(re));
}
if (UPDATES_LAST_INDEX_NON_GLOBAL) lastIndex = this[LAST_INDEX];

match = nativeExec.call(this, str);

if (UPDATES_LAST_INDEX_NON_GLOBAL && !this.global) this[LAST_INDEX] = lastIndex;
if (NPCG_INCLUDED && match && match.length > 1) {
// Fix browsers whose `exec` methods don't consistently return `undefined`
// for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
// eslint-disable-next-line no-loop-func
nativeReplace.call(match[0], reCopy, function () {
for (i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undefined) match[i] = undefined;
}
});
}

return match;
};
}

module.exports = {
orig: nativeExec,
impl: patchedExec,
patched: patch
};
11 changes: 11 additions & 0 deletions packages/core-js/modules/es.regexp.exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

var regexpExec = require('../internals/regexp-exec');

require('../internals/export')({
target: 'RegExp',
proto: true,
forced: regexpExec.patched
}, {
exec: regexpExec.impl
});
19 changes: 6 additions & 13 deletions packages/core-js/modules/es.string.split.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ var anObject = require('../internals/an-object');
var speciesConstructor = require('../internals/species-constructor');
var advanceStringIndex = require('../internals/advance-string-index');
var toLength = require('../internals/to-length');
var regExpExec = require('../internals/regexp-exec-abstract');
var callRegExpExec = require('../internals/regexp-exec-abstract');
var regexpExec = require('../internals/regexp-exec');
var arrayPush = [].push;
var min = Math.min;
var LENGTH = 'length';
Expand All @@ -27,7 +28,6 @@ require('../internals/fix-regexp-well-known-symbol-logic')(
'.'.split(/()()/)[LENGTH] > 1 ||
''.split(/.?/)[LENGTH]
) {
var NPCG = /()??/.exec('')[1] === undefined; // nonparticipating capturing group
// based on es5-shim implementation, need to rework it
internalSplit = function (separator, limit) {
var string = String(this);
Expand All @@ -45,19 +45,12 @@ require('../internals/fix-regexp-well-known-symbol-logic')(
var splitLimit = limit === undefined ? 4294967295 : limit >>> 0;
// Make `global` and avoid `lastIndex` issues by working with a copy
var separatorCopy = new RegExp(separator.source, flags + 'g');
var separator2, match, lastIndex, lastLength, i;
// Doesn't need flags gy, but they don't hurt
if (!NPCG) separator2 = new RegExp('^' + separatorCopy.source + '$(?!\\s)', flags);
while (match = separatorCopy.exec(string)) {
var match, lastIndex, lastLength;
while (match = regexpExec.impl.call(separatorCopy, string)) {
// `separatorCopy.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0][LENGTH];
if (lastIndex > lastLastIndex) {
output.push(string.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for NPCG
// eslint-disable-next-line no-loop-func
if (!NPCG && match[LENGTH] > 1) match[0].replace(separator2, function () {
for (i = 1; i < arguments[LENGTH] - 2; i++) if (arguments[i] === undefined) match[i] = undefined;
});
if (match[LENGTH] > 1 && match.index < string[LENGTH]) arrayPush.apply(output, match.slice(1));
lastLength = match[0][LENGTH];
lastLastIndex = lastIndex;
Expand Down Expand Up @@ -111,13 +104,13 @@ require('../internals/fix-regexp-well-known-symbol-logic')(
var splitter = new C(SUPPORTS_Y ? rx : '^(?:' + rx.source + ')', flags);
var lim = limit === undefined ? 0xffffffff : limit >>> 0;
if (lim === 0) return [];
if (S.length === 0) return regExpExec(splitter, S) === null ? [S] : [];
if (S.length === 0) return callRegExpExec(splitter, S) === null ? [S] : [];
var p = 0;
var q = 0;
var A = [];
while (q < S.length) {
splitter.lastIndex = SUPPORTS_Y ? q : 0;
var z = regExpExec(splitter, SUPPORTS_Y ? S : S.slice(q));
var z = callRegExpExec(splitter, SUPPORTS_Y ? S : S.slice(q));
var e;
if (
z === null ||
Expand Down

0 comments on commit 5bd11fc

Please sign in to comment.