Serenity Operating System
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}