ladybird/Libraries/LibWeb/Bindings/InterfaceObject.h
Andreas Kling 02f504f3ce LibWeb/Bindings: Share simple generated interface objects
Many generated Web IDL prototype and constructor objects have no
per-interface storage and no custom virtual behavior. They only differ
by metadata such as their exposed name, parent interface, constructor
hook, and generated initialization functions.

Add shared InterfacePrototypeObject and InterfaceConstructor classes for
these simple interfaces. The generators now emit per-interface metadata
and static helper functions instead of distinct GC::Cell subclasses when
the interface does not need special object behavior.

This avoids creating separate type-isolated GC allocators for hundreds
of otherwise empty prototype and constructor classes, while preserving
constructors, unforgeable attributes, IDL conversions, and immutable
prototypes in the global prototype chain.
2026-05-09 14:36:18 +02:00

75 lines
2.8 KiB
C++

/*
* Copyright (c) 2026-present, the Ladybird developers
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibWeb/Export.h>
namespace Web::Bindings {
class InterfaceConstructor;
struct InterfaceObjectMetadata {
using EnsurePrototypeFunction = JS::Object& (*)(JS::Realm&);
using EnsureConstructorFunction = JS::NativeFunction& (*)(JS::Realm&);
using InitializeConstructorFunction = void (*)(JS::Realm&, JS::NativeFunction&);
using InitializePrototypeFunction = void (*)(JS::Realm&, JS::Object&);
using DefineUnforgeableAttributesFunction = void (*)(JS::Realm&, JS::Object&);
using ConstructFunction = JS::ThrowCompletionOr<GC::Ref<JS::Object>> (*)(InterfaceConstructor&, JS::FunctionObject&);
StringView name;
StringView namespaced_name;
EnsurePrototypeFunction ensure_parent_prototype { nullptr };
EnsureConstructorFunction ensure_parent_constructor { nullptr };
InitializeConstructorFunction initialize_constructor { nullptr };
InitializePrototypeFunction initialize_prototype { nullptr };
DefineUnforgeableAttributesFunction define_unforgeable_attributes { nullptr };
ConstructFunction construct { nullptr };
bool has_immutable_prototype { false };
};
class WEB_API InterfacePrototypeObject final : public JS::Object {
JS_OBJECT_WITH_CUSTOM_CLASS_NAME(InterfacePrototypeObject, JS::Object);
public:
explicit InterfacePrototypeObject(JS::Realm&, InterfaceObjectMetadata const&);
virtual void initialize(JS::Realm&) override;
virtual ~InterfacePrototypeObject() override = default;
virtual StringView class_name() const override { return m_metadata.name; }
virtual JS::ThrowCompletionOr<bool> internal_set_prototype_of(JS::Object* prototype) override;
void define_unforgeable_attributes(JS::Realm&, JS::Object&);
GC_DECLARE_ALLOCATOR(InterfacePrototypeObject);
private:
InterfaceObjectMetadata const& m_metadata;
};
class WEB_API InterfaceConstructor final : public JS::NativeFunction {
JS_OBJECT_WITH_CUSTOM_CLASS_NAME(InterfaceConstructor, JS::NativeFunction);
public:
explicit InterfaceConstructor(JS::Realm&, InterfaceObjectMetadata const&);
virtual void initialize(JS::Realm&) override;
virtual ~InterfaceConstructor() override = default;
virtual StringView class_name() const override { return m_metadata.name; }
virtual JS::ThrowCompletionOr<JS::Value> call() override;
virtual JS::ThrowCompletionOr<GC::Ref<JS::Object>> construct(JS::FunctionObject& new_target) override;
GC_DECLARE_ALLOCATOR(InterfaceConstructor);
private:
virtual bool has_constructor() const override { return true; }
InterfaceObjectMetadata const& m_metadata;
};
}