LibJS: Use premade shape when creating unmapped arguments objects

Takes Speedometer2.1/EmberJS-Debug-TodoMVC from ~4500ms to ~4000ms
on my M3 MacBook Pro.
This commit is contained in:
Andreas Kling 2025-04-14 23:41:31 +02:00
parent 2a15b85658
commit c03f3d47a7
3 changed files with 36 additions and 6 deletions

View file

@ -1045,11 +1045,11 @@ Object* create_unmapped_arguments_object(VM& vm, ReadonlySpan<Value> arguments)
// 2. Let obj be OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »).
// 3. Set obj.[[ParameterMap]] to undefined.
auto object = Object::create(realm, realm.intrinsics().object_prototype());
auto object = Object::create_with_premade_shape(realm.intrinsics().unmapped_arguments_object_shape());
object->set_has_parameter_map();
// 4. 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().unmapped_arguments_object_length_offset(), Value(length));
// 5. Let index be 0.
// 6. Repeat, while index < len,
@ -1058,18 +1058,17 @@ Object* create_unmapped_arguments_object(VM& vm, ReadonlySpan<Value> arguments)
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.
}
// 7. 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().unmapped_arguments_object_well_known_symbol_iterator_offset(), array_prototype_values);
// 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false }).
auto throw_type_error = realm.intrinsics().throw_type_error_function();
MUST(object->define_property_or_throw(vm.names.callee, { .get = throw_type_error, .set = throw_type_error, .enumerable = false, .configurable = false }));
object->put_direct(realm.intrinsics().unmapped_arguments_object_callee_offset(), realm.intrinsics().throw_type_error_accessor());
// 9. Return obj.
return object;

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Accessor.h>
#include <LibJS/Runtime/AggregateErrorConstructor.h>
#include <LibJS/Runtime/AggregateErrorPrototype.h>
#include <LibJS/Runtime/ArrayBufferConstructor.h>
@ -219,6 +220,15 @@ void Intrinsics::initialize_intrinsics(Realm& realm)
m_native_function_length_offset = m_native_function_shape->lookup(vm.names.length.to_string_or_symbol()).value().offset;
m_native_function_name_offset = m_native_function_shape->lookup(vm.names.name.to_string_or_symbol()).value().offset;
m_unmapped_arguments_object_shape = heap().allocate<Shape>(realm);
m_unmapped_arguments_object_shape->set_prototype_without_transition(m_object_prototype);
m_unmapped_arguments_object_shape->add_property_without_transition(vm.names.length, Attribute::Writable | Attribute::Configurable);
m_unmapped_arguments_object_shape->add_property_without_transition(vm.well_known_symbol_iterator(), Attribute::Writable | Attribute::Configurable);
m_unmapped_arguments_object_shape->add_property_without_transition(vm.names.callee, 0);
m_unmapped_arguments_object_length_offset = m_unmapped_arguments_object_shape->lookup(vm.names.length.to_string_or_symbol()).value().offset;
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;
// Normally Realm::create() takes care of this, but these are allocated via Heap::allocate().
m_function_prototype->initialize(realm);
m_object_prototype->initialize(realm);
@ -272,6 +282,11 @@ void Intrinsics::initialize_intrinsics(Realm& realm)
m_throw_type_error_function->define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), 0);
MUST(m_throw_type_error_function->internal_prevent_extensions());
m_throw_type_error_accessor = Accessor::create(
vm,
m_throw_type_error_function,
m_throw_type_error_function);
initialize_constructor(vm, vm.names.Error, *m_error_constructor, m_error_prototype);
initialize_constructor(vm, vm.names.Function, *m_function_constructor, m_function_prototype);
initialize_constructor(vm, vm.names.Object, *m_object_constructor, m_object_prototype);
@ -390,6 +405,7 @@ void Intrinsics::visit_edges(Visitor& visitor)
visitor.visit(m_normal_function_prototype_shape);
visitor.visit(m_normal_function_shape);
visitor.visit(m_native_function_shape);
visitor.visit(m_unmapped_arguments_object_shape);
visitor.visit(m_proxy_constructor);
visitor.visit(m_async_from_sync_iterator_prototype);
visitor.visit(m_async_generator_prototype);
@ -414,6 +430,7 @@ void Intrinsics::visit_edges(Visitor& visitor)
visitor.visit(m_json_stringify_function);
visitor.visit(m_object_prototype_to_string_function);
visitor.visit(m_throw_type_error_function);
visitor.visit(m_throw_type_error_accessor);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
visitor.visit(m_##snake_name##_constructor); \

View file

@ -40,6 +40,13 @@ public:
[[nodiscard]] u32 native_function_length_offset() const { return m_native_function_length_offset; }
[[nodiscard]] u32 native_function_name_offset() const { return m_native_function_name_offset; }
[[nodiscard]] GC::Ref<Shape> unmapped_arguments_object_shape() { return *m_unmapped_arguments_object_shape; }
[[nodiscard]] u32 unmapped_arguments_object_length_offset() const { return m_unmapped_arguments_object_length_offset; }
[[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<Accessor> throw_type_error_accessor() { return *m_throw_type_error_accessor; }
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
GC::Ref<ProxyConstructor> proxy_constructor() { return *m_proxy_constructor; }
@ -157,6 +164,13 @@ private:
u32 m_native_function_length_offset { 0 };
u32 m_native_function_name_offset { 0 };
GC::Ptr<Shape> m_unmapped_arguments_object_shape;
u32 m_unmapped_arguments_object_length_offset { 0 };
u32 m_unmapped_arguments_object_well_known_symbol_iterator_offset { 0 };
u32 m_unmapped_arguments_object_callee_offset { 0 };
GC::Ptr<Accessor> m_throw_type_error_accessor;
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
GC::Ptr<ProxyConstructor> m_proxy_constructor;