mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 1519483: Add hasIndices to RegExpFlags r=mgaudet
Differential Revision: https://phabricator.services.mozilla.com/D107135
This commit is contained in:
parent
4ad23f942b
commit
ca22d6b543
@ -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, /s, /u, /y.
|
||||
* in alphabetical order by syntax -- /d, /g, /i, /m, /s, /u, /y.
|
||||
*/
|
||||
class RegExpFlag {
|
||||
// WARNING TO SPIDERMONKEY HACKERS (embedders must assume these values can
|
||||
@ -30,35 +30,40 @@ class RegExpFlag {
|
||||
// ascending order) unless you also add a translation layer.
|
||||
|
||||
public:
|
||||
/**
|
||||
* Add .indices property to the match result, i.e. /d
|
||||
*/
|
||||
static constexpr uint8_t HasIndices = 0b100'0000;
|
||||
|
||||
/**
|
||||
* Act globally and find *all* matches (rather than stopping after just the
|
||||
* first one), i.e. /g.
|
||||
*/
|
||||
static constexpr uint8_t Global = 0b00'0010;
|
||||
static constexpr uint8_t Global = 0b000'0010;
|
||||
|
||||
/**
|
||||
* Interpret regular expression source text case-insensitively by folding
|
||||
* uppercase letters to lowercase, i.e. /i.
|
||||
*/
|
||||
static constexpr uint8_t IgnoreCase = 0b00'0001;
|
||||
static constexpr uint8_t IgnoreCase = 0b000'0001;
|
||||
|
||||
/** Treat ^ and $ as begin and end of line, i.e. /m. */
|
||||
static constexpr uint8_t Multiline = 0b00'0100;
|
||||
static constexpr uint8_t Multiline = 0b000'0100;
|
||||
|
||||
/* Allow . to match newline characters, i.e. /s. */
|
||||
static constexpr uint8_t DotAll = 0b10'0000;
|
||||
static constexpr uint8_t DotAll = 0b010'0000;
|
||||
|
||||
/** Use Unicode semantics, i.e. /u. */
|
||||
static constexpr uint8_t Unicode = 0b01'0000;
|
||||
static constexpr uint8_t Unicode = 0b001'0000;
|
||||
|
||||
/** Only match starting from <regular expression>.lastIndex, i.e. /y. */
|
||||
static constexpr uint8_t Sticky = 0b00'1000;
|
||||
static constexpr uint8_t Sticky = 0b000'1000;
|
||||
|
||||
/** No regular expression flags. */
|
||||
static constexpr uint8_t NoFlags = 0b00'0000;
|
||||
static constexpr uint8_t NoFlags = 0b000'0000;
|
||||
|
||||
/** All regular expression flags. */
|
||||
static constexpr uint8_t AllFlags = 0b11'1111;
|
||||
static constexpr uint8_t AllFlags = 0b111'1111;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,6 +113,7 @@ class RegExpFlags {
|
||||
return RegExpFlags(~flags_ & RegExpFlag::AllFlags);
|
||||
}
|
||||
|
||||
bool hasIndices() const { return flags_ & RegExpFlag::HasIndices; }
|
||||
bool global() const { return flags_ & RegExpFlag::Global; }
|
||||
bool ignoreCase() const { return flags_ & RegExpFlag::IgnoreCase; }
|
||||
bool multiline() const { return flags_ & RegExpFlag::Multiline; }
|
||||
|
@ -706,6 +706,14 @@ static bool RegExpGetter(JSContext* cx, CallArgs& args, const char* methodName,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool js::regexp_hasIndices(JSContext* cx, unsigned argc, JS::Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return RegExpGetter(cx, args, "hasIndices", [args](RegExpObject* unwrapped) {
|
||||
args.rval().setBoolean(unwrapped->hasIndices());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// ES2021 draft rev 0b3a808af87a9123890767152a26599cc8fde161
|
||||
// 21.2.5.5 get RegExp.prototype.global
|
||||
bool js::regexp_global(JSContext* cx, unsigned argc, JS::Value* vp) {
|
||||
@ -794,6 +802,7 @@ bool js::regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp) {
|
||||
|
||||
const JSPropertySpec js::regexp_properties[] = {
|
||||
JS_SELF_HOSTED_GET("flags", "$RegExpFlagsGetter", 0),
|
||||
JS_PSG("hasIndices", regexp_hasIndices, 0),
|
||||
JS_PSG("global", regexp_global, 0),
|
||||
JS_PSG("ignoreCase", regexp_ignoreCase, 0),
|
||||
JS_PSG("multiline", regexp_multiline, 0),
|
||||
@ -1820,6 +1829,16 @@ bool js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSNative hasIndicesGetter;
|
||||
if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().hasIndices),
|
||||
&hasIndicesGetter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasIndicesGetter != regexp_hasIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSNative ignoreCaseGetter;
|
||||
if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().ignoreCase),
|
||||
&ignoreCaseGetter)) {
|
||||
|
@ -126,6 +126,8 @@ extern const JSPropertySpec regexp_properties[];
|
||||
extern const JSFunctionSpec regexp_methods[];
|
||||
|
||||
// Used in RegExpObject::isOriginalFlagGetter.
|
||||
[[nodiscard]] extern bool regexp_hasIndices(JSContext* cx, unsigned argc,
|
||||
JS::Value* vp);
|
||||
[[nodiscard]] extern bool regexp_global(JSContext* cx, unsigned argc,
|
||||
JS::Value* vp);
|
||||
[[nodiscard]] extern bool regexp_ignoreCase(JSContext* cx, unsigned argc,
|
||||
|
@ -16,30 +16,34 @@ function $RegExpFlagsGetter() {
|
||||
var result = "";
|
||||
|
||||
// Steps 4-5.
|
||||
if (R.hasIndices)
|
||||
result += "d";
|
||||
|
||||
// Steps 6-7.
|
||||
if (R.global)
|
||||
result += "g";
|
||||
|
||||
// Steps 6-7.
|
||||
// Steps 8-9.
|
||||
if (R.ignoreCase)
|
||||
result += "i";
|
||||
|
||||
// Steps 8-9.
|
||||
// Steps 10-11.
|
||||
if (R.multiline)
|
||||
result += "m";
|
||||
|
||||
// Steps 10-11.
|
||||
// Steps 12-13.
|
||||
if (R.dotAll)
|
||||
result += "s";
|
||||
|
||||
// Steps 12-13.
|
||||
// Steps 14-15.
|
||||
if (R.unicode)
|
||||
result += "u";
|
||||
|
||||
// Steps 14-15.
|
||||
// Steps 16-17
|
||||
if (R.sticky)
|
||||
result += "y";
|
||||
|
||||
// Step 16.
|
||||
// Step 18.
|
||||
return result;
|
||||
}
|
||||
_SetCanonicalName($RegExpFlagsGetter, "get flags");
|
||||
@ -229,6 +233,7 @@ function RegExpGlobalMatchOpt(rx, S, fullUnicode) {
|
||||
// Checks if following properties and getters are not modified, and accessing
|
||||
// them not observed by content script:
|
||||
// * flags
|
||||
// * hasIndices
|
||||
// * global
|
||||
// * ignoreCase
|
||||
// * multiline
|
||||
|
@ -91,6 +91,7 @@
|
||||
#define REGEXP_STICKY_FLAG 0x08
|
||||
#define REGEXP_UNICODE_FLAG 0x10
|
||||
#define REGEXP_DOTALL_FLAG 0x20
|
||||
#define REGEXP_HASINDICES_FLAG 0x40
|
||||
|
||||
#define REGEXP_STRING_ITERATOR_REGEXP_SLOT 0
|
||||
#define REGEXP_STRING_ITERATOR_STRING_SLOT 1
|
||||
|
@ -2649,7 +2649,9 @@ template <typename Unit, class AnyCharsAccess>
|
||||
while (true) {
|
||||
uint8_t flag;
|
||||
unit = getCodeUnit();
|
||||
if (unit == 'g') {
|
||||
if (unit == 'd') {
|
||||
flag = RegExpFlag::HasIndices;
|
||||
} else if (unit == 'g') {
|
||||
flag = RegExpFlag::Global;
|
||||
} else if (unit == 'i') {
|
||||
flag = RegExpFlag::IgnoreCase;
|
||||
|
@ -186,6 +186,9 @@ class JSAPITest {
|
||||
|
||||
JSAPITestString toSource(JS::RegExpFlags flags) {
|
||||
JSAPITestString str;
|
||||
if (flags.hasIndices()) {
|
||||
str += "d";
|
||||
}
|
||||
if (flags.global()) {
|
||||
str += "g";
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ assertEq(genericFlags({}), "");
|
||||
assertEq(genericFlags({ignoreCase: true}), "i");
|
||||
assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "uy");
|
||||
assertEq(genericFlags({__proto__: {multiline: true}}), "m");
|
||||
assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimsuy");
|
||||
assertEq(genericFlags(new Proxy({}, {get(){return true}})), "dgimsuy");
|
||||
|
||||
assertThrowsInstanceOf(() => genericFlags(), TypeError);
|
||||
assertThrowsInstanceOf(() => genericFlags(1), TypeError);
|
||||
|
2
js/src/tests/non262/RegExp/prototype.js
vendored
2
js/src/tests/non262/RegExp/prototype.js
vendored
@ -1,7 +1,7 @@
|
||||
const t = RegExp.prototype;
|
||||
|
||||
let properties = "toString,compile,exec,test," +
|
||||
"flags,dotAll,global,ignoreCase,multiline,source,sticky,unicode," +
|
||||
"flags,dotAll,global,hasIndices,ignoreCase,multiline,source,sticky,unicode," +
|
||||
"constructor," +
|
||||
"Symbol(Symbol.match),Symbol(Symbol.replace),Symbol(Symbol.search),Symbol(Symbol.split)";
|
||||
if (Object.prototype.toSource) {
|
||||
|
@ -214,6 +214,7 @@
|
||||
MACRO_(Handle, Handle, "Handle") \
|
||||
MACRO_(has, has, "has") \
|
||||
MACRO_(hashConstructor, hashConstructor, "#constructor") \
|
||||
MACRO_(hasIndices, hasIndices, "hasIndices") \
|
||||
MACRO_(hasOwn, hasOwn, "hasOwn") \
|
||||
MACRO_(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
|
||||
MACRO_(highWaterMark, highWaterMark, "highWaterMark") \
|
||||
|
@ -49,6 +49,8 @@ using mozilla::PodCopy;
|
||||
|
||||
using JS::AutoCheckCannotGC;
|
||||
|
||||
static_assert(RegExpFlag::HasIndices == REGEXP_HASINDICES_FLAG,
|
||||
"self-hosted JS and /d flag bits must agree");
|
||||
static_assert(RegExpFlag::Global == REGEXP_GLOBAL_FLAG,
|
||||
"self-hosted JS and /g flag bits must agree");
|
||||
static_assert(RegExpFlag::IgnoreCase == REGEXP_IGNORECASE_FLAG,
|
||||
@ -120,6 +122,10 @@ RegExpShared* RegExpObject::getShared(JSContext* cx,
|
||||
|
||||
/* static */
|
||||
bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask) {
|
||||
if (native == regexp_hasIndices) {
|
||||
*mask = RegExpFlag::HasIndices;
|
||||
return true;
|
||||
}
|
||||
if (native == regexp_global) {
|
||||
*mask = RegExpFlag::Global;
|
||||
return true;
|
||||
@ -465,6 +471,9 @@ JSLinearString* RegExpObject::toString(JSContext* cx,
|
||||
sb.infallibleAppend('/');
|
||||
|
||||
// Steps 5-7.
|
||||
if (obj->hasIndices() && !sb.append('d')) {
|
||||
return nullptr;
|
||||
}
|
||||
if (obj->global() && !sb.append('g')) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -984,6 +993,9 @@ static bool ParseRegExpFlags(const CharT* chars, size_t length,
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
uint8_t flag;
|
||||
switch (chars[i]) {
|
||||
case 'd':
|
||||
flag = RegExpFlag::HasIndices;
|
||||
break;
|
||||
case 'g':
|
||||
flag = RegExpFlag::Global;
|
||||
break;
|
||||
|
@ -132,6 +132,7 @@ class RegExpObject : public NativeObject {
|
||||
setFixedSlot(FLAGS_SLOT, Int32Value(flags.value()));
|
||||
}
|
||||
|
||||
bool hasIndices() const { return getFlags().hasIndices(); }
|
||||
bool global() const { return getFlags().global(); }
|
||||
bool ignoreCase() const { return getFlags().ignoreCase(); }
|
||||
bool multiline() const { return getFlags().multiline(); }
|
||||
|
@ -211,6 +211,7 @@ class RegExpShared
|
||||
|
||||
JS::RegExpFlags getFlags() const { return flags; }
|
||||
|
||||
bool hasIndices() const { return flags.hasIndices(); }
|
||||
bool global() const { return flags.global(); }
|
||||
bool ignoreCase() const { return flags.ignoreCase(); }
|
||||
bool multiline() const { return flags.multiline(); }
|
||||
|
@ -270,7 +270,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
||||
gPrototypeProperties['RegExp'] =
|
||||
["constructor", "toSource", "toString", "compile", "exec", "test",
|
||||
Symbol.match, Symbol.matchAll, Symbol.replace, Symbol.search, Symbol.split,
|
||||
"flags", "dotAll", "global", "ignoreCase", "multiline", "source", "sticky", "unicode"];
|
||||
"flags", "dotAll", "global", "hasIndices", "ignoreCase", "multiline", "source", "sticky",
|
||||
"unicode"];
|
||||
gConstructorProperties['RegExp'] =
|
||||
constructorProps(["input", "lastMatch", "lastParen",
|
||||
"leftContext", "rightContext", "$1", "$2", "$3", "$4",
|
||||
|
Loading…
Reference in New Issue
Block a user