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.
84 lines
2.8 KiB
C++
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));
|
|
}
|
|
|
|
}
|