IDLGenerators: Allow specifying multiple constructors

This commit is contained in:
Matthew Olsson 2023-10-31 18:32:54 +00:00 committed by Andreas Kling
parent 9b022239c3
commit 99bb5a1d08
Notes: sideshowbarker 2024-07-17 08:25:15 +09:00

View file

@ -5,6 +5,7 @@
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -3635,46 +3636,136 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::constru
{
)~~~");
// FIXME: Unify this code with the overload arbiter and possibly WebIDL::resolve_overload
if (interface.constructors.is_empty()) {
// No constructor
generator.set("constructor.length", "0");
generator.append(R"~~~(
return vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAConstructor, "@namespaced_name@");
)~~~");
} else if (interface.constructors.size() == 1) {
// Single constructor
} else {
auto shortest_length = NumericLimits<size_t>::max();
Optional<IDL::Constructor> html_constructor;
auto& constructor = interface.constructors[0];
generator.set("constructor.length", DeprecatedString::number(constructor.shortest_length()));
for (auto const& constructor : interface.constructors) {
shortest_length = min(shortest_length, constructor.shortest_length());
if (constructor.extended_attributes.contains("HTMLConstructor"sv)) {
html_constructor = constructor;
break;
}
}
generator.set("constructor.length", DeprecatedString::number(shortest_length));
generator.append(R"~~~(
auto& vm = this->vm();
auto& realm = *vm.current_realm();
)~~~");
if (!constructor.extended_attributes.contains("HTMLConstructor"sv)) {
if (!constructor.parameters.is_empty()) {
generate_argument_count_check(generator, constructor.name, constructor.shortest_length());
if (!html_constructor.has_value()) {
if (interface.constructors.size() == 1) {
auto const& constructor = interface.constructors[0];
StringBuilder arguments_builder;
generate_arguments(generator, constructor.parameters, arguments_builder, interface);
generator.set(".constructor_arguments", arguments_builder.string_view());
if (!constructor.parameters.is_empty()) {
generate_argument_count_check(generator, constructor.name, constructor.shortest_length());
generator.append(R"~~~(
StringBuilder arguments_builder;
generate_arguments(generator, constructor.parameters, arguments_builder, interface);
generator.set(".constructor_arguments", arguments_builder.string_view());
generator.append(R"~~~(
auto impl = TRY(throw_dom_exception_if_needed(vm, [&] { return @fully_qualified_name@::construct_impl(realm, @.constructor_arguments@); }));
)~~~");
} else {
generator.append(R"~~~(
} else {
generator.append(R"~~~(
auto impl = TRY(throw_dom_exception_if_needed(vm, [&] { return @fully_qualified_name@::construct_impl(realm); }));
)~~~");
}
generator.append(R"~~~(
}
generator.append(R"~~~(
return *impl;
)~~~");
} else {
HashTable<size_t> observed_parameter_counts;
generator.append(R"~~~(
switch (vm.argument_count()) {
)~~~");
for (auto const& constructor : interface.constructors) {
for (ssize_t i = static_cast<ssize_t>(constructor.parameters.size()); i >= 0; --i) {
if (observed_parameter_counts.contains(i)) {
dbgln("Interface {} has multiple constructors that can accept {} parameters", interface.name, i);
VERIFY_NOT_REACHED();
}
observed_parameter_counts.set(i);
generator.set("parameter_count", MUST(String::number(i)));
generator.append(R"~~~(
case @parameter_count@:
)~~~");
if (!constructor.parameters[i - 1].optional)
break;
}
generator.append(R"~~~(
{
)~~~");
if (!constructor.parameters.is_empty()) {
StringBuilder arguments_builder;
generate_arguments(generator, constructor.parameters, arguments_builder, interface);
generator.set(".constructor_arguments", arguments_builder.string_view());
generator.append(R"~~~(
auto impl = TRY(throw_dom_exception_if_needed(vm, [&] { return @fully_qualified_name@::construct_impl(realm, @.constructor_arguments@); }));
return *impl;
)~~~");
} else {
generator.append(R"~~~(
auto impl = TRY(throw_dom_exception_if_needed(vm, [&] { return @fully_qualified_name@::construct_impl(realm); }));
return *impl;
)~~~");
}
generator.append(R"~~~(
}
)~~~");
}
if (shortest_length == 1) {
generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountOne");
generator.set(".arg_count_suffix", "");
} else {
generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountMany");
generator.set(".arg_count_suffix", DeprecatedString::formatted(", \"{}\"", shortest_length));
}
generator.append(R"~~~(
default:
return vm.throw_completion<JS::TypeError>(@.bad_arg_count@, "constructor"@.arg_count_suffix@);
}
)~~~");
}
} else {
// https://html.spec.whatwg.org/multipage/dom.html#html-element-constructors
// NOTE: The active function object in this context is always going to be the current constructor that has just been called.
// The [HTMLConstructor] extended attribute must take no arguments, and must only appear on constructor operations. It must
// appear only once on a constructor operation, and the interface must contain only the single, annotated constructor
// operation, and no others
if (interface.constructors.size() != 1) {
dbgln("Interface {}'s constructor annotated with [HTMLConstructor] must be the only constructor", interface.name);
VERIFY_NOT_REACHED();
}
if (!interface.constructors[0].parameters.is_empty()) {
dbgln("Interface {}'s constructor marked with [HTMLConstructor] must not have any parameters", interface.name);
VERIFY_NOT_REACHED();
}
generator.append(R"~~~(
auto& window = verify_cast<HTML::Window>(HTML::current_global_object());
@ -3794,9 +3885,6 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::constru
return *actual_element;
)~~~");
}
} else {
// Multiple constructor overloads - can't do that yet.
TODO();
}
generator.append(R"~~~(