Jelajahi Sumber

LibIDL+WrapperGenerator: Implement Type::is_distinguishable_from()

As part of this, I've moved a couple of methods for checking for
null/undefined from UnionType to Type, and filled in more of their
steps.

This now detects more, and so causes us to hit a `TODO()` which is too
big for me to go after right now, so I've replaced that assertion with
a log message.
Sam Atkins 2 tahun lalu
induk
melakukan
8b4cc07a54

+ 1 - 1
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLGenerators.cpp

@@ -1019,7 +1019,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
 
         // FIXME: 2. If the union type includes a nullable type and V is null or undefined, then return the IDL value null.
         if (union_type.includes_nullable_type()) {
-            TODO();
+            dbgln("FIXME: 2. If the union type includes a nullable type and V is null or undefined, then return the IDL value null.");
         } else if (dictionary_type) {
             // 4. If V is null or undefined, then
             //    4.1 If types includes a dictionary type, then return the result of converting V to that dictionary type.

+ 160 - 0
Userland/Libraries/LibIDL/Types.cpp

@@ -28,4 +28,164 @@ UnionType& Type::as_union()
     return verify_cast<UnionType>(*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()) {
+        for (auto& type : as_union().member_types()) {
+            if (type.includes_undefined())
+                return true;
+        }
+    }
+
+    return false;
+}
+
+// https://webidl.spec.whatwg.org/#dfn-distinguishable
+bool Type::is_distinguishable_from(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.
+    // FIXME: "is a union type with flattened member types including a dictionary type, or is a dictionary type,"
+    if (includes_nullable_type() && other.includes_nullable_type())
+        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(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(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 = [](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
+        // FIXME: 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("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) {
+        // Two interface-likes are distinguishable if:
+        // "The two identified interface-like types are not the same, and no single platform object
+        // implements both interface-like types."
+        // FIXME: Implement this.
+        return false;
+    }
+
+    return table[to_underlying(this_distinguishability)][to_underlying(other_distinguishability)];
+}
+
 }

+ 51 - 31
Userland/Libraries/LibIDL/Types.h

@@ -71,29 +71,70 @@ public:
 
     Kind kind() const { return m_kind; }
 
+    bool is_plain() const { return m_kind == Kind::Plain; }
+
+    bool is_parameterized() const { return m_kind == Kind::Parameterized; }
+    ParameterizedType const& as_parameterized() const;
+    ParameterizedType& as_parameterized();
+
+    bool is_union() const { return m_kind == Kind::Union; }
+    UnionType const& as_union() const;
+    UnionType& as_union();
+
     String const& name() const { return m_name; }
 
     bool is_nullable() const { return m_nullable; }
     void set_nullable(bool value) { m_nullable = value; }
 
-    bool is_string() const { return m_name.is_one_of("ByteString", "CSSOMString", "DOMString", "USVString"); }
+    // https://webidl.spec.whatwg.org/#dfn-includes-a-nullable-type
+    bool includes_nullable_type() const;
+
+    // -> https://webidl.spec.whatwg.org/#dfn-includes-undefined
+    bool includes_undefined() const;
+
+    Type const& innermost_type() const
+    {
+        // From step 4 of https://webidl.spec.whatwg.org/#dfn-distinguishable
+        // "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."
+        // FIXME: Annotated types.
+        VERIFY(!is_union());
+        return *this;
+    }
+
+    // https://webidl.spec.whatwg.org/#idl-any
+    bool is_any() const { return is_plain() && m_name == "any"; }
+
+    // https://webidl.spec.whatwg.org/#idl-undefined
+    bool is_undefined() const { return is_plain() && m_name == "undefined"; }
+
+    // https://webidl.spec.whatwg.org/#idl-boolean
+    bool is_boolean() const { return is_plain() && m_name == "boolean"; }
+
+    // https://webidl.spec.whatwg.org/#idl-bigint
+    bool is_bigint() const { return is_plain() && m_name == "bigint"; }
+
+    // https://webidl.spec.whatwg.org/#idl-object
+    bool is_object() const { return is_plain() && m_name == "object"; }
+
+    // https://webidl.spec.whatwg.org/#idl-symbol
+    bool is_symbol() const { return is_plain() && m_name == "symbol"; }
+
+    bool is_string() const { return is_plain() && m_name.is_one_of("ByteString", "CSSOMString", "DOMString", "USVString"); }
 
     // https://webidl.spec.whatwg.org/#dfn-integer-type
-    bool is_integer() const { return m_name.is_one_of("byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"); }
+    bool is_integer() const { return is_plain() && m_name.is_one_of("byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"); }
 
     // https://webidl.spec.whatwg.org/#dfn-numeric-type
-    bool is_numeric() const { return is_integer() || m_name.is_one_of("float", "unrestricted float", "double", "unrestricted double"); }
+    bool is_numeric() const { return is_plain() && (is_integer() || m_name.is_one_of("float", "unrestricted float", "double", "unrestricted double")); }
 
     // https://webidl.spec.whatwg.org/#dfn-primitive-type
-    bool is_primitive() const { return is_numeric() || m_name.is_one_of("bigint", "boolean"); }
+    bool is_primitive() const { return is_plain() && (is_numeric() || is_boolean() || m_name == "bigint"); }
 
-    bool is_parameterized() const { return m_kind == Kind::Parameterized; }
-    ParameterizedType const& as_parameterized() const;
-    ParameterizedType& as_parameterized();
+    // https://webidl.spec.whatwg.org/#idl-sequence
+    bool is_sequence() const { return is_parameterized() && m_name == "sequence"; }
 
-    bool is_union() const { return m_kind == Kind::Union; }
-    UnionType const& as_union() const;
-    UnionType& as_union();
+    // https://webidl.spec.whatwg.org/#dfn-distinguishable
+    bool is_distinguishable_from(Type const& other) const;
 
 private:
     Kind m_kind;
@@ -349,27 +390,6 @@ public:
         return num_nullable_member_types;
     }
 
-    // https://webidl.spec.whatwg.org/#dfn-includes-a-nullable-type
-    bool includes_nullable_type() const
-    {
-        // -> the type is a union type and its number of nullable member types is 1.
-        return number_of_nullable_member_types() == 1;
-    }
-
-    // -> https://webidl.spec.whatwg.org/#dfn-includes-undefined
-    bool includes_undefined() const
-    {
-        // -> the type is a union type and one of its member types includes undefined.
-        for (auto& type : m_member_types) {
-            if (type.is_union() && type.as_union().includes_undefined())
-                return true;
-
-            if (type.name() == "undefined"sv)
-                return true;
-        }
-        return false;
-    }
-
 private:
     NonnullRefPtrVector<Type> m_member_types;
 };