From 98ce3e1947d64e5a330045ab4eb8f748b0a5ba65 Mon Sep 17 00:00:00 2001 From: win7-7 Date: Sun, 18 Jan 2026 08:49:59 +0200 Subject: [PATCH] Crash is gone. Functionally seems work with indices. Going test with regexp zones and without to see this is ideal solution. Crash is gone. Functionally seems work with indices. Going test with regexp zones and without to see this is ideal solution. --- js/src/builtin/RegExp.cpp | 8 +- js/src/jit/CodeGenerator.cpp | 8 +- js/src/vm/RegExpObject.cpp | 149 ++++++++++++++++++++++------------- js/src/vm/RegExpShared.h | 17 +++- 4 files changed, 117 insertions(+), 65 deletions(-) diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 8294ca17dc..c1c82a2d7f 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -110,7 +110,9 @@ js::CreateRegExpMatchResult(JSContext* cx, RegExpShared& re, // MakeIndicesArray: steps 10-12 if (re.numNamedCaptures() > 0) { - RootedPlainObject groupsTemplate(cx, re.getGroupsTemplate()); + RootedPlainObject groupsTemplate(cx, re.getOrCreateGroupsTemplate(cx)); + if (!groupsTemplate) + return false; indicesGroups = CreateGroupsObject(cx, groupsTemplate); if (!indicesGroups) { return false; @@ -166,7 +168,9 @@ js::CreateRegExpMatchResult(JSContext* cx, RegExpShared& re, RootedPlainObject groups(cx); if (re.numNamedCaptures() > 0) { // construct a new object from the template saved on RegExpShared - RootedPlainObject groupsTemplate(cx, re.getGroupsTemplate()); + RootedPlainObject groupsTemplate(cx, re.getOrCreateGroupsTemplate(cx)); + if (!groupsTemplate) + return false; groups = CreateGroupsObject(cx, groupsTemplate); // Step 33 e-f diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 0d6815f74e..7c835e27f0 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1736,10 +1736,10 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx) Register shared = temp2; masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), shared); - masm.branchPtr(Assembler::NotEqual, - Address(shared, RegExpShared::offsetOfGroupsTemplate()), - ImmWord(0), - &oolEntry); + masm.branch32(Assembler::NotEqual, + Address(shared, RegExpShared::offsetOfNumNamedCaptures()), + Imm32(0), + &oolEntry); // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub. masm.branchTest32(Assembler::NonZero, diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index faba7b0510..b75d4f3b32 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -954,9 +954,22 @@ js::StringHasRegExpMetaChars(JSLinearString* str) RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags) : source(source), flags(flags), canStringMatch(false), parenCount(0), - numNamedCaptures_(0), groupsTemplate_(nullptr) + numNamedCaptures_(0), namedCaptureData_(nullptr) {} +struct RegExpShared::NamedCaptureData { + bool mapInited; + // per-compartment template cache (optional; you can also skip templates entirely) + using GroupsTemplateMap = + GCHashMap, + DefaultHasher, SystemAllocPolicy>; + GroupsTemplateMap groupsTemplateMap; + bool groupsTemplateMapInited = false; + + Vector names; + Vector indices; +}; + void RegExpShared::traceChildren(JSTracer* trc) { @@ -964,10 +977,19 @@ RegExpShared::traceChildren(JSTracer* trc) if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC()) discardJitCode(); + if (namedCaptureData_ && namedCaptureData_->mapInited) { + for (auto iter = namedCaptureData_->groupsTemplateMap.all(); !iter.empty(); iter.popFront()) { + TraceNullableEdge(trc, &iter.front().value(), "RegExpShared groupsTemplate per-compartment"); + } + } + + for (auto& atom : namedCaptureNames_) { + TraceEdge(trc, &atom, "RegExpShared namedCaptureName"); + } + TraceNullableEdge(trc, &source, "RegExpShared source"); for (auto& comp : compilationArray) TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code"); - TraceNullableEdge(trc, &groupsTemplate_, "RegExpShared groupsTemplate"); } void @@ -1008,69 +1030,33 @@ RegExpShared::initializeNamedCaptures(JSContext* cx, HandleRegExpShared re, MOZ_ASSERT(indices); MOZ_ASSERT(names->length() == indices->length()); - if (re->getGroupsTemplate()) { - // If initializeNamedCaptures was previously called for a different CompilationMode/Latin1Chars combination, - // the template object is already created and correct. -#ifdef DEBUG - // In debug builds, verify that. - MOZ_ASSERT(re->getGroupsTemplate()->propertyCount() == names->length()); - RootedId id(cx); - RootedNativeObject groupsTemplate(cx, re->getGroupsTemplate()); - Rooted desc(cx); - for (uint32_t i = 0; i < names->length(); i++) { - irregexp::CharacterVector* cv = (*names)[i]; - JSAtom* atom = AtomizeChars(cx, cv->begin(), cv->length()); - MOZ_ASSERT(atom); - id = NameToId(atom->asPropertyName()); - MOZ_ASSERT(NativeGetOwnPropertyDescriptor(cx, groupsTemplate, id, &desc)); - int32_t idx; - MOZ_ASSERT(ToInt32(cx, desc.value(), &idx)); - MOZ_ASSERT(idx == (*indices)[i]); - } -#endif - return true; +{ + uint32_t n = names->length(); + re->numNamedCaptures_ = n; + + if (!re->namedCaptureData_) { + re->namedCaptureData_ = js_new(); + if (!re->namedCaptureData_) + return false; } - // The irregexp parser returns named capture information in the form - // of two arrays. We create a template object with a property for each - // capture name, and store the capture index as Integer in the corresponding value. - uint32_t numNamedCaptures = names->length(); + auto* data = re->namedCaptureData_; + data->names.clear(); + data->indices.clear(); - // Create a plain template object. - RootedPlainObject templateObject(cx, NewObjectWithGivenProto(cx, nullptr, TenuredObject)); - if (!templateObject) { + if (!data->names.reserve(n) || !data->indices.reserve(n)) return false; - } - // Create a new group for the template. - Rooted proto(cx, templateObject->taggedProto()); - ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto); - if (!group) { - return false; - } - templateObject->setGroup(group); - - // Initialize the properties of the template. - RootedId id(cx); - for (uint32_t i = 0; i < numNamedCaptures; i++) { + for (uint32_t i = 0; i < n; i++) { irregexp::CharacterVector* cv = (*names)[i]; - // Need to explicitly create an Atom (not a String) or it won't get added to the atom table JSAtom* atom = AtomizeChars(cx, cv->begin(), cv->length()); - if (!atom) { + if (!atom) return false; - } - id = NameToId(atom->asPropertyName()); - RootedValue idx(cx, Int32Value((*indices)[i])); - if (!NativeDefineProperty(cx, templateObject, id, idx, - nullptr, nullptr, JSPROP_ENUMERATE)) { - return false; - } - AddTypePropertyId(cx, templateObject, id, TypeSet::Int32Type()); + data->names.infallibleAppend(atom); + data->indices.infallibleAppend((*indices)[i]); } - - re->groupsTemplate_ = templateObject; - re->numNamedCaptures_ = numNamedCaptures; return true; + } } /* static */ bool @@ -1350,6 +1336,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx, if (!group) return nullptr; templateObject->setGroup(group); + if (kind == ResultTemplateKind::Indices) { /* The |indices| array only has a |groups| property. */ @@ -1403,6 +1390,58 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx, return data.matchResultTemplateObjects_[kind]; } +bool CreateGroupsObjectForMatch(JSContext* cx, RegExpShared* shared, + MutableHandlePlainObject groups); + +PlainObject* +RegExpShared::getOrCreateGroupsTemplate(JSContext* cx) +{ + if (!namedCaptureData_ || numNamedCaptures_ == 0) + return nullptr; + + NamedCaptureData* data = namedCaptureData_; + + if (!data->mapInited) { + if (!data->groupsTemplateMap.init(4)) + return nullptr; + data->mapInited = true; + } + + JSCompartment* comp = cx->compartment(); + auto p = data->groupsTemplateMap.lookupForAdd(comp); + if (!p) { + ReadBarriered empty(nullptr); + if (!data->groupsTemplateMap.add(p, comp, empty)) + return nullptr; + } + + if (p->value()) + return p->value(); + + RootedPlainObject tmpl(cx, NewObjectWithGivenProto(cx, nullptr, TenuredObject)); + if (!tmpl) + return nullptr; + + // Define name->index properties from data vectors + MOZ_ASSERT(data->names.length() == numNamedCaptures_); + MOZ_ASSERT(data->indices.length() == numNamedCaptures_); + + RootedId id(cx); + for (uint32_t i = 0; i < numNamedCaptures_; i++) { + JSAtom* atom = data->names[i]; + id = NameToId(atom->asPropertyName()); + + RootedValue idx(cx, Int32Value(int32_t(data->indices[i]))); + if (!NativeDefineProperty(cx, tmpl, id, idx, nullptr, nullptr, JSPROP_ENUMERATE)) + return nullptr; + + AddTypePropertyId(cx, tmpl, id, TypeSet::Int32Type()); + } + + p->value().set(tmpl); + return tmpl; +} + bool RegExpCompartment::init(JSContext* cx) { diff --git a/js/src/vm/RegExpShared.h b/js/src/vm/RegExpShared.h index 58ff907e8a..0f617936a6 100644 --- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -128,8 +128,17 @@ class RegExpShared : public gc::TenuredCell bool canStringMatch; size_t parenCount; + struct NamedCaptureData; // forward-declare at namespace scope uint32_t numNamedCaptures_; - GCPtr groupsTemplate_; + NamedCaptureData* namedCaptureData_; // nullptr if none + public: + NamedCaptureData* namedCaptureData() const { return namedCaptureData_; } + +bool namedCaptureDataInited_; + +// NEW: realm-neutral named-capture data stored on the shared regexp +Vector, 0, SystemAllocPolicy> namedCaptureNames_; +Vector namedCaptureIndices_; RegExpCompilation compilationArray[4]; @@ -190,7 +199,7 @@ class RegExpShared : public gc::TenuredCell // not public due to circular inclusion problems static bool initializeNamedCaptures(JSContext* cx, MutableHandleRegExpShared re, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices); - PlainObject* getGroupsTemplate() { return groupsTemplate_; } + PlainObject* getOrCreateGroupsTemplate(JSContext* cx); uint32_t numNamedCaptures() const { return numNamedCaptures_; } JSAtom* getSource() const { return source; } RegExpFlag getFlags() const { return flags; } @@ -238,8 +247,8 @@ class RegExpShared : public gc::TenuredCell + offsetof(RegExpCompilation, jitCode); } - static size_t offsetOfGroupsTemplate() { - return offsetof(RegExpShared, groupsTemplate_); + static size_t offsetOfNumNamedCaptures() { + return offsetof(RegExpShared, numNamedCaptures_); } size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);