From 152691f9eb07c4cb295c95698afa85fcc54084a9 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 30 Mar 2025 00:08:39 +0000 Subject: [PATCH] LibWeb: Make RopeString subclass so PrimitiveString can be smaller By moving the LHS and RHS pointers used by rope strings into a RopeString subclass, we shrink PrimitiveString by 16 bytes. Most strings are not rope strings, so this ends up saving quite a bit of memory. --- Libraries/LibJS/Runtime/PrimitiveString.cpp | 33 +++++++++------ Libraries/LibJS/Runtime/PrimitiveString.h | 46 ++++++++++++++++----- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/Libraries/LibJS/Runtime/PrimitiveString.cpp b/Libraries/LibJS/Runtime/PrimitiveString.cpp index 998fe0ded33..a161042e2e5 100644 --- a/Libraries/LibJS/Runtime/PrimitiveString.cpp +++ b/Libraries/LibJS/Runtime/PrimitiveString.cpp @@ -20,14 +20,17 @@ namespace JS { GC_DEFINE_ALLOCATOR(PrimitiveString); +GC_DEFINE_ALLOCATOR(RopeString); -PrimitiveString::PrimitiveString(PrimitiveString& lhs, PrimitiveString& rhs) - : m_is_rope(true) - , m_lhs(&lhs) - , m_rhs(&rhs) +RopeString::RopeString(GC::Ref lhs, GC::Ref rhs) + : PrimitiveString(RopeTag::Rope) + , m_lhs(lhs) + , m_rhs(rhs) { } +RopeString::~RopeString() = default; + PrimitiveString::PrimitiveString(String string) : m_utf8_string(move(string)) { @@ -46,13 +49,11 @@ PrimitiveString::~PrimitiveString() vm().utf16_string_cache().remove(*m_utf16_string); } -void PrimitiveString::visit_edges(Cell::Visitor& visitor) +void RopeString::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); - if (m_is_rope) { - visitor.visit(m_lhs); - visitor.visit(m_rhs); - } + visitor.visit(m_lhs); + visitor.visit(m_rhs); } bool PrimitiveString::is_empty() const @@ -192,7 +193,7 @@ GC::Ref PrimitiveString::create(VM& vm, PrimitiveString& lhs, P if (rhs_empty) return lhs; - return vm.heap().allocate(lhs, rhs); + return vm.heap().allocate(lhs, rhs); } void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) const @@ -200,6 +201,13 @@ void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) cons if (!m_is_rope) return; + auto const& rope_string = static_cast(*this); + return rope_string.resolve(preference); +} + +void RopeString::resolve(EncodingPreference preference) const +{ + // This vector will hold all the pieces of the rope that need to be assembled // into the resolved string. Vector pieces; @@ -213,8 +221,9 @@ void PrimitiveString::resolve_rope_if_needed(EncodingPreference preference) cons while (!stack.is_empty()) { auto const* current = stack.take_last(); if (current->m_is_rope) { - stack.append(current->m_rhs); - stack.append(current->m_lhs); + auto& current_rope_string = static_cast(*current); + stack.append(current_rope_string.m_rhs); + stack.append(current_rope_string.m_lhs); continue; } diff --git a/Libraries/LibJS/Runtime/PrimitiveString.h b/Libraries/LibJS/Runtime/PrimitiveString.h index d1d0d03957f..4fefbd785c2 100644 --- a/Libraries/LibJS/Runtime/PrimitiveString.h +++ b/Libraries/LibJS/Runtime/PrimitiveString.h @@ -19,7 +19,7 @@ namespace JS { -class PrimitiveString final : public Cell { +class PrimitiveString : public Cell { GC_CELL(PrimitiveString, Cell); GC_DECLARE_ALLOCATOR(PrimitiveString); @@ -47,26 +47,50 @@ public: ThrowCompletionOr> get(VM&, PropertyKey const&) const; -private: - explicit PrimitiveString(PrimitiveString&, PrimitiveString&); - explicit PrimitiveString(String); - explicit PrimitiveString(Utf16String); +protected: + enum class RopeTag { Rope }; + explicit PrimitiveString(RopeTag) + : m_is_rope(true) + { + } - virtual void visit_edges(Cell::Visitor&) override; + mutable bool m_is_rope { false }; + + mutable Optional m_utf8_string; + mutable Optional m_utf16_string; enum class EncodingPreference { UTF8, UTF16, }; - void resolve_rope_if_needed(EncodingPreference) const; - mutable bool m_is_rope { false }; +private: + friend class RopeString; + + explicit PrimitiveString(String); + explicit PrimitiveString(Utf16String); + + void resolve_rope_if_needed(EncodingPreference) const; +}; + +class RopeString final : public PrimitiveString { + GC_CELL(RopeString, PrimitiveString); + GC_DECLARE_ALLOCATOR(RopeString); + +public: + virtual ~RopeString() override; + +private: + friend class PrimitiveString; + + explicit RopeString(GC::Ref, GC::Ref); + + virtual void visit_edges(Visitor&) override; + + void resolve(EncodingPreference) const; mutable GC::Ptr m_lhs; mutable GC::Ptr m_rhs; - - mutable Optional m_utf8_string; - mutable Optional m_utf16_string; }; }