From 69bae3fd9ae96b0aa159d204bbe8ddc26e83f9a9 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 5 Oct 2020 20:08:14 +0200 Subject: [PATCH] LibJS: Prevent object shape transitions during runtime object buildup While initialization common runtime objects like functions, prototypes, etc, we don't really care about tracking transitions for each and every property added to them. This patch puts objects into a "disable transitions" mode while we call initialize() on them. After that, adding more properties will cause new transitions to be generated and added to the chain. This gives a ~10% speed-up on test-js. :^) --- Libraries/LibJS/Heap/Heap.h | 6 ++++++ Libraries/LibJS/Runtime/Object.cpp | 5 ++++- Libraries/LibJS/Runtime/Object.h | 4 ++++ Libraries/LibJS/Runtime/Shape.cpp | 7 +++++++ Libraries/LibJS/Runtime/Shape.h | 2 ++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Libraries/LibJS/Heap/Heap.h b/Libraries/LibJS/Heap/Heap.h index 9f2697a0f2..ddf6d32c17 100644 --- a/Libraries/LibJS/Heap/Heap.h +++ b/Libraries/LibJS/Heap/Heap.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace JS { @@ -60,7 +61,12 @@ public: auto* memory = allocate_cell(sizeof(T)); new (memory) T(forward(args)...); auto* cell = static_cast(memory); + constexpr bool is_object = IsBaseOf::value; + if constexpr (is_object) + static_cast(cell)->disable_transitions(); cell->initialize(global_object); + if constexpr (is_object) + static_cast(cell)->enable_transitions(); return cell; } diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index 6e4ca6b98f..fa54720367 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -486,8 +486,11 @@ bool Object::put_own_property(Object& this_object, const StringOrSymbol& propert if (m_shape->is_unique()) { m_shape->add_property_to_unique_shape(property_name, attributes); m_storage.resize(m_shape->property_count()); - } else { + } else if (m_transitions_enabled) { set_shape(*m_shape->create_put_transition(property_name, attributes)); + } else { + m_shape->add_property_without_transition(property_name, attributes); + m_storage.resize(m_shape->property_count()); } metadata = shape().lookup(property_name); ASSERT(metadata.has_value()); diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 7f55582258..0885914195 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -151,6 +151,9 @@ public: void ensure_shape_is_unique(); + void enable_transitions() { m_transitions_enabled = true; } + void disable_transitions() { m_transitions_enabled = false; } + protected: enum class GlobalObjectTag { Tag }; enum class ConstructWithoutPrototypeTag { Tag }; @@ -169,6 +172,7 @@ private: void set_shape(Shape&); bool m_is_extensible { true }; + bool m_transitions_enabled { true }; Shape* m_shape { nullptr }; Vector m_storage; IndexedProperties m_indexed_properties; diff --git a/Libraries/LibJS/Runtime/Shape.cpp b/Libraries/LibJS/Runtime/Shape.cpp index 49d0bfc816..d30c701c60 100644 --- a/Libraries/LibJS/Runtime/Shape.cpp +++ b/Libraries/LibJS/Runtime/Shape.cpp @@ -210,4 +210,11 @@ void Shape::remove_property_from_unique_shape(const StringOrSymbol& property_nam } } +void Shape::add_property_without_transition(const StringOrSymbol& property_name, PropertyAttributes attributes) +{ + ensure_property_table(); + if (m_property_table->set(property_name, { m_property_count, attributes }) == AK::HashSetResult::InsertedNewEntry) + ++m_property_count; +} + } diff --git a/Libraries/LibJS/Runtime/Shape.h b/Libraries/LibJS/Runtime/Shape.h index 96717d1434..3c2f402a86 100644 --- a/Libraries/LibJS/Runtime/Shape.h +++ b/Libraries/LibJS/Runtime/Shape.h @@ -70,6 +70,8 @@ public: Shape* create_configure_transition(const StringOrSymbol&, PropertyAttributes attributes); Shape* create_prototype_transition(Object* new_prototype); + void add_property_without_transition(const StringOrSymbol&, PropertyAttributes); + bool is_unique() const { return m_unique; } Shape* create_unique_clone() const;