/* * Copyright (c) 2022, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace Web::WebIDL { // https://webidl.spec.whatwg.org/#dfn-convert-ecmascript-to-idl-value static JS::Value convert_ecmascript_type_to_idl_value(JS::Value value, IDL::Type const&) { // FIXME: We have this code already in the code generator, in `generate_to_cpp()`, but how do we use it here? return value; } template static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOverloadSet& overloads, size_t argument_index, Match match) { // NOTE: This is to save some repetition. // Almost every sub-step of step 12 of the overload resolution algorithm matches overloads with an argument that is: // - One of several specific types. // - "an annotated type whose inner type is one of the above types" // - "a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types" // So, this function lets you pass in the first check, and handles the others automatically. return overloads.has_overload_with_matching_argument_at_index(argument_index, [match](IDL::Type const& type, auto) { if (match(type)) return true; // FIXME: - an annotated type whose inner type is one of the above types if (type.is_union()) { auto flattened_members = type.as_union().flattened_member_types(); for (auto const& member : flattened_members) { if (match(member)) return true; // FIXME: - an annotated type whose inner type is one of the above types } return false; } return false; }); } // https://webidl.spec.whatwg.org/#es-overloads JS::ThrowCompletionOr resolve_overload(JS::VM& vm, IDL::EffectiveOverloadSet& overloads) { // 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). // 4. Remove from S all entries whose type list is not of length argcount. // NOTE: Our caller already performs these steps, so our effective overload set only contains overloads with the correct number of arguments. int argument_count = vm.argument_count(); // 5. If S is empty, then throw a TypeError. if (overloads.is_empty()) return vm.throw_completion(JS::ErrorType::OverloadResolutionFailed); // 6. Initialize d to −1. auto distinguishing_argument_index = -1; // 7. Initialize method to undefined. Optional method; // 8. If there is more than one entry in S, then set d to be the distinguishing argument index for the entries of S. if (overloads.size() > 1) distinguishing_argument_index = overloads.distinguishing_argument_index(); // 9. Initialize values to be an empty list, where each entry will be either an IDL value or the special value “missing”. Vector values; // 10. Initialize i to 0. auto i = 0; // 11. While i < d: while (i < distinguishing_argument_index) { // 1. Let V be args[i]. auto const& value = vm.argument(i); auto const& item = overloads.items().first(); // 2. Let type be the type at index i in the type list of any entry in S. auto const& type = item.types[i]; // 3. Let optionality be the value at index i in the list of optionality values of any entry in S. auto const& optionality = item.optionality_values[i]; // 4. If optionality is “optional” and V is undefined, then: if (optionality == IDL::Optionality::Optional && value.is_undefined()) { // FIXME: 1. If the argument at index i is declared with a default value, then append to values that default value. // 2. Otherwise, append to values the special value “missing”. values.empend(ResolvedOverload::Missing {}); } // 5. Otherwise, append to values the result of converting V to IDL type type. values.empend(convert_ecmascript_type_to_idl_value(value, type)); // 6. Set i to i + 1. ++i; } // 12. If i = d, then: if (i == distinguishing_argument_index) { // 1. Let V be args[i]. auto const& value = vm.argument(i); // 2. If V is undefined, and there is an entry in S whose list of optionality values has “optional” at index i, then remove from S all other entries. if (value.is_undefined() && overloads.has_overload_with_matching_argument_at_index(i, [](auto&, IDL::Optionality const& optionality) { return optionality == IDL::Optionality::Optional; })) { overloads.remove_all_other_entries(); } // 3. Otherwise: if V is null or undefined, and there is an entry in S that has one of the following types at position i of its type list, // - a nullable type // - a dictionary type // - an annotated type whose inner type is one of the above types // - a union type or annotated union type that includes a nullable type or that has a dictionary type in its flattened members // then remove from S all other entries. // NOTE: This is the one case we can't use `has_overload_with_argument_type_or_subtype_matching()` because we also need to look // for dictionary types in the flattened members. else if ((value.is_undefined() || value.is_null()) && overloads.has_overload_with_matching_argument_at_index(i, [](IDL::Type const& type, auto) { if (type.is_nullable()) return true; // FIXME: - a dictionary type // FIXME: - an annotated type whose inner type is one of the above types if (type.is_union()) { auto flattened_members = type.as_union().flattened_member_types(); for (auto const& member : flattened_members) { if (member.is_nullable()) return true; // FIXME: - a dictionary type // FIXME: - an annotated type whose inner type is one of the above types } return false; } return false; })) { overloads.remove_all_other_entries(); } // 4. Otherwise: if V is a platform object, and there is an entry in S that has one of the following types at position i of its type list, // - an interface type that V implements // - object // - a nullable version of any of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_object() && is(value.as_object()) && has_overload_with_argument_type_or_subtype_matching(overloads, i, [value](IDL::Type const& type) { // - an interface type that V implements if (static_cast(value.as_object()).implements_interface(type.name())) return true; // - object if (type.is_object()) return true; return false; })) { overloads.remove_all_other_entries(); } // 5. Otherwise: if Type(V) is Object, V has an [[ArrayBufferData]] internal slot, and there is an entry in S that has one of the following types at position i of its type list, // - ArrayBuffer // - object // - a nullable version of either of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_object() && is(value.as_object()) && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { if (type.is_plain() && type.name() == "ArrayBuffer") return true; if (type.is_object()) return true; return false; })) { overloads.remove_all_other_entries(); } // 6. Otherwise: if Type(V) is Object, V has a [[DataView]] internal slot, and there is an entry in S that has one of the following types at position i of its type list, // - DataView // - object // - a nullable version of either of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_object() && is(value.as_object()) && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { if (type.is_plain() && type.name() == "DataView") return true; if (type.is_object()) return true; return false; })) { overloads.remove_all_other_entries(); } // 7. Otherwise: if Type(V) is Object, V has a [[TypedArrayName]] internal slot, and there is an entry in S that has one of the following types at position i of its type list, // - a typed array type whose name is equal to the value of V’s [[TypedArrayName]] internal slot // - object // - a nullable version of either of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_object() && value.as_object().is_typed_array() && has_overload_with_argument_type_or_subtype_matching(overloads, i, [&](IDL::Type const& type) { if (type.is_plain() && type.name() == static_cast(value.as_object()).element_name()) return true; if (type.is_object()) return true; return false; })) { overloads.remove_all_other_entries(); } // 8. Otherwise: if IsCallable(V) is true, and there is an entry in S that has one of the following types at position i of its type list, // - a callback function type // - object // - a nullable version of any of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_function() && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { // FIXME: - a callback function type if (type.is_object()) return true; return false; })) { overloads.remove_all_other_entries(); } // FIXME: 9. Otherwise: if Type(V) is Object and there is an entry in S that has one of the following types at position i of its type list, // - a sequence type // - a frozen array type // - a nullable version of any of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // and after performing the following steps, // { // 1. Let method be ? GetMethod(V, @@iterator). // } // method is not undefined, then remove from S all other entries. // 10. Otherwise: if Type(V) is Object and there is an entry in S that has one of the following types at position i of its type list, // - a callback interface type // - a dictionary type // - a record type // - object // - a nullable version of any of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_object() && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { // FIXME: - a callback interface type // FIXME: - a dictionary type // FIXME: - a record type if (type.is_object()) return true; return false; })) { overloads.remove_all_other_entries(); } // 11. Otherwise: if Type(V) is Boolean and there is an entry in S that has one of the following types at position i of its type list, // - boolean // - a nullable boolean // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_boolean() && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_boolean(); })) { overloads.remove_all_other_entries(); } // 12. Otherwise: if Type(V) is Number and there is an entry in S that has one of the following types at position i of its type list, // - a numeric type // - a nullable numeric type // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_number() && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_numeric(); })) { overloads.remove_all_other_entries(); } // 13. Otherwise: if Type(V) is BigInt and there is an entry in S that has one of the following types at position i of its type list, // - bigint // - a nullable bigint // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (value.is_bigint() && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_bigint(); })) { overloads.remove_all_other_entries(); } // 14. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, // - a string type // - a nullable version of any of the above types // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_string(); })) { overloads.remove_all_other_entries(); } // 15. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, // - a numeric type // - a nullable numeric type // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_numeric(); })) { overloads.remove_all_other_entries(); } // 16. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, // - boolean // - a nullable boolean // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_boolean(); })) { overloads.remove_all_other_entries(); } // 17. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, // - bigint // - a nullable bigint // - an annotated type whose inner type is one of the above types // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types // then remove from S all other entries. else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_bigint(); })) { overloads.remove_all_other_entries(); } // 18. Otherwise: if there is an entry in S that has any at position i of its type list, then remove from S all other entries. else if (overloads.has_overload_with_matching_argument_at_index(i, [](auto const& type, auto) { return type.is_any(); })) { overloads.remove_all_other_entries(); } // 19. Otherwise: throw a TypeError. else { // FIXME: Remove this message once all the above sub-steps are implemented. dbgln("Failed to determine IDL overload. (Probably because of unimplemented steps.)"); return vm.throw_completion(JS::ErrorType::OverloadResolutionFailed); } } // 13. Let callable be the operation or extended attribute of the single entry in S. auto const& callable = overloads.only_item(); // 14. If i = d and method is not undefined, then if (i == distinguishing_argument_index && method.has_value()) { // 1. Let V be args[i]. auto const& value = vm.argument(i); // 2. Let T be the type at index i in the type list of the remaining entry in S. auto const& type = overloads.only_item().types[i]; (void)value; (void)type; // FIXME: 3. If T is a sequence type, then append to values the result of creating a sequence of type T from V and method. // FIXME: 4. Otherwise, T is a frozen array type. Append to values the result of creating a frozen array of type T from V and method. // 5. Set i to i + 1. ++i; } // 15. While i < argcount: while (i < argument_count) { // 1. Let V be args[i]. auto const& value = vm.argument(i); // 2. Let type be the type at index i in the type list of the remaining entry in S. auto const& entry = overloads.only_item(); auto const& type = entry.types[i]; // 3. Let optionality be the value at index i in the list of optionality values of the remaining entry in S. auto const& optionality = entry.optionality_values[i]; // 4. If optionality is “optional” and V is undefined, then: if (optionality == IDL::Optionality::Optional && value.is_undefined()) { // FIXME: 1. If the argument at index i is declared with a default value, then append to values that default value. // 2. Otherwise, append to values the special value “missing”. values.empend(ResolvedOverload::Missing {}); } // 5. Otherwise, append to values the result of converting V to IDL type type. else { values.append(convert_ecmascript_type_to_idl_value(value, type)); } // 6. Set i to i + 1. ++i; } // 16. While i is less than the number of arguments callable is declared to take: while (i < static_cast(callable.types.size())) { // FIXME: 1. If callable’s argument at index i is declared with a default value, then append to values that default value. if (false) { } // 2. Otherwise, if callable’s argument at index i is not variadic, then append to values the special value “missing”. else if (callable.optionality_values[i] != IDL::Optionality::Variadic) { values.empend(ResolvedOverload::Missing {}); } // 3. Set i to i + 1. ++i; } // 17. Return the pair . return ResolvedOverload { callable.callable_id, move(values) }; } }