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.
This commit is contained in:
parent
8a259c1cde
commit
0dd6b32a1b
4 changed files with 123 additions and 28 deletions
|
|
@ -56,15 +56,20 @@ JS::Object& Intrinsics::existing_web_prototype(FlyString const& class_name)
|
|||
|
||||
void Intrinsics::create_web_prototype_and_constructor(JS::Realm& realm, InterfaceObjectMetadata const& metadata)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
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);
|
||||
prototype.define_direct_property(vm.names.constructor, constructor.ptr(), JS::Attribute::Writable | JS::Attribute::Configurable);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ private:
|
|||
template<typename PrototypeType>
|
||||
void create_web_prototype_and_constructor(JS::Realm& realm);
|
||||
void create_web_prototype_and_constructor(JS::Realm& realm, InterfaceObjectMetadata const&);
|
||||
void create_web_constructor(JS::Realm& realm, InterfaceObjectMetadata const&, JS::Object& prototype);
|
||||
|
||||
HashMap<FlyString, GC::Ref<JS::Object>> m_namespaces;
|
||||
HashMap<FlyString, GC::Ref<JS::Object>> m_prototypes;
|
||||
|
|
|
|||
|
|
@ -153,12 +153,15 @@ def should_have_interface_object(interface: Interface) -> bool:
|
|||
return True
|
||||
|
||||
|
||||
def can_use_shared_interface_objects(interface: Interface) -> bool:
|
||||
def can_use_shared_interface_constructor(interface: Interface) -> bool:
|
||||
return not interface.is_namespace and not interface.is_callback_interface
|
||||
|
||||
|
||||
def can_use_shared_interface_prototype(interface: Interface) -> bool:
|
||||
return (
|
||||
not interface.is_namespace
|
||||
and not interface.is_callback_interface
|
||||
and "Global" not in interface.extended_attributes
|
||||
and lookup_legacy_constructor(interface) is None
|
||||
and not interface.has_special_member
|
||||
and interface.named_property_getter is None
|
||||
and interface.indexed_property_getter is None
|
||||
|
|
@ -397,7 +400,7 @@ void Intrinsics::create_web_namespace<{interface.namespace_class}>(JS::Realm& re
|
|||
|
||||
|
||||
def write_interface_creation(out: TextIO, interface: Interface) -> None:
|
||||
if can_use_shared_interface_objects(interface):
|
||||
if can_use_shared_interface_prototype(interface):
|
||||
out.write(
|
||||
f"""template<>
|
||||
WEB_API void Intrinsics::create_web_prototype_and_constructor<{interface.prototype_class}>(JS::Realm& realm)
|
||||
|
|
@ -424,7 +427,77 @@ WEB_API void Intrinsics::create_web_prototype_and_constructor<{interface.prototy
|
|||
.has_immutable_prototype = {str(interface_prototype_has_immutable_prototype(interface)).lower()},
|
||||
}};
|
||||
create_web_prototype_and_constructor(realm, metadata);
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
legacy_constructor = lookup_legacy_constructor(interface)
|
||||
if legacy_constructor is not None:
|
||||
out.write(
|
||||
f""" auto legacy_constructor = realm.create<{legacy_constructor.constructor_class}>(realm);
|
||||
m_constructors.set("{legacy_constructor.name}"_fly_string, legacy_constructor.ptr());
|
||||
"""
|
||||
)
|
||||
|
||||
out.write(
|
||||
"""}
|
||||
"""
|
||||
)
|
||||
return
|
||||
|
||||
if can_use_shared_interface_constructor(interface):
|
||||
out.write(
|
||||
f"""template<>
|
||||
WEB_API void Intrinsics::create_web_prototype_and_constructor<{interface.prototype_class}>(JS::Realm& realm)
|
||||
{{
|
||||
"""
|
||||
)
|
||||
|
||||
ensure_parent_constructor = "nullptr"
|
||||
if interface.parent_name:
|
||||
ensure_parent_constructor = f"""[](JS::Realm& realm) -> JS::NativeFunction& {{ return Web::Bindings::ensure_web_constructor<{interface.parent_name}Prototype>(realm, "{interface.parent_name}"_fly_string); }}"""
|
||||
|
||||
out.write(
|
||||
f""" static constexpr InterfaceObjectMetadata metadata {{
|
||||
.name = "{interface.name}"sv,
|
||||
.namespaced_name = "{interface.namespaced_name}"sv,
|
||||
.ensure_parent_constructor = {ensure_parent_constructor},
|
||||
.initialize_constructor = &{interface.constructor_class}::initialize,
|
||||
.construct = &{interface.constructor_class}::construct,
|
||||
}};
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
named_properties_class = ""
|
||||
if "Global" in interface.extended_attributes and interface.supports_named_properties():
|
||||
named_properties_class = f"{interface.name}Properties"
|
||||
|
||||
if named_properties_class:
|
||||
out.write(
|
||||
f""" auto named_properties_object = realm.create<{named_properties_class}>(realm);
|
||||
m_prototypes.set("{named_properties_class}"_fly_string, named_properties_object);
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
out.write(
|
||||
f""" auto prototype = realm.create<{interface.prototype_class}>(realm);
|
||||
m_prototypes.set("{interface.namespaced_name}"_fly_string, prototype);
|
||||
|
||||
create_web_constructor(realm, metadata, prototype);
|
||||
"""
|
||||
)
|
||||
|
||||
legacy_constructor = lookup_legacy_constructor(interface)
|
||||
if legacy_constructor is not None:
|
||||
out.write(
|
||||
f""" auto legacy_constructor = realm.create<{legacy_constructor.constructor_class}>(realm);
|
||||
m_constructors.set("{legacy_constructor.name}"_fly_string, legacy_constructor.ptr());
|
||||
"""
|
||||
)
|
||||
|
||||
out.write(
|
||||
"""}
|
||||
"""
|
||||
)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -4485,12 +4485,17 @@ static bool interface_prototype_has_immutable_prototype(IDL::Interface const& in
|
|||
|| interface.name == "EventTarget"sv;
|
||||
}
|
||||
|
||||
static bool can_use_shared_interface_objects(IDL::Interface const& interface)
|
||||
static bool can_use_shared_interface_constructor(IDL::Interface const& interface)
|
||||
{
|
||||
return !interface.is_namespace
|
||||
&& !interface.is_callback_interface;
|
||||
}
|
||||
|
||||
static bool can_use_shared_interface_prototype(IDL::Interface const& interface)
|
||||
{
|
||||
return !interface.is_namespace
|
||||
&& !interface.is_callback_interface
|
||||
&& !interface.extended_attributes.contains("Global")
|
||||
&& !interface.extended_attributes.contains("LegacyFactoryFunction")
|
||||
&& !interface.value_iterator_type.has_value()
|
||||
&& !interface.pair_iterator_types.has_value()
|
||||
&& !interface.async_value_iterator_type.has_value()
|
||||
|
|
@ -7169,10 +7174,12 @@ namespace Web::Bindings {
|
|||
|
||||
)~~~"sv);
|
||||
|
||||
if (can_use_shared_interface_objects(interface)) {
|
||||
auto can_use_shared_constructor = can_use_shared_interface_constructor(interface);
|
||||
auto can_use_shared_prototype = can_use_shared_interface_prototype(interface);
|
||||
|
||||
if (can_use_shared_constructor) {
|
||||
SourceGenerator generator { builder };
|
||||
generator.set("constructor_class", interface.constructor_class);
|
||||
generator.set("prototype_class", interface.prototype_class);
|
||||
generator.append(R"~~~(
|
||||
struct @constructor_class@ {
|
||||
public:
|
||||
|
|
@ -7184,7 +7191,13 @@ private:
|
|||
generate_constructor_declarations(interface, builder);
|
||||
generator.append(R"~~~(
|
||||
};
|
||||
)~~~");
|
||||
}
|
||||
|
||||
if (can_use_shared_prototype) {
|
||||
SourceGenerator generator { builder };
|
||||
generator.set("prototype_class", interface.prototype_class);
|
||||
generator.append(R"~~~(
|
||||
struct @prototype_class@ {
|
||||
public:
|
||||
static void initialize(JS::Realm&, JS::Object&);
|
||||
|
|
@ -7202,8 +7215,10 @@ private:
|
|||
if (interface.is_namespace) {
|
||||
generate_namespace_header(interface, builder);
|
||||
} else {
|
||||
generate_constructor_header(interface, builder);
|
||||
generate_prototype_header(interface, builder);
|
||||
if (!can_use_shared_constructor)
|
||||
generate_constructor_header(interface, builder);
|
||||
if (!can_use_shared_prototype)
|
||||
generate_prototype_header(interface, builder);
|
||||
}
|
||||
|
||||
if (interface.pair_iterator_types.has_value())
|
||||
|
|
@ -7262,27 +7277,28 @@ namespace Web::Bindings {
|
|||
|
||||
static void generate_implementation_for_interface(IDL::Interface const& interface, StringBuilder& builder)
|
||||
{
|
||||
if (can_use_shared_interface_objects(interface)) {
|
||||
generate_implementation_prologue(interface, builder);
|
||||
generate_constructor_initialization_for_existing_object(interface, builder);
|
||||
generate_constructor_static_member_definitions(interface, builder);
|
||||
generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::No, InitializeExistingObject::Yes);
|
||||
generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::Yes);
|
||||
generate_prototype_or_global_mixin_definitions(interface, builder);
|
||||
generate_idl_value_conversion_implementations(module_for_path(interface.context, interface.module_own_path), builder);
|
||||
builder.append(R"~~~(
|
||||
} // namespace Web::Bindings
|
||||
)~~~"sv);
|
||||
return;
|
||||
}
|
||||
auto can_use_shared_constructor = can_use_shared_interface_constructor(interface);
|
||||
auto can_use_shared_prototype = can_use_shared_interface_prototype(interface);
|
||||
|
||||
generate_implementation_prologue(interface, builder);
|
||||
|
||||
if (interface.is_namespace) {
|
||||
generate_namespace_implementation(interface, builder);
|
||||
} else {
|
||||
generate_constructor_implementation(interface, builder);
|
||||
generate_prototype_implementation(interface, builder);
|
||||
if (can_use_shared_constructor) {
|
||||
generate_constructor_initialization_for_existing_object(interface, builder);
|
||||
generate_constructor_static_member_definitions(interface, builder);
|
||||
} else {
|
||||
generate_constructor_implementation(interface, builder);
|
||||
}
|
||||
|
||||
if (can_use_shared_prototype) {
|
||||
generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::No, InitializeExistingObject::Yes);
|
||||
generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::Yes);
|
||||
generate_prototype_or_global_mixin_definitions(interface, builder);
|
||||
} else {
|
||||
generate_prototype_implementation(interface, builder);
|
||||
}
|
||||
}
|
||||
|
||||
if (interface.pair_iterator_types.has_value())
|
||||
|
|
|
|||
Loading…
Reference in a new issue