/* * Copyright (c) 2022, Sam Atkins * Copyright (c) 2023, Luke Wilde * * SPDX-License-Identifier: BSD-2-Clause */ #include #include namespace IDL { Interface& Context::add_interface(NonnullOwnPtr interface) { auto& interface_ref = *interface; if (!interface_ref.name.is_empty()) interfaces.set(interface_ref.name, &interface_ref); owned_interfaces.append(move(interface)); return interface_ref; } Interface& Context::add_mixin(NonnullOwnPtr interface) { auto& interface_ref = *interface; mixins.set(interface_ref.name, &interface_ref); for (auto& partial_mixin : partial_mixins) { if (partial_mixin->name == interface_ref.name) interface_ref.extend_with_partial_interface(*partial_mixin); } owned_mixins.append(move(interface)); return interface_ref; } Module& Context::add_module(NonnullOwnPtr module) { auto& module_ref = *module; owned_modules.append(move(module)); return module_ref; } Module* Context::find_parsed_module(ByteString const& module_path) { for (auto const& module : owned_modules) { if (module->module_own_path == module_path) return module.ptr(); } return nullptr; } ParameterizedType const& Type::as_parameterized() const { return as(*this); } ParameterizedType& Type::as_parameterized() { return as(*this); } UnionType const& Type::as_union() const { return as(*this); } UnionType& Type::as_union() { return as(*this); } // https://webidl.spec.whatwg.org/#dfn-includes-a-nullable-type bool Type::includes_nullable_type() const { // A type includes a nullable type if: // - the type is a nullable type, or if (is_nullable()) return true; // FIXME: - the type is an annotated type and its inner type is a nullable type, or // - the type is a union type and its number of nullable member types is 1. if (is_union() && as_union().number_of_nullable_member_types() == 1) return true; return false; } // https://webidl.spec.whatwg.org/#dfn-includes-undefined bool Type::includes_undefined() const { // A type includes undefined if: // - the type is undefined, or if (is_undefined()) return true; // - the type is a nullable type and its inner type includes undefined, or // NOTE: We don't treat nullable as its own type, so this is handled by the other cases. // FIXME: - the type is an annotated type and its inner type includes undefined, or // - the type is a union type and one of its member types includes undefined. if (is_union()) return as_union().member_types().contains([](auto& type) { return type->includes_undefined(); }); return false; } // https://webidl.spec.whatwg.org/#dfn-distinguishable bool Type::is_distinguishable_from(IDL::Interface const& interface, IDL::Type const& other) const { // 1. If one type includes a nullable type and the other type either includes a nullable type, // is a union type with flattened member types including a dictionary type, or is a dictionary type, // return false. if (includes_nullable_type() && (other.includes_nullable_type() || (other.is_union() && any_of(other.as_union().flattened_member_types(), [&interface](auto const& type) { return interface.context.dictionaries.contains(type->name()); })) || interface.context.dictionaries.contains(other.name()))) return false; // 2. If both types are either a union type or nullable union type, return true if each member type // of the one is distinguishable with each member type of the other, or false otherwise. if (is_union() && other.is_union()) { auto const& this_union = as_union(); auto const& other_union = other.as_union(); for (auto& this_member_type : this_union.member_types()) { for (auto& other_member_type : other_union.member_types()) { if (!this_member_type->is_distinguishable_from(interface, other_member_type)) return false; } } return true; } // 3. If one type is a union type or nullable union type, return true if each member type of the union // type is distinguishable with the non-union type, or false otherwise. if (is_union() || other.is_union()) { auto const& the_union = is_union() ? as_union() : other.as_union(); auto const& non_union = is_union() ? other : *this; for (auto& member_type : the_union.member_types()) { if (!non_union.is_distinguishable_from(interface, member_type)) return false; } return true; } // 4. Consider the two "innermost" types derived by taking each type’s inner type if it is an annotated type, // and then taking its inner type inner type if the result is a nullable type. If these two innermost types // appear or are in categories appearing in the following table and there is a “●” mark in the corresponding // entry or there is a letter in the corresponding entry and the designated additional requirement below the // table is satisfied, then return true. Otherwise return false. auto const& this_innermost_type = innermost_type(); auto const& other_innermost_type = other.innermost_type(); enum class DistinguishabilityCategory { Undefined, Boolean, Numeric, BigInt, String, Object, Symbol, InterfaceLike, CallbackFunction, DictionaryLike, SequenceLike, __Count }; // See https://webidl.spec.whatwg.org/#distinguishable-table // clang-format off static constexpr bool table[to_underlying(DistinguishabilityCategory::__Count)][to_underlying(DistinguishabilityCategory::__Count)] { {false, true, true, true, true, true, true, true, true, false, true}, { true, false, true, true, true, true, true, true, true, true, true}, { true, true, false, true, true, true, true, true, true, true, true}, { true, true, true, false, true, true, true, true, true, true, true}, { true, true, true, true, false, true, true, true, true, true, true}, { true, true, true, true, true, false, true, false, false, false, false}, { true, true, true, true, true, true, false, true, true, true, true}, { true, true, true, true, true, false, true, false, true, true, true}, { true, true, true, true, true, false, true, true, false, false, true}, {false, true, true, true, true, false, true, true, false, false, true}, { true, true, true, true, true, false, true, true, true, true, false}, }; // clang-format on auto determine_category = [&interface](Type const& type) -> DistinguishabilityCategory { if (type.is_undefined()) return DistinguishabilityCategory::Undefined; if (type.is_boolean()) return DistinguishabilityCategory::Boolean; if (type.is_numeric()) return DistinguishabilityCategory::Numeric; if (type.is_bigint()) return DistinguishabilityCategory::BigInt; if (type.is_string()) return DistinguishabilityCategory::String; if (type.is_object()) return DistinguishabilityCategory::Object; if (type.is_symbol()) return DistinguishabilityCategory::Symbol; // FIXME: InterfaceLike - see below // FIXME: CallbackFunction // DictionaryLike // * Dictionary Types // * Record Types // FIXME: * Callback Interface Types if (interface.context.dictionaries.contains(type.name()) || (type.is_parameterized() && type.name() == "record"sv)) return DistinguishabilityCategory::DictionaryLike; // FIXME: Frozen array types are included in "sequence-like" if (type.is_sequence()) return DistinguishabilityCategory::SequenceLike; // FIXME: For lack of a better way of determining if something is an interface type, this just assumes anything we don't recognise is one. dbgln_if(IDL_DEBUG, "Unable to determine category for type named '{}', assuming it's an interface type.", type.name()); return DistinguishabilityCategory::InterfaceLike; }; auto this_distinguishability = determine_category(this_innermost_type); auto other_distinguishability = determine_category(other_innermost_type); if (this_distinguishability == DistinguishabilityCategory::InterfaceLike && other_distinguishability == DistinguishabilityCategory::InterfaceLike) { // The two identified interface-like types are not the same, and // FIXME: no single platform object implements both interface-like types. return this_innermost_type.name() != other_innermost_type.name(); } return table[to_underlying(this_distinguishability)][to_underlying(other_distinguishability)]; } // https://webidl.spec.whatwg.org/#dfn-json-types bool Type::is_json(Context const& context) const { // The JSON types are: // - numeric types, if (is_numeric()) return true; // - boolean, if (is_boolean()) return true; // - string types, if (is_string() || context.enumerations.contains(m_name)) return true; // - object, if (is_object()) return true; // - nullable types whose inner type is a JSON type, // - annotated types whose inner type is a JSON type, // NOTE: We don't separate nullable and annotated into separate types. // - union types whose member types are JSON types, if (is_union()) { auto const& union_type = as_union(); for (auto const& type : union_type.member_types()) { if (!type->is_json(context)) return false; } return true; } // - typedefs whose type being given a new name is a JSON type, auto typedef_iterator = context.typedefs.find(m_name); if (typedef_iterator != context.typedefs.end()) return typedef_iterator->value.type->is_json(context); // - sequence types whose parameterized type is a JSON type, // - frozen array types whose parameterized type is a JSON type, // - records where all of their values are JSON types, if (is_parameterized() && m_name.is_one_of("sequence", "FrozenArray", "record")) { auto const& parameterized_type = as_parameterized(); for (auto const& parameter : parameterized_type.parameters()) { if (!parameter->is_json(context)) return false; } return true; } // - dictionary types where the types of all members declared on the dictionary and all its inherited dictionaries are JSON types, auto dictionary_iterator = context.dictionaries.find(m_name); if (dictionary_iterator != context.dictionaries.end()) { auto const& dictionary = dictionary_iterator->value; for (auto const& member : dictionary.members) { if (!member.type->is_json(context)) return false; } return true; } // - interface types that have a toJSON operation declared on themselves or one of their inherited interfaces. auto current_interface = context.interfaces.get(m_name); while (current_interface.has_value()) { auto to_json_iterator = current_interface.value()->functions.find_if([](IDL::Function const& function) { return function.name == "toJSON"sv; }); if (to_json_iterator != current_interface.value()->functions.end()) return true; if (current_interface.value()->parent_name.is_empty()) break; current_interface = context.interfaces.get(current_interface.value()->parent_name); VERIFY(current_interface.has_value()); } return false; } bool Interface::will_generate_code() const { return !is_partial && (!name.is_empty() || !own_dictionaries.is_empty() || !own_enumerations.is_empty()); } void EffectiveOverloadSet::remove_all_other_entries() { Vector new_items; new_items.append(m_items[*m_last_matching_item_index]); m_items = move(new_items); } void Interface::dump() { dbgln("Attributes:"); for (auto& attribute : attributes) { dbgln(" {}{}{}{} {}", attribute.inherit ? "inherit " : "", attribute.readonly ? "readonly " : "", attribute.type->name(), attribute.type->is_nullable() ? "?" : "", attribute.name); } dbgln("Functions:"); for (auto& function : functions) { dbgln(" {}{} {}", function.return_type->name(), function.return_type->is_nullable() ? "?" : "", function.name); for (auto& parameter : function.parameters) { dbgln(" {}{} {}", parameter.type->name(), parameter.type->is_nullable() ? "?" : "", parameter.name); } } dbgln("Static Functions:"); for (auto& function : static_functions) { dbgln(" static {}{} {}", function.return_type->name(), function.return_type->is_nullable() ? "?" : "", function.name); for (auto& parameter : function.parameters) { dbgln(" {}{} {}", parameter.type->name(), parameter.type->is_nullable() ? "?" : "", parameter.name); } } } void Interface::extend_with_partial_interface(Interface const& partial) { for (auto const& attribute : partial.attributes) { auto attribute_copy = attribute; attribute_copy.extended_attributes.update(partial.extended_attributes); attributes.append(move(attribute_copy)); } for (auto const& static_attribute : partial.static_attributes) { auto static_attribute_copy = static_attribute; static_attribute_copy.extended_attributes.update(partial.extended_attributes); static_attributes.append(move(static_attribute_copy)); } constants.extend(partial.constants); for (auto const& function : partial.functions) { auto function_copy = function; function_copy.extended_attributes.update(partial.extended_attributes); functions.append(move(function_copy)); } for (auto const& static_function : partial.static_functions) { auto static_function_copy = static_function; static_function_copy.extended_attributes.update(partial.extended_attributes); static_functions.append(move(static_function_copy)); } } }