From 5bd11fc571ee2a852ba94a57a6ac71ddf8bec6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 12 Nov 2018 23:49:01 +0100 Subject: [PATCH] Fix #lastIndex in IE8 and move NPCG check from #split to #exec IE8 is not fully fixed yet --- packages/core-js-builder/config.js | 1 + packages/core-js/es/index.js | 1 + packages/core-js/es/regexp/index.js | 1 + packages/core-js/index.js | 1 + .../fix-regexp-well-known-symbol-logic.js | 4 +- .../core-js/internals/regexp-exec-abstract.js | 4 +- packages/core-js/internals/regexp-exec.js | 58 +++++++++++++++++++ packages/core-js/modules/es.regexp.exec.js | 11 ++++ packages/core-js/modules/es.string.split.js | 19 ++---- 9 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 packages/core-js/internals/regexp-exec.js create mode 100644 packages/core-js/modules/es.regexp.exec.js diff --git a/packages/core-js-builder/config.js b/packages/core-js-builder/config.js index ecaaa31f9d96..75b3b7181c78 100644 --- a/packages/core-js-builder/config.js +++ b/packages/core-js-builder/config.js @@ -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', diff --git a/packages/core-js/es/index.js b/packages/core-js/es/index.js index 8e08a7494c1b..2dbc434eb84a 100644 --- a/packages/core-js/es/index.js +++ b/packages/core-js/es/index.js @@ -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'); diff --git a/packages/core-js/es/regexp/index.js b/packages/core-js/es/regexp/index.js index 6ea2b737da87..69467e49e33c 100644 --- a/packages/core-js/es/regexp/index.js +++ b/packages/core-js/es/regexp/index.js @@ -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'); diff --git a/packages/core-js/index.js b/packages/core-js/index.js index 807cc5002df8..ef9b4d2706c4 100644 --- a/packages/core-js/index.js +++ b/packages/core-js/index.js @@ -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'); 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 c5f839d1129c..53ee44fd95a6 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 @@ -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) { @@ -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. diff --git a/packages/core-js/internals/regexp-exec-abstract.js b/packages/core-js/internals/regexp-exec-abstract.js index 2ca155ded22b..5e1645bb667e 100644 --- a/packages/core-js/internals/regexp-exec-abstract.js +++ b/packages/core-js/internals/regexp-exec-abstract.js @@ -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 @@ -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); }; diff --git a/packages/core-js/internals/regexp-exec.js b/packages/core-js/internals/regexp-exec.js new file mode 100644 index 000000000000..a9a0c57ff851 --- /dev/null +++ b/packages/core-js/internals/regexp-exec.js @@ -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 +}; diff --git a/packages/core-js/modules/es.regexp.exec.js b/packages/core-js/modules/es.regexp.exec.js new file mode 100644 index 000000000000..52cbeebbe36e --- /dev/null +++ b/packages/core-js/modules/es.regexp.exec.js @@ -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 +}); diff --git a/packages/core-js/modules/es.string.split.js b/packages/core-js/modules/es.string.split.js index bd72233fec44..23d56a372bb3 100644 --- a/packages/core-js/modules/es.string.split.js +++ b/packages/core-js/modules/es.string.split.js @@ -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'; @@ -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); @@ -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; @@ -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 ||