diff --git a/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp b/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp index a988c84cf0..3a327924e0 100644 --- a/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -190,6 +190,16 @@ ThrowCompletionOr Intrinsics::initialize_intrinsics(Realm& realm) m_new_ordinary_function_prototype_object_shape->set_prototype_without_transition(m_object_prototype); m_new_ordinary_function_prototype_object_shape->add_property_without_transition(vm.names.constructor, Attribute::Writable | Attribute::Configurable); + // OPTIMIZATION: A lot of runtime algorithms create an "iterator result" object. + // We pre-bake a shape for these objects and remember the property offsets. + // This allows us to construct them very quickly. + m_iterator_result_object_shape = heap().allocate_without_realm(realm); + m_iterator_result_object_shape->set_prototype_without_transition(m_object_prototype); + m_iterator_result_object_shape->add_property_without_transition(vm.names.value, Attribute::Writable | Attribute::Configurable | Attribute::Enumerable); + m_iterator_result_object_shape->add_property_without_transition(vm.names.done, Attribute::Writable | Attribute::Configurable | Attribute::Enumerable); + m_iterator_result_object_value_offset = m_iterator_result_object_shape->lookup(vm.names.value.to_string_or_symbol()).value().offset; + m_iterator_result_object_done_offset = m_iterator_result_object_shape->lookup(vm.names.done.to_string_or_symbol()).value().offset; + // Normally Heap::allocate() takes care of this, but these are allocated via allocate_without_realm(). m_function_prototype->initialize(realm); m_object_prototype->initialize(realm); @@ -358,6 +368,7 @@ void Intrinsics::visit_edges(Visitor& visitor) visitor.visit(m_empty_object_shape); visitor.visit(m_new_object_shape); visitor.visit(m_new_ordinary_function_prototype_object_shape); + visitor.visit(m_iterator_result_object_shape); visitor.visit(m_proxy_constructor); visitor.visit(m_async_from_sync_iterator_prototype); visitor.visit(m_async_generator_prototype); diff --git a/Userland/Libraries/LibJS/Runtime/Intrinsics.h b/Userland/Libraries/LibJS/Runtime/Intrinsics.h index 36d03e8923..6a5ba36c80 100644 --- a/Userland/Libraries/LibJS/Runtime/Intrinsics.h +++ b/Userland/Libraries/LibJS/Runtime/Intrinsics.h @@ -23,6 +23,10 @@ public: NonnullGCPtr new_object_shape() { return *m_new_object_shape; } NonnullGCPtr new_ordinary_function_prototype_object_shape() { return *m_new_ordinary_function_prototype_object_shape; } + [[nodiscard]] NonnullGCPtr iterator_result_object_shape() { return *m_iterator_result_object_shape; } + [[nodiscard]] u32 iterator_result_object_value_offset() { return m_iterator_result_object_value_offset; } + [[nodiscard]] u32 iterator_result_object_done_offset() { return m_iterator_result_object_done_offset; } + // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype NonnullGCPtr proxy_constructor() { return *m_proxy_constructor; } @@ -123,6 +127,10 @@ private: GCPtr m_new_object_shape; GCPtr m_new_ordinary_function_prototype_object_shape; + GCPtr m_iterator_result_object_shape; + u32 m_iterator_result_object_value_offset { 0 }; + u32 m_iterator_result_object_done_offset { 0 }; + // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype GCPtr m_proxy_constructor; diff --git a/Userland/Libraries/LibJS/Runtime/Iterator.cpp b/Userland/Libraries/LibJS/Runtime/Iterator.cpp index d6ba888d02..664efc1bdd 100644 --- a/Userland/Libraries/LibJS/Runtime/Iterator.cpp +++ b/Userland/Libraries/LibJS/Runtime/Iterator.cpp @@ -264,13 +264,13 @@ NonnullGCPtr create_iterator_result_object(VM& vm, Value value, bool don auto& realm = *vm.current_realm(); // 1. Let obj be OrdinaryObjectCreate(%Object.prototype%). - auto object = Object::create(realm, realm.intrinsics().object_prototype()); + auto object = Object::create_with_premade_shape(realm.intrinsics().iterator_result_object_shape()); // 2. Perform ! CreateDataPropertyOrThrow(obj, "value", value). - MUST(object->create_data_property_or_throw(vm.names.value, value)); + object->put_direct(realm.intrinsics().iterator_result_object_value_offset(), value); // 3. Perform ! CreateDataPropertyOrThrow(obj, "done", done). - MUST(object->create_data_property_or_throw(vm.names.done, Value(done))); + object->put_direct(realm.intrinsics().iterator_result_object_done_offset(), Value(done)); // 4. Return obj. return object; diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index 657d7ce29c..6377430447 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -37,6 +37,11 @@ NonnullGCPtr Object::create(Realm& realm, Object* prototype) return realm.heap().allocate(realm, ConstructWithPrototypeTag::Tag, *prototype); } +NonnullGCPtr Object::create_with_premade_shape(Shape& shape) +{ + return shape.heap().allocate(shape.realm(), shape); +} + Object::Object(GlobalObjectTag, Realm& realm, MayInterfereWithIndexedPropertyAccess may_interfere_with_indexed_property_access) : m_may_interfere_with_indexed_property_access(may_interfere_with_indexed_property_access == MayInterfereWithIndexedPropertyAccess::Yes) { diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h index 1525feee5f..9b8de50445 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.h +++ b/Userland/Libraries/LibJS/Runtime/Object.h @@ -58,6 +58,7 @@ class Object : public Cell { public: static NonnullGCPtr create(Realm&, Object* prototype); + static NonnullGCPtr create_with_premade_shape(Shape&); virtual void initialize(Realm&) override; virtual ~Object();