зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1361856: Add dotAll to RegExpFlags r=jwalden
This patch adds the boilerplate necessary to support a new regexp flag. Externally visible changes (parsing the flag, the dotAll property on the prototype) are guarded behind ENABLE_NEW_REGEXP. Note: RegExpFlagsGetter turns out to be externally visible because it can be called on a non-regexp object with the dotAll property defined. The actual implementation of dotAll comes for free with the fresh import of irregexp (bug 1367105). There are two tests (tests/non262/RegExp/prototype.js and tests/non262/RegExp/flags.js) that need to be updated when this is turned on to add s/dotAll to the list of expected properties on the RegExp prototype. I will attach those changes to my patch that flips ENABLE_NEW_REGEXP to be on by default. Differential Revision: https://phabricator.services.mozilla.com/D66844 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
3550cada92
Коммит
96d7636dcb
|
@ -19,7 +19,7 @@ namespace JS {
|
|||
/**
|
||||
* Regular expression flag values, suitable for initializing a collection of
|
||||
* regular expression flags as defined below in |RegExpFlags|. Flags are listed
|
||||
* in alphabetical order by syntax -- /g, /i, /m, /u, /y.
|
||||
* in alphabetical order by syntax -- /g, /i, /m, /s, /u, /y.
|
||||
*/
|
||||
class RegExpFlag {
|
||||
// WARNING TO SPIDERMONKEY HACKERS (embedders must assume these values can
|
||||
|
@ -34,28 +34,31 @@ class RegExpFlag {
|
|||
* Act globally and find *all* matches (rather than stopping after just the
|
||||
* first one), i.e. /g.
|
||||
*/
|
||||
static constexpr uint8_t Global = 0b0'0010;
|
||||
static constexpr uint8_t Global = 0b00'0010;
|
||||
|
||||
/**
|
||||
* Interpret regular expression source text case-insensitively by folding
|
||||
* uppercase letters to lowercase, i.e. /i.
|
||||
*/
|
||||
static constexpr uint8_t IgnoreCase = 0b0'0001;
|
||||
static constexpr uint8_t IgnoreCase = 0b00'0001;
|
||||
|
||||
/** Treat ^ and $ as begin and end of line, i.e. /m. */
|
||||
static constexpr uint8_t Multiline = 0b0'0100;
|
||||
static constexpr uint8_t Multiline = 0b00'0100;
|
||||
|
||||
/* Allow . to match newline characters, i.e. /s. */
|
||||
static constexpr uint8_t DotAll = 0b10'0000;
|
||||
|
||||
/** Use Unicode semantics, i.e. /u. */
|
||||
static constexpr uint8_t Unicode = 0b1'0000;
|
||||
static constexpr uint8_t Unicode = 0b01'0000;
|
||||
|
||||
/** Only match starting from <regular expression>.lastIndex, i.e. /y. */
|
||||
static constexpr uint8_t Sticky = 0b0'1000;
|
||||
static constexpr uint8_t Sticky = 0b00'1000;
|
||||
|
||||
/** No regular expression flags. */
|
||||
static constexpr uint8_t NoFlags = 0b0'0000;
|
||||
static constexpr uint8_t NoFlags = 0b00'0000;
|
||||
|
||||
/** All regular expression flags. */
|
||||
static constexpr uint8_t AllFlags = 0b1'1111;
|
||||
static constexpr uint8_t AllFlags = 0b11'1111;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -96,6 +99,7 @@ class RegExpFlags {
|
|||
bool global() const { return flags_ & RegExpFlag::Global; }
|
||||
bool ignoreCase() const { return flags_ & RegExpFlag::IgnoreCase; }
|
||||
bool multiline() const { return flags_ & RegExpFlag::Multiline; }
|
||||
bool dotAll() const { return flags_ & RegExpFlag::DotAll; }
|
||||
bool unicode() const { return flags_ & RegExpFlag::Unicode; }
|
||||
bool sticky() const { return flags_ & RegExpFlag::Sticky; }
|
||||
|
||||
|
|
|
@ -723,6 +723,29 @@ static bool regexp_source(JSContext* cx, unsigned argc, JS::Value* vp) {
|
|||
return CallNonGenericMethod<IsRegExpObject, regexp_source_impl>(cx, args);
|
||||
}
|
||||
|
||||
// ES 2020 draft 21.2.5.3.
|
||||
MOZ_ALWAYS_INLINE bool regexp_dotAll_impl(JSContext* cx, const CallArgs& args) {
|
||||
MOZ_ASSERT(IsRegExpObject(args.thisv()));
|
||||
|
||||
// Steps 4-6.
|
||||
RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
|
||||
args.rval().setBoolean(reObj->dotAll());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool js::regexp_dotAll(JSContext* cx, unsigned argc, JS::Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 3.a.
|
||||
if (IsRegExpPrototype(args.thisv(), cx)) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Steps 1-3.
|
||||
return CallNonGenericMethod<IsRegExpObject, regexp_dotAll_impl>(cx, args);
|
||||
}
|
||||
|
||||
// ES 2017 draft 21.2.5.12.
|
||||
MOZ_ALWAYS_INLINE bool regexp_sticky_impl(JSContext* cx, const CallArgs& args) {
|
||||
MOZ_ASSERT(IsRegExpObject(args.thisv()));
|
||||
|
@ -775,6 +798,9 @@ const JSPropertySpec js::regexp_properties[] = {
|
|||
JS_PSG("global", regexp_global, 0),
|
||||
JS_PSG("ignoreCase", regexp_ignoreCase, 0),
|
||||
JS_PSG("multiline", regexp_multiline, 0),
|
||||
#ifdef ENABLE_NEW_REGEXP
|
||||
JS_PSG("dotAll", regexp_dotAll, 0),
|
||||
#endif
|
||||
JS_PSG("source", regexp_source, 0),
|
||||
JS_PSG("sticky", regexp_sticky, 0),
|
||||
JS_PSG("unicode", regexp_unicode, 0),
|
||||
|
@ -1669,6 +1695,18 @@ bool js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_NEW_REGEXP
|
||||
JSNative dotAllGetter;
|
||||
if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().dotAll),
|
||||
&dotAllGetter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dotAllGetter != regexp_dotAll) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if @@match, @@search, and exec are own data properties,
|
||||
// those values should be tested in selfhosted JS.
|
||||
bool has = false;
|
||||
|
|
|
@ -130,6 +130,8 @@ extern MOZ_MUST_USE bool regexp_ignoreCase(JSContext* cx, unsigned argc,
|
|||
JS::Value* vp);
|
||||
extern MOZ_MUST_USE bool regexp_multiline(JSContext* cx, unsigned argc,
|
||||
JS::Value* vp);
|
||||
extern MOZ_MUST_USE bool regexp_dotAll(JSContext* cx, unsigned argc,
|
||||
JS::Value* vp);
|
||||
extern MOZ_MUST_USE bool regexp_sticky(JSContext* cx, unsigned argc,
|
||||
JS::Value* vp);
|
||||
extern MOZ_MUST_USE bool regexp_unicode(JSContext* cx, unsigned argc,
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// ES6 draft rev34 (2015/02/20) 21.2.5.3 get RegExp.prototype.flags
|
||||
// ECMAScript 2020 draft (2020/03/12) 21.2.5.4 get RegExp.prototype.flags
|
||||
// https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
|
||||
// Uncloned functions with `$` prefix are allocated as extended function
|
||||
// to store the original name in `_SetCanonicalName`.
|
||||
function $RegExpFlagsGetter() {
|
||||
|
@ -14,27 +15,33 @@ function $RegExpFlagsGetter() {
|
|||
// Step 3.
|
||||
var result = "";
|
||||
|
||||
// Steps 4-6.
|
||||
// Steps 4-5.
|
||||
if (R.global)
|
||||
result += "g";
|
||||
|
||||
// Steps 7-9.
|
||||
// Steps 6-7.
|
||||
if (R.ignoreCase)
|
||||
result += "i";
|
||||
|
||||
// Steps 10-12.
|
||||
// Steps 8-9.
|
||||
if (R.multiline)
|
||||
result += "m";
|
||||
|
||||
// Steps 13-15.
|
||||
#ifdef ENABLE_NEW_REGEXP
|
||||
// Steps 10-11.
|
||||
if (R.dotAll)
|
||||
result += "s";
|
||||
#endif
|
||||
|
||||
// Steps 12-13.
|
||||
if (R.unicode)
|
||||
result += "u";
|
||||
|
||||
// Steps 16-18.
|
||||
// Steps 14-15.
|
||||
if (R.sticky)
|
||||
result += "y";
|
||||
|
||||
// Step 19.
|
||||
// Step 16.
|
||||
return result;
|
||||
}
|
||||
_SetCanonicalName($RegExpFlagsGetter, "get flags");
|
||||
|
@ -227,6 +234,7 @@ function RegExpGlobalMatchOpt(rx, S, fullUnicode) {
|
|||
// * global
|
||||
// * ignoreCase
|
||||
// * multiline
|
||||
// * dotAll
|
||||
// * sticky
|
||||
// * unicode
|
||||
// * exec
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
#define REGEXP_MULTILINE_FLAG 0x04
|
||||
#define REGEXP_STICKY_FLAG 0x08
|
||||
#define REGEXP_UNICODE_FLAG 0x10
|
||||
#define REGEXP_DOTALL_FLAG 0x20
|
||||
|
||||
#define REGEXP_STRING_ITERATOR_REGEXP_SLOT 0
|
||||
#define REGEXP_STRING_ITERATOR_STRING_SLOT 1
|
||||
|
|
|
@ -312,7 +312,13 @@ class BinASTParserPerTokenizer : public BinASTParserBase,
|
|||
*reflags |= JS::RegExpFlag::IgnoreCase;
|
||||
} else if (c == 'm' && !reflags->multiline()) {
|
||||
*reflags |= JS::RegExpFlag::Multiline;
|
||||
} else if (c == 'u' && !reflags->unicode()) {
|
||||
}
|
||||
#ifdef ENABLE_NEW_REGEXP
|
||||
else if (c == 's' && !reflags->dotAll()) {
|
||||
*reflags |= JS::RegExpFlag::DotAll;
|
||||
}
|
||||
#endif
|
||||
else if (c == 'u' && !reflags->unicode()) {
|
||||
*reflags |= JS::RegExpFlag::Unicode;
|
||||
} else if (c == 'y' && !reflags->sticky()) {
|
||||
*reflags |= JS::RegExpFlag::Sticky;
|
||||
|
|
|
@ -2650,7 +2650,13 @@ MOZ_MUST_USE bool TokenStreamSpecific<Unit, AnyCharsAccess>::regexpLiteral(
|
|||
flag = RegExpFlag::IgnoreCase;
|
||||
} else if (unit == 'm') {
|
||||
flag = RegExpFlag::Multiline;
|
||||
} else if (unit == 'u') {
|
||||
}
|
||||
#ifdef ENABLE_NEW_REGEXP
|
||||
else if (unit == 's') {
|
||||
flag = RegExpFlag::DotAll;
|
||||
}
|
||||
#endif
|
||||
else if (unit == 'u') {
|
||||
flag = RegExpFlag::Unicode;
|
||||
} else if (unit == 'y') {
|
||||
flag = RegExpFlag::Sticky;
|
||||
|
|
|
@ -177,6 +177,9 @@ class JSAPITest {
|
|||
if (flags.multiline()) {
|
||||
str += "m";
|
||||
}
|
||||
if (flags.dotAll()) {
|
||||
str += "s";
|
||||
}
|
||||
if (flags.unicode()) {
|
||||
str += "u";
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
MACRO(do, do_, "do") \
|
||||
MACRO(domNode, domNode, "domNode") \
|
||||
MACRO(done, done, "done") \
|
||||
MACRO(dotAll, dotAll, "dotAll") \
|
||||
MACRO(dotGenerator, dotGenerator, ".generator") \
|
||||
MACRO(dotThis, dotThis, ".this") \
|
||||
MACRO(dotInitializers, dotInitializers, ".initializers") \
|
||||
|
|
|
@ -57,6 +57,8 @@ static_assert(RegExpFlag::IgnoreCase == REGEXP_IGNORECASE_FLAG,
|
|||
"self-hosted JS and /i flag bits must agree");
|
||||
static_assert(RegExpFlag::Multiline == REGEXP_MULTILINE_FLAG,
|
||||
"self-hosted JS and /m flag bits must agree");
|
||||
static_assert(RegExpFlag::DotAll == REGEXP_DOTALL_FLAG,
|
||||
"self-hosted JS and /s flag bits must agree");
|
||||
static_assert(RegExpFlag::Unicode == REGEXP_UNICODE_FLAG,
|
||||
"self-hosted JS and /u flag bits must agree");
|
||||
static_assert(RegExpFlag::Sticky == REGEXP_STICKY_FLAG,
|
||||
|
@ -132,6 +134,10 @@ bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask) {
|
|||
*mask = RegExpFlag::Multiline;
|
||||
return true;
|
||||
}
|
||||
if (native == regexp_dotAll) {
|
||||
*mask = RegExpFlag::DotAll;
|
||||
return true;
|
||||
}
|
||||
if (native == regexp_sticky) {
|
||||
*mask = RegExpFlag::Sticky;
|
||||
return true;
|
||||
|
@ -536,6 +542,9 @@ JSLinearString* RegExpObject::toString(JSContext* cx) const {
|
|||
if (multiline() && !sb.append('m')) {
|
||||
return nullptr;
|
||||
}
|
||||
if (dotAll() && !sb.append('s')) {
|
||||
return nullptr;
|
||||
}
|
||||
if (unicode() && !sb.append('u')) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1348,6 +1357,9 @@ static bool ParseRegExpFlags(const CharT* chars, size_t length,
|
|||
case 'm':
|
||||
flag = RegExpFlag::Multiline;
|
||||
break;
|
||||
case 's':
|
||||
flag = RegExpFlag::DotAll;
|
||||
break;
|
||||
case 'u':
|
||||
flag = RegExpFlag::Unicode;
|
||||
break;
|
||||
|
|
|
@ -172,6 +172,7 @@ class RegExpObject : public NativeObject {
|
|||
bool global() const { return getFlags().global(); }
|
||||
bool ignoreCase() const { return getFlags().ignoreCase(); }
|
||||
bool multiline() const { return getFlags().multiline(); }
|
||||
bool dotAll() const { return getFlags().dotAll(); }
|
||||
bool unicode() const { return getFlags().unicode(); }
|
||||
bool sticky() const { return getFlags().sticky(); }
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ class RegExpShared : public gc::TenuredCell {
|
|||
bool global() const { return flags.global(); }
|
||||
bool ignoreCase() const { return flags.ignoreCase(); }
|
||||
bool multiline() const { return flags.multiline(); }
|
||||
bool dotAll() const { return flags.dotAll(); }
|
||||
bool unicode() const { return flags.unicode(); }
|
||||
bool sticky() const { return flags.sticky(); }
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче