LibWeb: Add support for dictionaries in overload resolution

By making use of the known set of supported dictionary names in that
overload set. Note that this list is typically very small (the max that
we have currently is 1).
This commit is contained in:
Shannon Booth 2024-10-29 00:57:16 +13:00 committed by Andreas Kling
commit 70599d3f8d
Notes: github-actions[bot] 2024-10-28 22:33:23 +00:00
3 changed files with 45 additions and 12 deletions

View file

@ -2250,6 +2250,21 @@ static size_t resolve_distinguishing_argument_index(Interface const& interface,
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
static void generate_dictionary_types(SourceGenerator& generator, Vector<ByteString> const& dictionary_types)
{
generator.append(R"~~~(
Vector<StringView> dictionary_types {
)~~~");
for (auto const& dictionary : dictionary_types) {
generator.append(" \"");
generator.append(dictionary);
generator.appendln("\"sv,");
}
generator.append("};\n");
}
static void generate_overload_arbiter(SourceGenerator& generator, auto const& overload_set, IDL::Interface const& interface, ByteString const& class_name, IsConstructor is_constructor) static void generate_overload_arbiter(SourceGenerator& generator, auto const& overload_set, IDL::Interface const& interface, ByteString const& class_name, IsConstructor is_constructor)
{ {
auto function_generator = generator.fork(); auto function_generator = generator.fork();
@ -2260,6 +2275,8 @@ static void generate_overload_arbiter(SourceGenerator& generator, auto const& ov
function_generator.set("function.name:snakecase", make_input_acceptable_cpp(overload_set.key.to_snakecase())); function_generator.set("function.name:snakecase", make_input_acceptable_cpp(overload_set.key.to_snakecase()));
HashTable<ByteString> dictionary_types;
if (is_constructor == IsConstructor::Yes) { if (is_constructor == IsConstructor::Yes) {
function_generator.append(R"~~~( function_generator.append(R"~~~(
JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct(JS::FunctionObject& new_target) JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct(JS::FunctionObject& new_target)
@ -2324,6 +2341,10 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
optionality_builder.append(", "sv); optionality_builder.append(", "sv);
} }
auto const& type = overload.types[i];
if (interface.dictionaries.contains(type->name()))
dictionary_types.set(type->name());
types_builder.append(generate_constructor_for_idl_type(overload.types[i])); types_builder.append(generate_constructor_for_idl_type(overload.types[i]));
optionality_builder.append("IDL::Optionality::"sv); optionality_builder.append("IDL::Optionality::"sv);
@ -2360,11 +2381,16 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
function_generator.append(R"~~~( function_generator.append(R"~~~(
} }
)~~~");
generate_dictionary_types(function_generator, dictionary_types.values());
function_generator.append(R"~~~(
if (!effective_overload_set.has_value()) if (!effective_overload_set.has_value())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::OverloadResolutionFailed); return vm.throw_completion<JS::TypeError>(JS::ErrorType::OverloadResolutionFailed);
auto chosen_overload = TRY(WebIDL::resolve_overload(vm, effective_overload_set.value())); auto chosen_overload = TRY(WebIDL::resolve_overload(vm, effective_overload_set.value(), dictionary_types));
switch (chosen_overload.callable_id) { switch (chosen_overload.callable_id) {
)~~~"); )~~~");

View file

@ -54,10 +54,12 @@ static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOv
} }
// https://webidl.spec.whatwg.org/#es-overloads // https://webidl.spec.whatwg.org/#es-overloads
JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::EffectiveOverloadSet& overloads) JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::EffectiveOverloadSet& overloads, ReadonlySpan<StringView> dictionary_types)
{ {
// FIXME: The vast majority of this algorithm can be (and must be, in order to resolve the dictionary auto is_dictionary = [&dictionary_types](IDL::Type const& type) {
// related FIXMEs below) evaluated at code-generation time. return dictionary_types.contains_slow(type.name());
};
// 1. Let maxarg be the length of the longest type list of the entries in S. // 1. Let maxarg be the length of the longest type list of the entries in S.
// 2. Let n be the size of args. // 2. Let n be the size of args.
// 3. Initialize argcount to be min(maxarg, n). // 3. Initialize argcount to be min(maxarg, n).
@ -135,17 +137,20 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// NOTE: This is the one case we can't use `has_overload_with_argument_type_or_subtype_matching()` because we also need to look // NOTE: This is the one case we can't use `has_overload_with_argument_type_or_subtype_matching()` because we also need to look
// for dictionary types in the flattened members. // for dictionary types in the flattened members.
else if ((value.is_undefined() || value.is_null()) else if ((value.is_undefined() || value.is_null())
&& overloads.has_overload_with_matching_argument_at_index(i, [](IDL::Type const& type, auto) { && overloads.has_overload_with_matching_argument_at_index(i, [&is_dictionary](IDL::Type const& type, auto) {
if (type.is_nullable()) if (type.is_nullable())
return true; return true;
// FIXME: - a dictionary type if (is_dictionary(type))
return true;
// FIXME: - an annotated type whose inner type is one of the above types // FIXME: - an annotated type whose inner type is one of the above types
if (type.is_union()) { if (type.is_union()) {
auto flattened_members = type.as_union().flattened_member_types(); auto flattened_members = type.as_union().flattened_member_types();
for (auto const& member : flattened_members) { for (auto const& member : flattened_members) {
if (member->is_nullable()) if (member->is_nullable())
return true; return true;
// FIXME: - a dictionary type if (is_dictionary(type))
return true;
// FIXME: - an annotated type whose inner type is one of the above types // FIXME: - an annotated type whose inner type is one of the above types
} }
return false; return false;
@ -270,10 +275,11 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries. // then remove from S all other entries.
else if (value.is_object() else if (value.is_object()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { && has_overload_with_argument_type_or_subtype_matching(overloads, i, [&is_dictionary](IDL::Type const& type) {
dbgln("FIXME: a callback interface type"); if (is_dictionary(type))
dbgln("FIXME: a dictionary type"); return true;
dbgln("FIXME: a record type"); // FIXME: a callback interface type
// FIXME: a record type
return type.is_object(); return type.is_object();
})) { })) {
overloads.remove_all_other_entries(); overloads.remove_all_other_entries();

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/Span.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibIDL/Types.h> #include <LibIDL/Types.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
@ -23,6 +24,6 @@ struct ResolvedOverload {
}; };
// https://webidl.spec.whatwg.org/#es-overloads // https://webidl.spec.whatwg.org/#es-overloads
JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM&, IDL::EffectiveOverloadSet&); JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM&, IDL::EffectiveOverloadSet&, ReadonlySpan<StringView> interface_dictionaries);
} }