-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for the 'y' flag to the RegExp constructor #492
Changes from all commits
80c19a6
c571f31
775b17f
b8fc64b
5d6a0bd
4160559
145e4e7
06b6d4f
f93b81f
c02cdf3
698daaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
require('../../modules/es.regexp.sticky'); | ||
|
||
module.exports = function (it) { | ||
return it.sticky; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
'use strict'; | ||
var hide = require('../internals/hide'); | ||
var redefine = require('../internals/redefine'); | ||
var fails = require('../internals/fails'); | ||
var wellKnownSymbol = require('../internals/well-known-symbol'); | ||
|
@@ -20,6 +19,12 @@ var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () { | |
return ''.replace(re, '$<a>') !== '7'; | ||
}); | ||
|
||
// IE <= 11 replaces $0 with the whole match, as if it was $& | ||
// https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0 | ||
var REPLACE_KEEPS_$0 = (function () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you prefer I can move this fix to a separate PR. I fixed it here just because I saw the tests failing on IE11 and it is related to RegExps. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, it's fine 👍 |
||
return 'a'.replace(/./, '$0') === '$0'; | ||
})(); | ||
|
||
// 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 () { | ||
|
@@ -30,7 +35,7 @@ var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () { | |
return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b'; | ||
}); | ||
|
||
module.exports = function (KEY, length, exec, sham) { | ||
module.exports = function (KEY, length, exec) { | ||
var SYMBOL = wellKnownSymbol(KEY); | ||
|
||
var DELEGATES_TO_SYMBOL = !fails(function () { | ||
|
@@ -60,7 +65,7 @@ module.exports = function (KEY, length, exec, sham) { | |
if ( | ||
!DELEGATES_TO_SYMBOL || | ||
!DELEGATES_TO_EXEC || | ||
(KEY === 'replace' && !REPLACE_SUPPORTS_NAMED_GROUPS) || | ||
(KEY === 'replace' && !(REPLACE_SUPPORTS_NAMED_GROUPS && REPLACE_KEEPS_$0)) || | ||
(KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC) | ||
) { | ||
var nativeRegExpMethod = /./[SYMBOL]; | ||
|
@@ -75,7 +80,7 @@ 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 }); | ||
var stringMethod = methods[0]; | ||
var regexMethod = methods[1]; | ||
|
||
|
@@ -88,6 +93,5 @@ module.exports = function (KEY, length, exec, sham) { | |
// 21.2.5.9 RegExp.prototype[@@search](string) | ||
: function (string) { return regexMethod.call(string, this); } | ||
); | ||
if (sham) hide(RegExp.prototype[SYMBOL], 'sham', true); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
'use strict'; | ||
|
||
var fails = require('./fails'); | ||
var speciesConstructor = require('../internals/species-constructor'); | ||
|
||
// babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError, | ||
// so we use an intermediate function. | ||
function RE(s, f) { | ||
return RegExp(s, f); | ||
} | ||
|
||
exports.UNSUPPORTED_Y = fails(function () { | ||
// babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError | ||
var re = RE('a', 'y'); | ||
re.lastIndex = 2; | ||
return re.exec('abcd') != null; | ||
}); | ||
|
||
exports.BROKEN_CARET = fails(function () { | ||
// https://bugzilla.mozilla.org/show_bug.cgi?id=773687 | ||
var re = RE('^r', 'gy'); | ||
re.lastIndex = 2; | ||
return re.exec('str') != null; | ||
}); | ||
|
||
exports.createStickyRegExp = function (re, otherFlags) { | ||
var C = speciesConstructor(re, RegExp); | ||
|
||
if (C !== RegExp) return new C(re, otherFlags + 'y'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will cause calling |
||
|
||
// y is either supported or polyfilled | ||
if (!exports.UNSUPPORTED_Y || RegExp.sham) { | ||
return new RegExp(re, otherFlags + 'y'); | ||
} | ||
|
||
// If y hasn't been polyfilled and it isn't supported, assigning | ||
// to .sticky won't throw. | ||
// This usually happens in engines where descriptors aren't supported. | ||
var fakeRe = new RegExp(re, otherFlags); | ||
fakeRe.sticky = true; | ||
return fakeRe; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
var DESCRIPTORS = require('../internals/descriptors'); | ||
var UNSUPPORTED_Y = require('../internals/regexp-sticky-helpers').UNSUPPORTED_Y; | ||
var defineProperty = require('../internals/object-define-property').f; | ||
var getInternalState = require('../internals/internal-state').get; | ||
var RegExpPrototype = RegExp.prototype; | ||
|
||
// `RegExp.prototype.sticky` getter | ||
if (DESCRIPTORS && UNSUPPORTED_Y) { | ||
defineProperty(RegExp.prototype, 'sticky', { | ||
configurable: true, | ||
get: function () { | ||
if (this === RegExpPrototype) return undefined; | ||
// We can't use InternalStateModule.getterFor because | ||
// we don't add metadata for regexps created by a literal. | ||
if (this instanceof RegExp) { | ||
return !!getInternalState(this).sticky; | ||
} | ||
throw TypeError('Incompatible receiver, RegExp required'); | ||
} | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,19 +3,15 @@ | |
var isRegExp = require('../internals/is-regexp'); | ||
var anObject = require('../internals/an-object'); | ||
var requireObjectCoercible = require('../internals/require-object-coercible'); | ||
var speciesConstructor = require('../internals/species-constructor'); | ||
var advanceStringIndex = require('../internals/advance-string-index'); | ||
var toLength = require('../internals/to-length'); | ||
var callRegExpExec = require('../internals/regexp-exec-abstract'); | ||
var regexpExec = require('../internals/regexp-exec'); | ||
var fails = require('../internals/fails'); | ||
var stickyHelpers = require('../internals/regexp-sticky-helpers'); | ||
var arrayPush = [].push; | ||
var min = Math.min; | ||
var MAX_UINT32 = 0xffffffff; | ||
|
||
// babel-minify transpiles RegExp('x', 'y') -> /x/y and it causes SyntaxError | ||
var SUPPORTS_Y = !fails(function () { return !RegExp(MAX_UINT32, 'y'); }); | ||
|
||
// @@split logic | ||
require('../internals/fix-regexp-well-known-symbol-logic')( | ||
'split', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In |
||
|
@@ -93,30 +89,26 @@ require('../internals/fix-regexp-well-known-symbol-logic')( | |
|
||
var rx = anObject(regexp); | ||
var S = String(this); | ||
var C = speciesConstructor(rx, RegExp); | ||
|
||
var unicodeMatching = rx.unicode; | ||
var flags = (rx.ignoreCase ? 'i' : '') + | ||
(rx.multiline ? 'm' : '') + | ||
(rx.unicode ? 'u' : '') + | ||
(SUPPORTS_Y ? 'y' : 'g'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ! |
||
var splitter = stickyHelpers.createStickyRegExp( | ||
rx, | ||
(rx.ignoreCase ? 'i' : '') + (rx.multiline ? 'm' : '') + (unicodeMatching ? 'u' : '') | ||
); | ||
|
||
// ^(? + rx + ) is needed, in combination with some S slicing, to | ||
// simulate the 'y' flag. | ||
var splitter = new C(SUPPORTS_Y ? rx : '^(?:' + rx.source + ')', flags); | ||
var lim = limit === undefined ? MAX_UINT32 : limit >>> 0; | ||
if (lim === 0) return []; | ||
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 = callRegExpExec(splitter, SUPPORTS_Y ? S : S.slice(q)); | ||
splitter.lastIndex = q; | ||
var z = callRegExpExec(splitter, S); | ||
var e; | ||
if ( | ||
z === null || | ||
(e = min(toLength(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p | ||
(e = min(toLength(splitter.lastIndex), S.length)) === p | ||
) { | ||
q = advanceStringIndex(S, q, unicodeMatching); | ||
} else { | ||
|
@@ -133,6 +125,5 @@ require('../internals/fix-regexp-well-known-symbol-logic')( | |
return A; | ||
} | ||
]; | ||
}, | ||
!SUPPORTS_Y | ||
} | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got this data from MDN. Note that on Firefox 3 the
y
flags doesn't correctly work with regexp methods, but this compat entry is only about the.sticky
accessor.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need data for modern
opera
- it's generated fromchrome
.