mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 12:05:15 +00:00
LibJS: Use a premade shape for normal function objects
This avoids going through all the shape transitions when setting up the most common form of ESFO. This is extremely hot on Uber Eats, and this provides some relief.
This commit is contained in:
parent
60fd406903
commit
c037bda455
Notes:
github-actions[bot]
2025-03-27 23:13:08 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/c037bda455d Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4128 Reviewed-by: https://github.com/Hendiadyoin1
5 changed files with 61 additions and 22 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Shape>(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);
|
||||
|
|
|
@ -31,6 +31,11 @@ public:
|
|||
[[nodiscard]] GC::Ref<Shape> 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<Shape> 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<ProxyConstructor> proxy_constructor() { return *m_proxy_constructor; }
|
||||
|
||||
|
@ -139,6 +144,11 @@ private:
|
|||
GC::Ptr<Shape> m_normal_function_prototype_shape;
|
||||
u32 m_normal_function_prototype_constructor_offset { 0 };
|
||||
|
||||
GC::Ptr<Shape> 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<ProxyConstructor> m_proxy_constructor;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue