ladybird/Libraries/LibWeb/Bindings/Intrinsics.cpp
Andreas Kling 0dd6b32a1b LibWeb/Bindings: Share more interface constructors
Split the generator decision for shared prototype and constructor
objects. Interfaces with custom prototype behavior can still use the
shared InterfaceConstructor when the constructor object itself has no
custom GC cell state.

Teach Intrinsics how to register a shared constructor for an existing
custom prototype object, and keep LegacyFactoryFunction aliases wired up
when the primary interface objects are shared.
2026-05-09 15:33:00 +02:00

84 lines
2.8 KiB
C++

/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2026, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Forward.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibWeb/Bindings/InterfaceObject.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/PrincipalHostDefined.h>
namespace Web::Bindings {
GC_DEFINE_ALLOCATOR(Intrinsics);
void Intrinsics::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_namespaces);
visitor.visit(m_prototypes);
visitor.visit(m_constructors);
visitor.visit(m_realm);
visitor.visit(m_unforgeable_functions);
}
Intrinsics& host_defined_intrinsics(JS::Realm& realm)
{
ASSERT(realm.host_defined());
return static_cast<Bindings::HostDefined&>(*realm.host_defined()).intrinsics;
}
GC::Ref<JS::NativeFunction> Intrinsics::ensure_web_unforgeable_function(
Utf16FlyString const& interface_name,
Utf16FlyString const& attribute_name,
Function<JS::ThrowCompletionOr<JS::Value>(JS::VM&)> behaviour,
UnforgeableKey::Type type)
{
UnforgeableKey key { interface_name, attribute_name, type };
if (auto it = m_unforgeable_functions.find(key); it != m_unforgeable_functions.end())
return *it->value;
auto function = JS::NativeFunction::create(*m_realm, move(behaviour), type == UnforgeableKey::Type::Setter ? 1 : 0, attribute_name, m_realm, type == UnforgeableKey::Type::Setter ? "set"sv : "get"sv);
m_unforgeable_functions.set(key, *function);
return *function;
}
JS::Object& Intrinsics::existing_web_prototype(FlyString const& class_name)
{
auto it = m_prototypes.find(class_name);
VERIFY(it != m_prototypes.end());
return *it->value;
}
void Intrinsics::create_web_prototype_and_constructor(JS::Realm& realm, InterfaceObjectMetadata const& metadata)
{
auto prototype = realm.create<InterfacePrototypeObject>(realm, metadata);
m_prototypes.set(FlyString::from_utf8_without_validation(metadata.namespaced_name.bytes()), prototype);
create_web_constructor(realm, metadata, prototype);
}
void Intrinsics::create_web_constructor(JS::Realm& realm, InterfaceObjectMetadata const& metadata, JS::Object& prototype)
{
auto& vm = realm.vm();
auto constructor = realm.create<InterfaceConstructor>(realm, metadata);
m_constructors.set(FlyString::from_utf8_without_validation(metadata.namespaced_name.bytes()), constructor);
prototype.define_direct_property(vm.names.constructor, constructor.ptr(), JS::Attribute::Writable | JS::Attribute::Configurable);
}
}
namespace AK {
unsigned Traits<Web::Bindings::UnforgeableKey>::hash(Web::Bindings::UnforgeableKey const& key)
{
return pair_int_hash(pair_int_hash(key.attribute_name.hash(), key.interface_name.hash()), to_underlying(key.type));
}
}