Browse Source

LibIDL+LibWeb: Resolve distinguishing argument index at build time

Aside from the obvious performance benefits, this will allow us to
properly handle dictionary types. (whose dictionary-ness is only known
at build-time)

Much of the rest of the overload resolution algorithm steps can (and
should) be evaluated at build-time as well, but this is a good first
step.
Idan Horowitz 1 year ago
parent
commit
f837f02eea

+ 38 - 15
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -1925,7 +1925,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@@overload_suffi
 }
 
 // https://webidl.spec.whatwg.org/#compute-the-effective-overload-set
-static EffectiveOverloadSet compute_the_effective_overload_set(auto const& overload_set)
+static Vector<EffectiveOverloadSet::Item> compute_the_effective_overload_set(auto const& overload_set)
 {
     // 1. Let S be an ordered set.
     Vector<EffectiveOverloadSet::Item> overloads;
@@ -2048,7 +2048,7 @@ static EffectiveOverloadSet compute_the_effective_overload_set(auto const& overl
         overload_id++;
     }
 
-    return EffectiveOverloadSet { move(overloads) };
+    return overloads;
 }
 
 static DeprecatedString generate_constructor_for_idl_type(Type const& type)
@@ -2090,6 +2090,30 @@ static DeprecatedString generate_constructor_for_idl_type(Type const& type)
     VERIFY_NOT_REACHED();
 }
 
+// https://webidl.spec.whatwg.org/#dfn-distinguishing-argument-index
+static size_t resolve_distinguishing_argument_index(Vector<EffectiveOverloadSet::Item> const& items, size_t argument_count)
+{
+    for (auto argument_index = 0u; argument_index < argument_count; ++argument_index) {
+        bool found_indistinguishable = false;
+
+        for (auto first_item_index = 0u; first_item_index < items.size(); ++first_item_index) {
+            for (auto second_item_index = first_item_index + 1; second_item_index < items.size(); ++second_item_index) {
+                if (!items[first_item_index].types[argument_index]->is_distinguishable_from(items[second_item_index].types[argument_index])) {
+                    found_indistinguishable = true;
+                    break;
+                }
+            }
+            if (found_indistinguishable)
+                break;
+        }
+
+        if (!found_indistinguishable)
+            return argument_index;
+    }
+
+    VERIFY_NOT_REACHED();
+}
+
 static void generate_overload_arbiter(SourceGenerator& generator, auto const& overload_set, DeprecatedString const& class_name)
 {
     auto function_generator = generator.fork();
@@ -2102,8 +2126,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
     Optional<IDL::EffectiveOverloadSet> effective_overload_set;
 )~~~");
 
-    auto all_possible_effective_overloads = compute_the_effective_overload_set(overload_set);
-    auto overloads_set = all_possible_effective_overloads.items();
+    auto overloads_set = compute_the_effective_overload_set(overload_set);
     auto maximum_argument_count = 0u;
     for (auto const& overload : overloads_set)
         maximum_argument_count = max(maximum_argument_count, overload.types.size());
