LibJS: Don't create a GC::Function for every JS::NativeFunction

Instead, let JS::NativeFunction store the AK::Function directly, and
take care of conservatively marking its captured data.

This avoids an extra GC allocation for every JS::NativeFunction.
This commit is contained in:
Andreas Kling 2025-04-20 13:08:56 +02:00 committed by Andreas Kling
commit 5290dcf650
Notes: github-actions[bot] 2025-04-20 16:44:46 +00:00
4 changed files with 13 additions and 15 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2020-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -8,7 +8,6 @@
#include <LibJS/AST.h>
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Runtime/FunctionEnvironment.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/Value.h>
@ -26,7 +25,7 @@ void NativeFunction::initialize(Realm& realm)
void NativeFunction::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_native_function);
visitor.visit_possible_values(m_native_function.raw_capture_range());
visitor.visit(m_realm);
visitor.visit(m_name_string);
}
@ -52,7 +51,7 @@ GC::Ref<NativeFunction> NativeFunction::create(Realm& allocating_realm, Function
// 7. Set func.[[Extensible]] to true.
// 8. Set func.[[Realm]] to realm.
// 9. Set func.[[InitialName]] to null.
auto function = allocating_realm.create<NativeFunction>(GC::create_function(vm.heap(), move(behaviour)), prototype, *realm.value());
auto function = allocating_realm.create<NativeFunction>(move(behaviour), prototype, *realm.value());
function->unsafe_set_shape(realm.value()->intrinsics().native_function_shape());
@ -71,10 +70,10 @@ GC::Ref<NativeFunction> NativeFunction::create(Realm& allocating_realm, Function
GC::Ref<NativeFunction> NativeFunction::create(Realm& realm, FlyString const& name, Function<ThrowCompletionOr<Value>(VM&)> function)
{
return realm.create<NativeFunction>(name, GC::create_function(realm.heap(), move(function)), realm.intrinsics().function_prototype());
return realm.create<NativeFunction>(name, move(function), realm.intrinsics().function_prototype());
}
NativeFunction::NativeFunction(GC::Ptr<GC::Function<ThrowCompletionOr<Value>(VM&)>> native_function, Object* prototype, Realm& realm)
NativeFunction::NativeFunction(AK::Function<ThrowCompletionOr<Value>(VM&)> native_function, Object* prototype, Realm& realm)
: FunctionObject(realm, prototype)
, m_native_function(move(native_function))
, m_realm(&realm)
@ -91,7 +90,7 @@ NativeFunction::NativeFunction(Object& prototype)
{
}
NativeFunction::NativeFunction(FlyString name, GC::Ptr<GC::Function<ThrowCompletionOr<Value>(VM&)>> native_function, Object& prototype)
NativeFunction::NativeFunction(FlyString name, AK::Function<ThrowCompletionOr<Value>(VM&)> native_function, Object& prototype)
: FunctionObject(prototype)
, m_name(move(name))
, m_native_function(move(native_function))
@ -231,7 +230,7 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ReadonlySp
ThrowCompletionOr<Value> NativeFunction::call()
{
VERIFY(m_native_function);
return m_native_function->function()(vm());
return m_native_function(vm());
}
ThrowCompletionOr<GC::Ref<Object>> NativeFunction::construct(FunctionObject&)

View file

@ -44,8 +44,8 @@ public:
protected:
NativeFunction(FlyString name, Object& prototype);
NativeFunction(GC::Ptr<GC::Function<ThrowCompletionOr<Value>(VM&)>>, Object* prototype, Realm& realm);
NativeFunction(FlyString name, GC::Ptr<GC::Function<ThrowCompletionOr<Value>(VM&)>>, Object& prototype);
NativeFunction(AK::Function<ThrowCompletionOr<Value>(VM&)>, Object* prototype, Realm& realm);
NativeFunction(FlyString name, AK::Function<ThrowCompletionOr<Value>(VM&)>, Object& prototype);
explicit NativeFunction(Object& prototype);
virtual void initialize(Realm&) override;
@ -57,7 +57,7 @@ private:
FlyString m_name;
GC::Ptr<PrimitiveString> m_name_string;
Optional<FlyString> m_initial_name; // [[InitialName]]
GC::Ptr<GC::Function<ThrowCompletionOr<Value>(VM&)>> m_native_function;
AK::Function<ThrowCompletionOr<Value>(VM&)> m_native_function;
GC::Ptr<Realm> m_realm;
};

View file

@ -375,16 +375,15 @@ GC_DEFINE_ALLOCATOR(ExportedWasmFunction);
GC::Ref<ExportedWasmFunction> ExportedWasmFunction::create(JS::Realm& realm, FlyString const& name, Function<JS::ThrowCompletionOr<JS::Value>(JS::VM&)> behavior, Wasm::FunctionAddress exported_address)
{
auto& vm = realm.vm();
auto prototype = realm.intrinsics().function_prototype();
return realm.create<ExportedWasmFunction>(
name,
GC::create_function(vm.heap(), move(behavior)),
move(behavior),
exported_address,
prototype);
}
ExportedWasmFunction::ExportedWasmFunction(FlyString name, GC::Ptr<GC::Function<JS::ThrowCompletionOr<JS::Value>(JS::VM&)>> behavior, Wasm::FunctionAddress exported_address, JS::Object& prototype)
ExportedWasmFunction::ExportedWasmFunction(FlyString name, AK::Function<JS::ThrowCompletionOr<JS::Value>(JS::VM&)> behavior, Wasm::FunctionAddress exported_address, JS::Object& prototype)
: NativeFunction(move(name), move(behavior), prototype)
, m_exported_address(exported_address)
{

View file

@ -77,7 +77,7 @@ public:
Wasm::FunctionAddress exported_address() const { return m_exported_address; }
protected:
ExportedWasmFunction(FlyString name, GC::Ptr<GC::Function<JS::ThrowCompletionOr<JS::Value>(JS::VM&)>>, Wasm::FunctionAddress, Object& prototype);
ExportedWasmFunction(FlyString name, AK::Function<JS::ThrowCompletionOr<JS::Value>(JS::VM&)>, Wasm::FunctionAddress, Object& prototype);
private:
Wasm::FunctionAddress m_exported_address;