From dbf52a1b5fa9d7e81816ffc6c5a587c832148bd8 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 8 Feb 2025 20:50:28 +0100 Subject: [PATCH] LibJS: Allocate RegExp.{lastMatch,leftContext,rightContext} lazily These properties are always substrings of the RegExp input string, and so we can store them as views and lazily construct strings if they're actually accessed (which most of the time they aren't). This avoids a bunch of unnecessary memory copying, saving roughly 2.1 seconds per iteration of Speedometer. --- .../Runtime/RegExpLegacyStaticProperties.cpp | 9 ++-- .../Runtime/RegExpLegacyStaticProperties.h | 50 +++++++++++++++---- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.cpp b/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.cpp index 8788bcc7956..620ceb33d7d 100644 --- a/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.cpp +++ b/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.cpp @@ -15,9 +15,12 @@ void RegExpLegacyStaticProperties::invalidate() { m_input = {}; m_last_match = {}; + m_last_match_string = {}; m_last_paren = {}; m_left_context = {}; + m_left_context_string = {}; m_right_context = {}; + m_right_context_string = {}; m_$1 = {}; m_$2 = {}; m_$3 = {}; @@ -92,7 +95,7 @@ void update_legacy_regexp_static_properties(RegExpConstructor& constructor, Utf1 // 8. Set the value of C’s [[RegExpLastMatch]] internal slot to a String whose length is endIndex - startIndex and containing the code units from S with indices startIndex through endIndex - 1, in ascending order. auto last_match = string.view().substring_view(start_index, end_index - start_index); - legacy_static_properties.set_last_match(Utf16String::create(last_match)); + legacy_static_properties.set_last_match(last_match); // 9. If n > 0, set the value of C’s [[RegExpLastParen]] internal slot to the last element of capturedValues. if (group_count > 0) { @@ -106,11 +109,11 @@ void update_legacy_regexp_static_properties(RegExpConstructor& constructor, Utf1 // 11. Set the value of C’s [[RegExpLeftContext]] internal slot to a String whose length is startIndex and containing the code units from S with indices 0 through startIndex - 1, in ascending order. auto left_context = string.view().substring_view(0, start_index); - legacy_static_properties.set_left_context(Utf16String::create(left_context)); + legacy_static_properties.set_left_context(left_context); // 12. Set the value of C’s [[RegExpRightContext]] internal slot to a String whose length is len - endIndex and containing the code units from S with indices endIndex through len - 1, in ascending order. auto right_context = string.view().substring_view(end_index, len - end_index); - legacy_static_properties.set_right_context(Utf16String::create(right_context)); + legacy_static_properties.set_right_context(right_context); // 13. For each integer i such that 1 ≤ i ≤ 9 for (size_t i = 1; i <= 9; i++) { diff --git a/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.h b/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.h index bac7ca0a533..f861ad6c879 100644 --- a/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.h +++ b/Libraries/LibJS/Runtime/RegExpLegacyStaticProperties.h @@ -23,10 +23,25 @@ namespace JS { class RegExpLegacyStaticProperties { public: Optional const& input() const { return m_input; } - Optional const& last_match() const { return m_last_match; } + Optional const& last_match() const + { + if (!m_last_match_string.has_value()) + m_last_match_string = Utf16String::create(m_last_match); + return m_last_match_string; + } Optional const& last_paren() const { return m_last_paren; } - Optional const& left_context() const { return m_left_context; } - Optional const& right_context() const { return m_right_context; } + Optional const& left_context() const + { + if (!m_left_context_string.has_value()) + m_left_context_string = Utf16String::create(m_left_context); + return m_left_context_string; + } + Optional const& right_context() const + { + if (!m_right_context_string.has_value()) + m_right_context_string = Utf16String::create(m_right_context); + return m_right_context_string; + } Optional const& $1() const { return m_$1; } Optional const& $2() const { return m_$2; } Optional const& $3() const { return m_$3; } @@ -38,10 +53,22 @@ public: Optional const& $9() const { return m_$9; } void set_input(Utf16String input) { m_input = move(input); } - void set_last_match(Utf16String last_match) { m_last_match = move(last_match); } + void set_last_match(Utf16View last_match) + { + m_last_match = last_match; + m_last_match_string = {}; + } void set_last_paren(Utf16String last_paren) { m_last_paren = move(last_paren); } - void set_left_context(Utf16String left_context) { m_left_context = move(left_context); } - void set_right_context(Utf16String right_context) { m_right_context = move(right_context); } + void set_left_context(Utf16View left_context) + { + m_left_context = left_context; + m_left_context_string = {}; + } + void set_right_context(Utf16View right_context) + { + m_right_context = right_context; + m_right_context_string = {}; + } void set_$1(Utf16String value) { m_$1 = move(value); } void set_$2(Utf16String value) { m_$2 = move(value); } void set_$3(Utf16String value) { m_$3 = move(value); } @@ -55,10 +82,7 @@ public: private: Optional m_input; - Optional m_last_match; Optional m_last_paren; - Optional m_left_context; - Optional m_right_context; Optional m_$1; Optional m_$2; Optional m_$3; @@ -68,6 +92,14 @@ private: Optional m_$7; Optional m_$8; Optional m_$9; + + // NOTE: These are views into m_input and we only turn them into full strings if/when needed. + Utf16View m_last_match; + Utf16View m_left_context; + Utf16View m_right_context; + mutable Optional m_last_match_string; + mutable Optional m_left_context_string; + mutable Optional m_right_context_string; }; ThrowCompletionOr set_legacy_regexp_static_property(VM& vm, RegExpConstructor& constructor, Value this_value, void (RegExpLegacyStaticProperties::*property_setter)(Utf16String), Value value);