From c8a650ec1fdb35e515aaec2f5fb4e71632bbf69d Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sat, 9 May 2026 19:22:16 +0200 Subject: [PATCH] LibWeb/Bindings: Handle variadic conversion at the top level Move variadic JS-to-C++ handling out of the per-type conversion helpers and into the top-level conversion path. This allows us to centrlize the logic, and also adds support to variadic to more types. --- .../BindingsGenerator/IDLGenerators.cpp | 115 +++++++++--------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index aaadf04637..72e62a4caa 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -950,7 +950,48 @@ static void generate_optional_to_cpp(SourceGenerator& generator, ParameterType& } template -static void generate_to_string(SourceGenerator& scoped_generator, ParameterType const& parameter, bool variadic) +static void generate_variadic_to_cpp(SourceGenerator& generator, ParameterType& parameter, ByteString const& cpp_name, Context const& context, size_t recursion_depth) +{ + auto variadic_generator = generator.fork(); + auto variadic_cpp_type = idl_type_name_to_cpp_type(*parameter.type, context, parameter.extended_attributes); + auto variadic_storage_type = sequence_storage_type_to_cpp_storage_type_name(variadic_cpp_type.sequence_storage_type); + auto inner_cpp_name = ByteString::formatted("{}_item", cpp_name); + auto value_name = ByteString::formatted("variadic_argument_{}", recursion_depth); + + variadic_generator.set("variadic.storage_type", variadic_storage_type); + variadic_generator.set("variadic.item_type", variadic_cpp_type.name); + variadic_generator.set("variadic.inner_cpp_name", inner_cpp_name); + variadic_generator.set("variadic.value_name", value_name); + + if (variadic_cpp_type.sequence_storage_type == SequenceStorageType::RootVector) { + variadic_generator.append(R"~~~( + @variadic.storage_type@<@variadic.item_type@> @cpp_name@ { vm.heap() }; +)~~~"); + } else { + variadic_generator.append(R"~~~( + @variadic.storage_type@<@variadic.item_type@> @cpp_name@; +)~~~"); + } + + variadic_generator.append(R"~~~( + if (vm.argument_count() > @js_suffix@) { + @cpp_name@.ensure_capacity(vm.argument_count() - @js_suffix@); + + for (size_t i = @js_suffix@; i < vm.argument_count(); ++i) { + auto @variadic.value_name@ = vm.argument(i); +)~~~"); + + generate_to_cpp(variadic_generator, parameter, value_name, "", inner_cpp_name, context, false, {}, false, recursion_depth + 1); + + variadic_generator.append(R"~~~( + @cpp_name@.unchecked_append(move(@variadic.inner_cpp_name@)); + } + } +)~~~"); +} + +template +static void generate_to_string(SourceGenerator& scoped_generator, ParameterType const& parameter) { auto is_utf16_string = parameter.type->name().contains("Utf16"sv); auto is_fly_string = parameter.extended_attributes.contains("FlyString"sv); @@ -970,35 +1011,20 @@ static void generate_to_string(SourceGenerator& scoped_generator, ParameterType else scoped_generator.set("to_string", is_utf16_string ? "to_utf16_string"sv : "to_string"sv); - if (variadic) { + if (parameter.type->is_nullable()) { scoped_generator.append(R"~~~( - Vector<@string_type@> @cpp_name@; - - if (vm.argument_count() > @js_suffix@) { - @cpp_name@.ensure_capacity(vm.argument_count() - @js_suffix@); - - for (size_t i = @js_suffix@; i < vm.argument_count(); ++i) { - auto to_string_result = TRY(WebIDL::@to_string@(vm, vm.argument(i))); - @cpp_name@.unchecked_append(move(to_string_result)); - } - } -)~~~"); - } else { - if (parameter.type->is_nullable()) { - scoped_generator.append(R"~~~( Optional<@string_type@> @cpp_name@; if (!@js_name@@js_suffix@.is_nullish()) { @cpp_name@ = TRY(WebIDL::@to_string@(vm, @js_name@@js_suffix@)); } )~~~"); - } else { - scoped_generator.append(R"~~~( + } else { + scoped_generator.append(R"~~~( @string_type@ @cpp_name@; if (!@legacy_null_to_empty_string@ || !@js_name@@js_suffix@.is_null()) { @cpp_name@ = TRY(WebIDL::@to_string@(vm, @js_name@@js_suffix@)); } )~~~"); - } } } @@ -1365,24 +1391,11 @@ static void generate_array_buffer_view_to_cpp(SourceGenerator& scoped_generator, } } -static void generate_any_to_cpp(SourceGenerator& scoped_generator, bool variadic) +static void generate_any_to_cpp(SourceGenerator& scoped_generator) { - if (variadic) { - scoped_generator.append(R"~~~( - GC::RootVector @cpp_name@ { vm.heap() }; - - if (vm.argument_count() > @js_suffix@) { - @cpp_name@.ensure_capacity(vm.argument_count() - @js_suffix@); - - for (size_t i = @js_suffix@; i < vm.argument_count(); ++i) - @cpp_name@.unchecked_append(vm.argument(i)); - } -)~~~"); - } else { - scoped_generator.append(R"~~~( + scoped_generator.append(R"~~~( auto @cpp_name@ = @js_name@@js_suffix@; )~~~"); - } } enum class ThrowOnInvalidEnumValue { @@ -1585,7 +1598,7 @@ static void generate_sequence_to_cpp(SourceGenerator& scoped_generator, IDL::Par } template -static void generate_union_to_cpp(SourceGenerator& scoped_generator, ParameterType& parameter, ByteString const& js_name, ByteString const& js_suffix, ByteString const& cpp_name, Context const& context, bool variadic, size_t recursion_depth) +static void generate_union_to_cpp(SourceGenerator& scoped_generator, ParameterType& parameter, ByteString const& js_name, ByteString const& js_suffix, ByteString const& cpp_name, Context const& context, size_t recursion_depth) { // https://webidl.spec.whatwg.org/#es-union @@ -1624,8 +1637,6 @@ static void generate_union_to_cpp(SourceGenerator& scoped_generator, ParameterTy // This is because we cannot unconditionally throw in generate_to_cpp as generate_to_cpp is supposed to assign to a variable and then continue. // Note that all the other types only throw on a condition. - // The lambda must take the JS::Value to convert as a parameter instead of capturing it in order to support union types being variadic. - union_generator.append(R"~~~( auto @js_name@@js_suffix@_to_variant = [&vm, &realm](JS::Value @js_name@@js_suffix@) -> JS::ThrowCompletionOr<@union_type@> { // These might be unused. @@ -2026,24 +2037,9 @@ static void generate_union_to_cpp(SourceGenerator& scoped_generator, ParameterTy }; )~~~"); - if (!variadic) { - union_generator.append(R"~~~( + union_generator.append(R"~~~( @union_type@ @cpp_name@ = TRY(@js_name@@js_suffix@_to_variant(@js_name@@js_suffix@)); )~~~"); - } else { - union_generator.append(R"~~~( - Vector<@union_type@> @cpp_name@; - - if (vm.argument_count() > @js_suffix@) { - @cpp_name@.ensure_capacity(vm.argument_count() - @js_suffix@); - - for (size_t i = @js_suffix@; i < vm.argument_count(); ++i) { - auto result = TRY(@js_name@@js_suffix@_to_variant(vm.argument(i))); - @cpp_name@.unchecked_append(move(result)); - } - } - )~~~"); - } } template @@ -2065,14 +2061,19 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter scoped_generator.set("parameter.name", parameter.name); - if (optional && !variadic) { + if (variadic) { + generate_variadic_to_cpp(scoped_generator, parameter, acceptable_cpp_name, context, recursion_depth); + return; + } + + if (optional) { generate_optional_to_cpp(scoped_generator, parameter, js_name, js_suffix, acceptable_cpp_name, context, optional_default_value, recursion_depth); return; } // FIXME: Add support for optional, variadic, nullable and default values to all types if (parameter.type->is_string()) { - generate_to_string(scoped_generator, parameter, variadic); + generate_to_string(scoped_generator, parameter); } else if (parameter.type->is_boolean() || parameter.type->is_integer()) { generate_to_integral(scoped_generator, parameter); } else if (auto const* callback_interface = callback_interface_for_type(context, parameter.type)) { @@ -2090,7 +2091,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter } else if (parameter.type->name() == "ArrayBufferView") { generate_array_buffer_view_to_cpp(scoped_generator, *parameter.type); } else if (parameter.type->name() == "any") { - generate_any_to_cpp(scoped_generator, variadic); + generate_any_to_cpp(scoped_generator); } else if (context.enumerations.contains(parameter.type->name())) { // NOTE: Attribute setters return undefined instead of throwing when the string doesn't match an enum value. ThrowOnInvalidEnumValue throw_on_invalid = ThrowOnInvalidEnumValue::No; @@ -2111,7 +2112,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter } else if (parameter.type->name() == "record") { generate_record_to_cpp(scoped_generator, as(*parameter.type), acceptable_cpp_name, context, recursion_depth); } else if (is(*parameter.type)) { - generate_union_to_cpp(scoped_generator, parameter, js_name, js_suffix, acceptable_cpp_name, context, variadic, recursion_depth); + generate_union_to_cpp(scoped_generator, parameter, js_name, js_suffix, acceptable_cpp_name, context, recursion_depth); } else { dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type->name()); VERIFY_NOT_REACHED();