diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index b7f6bf79140..5658571fc6f 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -76,6 +76,9 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so , m_is_arrow_function(is_arrow_function) , m_kind(kind) { + if (!m_is_arrow_function && m_kind == FunctionKind::Normal) + unsafe_set_shape(m_realm->intrinsics().normal_function_shape()); + // NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate // 9. If thisMode is lexical-this, set F.[[ThisMode]] to lexical. @@ -347,30 +350,38 @@ void ECMAScriptFunctionObject::initialize(Realm& realm) m_name_string = PrimitiveString::create(vm, m_name); - MUST(define_property_or_throw(vm.names.length, { .value = Value(m_function_length), .writable = false, .enumerable = false, .configurable = true })); - MUST(define_property_or_throw(vm.names.name, { .value = m_name_string, .writable = false, .enumerable = false, .configurable = true })); + if (!m_is_arrow_function && m_kind == FunctionKind::Normal) { + put_direct(realm.intrinsics().normal_function_length_offset(), Value(m_function_length)); + put_direct(realm.intrinsics().normal_function_name_offset(), m_name_string); - if (!m_is_arrow_function) { - Object* prototype = nullptr; - switch (m_kind) { - case FunctionKind::Normal: - prototype = Object::create_with_premade_shape(realm.intrinsics().normal_function_prototype_shape()); - prototype->put_direct(realm.intrinsics().normal_function_prototype_constructor_offset(), this); - break; - case FunctionKind::Generator: - // prototype is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png) - prototype = Object::create_prototype(realm, realm.intrinsics().generator_function_prototype_prototype()); - break; - case FunctionKind::Async: - break; - case FunctionKind::AsyncGenerator: - prototype = Object::create_prototype(realm, realm.intrinsics().async_generator_function_prototype_prototype()); - break; + auto prototype = Object::create_with_premade_shape(realm.intrinsics().normal_function_prototype_shape()); + prototype->put_direct(realm.intrinsics().normal_function_prototype_constructor_offset(), this); + put_direct(realm.intrinsics().normal_function_prototype_offset(), prototype); + } else { + MUST(define_property_or_throw(vm.names.length, { .value = Value(m_function_length), .writable = false, .enumerable = false, .configurable = true })); + MUST(define_property_or_throw(vm.names.name, { .value = m_name_string, .writable = false, .enumerable = false, .configurable = true })); + + if (!m_is_arrow_function) { + Object* prototype = nullptr; + switch (m_kind) { + case FunctionKind::Normal: + VERIFY_NOT_REACHED(); + break; + case FunctionKind::Generator: + // prototype is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png) + prototype = Object::create_prototype(realm, realm.intrinsics().generator_function_prototype_prototype()); + break; + case FunctionKind::Async: + break; + case FunctionKind::AsyncGenerator: + prototype = Object::create_prototype(realm, realm.intrinsics().async_generator_function_prototype_prototype()); + break; + } + // 27.7.4 AsyncFunction Instances, https://tc39.es/ecma262/#sec-async-function-instances + // AsyncFunction instances do not have a prototype property as they are not constructible. + if (m_kind != FunctionKind::Async) + define_direct_property(vm.names.prototype, prototype, Attribute::Writable); } - // 27.7.4 AsyncFunction Instances, https://tc39.es/ecma262/#sec-async-function-instances - // AsyncFunction instances do not have a prototype property as they are not constructible. - if (m_kind != FunctionKind::Async) - define_direct_property(vm.names.prototype, prototype, Attribute::Writable); } } diff --git a/Libraries/LibJS/Runtime/Intrinsics.cpp b/Libraries/LibJS/Runtime/Intrinsics.cpp index c4d6de09429..50ea3cc782b 100644 --- a/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -203,6 +203,15 @@ void Intrinsics::initialize_intrinsics(Realm& realm) m_normal_function_prototype_shape->add_property_without_transition(vm.names.constructor, Attribute::Writable | Attribute::Configurable); m_normal_function_prototype_constructor_offset = m_normal_function_prototype_shape->lookup(vm.names.constructor.to_string_or_symbol()).value().offset; + m_normal_function_shape = heap().allocate(realm); + m_normal_function_shape->set_prototype_without_transition(m_function_prototype); + m_normal_function_shape->add_property_without_transition(vm.names.length, Attribute::Configurable); + m_normal_function_shape->add_property_without_transition(vm.names.name, Attribute::Configurable); + m_normal_function_shape->add_property_without_transition(vm.names.prototype, Attribute::Writable); + m_normal_function_length_offset = m_normal_function_shape->lookup(vm.names.length.to_string_or_symbol()).value().offset; + m_normal_function_name_offset = m_normal_function_shape->lookup(vm.names.name.to_string_or_symbol()).value().offset; + m_normal_function_prototype_offset = m_normal_function_shape->lookup(vm.names.prototype.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); @@ -372,6 +381,7 @@ void Intrinsics::visit_edges(Visitor& visitor) visitor.visit(m_new_object_shape); visitor.visit(m_iterator_result_object_shape); visitor.visit(m_normal_function_prototype_shape); + visitor.visit(m_normal_function_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 cc10f933493..a38f2894504 100644 --- a/Libraries/LibJS/Runtime/Intrinsics.h +++ b/Libraries/LibJS/Runtime/Intrinsics.h @@ -31,6 +31,11 @@ public: [[nodiscard]] GC::Ref normal_function_prototype_shape() { return *m_normal_function_prototype_shape; } [[nodiscard]] u32 normal_function_prototype_constructor_offset() const { return m_normal_function_prototype_constructor_offset; } + [[nodiscard]] GC::Ref normal_function_shape() { return *m_normal_function_shape; } + [[nodiscard]] u32 normal_function_length_offset() const { return m_normal_function_length_offset; } + [[nodiscard]] u32 normal_function_name_offset() const { return m_normal_function_name_offset; } + [[nodiscard]] u32 normal_function_prototype_offset() const { return m_normal_function_prototype_offset; } + // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype GC::Ref proxy_constructor() { return *m_proxy_constructor; } @@ -139,6 +144,11 @@ private: GC::Ptr m_normal_function_prototype_shape; u32 m_normal_function_prototype_constructor_offset { 0 }; + GC::Ptr m_normal_function_shape; + u32 m_normal_function_length_offset { 0 }; + u32 m_normal_function_name_offset { 0 }; + u32 m_normal_function_prototype_offset { 0 }; + // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype GC::Ptr m_proxy_constructor; diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index 7f0bd7b5511..69a9172d7f7 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -97,6 +97,12 @@ void Object::initialize(Realm&) { } +void Object::unsafe_set_shape(Shape& shape) +{ + m_shape = shape; + m_storage.resize(shape.property_count()); +} + // 7.2 Testing and Comparison Operations, https://tc39.es/ecma262/#sec-testing-and-comparison-operations // 7.2.5 IsExtensible ( O ), https://tc39.es/ecma262/#sec-isextensible-o diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 6fdce3c335b..5ae213adcb8 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -246,6 +246,8 @@ protected: Object(ConstructWithPrototypeTag, Object& prototype, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No); explicit Object(Shape&, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No); + void unsafe_set_shape(Shape&); + // [[Extensible]] bool m_is_extensible { true };