Serenity Operating System
at master 435 lines 22 kB view raw
1/* 2 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibJS/Runtime/ArrayBuffer.h> 8#include <LibJS/Runtime/DataView.h> 9#include <LibJS/Runtime/FunctionObject.h> 10#include <LibJS/Runtime/TypedArray.h> 11#include <LibJS/Runtime/Value.h> 12#include <LibWeb/Bindings/PlatformObject.h> 13#include <LibWeb/WebIDL/OverloadResolution.h> 14 15namespace Web::WebIDL { 16 17// https://webidl.spec.whatwg.org/#dfn-convert-ecmascript-to-idl-value 18static JS::Value convert_ecmascript_type_to_idl_value(JS::Value value, IDL::Type const&) 19{ 20 // FIXME: We have this code already in the code generator, in `generate_to_cpp()`, but how do we use it here? 21 return value; 22} 23 24template<typename Match> 25static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOverloadSet& overloads, size_t argument_index, Match match) 26{ 27 // NOTE: This is to save some repetition. 28 // Almost every sub-step of step 12 of the overload resolution algorithm matches overloads with an argument that is: 29 // - One of several specific types. 30 // - "an annotated type whose inner type is one of the above types" 31 // - "a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types" 32 // So, this function lets you pass in the first check, and handles the others automatically. 33 34 return overloads.has_overload_with_matching_argument_at_index(argument_index, 35 [match](IDL::Type const& type, auto) { 36 if (match(type)) 37 return true; 38 39 // FIXME: - an annotated type whose inner type is one of the above types 40 41 if (type.is_union()) { 42 auto flattened_members = type.as_union().flattened_member_types(); 43 for (auto const& member : flattened_members) { 44 if (match(member)) 45 return true; 46 47 // FIXME: - an annotated type whose inner type is one of the above types 48 } 49 return false; 50 } 51 52 return false; 53 }); 54} 55 56// https://webidl.spec.whatwg.org/#es-overloads 57JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::EffectiveOverloadSet& overloads) 58{ 59 // 1. Let maxarg be the length of the longest type list of the entries in S. 60 // 2. Let n be the size of args. 61 // 3. Initialize argcount to be min(maxarg, n). 62 // 4. Remove from S all entries whose type list is not of length argcount. 63 // NOTE: Our caller already performs these steps, so our effective overload set only contains overloads with the correct number of arguments. 64 int argument_count = vm.argument_count(); 65 66 // 5. If S is empty, then throw a TypeError. 67 if (overloads.is_empty()) 68 return vm.throw_completion<JS::TypeError>(JS::ErrorType::OverloadResolutionFailed); 69 70 // 6. Initialize d to −1. 71 auto distinguishing_argument_index = -1; 72 73 // 7. Initialize method to undefined. 74 Optional<JS::FunctionObject&> method; 75 76 // 8. If there is more than one entry in S, then set d to be the distinguishing argument index for the entries of S. 77 if (overloads.size() > 1) 78 distinguishing_argument_index = overloads.distinguishing_argument_index(); 79 80 // 9. Initialize values to be an empty list, where each entry will be either an IDL value or the special value “missing”. 81 Vector<ResolvedOverload::Argument> values; 82 83 // 10. Initialize i to 0. 84 auto i = 0; 85 86 // 11. While i < d: 87 while (i < distinguishing_argument_index) { 88 // 1. Let V be args[i]. 89 auto const& value = vm.argument(i); 90 91 auto const& item = overloads.items().first(); 92 93 // 2. Let type be the type at index i in the type list of any entry in S. 94 auto const& type = item.types[i]; 95 96 // 3. Let optionality be the value at index i in the list of optionality values of any entry in S. 97 auto const& optionality = item.optionality_values[i]; 98 99 // 4. If optionality is “optional” and V is undefined, then: 100 if (optionality == IDL::Optionality::Optional && value.is_undefined()) { 101 // FIXME: 1. If the argument at index i is declared with a default value, then append to values that default value. 102 103 // 2. Otherwise, append to values the special value “missing”. 104 values.empend(ResolvedOverload::Missing {}); 105 } 106 107 // 5. Otherwise, append to values the result of converting V to IDL type type. 108 values.empend(convert_ecmascript_type_to_idl_value(value, type)); 109 110 // 6. Set i to i + 1. 111 ++i; 112 } 113 114 // 12. If i = d, then: 115 if (i == distinguishing_argument_index) { 116 // 1. Let V be args[i]. 117 auto const& value = vm.argument(i); 118 119 // 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. 120 if (value.is_undefined() 121 && overloads.has_overload_with_matching_argument_at_index(i, [](auto&, IDL::Optionality const& optionality) { return optionality == IDL::Optionality::Optional; })) { 122 overloads.remove_all_other_entries(); 123 } 124 125 // 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, 126 // - a nullable type 127 // - a dictionary type 128 // - an annotated type whose inner type is one of the above types 129 // - a union type or annotated union type that includes a nullable type or that has a dictionary type in its flattened members 130 // then remove from S all other entries. 131 // NOTE: This is the one case we can't use `has_overload_with_argument_type_or_subtype_matching()` because we also need to look 132 // for dictionary types in the flattened members. 133 else if ((value.is_undefined() || value.is_null()) 134 && overloads.has_overload_with_matching_argument_at_index(i, [](IDL::Type const& type, auto) { 135 if (type.is_nullable()) 136 return true; 137 // FIXME: - a dictionary type 138 // FIXME: - an annotated type whose inner type is one of the above types 139 if (type.is_union()) { 140 auto flattened_members = type.as_union().flattened_member_types(); 141 for (auto const& member : flattened_members) { 142 if (member->is_nullable()) 143 return true; 144 // FIXME: - a dictionary type 145 // FIXME: - an annotated type whose inner type is one of the above types 146 } 147 return false; 148 } 149 return false; 150 })) { 151 overloads.remove_all_other_entries(); 152 } 153 154 // 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, 155 // - an interface type that V implements 156 // - object 157 // - a nullable version of any of the above types 158 // - an annotated type whose inner type is one of the above types 159 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 160 // then remove from S all other entries. 161 else if (value.is_object() && is<Bindings::PlatformObject>(value.as_object()) 162 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [value](IDL::Type const& type) { 163 // - an interface type that V implements 164 if (static_cast<Bindings::PlatformObject const&>(value.as_object()).implements_interface(type.name())) 165 return true; 166 167 // - object 168 if (type.is_object()) 169 return true; 170 171 return false; 172 })) { 173 overloads.remove_all_other_entries(); 174 } 175 176 // 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, 177 // - ArrayBuffer 178 // - object 179 // - a nullable version of either of the above types 180 // - an annotated type whose inner type is one of the above types 181 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 182 // then remove from S all other entries. 183 else if (value.is_object() && is<JS::ArrayBuffer>(value.as_object()) 184 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { 185 if (type.is_plain() && type.name() == "ArrayBuffer") 186 return true; 187 if (type.is_object()) 188 return true; 189 return false; 190 })) { 191 overloads.remove_all_other_entries(); 192 } 193 194 // 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, 195 // - DataView 196 // - object 197 // - a nullable version of either of the above types 198 // - an annotated type whose inner type is one of the above types 199 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 200 // then remove from S all other entries. 201 else if (value.is_object() && is<JS::DataView>(value.as_object()) 202 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { 203 if (type.is_plain() && type.name() == "DataView") 204 return true; 205 if (type.is_object()) 206 return true; 207 return false; 208 })) { 209 overloads.remove_all_other_entries(); 210 } 211 212 // 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, 213 // - a typed array type whose name is equal to the value of V’s [[TypedArrayName]] internal slot 214 // - object 215 // - a nullable version of either of the above types 216 // - an annotated type whose inner type is one of the above types 217 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 218 // then remove from S all other entries. 219 else if (value.is_object() && value.as_object().is_typed_array() 220 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [&](IDL::Type const& type) { 221 if (type.is_plain() && type.name() == static_cast<JS::TypedArrayBase const&>(value.as_object()).element_name()) 222 return true; 223 if (type.is_object()) 224 return true; 225 return false; 226 })) { 227 overloads.remove_all_other_entries(); 228 } 229 230 // 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, 231 // - a callback function type 232 // - object 233 // - a nullable version of any of the above types 234 // - an annotated type whose inner type is one of the above types 235 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 236 // then remove from S all other entries. 237 else if (value.is_function() 238 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { 239 // FIXME: - a callback function type 240 if (type.is_object()) 241 return true; 242 return false; 243 })) { 244 overloads.remove_all_other_entries(); 245 } 246 247 // 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, 248 // - a sequence type 249 // - a frozen array type 250 // - a nullable version of any of the above types 251 // - an annotated type whose inner type is one of the above types 252 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 253 // and after performing the following steps, 254 // { 255 // 1. Let method be ? GetMethod(V, @@iterator). 256 // } 257 // method is not undefined, then remove from S all other entries. 258 259 // 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, 260 // - a callback interface type 261 // - a dictionary type 262 // - a record type 263 // - object 264 // - a nullable version of any of the above types 265 // - an annotated type whose inner type is one of the above types 266 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 267 // then remove from S all other entries. 268 else if (value.is_object() 269 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { 270 // FIXME: - a callback interface type 271 // FIXME: - a dictionary type 272 // FIXME: - a record type 273 if (type.is_object()) 274 return true; 275 return false; 276 })) { 277 overloads.remove_all_other_entries(); 278 } 279 280 // 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, 281 // - boolean 282 // - a nullable boolean 283 // - an annotated type whose inner type is one of the above types 284 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 285 // then remove from S all other entries. 286 else if (value.is_boolean() 287 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_boolean(); })) { 288 overloads.remove_all_other_entries(); 289 } 290 291 // 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, 292 // - a numeric type 293 // - a nullable numeric type 294 // - an annotated type whose inner type is one of the above types 295 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 296 // then remove from S all other entries. 297 else if (value.is_number() 298 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_numeric(); })) { 299 overloads.remove_all_other_entries(); 300 } 301 302 // 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, 303 // - bigint 304 // - a nullable bigint 305 // - an annotated type whose inner type is one of the above types 306 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 307 // then remove from S all other entries. 308 else if (value.is_bigint() 309 && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_bigint(); })) { 310 overloads.remove_all_other_entries(); 311 } 312 313 // 14. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, 314 // - a string type 315 // - a nullable version of any of the above types 316 // - an annotated type whose inner type is one of the above types 317 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 318 // then remove from S all other entries. 319 else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_string(); })) { 320 overloads.remove_all_other_entries(); 321 } 322 323 // 15. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, 324 // - a numeric type 325 // - a nullable numeric type 326 // - an annotated type whose inner type is one of the above types 327 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 328 // then remove from S all other entries. 329 else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_numeric(); })) { 330 overloads.remove_all_other_entries(); 331 } 332 333 // 16. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, 334 // - boolean 335 // - a nullable boolean 336 // - an annotated type whose inner type is one of the above types 337 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 338 // then remove from S all other entries. 339 else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_boolean(); })) { 340 overloads.remove_all_other_entries(); 341 } 342 343 // 17. Otherwise: if there is an entry in S that has one of the following types at position i of its type list, 344 // - bigint 345 // - a nullable bigint 346 // - an annotated type whose inner type is one of the above types 347 // - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types 348 // then remove from S all other entries. 349 else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_bigint(); })) { 350 overloads.remove_all_other_entries(); 351 } 352 353 // 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. 354 else if (overloads.has_overload_with_matching_argument_at_index(i, [](auto const& type, auto) { return type->is_any(); })) { 355 overloads.remove_all_other_entries(); 356 } 357 358 // 19. Otherwise: throw a TypeError. 359 else { 360 // FIXME: Remove this message once all the above sub-steps are implemented. 361 dbgln("Failed to determine IDL overload. (Probably because of unimplemented steps.)"); 362 return vm.throw_completion<JS::TypeError>(JS::ErrorType::OverloadResolutionFailed); 363 } 364 } 365 366 // 13. Let callable be the operation or extended attribute of the single entry in S. 367 auto const& callable = overloads.only_item(); 368 369 // 14. If i = d and method is not undefined, then 370 if (i == distinguishing_argument_index && method.has_value()) { 371 // 1. Let V be args[i]. 372 auto const& value = vm.argument(i); 373 374 // 2. Let T be the type at index i in the type list of the remaining entry in S. 375 auto const& type = overloads.only_item().types[i]; 376 377 (void)value; 378 (void)type; 379 // 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. 380 381 // 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. 382 383 // 5. Set i to i + 1. 384 ++i; 385 } 386 387 // 15. While i < argcount: 388 while (i < argument_count) { 389 // 1. Let V be args[i]. 390 auto const& value = vm.argument(i); 391 392 // 2. Let type be the type at index i in the type list of the remaining entry in S. 393 auto const& entry = overloads.only_item(); 394 auto const& type = entry.types[i]; 395 396 // 3. Let optionality be the value at index i in the list of optionality values of the remaining entry in S. 397 auto const& optionality = entry.optionality_values[i]; 398 399 // 4. If optionality is “optional” and V is undefined, then: 400 if (optionality == IDL::Optionality::Optional && value.is_undefined()) { 401 // FIXME: 1. If the argument at index i is declared with a default value, then append to values that default value. 402 403 // 2. Otherwise, append to values the special value “missing”. 404 values.empend(ResolvedOverload::Missing {}); 405 } 406 407 // 5. Otherwise, append to values the result of converting V to IDL type type. 408 else { 409 values.append(convert_ecmascript_type_to_idl_value(value, type)); 410 } 411 412 // 6. Set i to i + 1. 413 ++i; 414 } 415 416 // 16. While i is less than the number of arguments callable is declared to take: 417 while (i < static_cast<int>(callable.types.size())) { 418 // FIXME: 1. If callable’s argument at index i is declared with a default value, then append to values that default value. 419 if (false) { 420 } 421 422 // 2. Otherwise, if callable’s argument at index i is not variadic, then append to values the special value “missing”. 423 else if (callable.optionality_values[i] != IDL::Optionality::Variadic) { 424 values.empend(ResolvedOverload::Missing {}); 425 } 426 427 // 3. Set i to i + 1. 428 ++i; 429 } 430 431 // 17. Return the pair <callable, values>. 432 return ResolvedOverload { callable.callable_id, move(values) }; 433} 434 435}