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:
Iain Ireland 2020-03-21 14:21:04 +00:00
Родитель 3550cada92
Коммит 96d7636dcb
12 изменённых файлов: 100 добавлений и 17 удалений

Просмотреть файл

@ -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(); }