Issue #2305 Part 2: Support .indices in match result template objects

Before match indices, all match results had the same shape. (If there are
no named capture groups, `.groups` exists but is undefined.) Match results
with `.indices` have a distinct shape, so we need a distinct template
object. We also need a template object for `.indices` itself.
This commit is contained in:
Moonchild
2024-11-09 18:51:54 +01:00
committed by roytam1
parent 47ef1701b3
commit 845d36d582
3 changed files with 68 additions and 38 deletions
+1
View File
@@ -198,6 +198,7 @@
macro(includes, includes, "includes") \
macro(incumbentGlobal, incumbentGlobal, "incumbentGlobal") \
macro(index, index, "index") \
macro(indices, indices, "indices") \
macro(infinity, infinity, "infinity") \
macro(Infinity, Infinity, "Infinity") \
macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
+43 -29
View File
@@ -1297,10 +1297,13 @@ RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
RegExpCompartment::RegExpCompartment(Zone* zone)
: set_(zone, Set(zone->runtimeFromMainThread())),
matchResultTemplateObject_(nullptr),
optimizableRegExpPrototypeShape_(nullptr),
optimizableRegExpInstanceShape_(nullptr)
{}
{
for (auto& templateObj : matchResultTemplateObjects_) {
templateObj = nullptr;
}
}
RegExpCompartment::~RegExpCompartment()
{
@@ -1308,38 +1311,53 @@ RegExpCompartment::~RegExpCompartment()
}
ArrayObject*
RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
RegExpCompartment::createMatchResultTemplateObject(JSContext* cx, ResultTemplateKind kind)
{
MOZ_ASSERT(!matchResultTemplateObject_);
MOZ_ASSERT(!matchResultTemplateObjects_[kind]);
/* Create template array object */
RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
nullptr, TenuredObject));
if (!templateObject)
return matchResultTemplateObject_; // = nullptr
if (!templateObject) {
return nullptr;
}
// Create a new group for the template.
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
if (!group)
return matchResultTemplateObject_; // = nullptr
return nullptr;
templateObject->setGroup(group);
if (kind == ResultTemplateKind::Indices) {
/* The |indices| array only has a |groups| property. */
RootedValue groupsVal(cx, UndefinedValue());
if (!NativeDefineProperty(cx, templateObject, cx->names().groups,
groupsVal, nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
AddTypePropertyId(cx, templateObject, NameToId(cx->names().groups), TypeSet::AnyObjectType());
matchResultTemplateObjects_[kind].set(templateObject);
return matchResultTemplateObjects_[kind];
}
/* Set dummy index property */
RootedValue index(cx, Int32Value(0));
if (!NativeDefineProperty(cx, templateObject, cx->names().index, index, nullptr, nullptr,
JSPROP_ENUMERATE))
{
return matchResultTemplateObject_; // = nullptr
return nullptr;
}
AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType());
/* Set dummy input property */
RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
if (!NativeDefineProperty(cx, templateObject, cx->names().input, inputVal, nullptr, nullptr,
JSPROP_ENUMERATE))
{
return matchResultTemplateObject_; // = nullptr
return nullptr;
}
AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType());
/* Set dummy groups property */
RootedValue groupsVal(cx, UndefinedValue());
@@ -1347,25 +1365,21 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
cx, templateObject, cx->names().groups, groupsVal, nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
// Make sure that the properties are in the right slots.
DebugOnly<Shape*> shape = templateObject->lastProperty();
MOZ_ASSERT(shape->slot() == 2 &&
shape->propidRef() == NameToId(cx->names().groups));
MOZ_ASSERT(shape->previous()->slot() == 1 &&
shape->previous()->propidRef() == NameToId(cx->names().input));
MOZ_ASSERT(shape->previous()->previous()->slot() == 0 &&
shape->previous()->previous()->propidRef() == NameToId(cx->names().index));
// Make sure type information reflects the indexed properties which might
// be added.
AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType());
AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType());
AddTypePropertyId(cx, templateObject, NameToId(cx->names().groups), TypeSet::AnyObjectType());
matchResultTemplateObject_.set(templateObject);
if (kind == ResultTemplateKind::WithIndices) {
/* Set dummy indices property */
RootedValue indicesVal(cx, UndefinedValue());
if (!NativeDefineProperty(cx, templateObject, cx->names().indices,
indicesVal, nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
AddTypePropertyId(cx, templateObject, NameToId(cx->names().indices), TypeSet::AnyObjectType());
}
return matchResultTemplateObject_;
matchResultTemplateObjects_[kind].set(templateObject);
return matchResultTemplateObjects_[kind];
}
bool
@@ -1383,10 +1397,10 @@ RegExpCompartment::init(JSContext* cx)
void
RegExpCompartment::sweep(JSRuntime* rt)
{
if (matchResultTemplateObject_ &&
IsAboutToBeFinalized(&matchResultTemplateObject_))
{
matchResultTemplateObject_.set(nullptr);
for (auto& templateObject : matchResultTemplateObjects_) {
if (templateObject && IsAboutToBeFinalized(&templateObject)) {
templateObject.set(nullptr);
}
}
if (optimizableRegExpPrototypeShape_ &&
+24 -9
View File
@@ -298,12 +298,27 @@ class RegExpCompartment
using Set = GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
JS::WeakCache<Set> set_;
public:
enum ResultTemplateKind { Normal, WithIndices, Indices, NumKinds };
private:
/*
* This is the template object where the result of re.exec() is based on,
* if there is a result. This is used in CreateRegExpMatchResult to set
* the input/index properties faster.
* The template objects that the result of re.exec() is based on, if
* there is a result. These are used in CreateRegExpMatchResult.
* There are three template objects, each of which is an ArrayObject
* with some additional properties. We decide which to use based on
* the |hasIndices| (/d) flag.
*
* Normal: Has |index|, |input|, and |groups| properties.
* Used for the result object if |hasIndices| is not set.
*
* WithIndices: Has |index|, |input|, |groups|, and |indices| properties.
* Used for the result object if |hasIndices| is set.
*
* Indices: Has a |groups| property. If |hasIndices| is set, used
* for the |.indices| property of the result object.
*/
ReadBarriered<ArrayObject*> matchResultTemplateObject_;
ReadBarriered<ArrayObject*> matchResultTemplateObjects_[ResultTemplateKind::NumKinds];
/*
* The shape of RegExp.prototype object that satisfies following:
@@ -326,7 +341,7 @@ class RegExpCompartment
*/
ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
ArrayObject* createMatchResultTemplateObject(JSContext* cx, ResultTemplateKind kind);
public:
explicit RegExpCompartment(Zone* zone);
@@ -344,10 +359,10 @@ class RegExpCompartment
MutableHandleRegExpShared shared);
/* Get or create template object used to base the result of .exec() on. */
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
if (matchResultTemplateObject_)
return matchResultTemplateObject_;
return createMatchResultTemplateObject(cx);
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx, ResultTemplateKind kind = ResultTemplateKind::Normal) {
if (matchResultTemplateObjects_[kind])
return matchResultTemplateObjects_[kind];
return createMatchResultTemplateObject(cx, kind);
}
Shape* getOptimizableRegExpPrototypeShape() {