diff --git a/Userland/Libraries/LibJS/Runtime/Shape.cpp b/Userland/Libraries/LibJS/Runtime/Shape.cpp index da93fafd88..09c2f216dd 100644 --- a/Userland/Libraries/LibJS/Runtime/Shape.cpp +++ b/Userland/Libraries/LibJS/Runtime/Shape.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -23,10 +23,23 @@ Shape* Shape::create_unique_clone() const return new_shape; } +Shape* Shape::get_or_prune_cached_forward_transition(TransitionKey const& key) +{ + auto it = m_forward_transitions.find(key); + if (it == m_forward_transitions.end()) + return nullptr; + if (!it->value) { + // The cached forward transition has gone stale (from garbage collection). Prune it. + m_forward_transitions.remove(it); + return nullptr; + } + return it->value; +} + Shape* Shape::create_put_transition(const StringOrSymbol& property_name, PropertyAttributes attributes) { TransitionKey key { property_name, attributes }; - if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr)) + if (auto* existing_shape = get_or_prune_cached_forward_transition(key)) return existing_shape; auto* new_shape = heap().allocate_without_global_object(*this, property_name, attributes, TransitionType::Put); m_forward_transitions.set(key, new_shape); @@ -36,7 +49,7 @@ Shape* Shape::create_put_transition(const StringOrSymbol& property_name, Propert Shape* Shape::create_configure_transition(const StringOrSymbol& property_name, PropertyAttributes attributes) { TransitionKey key { property_name, attributes }; - if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr)) + if (auto* existing_shape = get_or_prune_cached_forward_transition(key)) return existing_shape; auto* new_shape = heap().allocate_without_global_object(*this, property_name, attributes, TransitionType::Configure); m_forward_transitions.set(key, new_shape); @@ -88,9 +101,6 @@ void Shape::visit_edges(Cell::Visitor& visitor) visitor.visit(m_prototype); visitor.visit(m_previous); m_property_name.visit_edges(visitor); - for (auto& it : m_forward_transitions) - visitor.visit(it.value); - if (m_property_table) { for (auto& it : *m_property_table) it.key.visit_edges(visitor); diff --git a/Userland/Libraries/LibJS/Runtime/Shape.h b/Userland/Libraries/LibJS/Runtime/Shape.h index bff033b42e..6f77ef6744 100644 --- a/Userland/Libraries/LibJS/Runtime/Shape.h +++ b/Userland/Libraries/LibJS/Runtime/Shape.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -31,7 +33,9 @@ struct TransitionKey { } }; -class Shape final : public Cell { +class Shape final + : public Cell + , public Weakable { public: virtual ~Shape() override; @@ -84,6 +88,7 @@ private: virtual const char* class_name() const override { return "Shape"; } virtual void visit_edges(Visitor&) override; + Shape* get_or_prune_cached_forward_transition(TransitionKey const&); void ensure_property_table() const; PropertyAttributes m_attributes { 0 }; @@ -94,7 +99,7 @@ private: mutable OwnPtr> m_property_table; - HashMap m_forward_transitions; + HashMap> m_forward_transitions; Shape* m_previous { nullptr }; StringOrSymbol m_property_name; Object* m_prototype { nullptr };