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.
This commit is contained in:
Andreas Kling 2025-02-08 20:50:28 +01:00 committed by Andreas Kling
parent 39abd75d59
commit dbf52a1b5f
Notes: github-actions[bot] 2025-02-08 23:29:09 +00:00
2 changed files with 47 additions and 12 deletions

View file

@ -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 Cs [[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 Cs [[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 Cs [[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 Cs [[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++) {

View file

@ -23,10 +23,25 @@ namespace JS {
class RegExpLegacyStaticProperties {
public:
Optional<Utf16String> const& input() const { return m_input; }
Optional<Utf16String> const& last_match() const { return m_last_match; }
Optional<Utf16String> 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<Utf16String> const& last_paren() const { return m_last_paren; }
Optional<Utf16String> const& left_context() const { return m_left_context; }
Optional<Utf16String> const& right_context() const { return m_right_context; }
Optional<Utf16String> 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<Utf16String> 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<Utf16String> const& $1() const { return m_$1; }
Optional<Utf16String> const& $2() const { return m_$2; }
Optional<Utf16String> const& $3() const { return m_$3; }
@ -38,10 +53,22 @@ public:
Optional<Utf16String> 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<Utf16String> m_input;
Optional<Utf16String> m_last_match;
Optional<Utf16String> m_last_paren;
Optional<Utf16String> m_left_context;
Optional<Utf16String> m_right_context;
Optional<Utf16String> m_$1;
Optional<Utf16String> m_$2;
Optional<Utf16String> m_$3;
@ -68,6 +92,14 @@ private:
Optional<Utf16String> m_$7;
Optional<Utf16String> m_$8;
Optional<Utf16String> 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<Utf16String> m_last_match_string;
mutable Optional<Utf16String> m_left_context_string;
mutable Optional<Utf16String> m_right_context_string;
};
ThrowCompletionOr<void> set_legacy_regexp_static_property(VM& vm, RegExpConstructor& constructor, Value this_value, void (RegExpLegacyStaticProperties::*property_setter)(Utf16String), Value value);