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 9f2697a0f29..ddf6d32c17f 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 6e4ca6b98fb..fa547203679 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 7f555822589..0885914195e 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 49d0bfc816e..d30c701c605 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 96717d14348..3c2f402a865 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;