LibJS: Lazily instantiate "prototype" field on ECMAScriptFunctionObject

This field is rarely accessed but we were creating it for every single
script function instantiated.

It's a little awkward but the same optimization can be found in other
engines, so it's nothing crazy.

This avoids creating roughly 80,000 objects on my x.com home feed.
This commit is contained in:
Andreas Kling
2025-12-16 19:20:59 -06:00
committed by Andreas Kling
parent 941336505c
commit cbe75f8251
Notes: github-actions[bot] 2025-12-17 18:51:45 +00:00
4 changed files with 21 additions and 8 deletions

View File

@@ -176,10 +176,7 @@ void ECMAScriptFunctionObject::initialize(Realm& realm)
if (!is_arrow_function() && kind() == FunctionKind::Normal) {
put_direct(realm.intrinsics().normal_function_length_offset(), Value(function_length()));
put_direct(realm.intrinsics().normal_function_name_offset(), m_name_string);
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);
m_may_need_lazy_prototype_instantiation = true;
} else {
PropertyDescriptor length_descriptor { .value = Value(function_length()), .writable = false, .enumerable = false, .configurable = true };
MUST(define_property_or_throw(vm.names.length, length_descriptor));
@@ -641,4 +638,20 @@ Utf16String ECMAScriptFunctionObject::name_for_call_stack() const
return m_name_string->utf16_string();
}
ThrowCompletionOr<Optional<PropertyDescriptor>> ECMAScriptFunctionObject::internal_get_own_property(PropertyKey const& property_key) const
{
if (m_may_need_lazy_prototype_instantiation && property_key == vm().names.prototype) {
auto& realm = *this->realm();
auto metadata = shape().lookup(property_key);
if (!metadata.has_value()) {
auto prototype = Object::create_with_premade_shape(realm.intrinsics().normal_function_prototype_shape());
prototype->put_direct(realm.intrinsics().normal_function_prototype_constructor_offset(), this);
const_cast<ECMAScriptFunctionObject*>(this)->define_direct_property(vm().names.prototype, prototype, Attribute::Writable);
}
m_may_need_lazy_prototype_instantiation = false;
}
return Base::internal_get_own_property(property_key);
}
}