ladybird/Meta/Generators/libweb_bindings/global_mixins.py
Shannon Booth 4efd7dcef8 LibWeb: Always treat [Replaceable] attributes as having setters
[Replaceable] readonly attributes still need JS accessor setters for
replacement and receiver checks. Remove the include_replaceable opt-in
so the binding generator handles them consistently.
2026-06-13 22:05:45 +02:00

134 lines
6.1 KiB
Python

# Copyright (c) 2026-present, the Ladybird developers.
#
# SPDX-License-Identifier: BSD-2-Clause
from typing import TextIO
from Generators.libweb_bindings import overload_resolution
from Generators.libweb_bindings.attributes import attribute_getter_callback_name
from Generators.libweb_bindings.attributes import attribute_has_setter
from Generators.libweb_bindings.attributes import attribute_setter_callback_name
from Generators.libweb_bindings.attributes import define_the_regular_attributes
from Generators.libweb_bindings.attributes import define_the_unforgeable_attributes
from Generators.libweb_bindings.attributes import write_attribute_getter
from Generators.libweb_bindings.attributes import write_attribute_setter
from Generators.libweb_bindings.constants import define_the_constants
from Generators.libweb_bindings.context import GenerationContext
from Generators.libweb_bindings.cpp_types import idl_identifier_cpp_name
from Generators.libweb_bindings.includes import GeneratedIncludes
from Generators.libweb_bindings.operations import define_the_regular_operations
from Generators.libweb_bindings.operations import define_the_stringifier
from Generators.libweb_bindings.operations import write_regular_operations_for_receiver
from Generators.libweb_bindings.operations import write_stringifier
from Utils.webidl_parser import Interface
def write_global_mixin_declaration(out: TextIO, context: GenerationContext, interface: Interface) -> None:
out.write(
f"""class {interface.name}GlobalMixin {{
public:
void initialize(JS::Realm&, JS::Object&);
void define_unforgeable_attributes(JS::Realm&, JS::Object&);
{interface.name}GlobalMixin();
virtual ~{interface.name}GlobalMixin();
private:
"""
)
declared_callbacks: set[str] = set()
for attribute in interface.regular_attributes:
if "FIXME" in attribute.extended_attributes:
continue
getter_callback = attribute_getter_callback_name(attribute)
if getter_callback not in declared_callbacks:
declared_callbacks.add(getter_callback)
out.write(f" JS_DECLARE_NATIVE_FUNCTION({getter_callback});\n")
if attribute_has_setter(attribute):
setter_callback = attribute_setter_callback_name(attribute)
if setter_callback not in declared_callbacks:
declared_callbacks.add(setter_callback)
out.write(f" JS_DECLARE_NATIVE_FUNCTION({setter_callback});\n")
for operations in overload_resolution.operation_overload_sets(interface).values():
operation = operations[0]
callback = idl_identifier_cpp_name(operation)
if callback not in declared_callbacks:
declared_callbacks.add(callback)
out.write(f" JS_DECLARE_NATIVE_FUNCTION({callback});\n")
if len(operations) > 1:
for overload_index, overloaded_operation in enumerate(operations):
overloaded_callback = idl_identifier_cpp_name(overloaded_operation, suffix=overload_index)
if overloaded_callback not in declared_callbacks:
declared_callbacks.add(overloaded_callback)
out.write(f" JS_DECLARE_NATIVE_FUNCTION({overloaded_callback});\n")
if interface.stringifier is not None and "to_string" not in declared_callbacks:
declared_callbacks.add("to_string")
out.write(" JS_DECLARE_NATIVE_FUNCTION(to_string);\n")
out.write(
"""};
"""
)
def write_global_mixin_implementation(
out: TextIO, context: GenerationContext, includes: GeneratedIncludes, interface: Interface
) -> None:
if "Global" not in interface.extended_attributes:
return
includes.add("LibWeb/Bindings/Intrinsics.h")
out.write(
f"""{interface.name}GlobalMixin::{interface.name}GlobalMixin() = default;
{interface.name}GlobalMixin::~{interface.name}GlobalMixin() = default;
void {interface.name}GlobalMixin::initialize(JS::Realm& realm, [[maybe_unused]] JS::Object& object)
{{
[[maybe_unused]] auto& vm = realm.vm();
[[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable;
object.set_prototype(&ensure_web_prototype<{interface.prototype_class}>(realm, "{interface.namespaced_name}"_fly_string));
"""
)
define_the_regular_attributes(out, includes, interface)
define_the_regular_operations(out, includes, interface)
define_the_stringifier(out, includes, interface)
define_the_constants(out, context, includes, interface)
out.write(
f"""}}
void {interface.name}GlobalMixin::define_unforgeable_attributes(JS::Realm& realm, [[maybe_unused]] JS::Object& object)
{{
[[maybe_unused]] auto& vm = realm.vm();
[[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable;
"""
)
define_the_unforgeable_attributes(out, includes, interface)
define_the_regular_operations(out, includes, interface, unforgeable=True)
define_the_stringifier(out, includes, interface, unforgeable=True)
out.write(
"""}
"""
)
defined_callbacks: set[str] = set()
for attribute in interface.regular_attributes:
if "FIXME" in attribute.extended_attributes:
continue
getter_callback = attribute_getter_callback_name(attribute)
if getter_callback not in defined_callbacks:
defined_callbacks.add(getter_callback)
write_attribute_getter(out, context, includes, interface, attribute, f"{interface.name}GlobalMixin")
if attribute_has_setter(attribute):
setter_callback = attribute_setter_callback_name(attribute)
if setter_callback not in defined_callbacks:
defined_callbacks.add(setter_callback)
write_attribute_setter(out, context, includes, interface, attribute, f"{interface.name}GlobalMixin")
write_regular_operations_for_receiver(
out, context, includes, interface, f"{interface.name}GlobalMixin", defined_callbacks
)
if interface.stringifier is not None and "to_string" not in defined_callbacks:
defined_callbacks.add("to_string")
write_stringifier(out, context, includes, interface, f"{interface.name}GlobalMixin")