@@ -2115,29 +2138,28 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
     // Namely, since that discards any overloads that don't have the exact number of arguments that were given,
     // we simply only provide the overloads that do have that number of arguments.
     for (auto argument_count = 0u; argument_count <= maximum_argument_count; ++argument_count) {
-        // FIXME: Calculate the distinguishing argument index now instead of at runtime.
-
-        auto effective_overload_count = 0;
+        Vector<EffectiveOverloadSet::Item> effective_overload_set;
         for (auto const& overload : overloads_set) {
             if (overload.types.size() == argument_count)
-                effective_overload_count++;
+                effective_overload_set.append(overload);
         }
 
-        if (effective_overload_count == 0)
+        if (effective_overload_set.size() == 0)
             continue;
 
+        auto distinguishing_argument_index = 0u;
+        if (effective_overload_set.size() > 1)
+            distinguishing_argument_index = resolve_distinguishing_argument_index(effective_overload_set, argument_count);
+
         function_generator.set("current_argument_count", DeprecatedString::number(argument_count));
-        function_generator.set("overload_count", DeprecatedString::number(effective_overload_count));
+        function_generator.set("overload_count", DeprecatedString::number(effective_overload_set.size()));
         function_generator.appendln(R"~~~(
     case @current_argument_count@: {
         Vector<IDL::EffectiveOverloadSet::Item> overloads;
         overloads.ensure_capacity(@overload_count@);
 )~~~");
 
-        for (auto& overload : overloads_set) {
-            if (overload.types.size() != argument_count)
-                continue;
-
+        for (auto& overload : effective_overload_set) {
             StringBuilder types_builder;
             types_builder.append("Vector<NonnullRefPtr<IDL::Type const>> { "sv);
             StringBuilder optionality_builder;
@@ -2175,8 +2197,9 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
             function_generator.appendln("        overloads.empend(@overload.callable_id@, @overload.types@, @overload.optionality_values@);");
         }
 
+        function_generator.set("overload_set.distinguishing_argument_index", DeprecatedString::number(distinguishing_argument_index));
         function_generator.append(R"~~~(
-        effective_overload_set.emplace(move(overloads));
+        effective_overload_set.emplace(move(overloads), @overload_set.distinguishing_argument_index@);
         break;
     }
 )~~~");

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

@@ -293,30 +293,6 @@ bool Type::is_json(Interface const& interface) const
     return false;
 }
 
-// https://webidl.spec.whatwg.org/#dfn-distinguishing-argument-index
-int EffectiveOverloadSet::distinguishing_argument_index()
-{
-    for (auto argument_index = 0u; argument_index < m_argument_count; ++argument_index) {
-        bool found_indistinguishable = false;
-
-        for (auto first_item_index = 0u; first_item_index < m_items.size(); ++first_item_index) {
-            for (auto second_item_index = first_item_index + 1; second_item_index < m_items.size(); ++second_item_index) {
-                if (!m_items[first_item_index].types[argument_index]->is_distinguishable_from(m_items[second_item_index].types[argument_index])) {
-                    found_indistinguishable = true;
-                    break;
-                }
-            }
-            if (found_indistinguishable)
-                break;
-        }
-
-        if (!found_indistinguishable)
-            return argument_index;
-    }
-
-    VERIFY_NOT_REACHED();
-}
-
 void EffectiveOverloadSet::remove_all_other_entries()
 {
     Vector<Item> new_items;

+ 4 - 4
Userland/Libraries/LibIDL/Types.h

@@ -415,9 +415,9 @@ public:
         Vector<Optionality> optionality_values;
     };
 
-    EffectiveOverloadSet(Vector<Item> items)
+    EffectiveOverloadSet(Vector<Item> items, size_t distinguishing_argument_index)
         : m_items(move(items))
-        , m_argument_count(m_items.is_empty() ? 0 : m_items.first().types.size())
+        , m_distinguishing_argument_index(distinguishing_argument_index)
     {
     }
 
@@ -433,7 +433,7 @@ public:
     bool is_empty() const { return m_items.is_empty(); }
     size_t size() const { return m_items.size(); }
 
-    int distinguishing_argument_index();
+    size_t distinguishing_argument_index() const { return m_distinguishing_argument_index; }
 
     template<typename Matches>
     bool has_overload_with_matching_argument_at_index(size_t index, Matches matches)
@@ -454,7 +454,7 @@ public:
 private:
     // FIXME: This should be an "ordered set".
     Vector<Item> m_items;
-    size_t m_argument_count;
+    size_t m_distinguishing_argument_index { 0 };
 
     Optional<size_t> m_last_matching_item_index;
 };

+ 2 - 0
Userland/Libraries/LibWeb/WebIDL/OverloadResolution.cpp

@@ -56,6 +56,8 @@ static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOv
 // https://webidl.spec.whatwg.org/#es-overloads
 JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::EffectiveOverloadSet& overloads)
 {
+    // FIXME: The vast majority of this algorithm can be (and must be, in order to resolve the dictionary
+    //  related FIXMEs below) evaluated at code-generation time.
     // 1. Let maxarg be the length of the longest type list of the entries in S.
     // 2. Let n be the size of args.
     // 3. Initialize argcount to be min(maxarg, n).