From 845d36d582190144444cfdc2c8efe07b23a0acde Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sat, 9 Nov 2024 18:51:54 +0100 Subject: [PATCH] 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. --- js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/RegExpObject.cpp | 72 ++++++++++++++++++++------------- js/src/vm/RegExpObject.h | 33 ++++++++++----- 3 files changed, 68 insertions(+), 38 deletions(-) diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 809990c0fc..8489648d6b 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -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") \ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 129ed2492f..c4493de28e 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -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 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 = 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_ && diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index b088315dea..1f97f7e47f 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -298,12 +298,27 @@ class RegExpCompartment using Set = GCHashSet, Key, RuntimeAllocPolicy>; JS::WeakCache 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 matchResultTemplateObject_; + ReadBarriered matchResultTemplateObjects_[ResultTemplateKind::NumKinds]; /* * The shape of RegExp.prototype object that satisfies following: @@ -326,7 +341,7 @@ class RegExpCompartment */ ReadBarriered 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() {