2022-09-08 17:25:53 +01:00
/*
* Copyright ( c ) 2022 , Sam Atkins < atkinssj @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/ArrayBuffer.h>
# include <LibJS/Runtime/DataView.h>
# include <LibJS/Runtime/FunctionObject.h>
# include <LibJS/Runtime/TypedArray.h>
# include <LibJS/Runtime/Value.h>
# include <LibWeb/Bindings/PlatformObject.h>
2022-09-24 16:18:31 +01:00
# include <LibWeb/WebIDL/OverloadResolution.h>
2022-09-08 17:25:53 +01:00
2022-09-24 16:18:31 +01:00
namespace Web : : WebIDL {
2022-09-08 17:25:53 +01:00
// 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 < typename Match >
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 < ResolvedOverload > resolve_overload ( JS : : VM & vm , IDL : : EffectiveOverloadSet & overloads )
{
2023-11-10 23:38:59 +02:00
// 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.
2022-09-08 17:25:53 +01:00
// 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.
2023-05-06 22:09:16 -04:00
// NOTE: The IDL-generated callers already only provide an overload set containing overloads with the correct number
// of arguments. Therefore, we do not need to remove any entry from that set here. However, we do need to handle
// when the number of user-provided arguments exceeds the overload set's argument count.
int argument_count = min ( vm . argument_count ( ) , overloads . is_empty ( ) ? 0 : overloads . items ( ) [ 0 ] . types . size ( ) ) ;
2022-09-08 17:25:53 +01:00
// 5. If S is empty, then throw a TypeError.
if ( overloads . is_empty ( ) )
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : OverloadResolutionFailed ) ;
// 6. Initialize d to − 1.
auto distinguishing_argument_index = - 1 ;
// 7. Initialize method to undefined.
Optional < JS : : FunctionObject & > 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 < ResolvedOverload : : Argument > 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 ) {
2023-03-06 14:17:01 +01:00
if ( member - > is_nullable ( ) )
2022-09-08 17:25:53 +01:00
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 < Bindings : : PlatformObject > ( value . as_object ( ) )
2022-11-29 14:45:04 +01:00
& & has_overload_with_argument_type_or_subtype_matching ( overloads , i , [ value ] ( IDL : : Type const & type ) {
// - an interface type that V implements
2023-12-24 15:48:00 +13:00
if ( static_cast < Bindings : : PlatformObject const & > ( value . as_object ( ) ) . implements_interface ( MUST ( String : : from_byte_string ( type . name ( ) ) ) ) )
2022-11-29 14:45:04 +01:00
return true ;
// - object
2022-09-08 17:25:53 +01:00
if ( type . is_object ( ) )
return true ;
2022-11-29 14:45:04 +01:00
2022-09-08 17:25:53 +01:00
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 < JS : : ArrayBuffer > ( value . as_object ( ) )
& & has_overload_with_argument_type_or_subtype_matching ( overloads , i , [ ] ( IDL : : Type const & type ) {
2023-11-14 02:43:44 +00:00
if ( type . is_plain ( ) & & ( type . name ( ) = = " ArrayBuffer " | | type . name ( ) = = " BufferSource " ) )
2022-09-08 17:25:53 +01:00
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 < JS : : DataView > ( value . as_object ( ) )
& & has_overload_with_argument_type_or_subtype_matching ( overloads , i , [ ] ( IDL : : Type const & type ) {
2023-11-14 02:43:44 +00:00
if ( type . is_plain ( ) & & ( type . name ( ) = = " DataView " | | type . name ( ) = = " BufferSource " ) )
2022-09-08 17:25:53 +01:00
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 ) {
2023-11-14 02:43:44 +00:00
if ( type . is_plain ( ) & & ( type . name ( ) = = static_cast < JS : : TypedArrayBase const & > ( value . as_object ( ) ) . element_name ( ) | | type . name ( ) = = " BufferSource " ) )
2022-09-08 17:25:53 +01:00
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 ) {
2024-07-24 16:21:23 +09:00
dbgln ( " FIXME: a callback interface type " ) ;
dbgln ( " FIXME: a dictionary type " ) ;
dbgln ( " FIXME: a record type " ) ;
2022-09-08 17:25:53 +01:00
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.
2023-03-06 14:17:01 +01:00
else if ( overloads . has_overload_with_matching_argument_at_index ( i , [ ] ( auto const & type , auto ) { return type - > is_any ( ) ; } ) ) {
2022-09-08 17:25:53 +01:00
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 : : TypeError > ( 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 < int > ( 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 <callable, values>.
return ResolvedOverload { callable . callable_id , move ( values ) } ;
}
}