Repository: LadybirdBrowser/ladybird
File: Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp (C++)

Code before:
        // 11. If Type(V) is Boolean, then:
        //     1. If types includes boolean, then return the result of converting V to boolean.
        bool includes_boolean = false;
        for (auto& type : types) {
            if (type->name() == "boolean") {
                includes_boolean = true;
                break;
            }
        }

        if (includes_boolean) {
            union_generator.append(R"~~~(
        if (@js_name@@js_suffix@.is_boolean())
            return @union_type@ { @js_name@@js_suffix@.as_bool() };
)~~~");
        }

        bool includes_enumeration = false;
        for (auto& type : types) {
            if (interface.enumerations.contains(type->name())) {
                includes_enumeration = true;
                break;
            }
        }

        // If V is a string and the union contains an enumeration type, attempt to match the string against the enumeration's values.
        // This is important for unions like (SomeEnum or double), where strings should be interpreted as enums before numeric conversion.
        // Example: Libraries/LibWeb/WebAudio/AudioContext.idl (AudioContextOptions.latencyHint: (AudioContextLatencyCategory or double)).

        if (includes_enumeration) {
            union_generator.append(R"~~~(
        if (@js_name@@js_suffix@.is_string()) {
            auto @js_name@@js_suffix@_enum_string = TRY(@js_name@@js_suffix@.to_string(vm));
)~~~");

            for (auto& type : types) {
                if (!interface.enumerations.contains(type->name()))
                    continue;

                auto& enumeration = interface.enumerations.find(type->name())->value;
                auto enum_type = IDL::idl_type_name_to_cpp_type(*type, interface);

                auto enum_generator = union_generator.fork();
                enum_generator.set("enum.type", enum_type.name);

                for (auto& it : enumeration.translated_cpp_names) {
                    enum_generator.set("enum.alt.name", it.key);
                    enum_generator.set("enum.alt.value", it.value);
                    enum_generator.append(R"~~~(
            if (@js_name@@js_suffix@_enum_string == "@enum.alt.name@"sv)
                return @union_type@ { @enum.type@::@enum.alt.value@ };

Review comment (suggestion):
The comment here says: "where strings should be interpreted as enums before numeric conversion"
Is there really any particular reason that this needs to happen before numeric conversion? From what I can gather, the problem here is that the IDL spec (and web specs in general) treats enumerations as a javascript string, but since we are c++ land we give this a more typesafe C++ enumuration in the IDL generator. And the problem here is that previously we did not support making this conversion inside of unions, which gives a compiler error. Logically, if we are matching the spec this feels like it should be part of step 14 below

Code after:
        // 11. If Type(V) is Boolean, then:
        //     1. If types includes boolean, then return the result of converting V to boolean.
        bool includes_boolean = false;
        for (auto& type : types) {
            if (type->name() == "boolean") {
                includes_boolean = true;
                break;
            }
        }

        if (includes_boolean) {
            union_generator.append(R"~~~(
        if (@js_name@@js_suffix@.is_boolean())
            return @union_type@ { @js_name@@js_suffix@.as_bool() };
)~~~");
        }

        RefPtr<IDL::Type const> numeric_type;
        for (auto& type : types) {
            if (type->is_numeric()) {
                numeric_type = type;
                break;
            }
        }

        // 12. If Type(V) is Number, then:
        //     1. If types includes a numeric type, then return the result of converting V to that numeric type.
        if (numeric_type) {
            union_generator.append(R"~~~(
        if (@js_name@@js_suffix@.is_number()) {
)~~~");
            // NOTE: generate_to_cpp doesn't use the parameter name.
            // NOTE: generate_to_cpp will use to_{u32,etc.} which uses to_number internally and will thus use TRY, but it cannot throw as we know we are dealing with a number.
            IDL::Parameter idl_parameter { .type = *numeric_type, .name = parameter.name, .optional_default_value = {}, .extended_attributes = {} };
            generate_to_cpp(union_generator, idl_parameter, js_name, js_suffix, ByteString::formatted("{}{}_number", js_name, js_suffix), interface, false, false, {}, false, recursion_depth + 1);

            union_generator.append(R"~~~(
            return { @js_name@@js_suffix@_number };
        }
)~~~");
        }

        // 13. If Type(V) is BigInt, then:
        //     1. If types includes bigint, then return the result of converting V to bigint
        bool includes_bigint = false;
        for (auto& type : types) {
            if (type->name() == "bigint") {
                includes_bigint = true;
                break;
            }
        }