From e0b32b18637a2208092449d82b588f9068908226 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 18 Apr 2025 18:11:10 +0200 Subject: [PATCH] LibJS: Use premade shape when creating mapped arguments objects Knocks out a 0.4% profile item on Speedometer 3. --- Libraries/LibJS/Runtime/AbstractOperations.cpp | 8 ++++---- Libraries/LibJS/Runtime/ArgumentsObject.cpp | 2 +- Libraries/LibJS/Runtime/Intrinsics.cpp | 10 ++++++++++ Libraries/LibJS/Runtime/Intrinsics.h | 10 ++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index 550259ebcbd..3ff069b1a68 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -1101,13 +1101,13 @@ Object* create_mapped_arguments_object(VM& vm, FunctionObject& function, Nonnull auto value = arguments[index]; // b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val). - MUST(object->create_data_property_or_throw(index, value)); + object->indexed_properties().put(index, value); // c. Set index to index + 1. } // 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). - MUST(object->define_property_or_throw(vm.names.length, { .value = Value(length), .writable = true, .enumerable = false, .configurable = true })); + object->put_direct(realm.intrinsics().mapped_arguments_object_length_offset(), Value(length)); // 17. Let mappedNames be a new empty List. HashTable mapped_names; @@ -1147,10 +1147,10 @@ Object* create_mapped_arguments_object(VM& vm, FunctionObject& function, Nonnull // 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). auto array_prototype_values = realm.intrinsics().array_prototype_values_function(); - MUST(object->define_property_or_throw(vm.well_known_symbol_iterator(), { .value = array_prototype_values, .writable = true, .enumerable = false, .configurable = true })); + object->put_direct(realm.intrinsics().mapped_arguments_object_well_known_symbol_iterator_offset(), array_prototype_values); // 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). - MUST(object->define_property_or_throw(vm.names.callee, { .value = &function, .writable = true, .enumerable = false, .configurable = true })); + object->put_direct(realm.intrinsics().mapped_arguments_object_callee_offset(), Value(&function)); // 22. Return obj. return object; diff --git a/Libraries/LibJS/Runtime/ArgumentsObject.cpp b/Libraries/LibJS/Runtime/ArgumentsObject.cpp index 3465cf20fca..20bc1cb0db4 100644 --- a/Libraries/LibJS/Runtime/ArgumentsObject.cpp +++ b/Libraries/LibJS/Runtime/ArgumentsObject.cpp @@ -13,7 +13,7 @@ namespace JS { GC_DEFINE_ALLOCATOR(ArgumentsObject); ArgumentsObject::ArgumentsObject(Realm& realm, Environment& environment) - : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype(), MayInterfereWithIndexedPropertyAccess::Yes) + : Object(realm.intrinsics().mapped_arguments_object_shape(), MayInterfereWithIndexedPropertyAccess::Yes) , m_environment(environment) { } diff --git a/Libraries/LibJS/Runtime/Intrinsics.cpp b/Libraries/LibJS/Runtime/Intrinsics.cpp index 649f91ce001..716faf8a41e 100644 --- a/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -229,6 +229,15 @@ void Intrinsics::initialize_intrinsics(Realm& realm) m_unmapped_arguments_object_well_known_symbol_iterator_offset = m_unmapped_arguments_object_shape->lookup(StringOrSymbol(vm.well_known_symbol_iterator())).value().offset; m_unmapped_arguments_object_callee_offset = m_unmapped_arguments_object_shape->lookup(vm.names.callee.to_string_or_symbol()).value().offset; + m_mapped_arguments_object_shape = heap().allocate(realm); + m_mapped_arguments_object_shape->set_prototype_without_transition(m_object_prototype); + m_mapped_arguments_object_shape->add_property_without_transition(vm.names.length, Attribute::Writable | Attribute::Configurable); + m_mapped_arguments_object_shape->add_property_without_transition(vm.well_known_symbol_iterator(), Attribute::Writable | Attribute::Configurable); + m_mapped_arguments_object_shape->add_property_without_transition(vm.names.callee, Attribute::Writable | Attribute::Configurable); + m_mapped_arguments_object_length_offset = m_mapped_arguments_object_shape->lookup(vm.names.length.to_string_or_symbol()).value().offset; + m_mapped_arguments_object_well_known_symbol_iterator_offset = m_mapped_arguments_object_shape->lookup(StringOrSymbol(vm.well_known_symbol_iterator())).value().offset; + m_mapped_arguments_object_callee_offset = m_mapped_arguments_object_shape->lookup(vm.names.callee.to_string_or_symbol()).value().offset; + // Normally Realm::create() takes care of this, but these are allocated via Heap::allocate(). m_function_prototype->initialize(realm); m_object_prototype->initialize(realm); @@ -406,6 +415,7 @@ void Intrinsics::visit_edges(Visitor& visitor) visitor.visit(m_normal_function_shape); visitor.visit(m_native_function_shape); visitor.visit(m_unmapped_arguments_object_shape); + visitor.visit(m_mapped_arguments_object_shape); visitor.visit(m_proxy_constructor); visitor.visit(m_async_from_sync_iterator_prototype); visitor.visit(m_async_generator_prototype); diff --git a/Libraries/LibJS/Runtime/Intrinsics.h b/Libraries/LibJS/Runtime/Intrinsics.h index a0a132c78b5..f98a2694420 100644 --- a/Libraries/LibJS/Runtime/Intrinsics.h +++ b/Libraries/LibJS/Runtime/Intrinsics.h @@ -45,6 +45,11 @@ public: [[nodiscard]] u32 unmapped_arguments_object_well_known_symbol_iterator_offset() const { return m_unmapped_arguments_object_well_known_symbol_iterator_offset; } [[nodiscard]] u32 unmapped_arguments_object_callee_offset() const { return m_unmapped_arguments_object_callee_offset; } + [[nodiscard]] GC::Ref mapped_arguments_object_shape() { return *m_mapped_arguments_object_shape; } + [[nodiscard]] u32 mapped_arguments_object_length_offset() const { return m_mapped_arguments_object_length_offset; } + [[nodiscard]] u32 mapped_arguments_object_well_known_symbol_iterator_offset() const { return m_mapped_arguments_object_well_known_symbol_iterator_offset; } + [[nodiscard]] u32 mapped_arguments_object_callee_offset() const { return m_mapped_arguments_object_callee_offset; } + [[nodiscard]] GC::Ref throw_type_error_accessor() { return *m_throw_type_error_accessor; } // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype @@ -169,6 +174,11 @@ private: u32 m_unmapped_arguments_object_well_known_symbol_iterator_offset { 0 }; u32 m_unmapped_arguments_object_callee_offset { 0 }; + GC::Ptr m_mapped_arguments_object_shape; + u32 m_mapped_arguments_object_length_offset { 0 }; + u32 m_mapped_arguments_object_well_known_symbol_iterator_offset { 0 }; + u32 m_mapped_arguments_object_callee_offset { 0 }; + GC::Ptr m_throw_type_error_accessor; // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype