ladybird/Libraries/LibJS/Runtime/FunctionObject.cpp
Andreas Kling c5427e5f4e LibJS: Convert Object bitfields to a flags byte
Replace individual bool bitfields in Object (m_is_extensible,
m_has_parameter_map, m_has_magical_length_property, etc.) with a
single u8 m_flags field and Flag:: constants.

This consolidates 8 scattered bitfields into one byte with explicit
bit positions, making them easy to access from generated assembly
code at a known offset. It also converts the virtual is_function()
and is_ecmascript_function_object() methods to flag-based checks,
avoiding virtual dispatch for these hot queries.

ProxyObject now explicitly clears the IsFunction flag in its
constructor when wrapping a non-callable target, instead of relying
on a virtual is_function() override.
2026-03-07 13:09:59 +01:00

117 lines
4.5 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
FunctionObject::FunctionObject(Realm& realm, Object* prototype, MayInterfereWithIndexedPropertyAccess may_interfere_with_indexed_property_access)
: Object(realm, prototype, may_interfere_with_indexed_property_access)
{
set_is_function();
}
FunctionObject::FunctionObject(Object& prototype, MayInterfereWithIndexedPropertyAccess may_interfere_with_indexed_property_access)
: Object(ConstructWithPrototypeTag::Tag, prototype, may_interfere_with_indexed_property_access)
{
set_is_function();
}
// 10.2.9 SetFunctionName ( F, name [ , prefix ] ), https://tc39.es/ecma262/#sec-setfunctionname
GC::Ref<PrimitiveString> FunctionObject::make_function_name(Variant<PropertyKey, PrivateName> const& name_arg, Optional<StringView> const& prefix)
{
auto& vm = this->vm();
Utf16String name;
// 2. If Type(name) is Symbol, then
if (auto const* property_key = name_arg.get_pointer<PropertyKey>(); property_key && property_key->is_symbol()) {
// a. Let description be name's [[Description]] value.
auto const& description = property_key->as_symbol()->description();
// b. If description is undefined, set name to the empty String.
if (!description.has_value())
name = {};
// c. Else, set name to the string-concatenation of "[", description, and "]".
else
name = Utf16String::formatted("[{}]", *description);
}
// 3. Else if name is a Private Name, then
else if (auto const* private_name = name_arg.get_pointer<PrivateName>()) {
// a. Set name to name.[[Description]].
name = private_name->description.to_utf16_string();
}
// NOTE: This is necessary as we use a different parameter name.
else {
name = name_arg.get<PropertyKey>().to_string();
}
// 4. If F has an [[InitialName]] internal slot, then
auto* native_function = as_if<NativeFunction>(this);
if (native_function) {
// a. Set F.[[InitialName]] to name.
native_function->set_initial_name({}, name);
}
// 5. If prefix is present, then
if (prefix.has_value()) {
// a. Set name to the string-concatenation of prefix, the code unit 0x0020 (SPACE), and name.
name = Utf16String::formatted("{} {}", *prefix, name);
// b. If F has an [[InitialName]] internal slot, then
if (native_function) {
// i. Optionally, set F.[[InitialName]] to name.
native_function->set_initial_name({}, name);
}
}
return PrimitiveString::create(vm, move(name));
}
// 10.2.9 SetFunctionName ( F, name [ , prefix ] ), https://tc39.es/ecma262/#sec-setfunctionname
void FunctionObject::set_function_name(Variant<PropertyKey, PrivateName> const& name_arg, Optional<StringView> const& prefix)
{
auto& vm = this->vm();
// 1. Assert: F is an extensible object that does not have a "name" own property.
VERIFY(extensible());
VERIFY(!storage_has(vm.names.name));
auto name = make_function_name(name_arg, prefix);
// 6. Perform ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).
PropertyDescriptor descriptor { .value = name, .writable = false, .enumerable = false, .configurable = true };
MUST(define_property_or_throw(vm.names.name, descriptor));
// 7. Return unused.
}
// 10.2.10 SetFunctionLength ( F, length ), https://tc39.es/ecma262/#sec-setfunctionlength
void FunctionObject::set_function_length(double length)
{
auto& vm = this->vm();
// "length (a non-negative integer or +∞)"
VERIFY(trunc(length) == length || __builtin_isinf_sign(length) == 1);
// 1. Assert: F is an extensible object that does not have a "length" own property.
VERIFY(extensible());
VERIFY(!storage_has(vm.names.length));
// 2. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).
PropertyDescriptor descriptor { .value = Value { length }, .writable = false, .enumerable = false, .configurable = true };
MUST(define_property_or_throw(vm.names.length, descriptor));
// 3. Return unused.
}
}