@@ -1262,7 +1262,8 @@ public Expression<Func<JSCallbackDescriptor>> BuildConstructorOverloadDescriptor
12621262 // TODO: Default parameter values
12631263
12641264 Type[ ] parameterTypes = constructors[ i] . GetParameters( )
1265- . Select( p => p. ParameterType) . ToArray( ) ;
1265+ . Select( p => EnsureObjectCollectionTypeForOverloadResolution( p. ParameterType) )
1266+ . ToArray( ) ;
12661267 statements[ i + 1 ] = Expression. Assign(
12671268 Expression. ArrayAccess( overloadsVariable, Expression. Constant( i) ) ,
12681269 Expression. New(
@@ -1353,7 +1354,8 @@ public Expression<Func<JSCallbackDescriptor>> BuildMethodOverloadDescriptorExpre
13531354 // TODO: Default parameter values
13541355
13551356 Type [ ] parameterTypes = methods[ i ] . GetParameters ( )
1356- . Select ( p => p . ParameterType ) . ToArray ( ) ;
1357+ . Select ( p => EnsureObjectCollectionTypeForOverloadResolution ( p . ParameterType ) )
1358+ . ToArray ( ) ;
13571359 statements [ i + 1 ] = Expression . Assign (
13581360 Expression . ArrayAccess ( overloadsVariable , Expression . Constant ( i ) ) ,
13591361 Expression . New (
@@ -1381,6 +1383,77 @@ public Expression<Func<JSCallbackDescriptor>> BuildMethodOverloadDescriptorExpre
13811383 Array . Empty < ParameterExpression > ( ) ) ;
13821384 }
13831385
1386+ /// <summary>
1387+ /// For purposes of overload resolution, convert non-generic collections and collections
1388+ /// with specific generic element types to collection interfaces with object element types.
1389+ /// This simplifies the checks required during overload resolution and avoids the need
1390+ /// for reflection at resolution time, which is not supported in AOT. (The JS collections
1391+ /// will still be marshalled to the more specific collection type after resolution when
1392+ /// an overload is invoked.)
1393+ /// </summary>
1394+ private static Type EnsureObjectCollectionTypeForOverloadResolution( Type type )
1395+ {
1396+ if ( typeof ( System . Collections . IEnumerable ) . IsAssignableFrom ( type ) &&
1397+ ! type . IsArray && type != typeof ( string ) )
1398+ {
1399+ if ( TypeImplementsGenericInterface ( type , typeof ( IDictionary < , > ) ) ||
1400+ TypeImplementsGenericInterface ( type , typeof ( IReadOnlyDictionary < , > ) ) ||
1401+ typeof ( System . Collections . IDictionary ) . IsAssignableFrom ( type ) )
1402+ {
1403+ type = typeof ( IDictionary < object , object > ) ;
1404+ }
1405+ else if ( TypeImplementsGenericInterface ( type , typeof ( IList < > ) ) ||
1406+ TypeImplementsGenericInterface ( type , typeof ( IReadOnlyList < > ) ) ||
1407+ typeof ( System . Collections . IList ) . IsAssignableFrom ( type ) )
1408+ {
1409+ type = typeof ( IList < object > ) ;
1410+ }
1411+ else if ( TypeImplementsGenericInterface ( type , typeof ( ISet < > ) )
1412+ #if READONLY_SET
1413+ || TypeImplementsGenericInterface ( type , typeof ( IReadOnlySet < > ) )
1414+ #endif
1415+ )
1416+ {
1417+ type = typeof ( ISet < object > ) ;
1418+ }
1419+ else if ( TypeImplementsGenericInterface ( type , typeof ( ICollection < > ) ) ||
1420+ TypeImplementsGenericInterface ( type , typeof ( IReadOnlyCollection < > ) ) )
1421+ {
1422+ type = typeof ( ICollection < object > ) ;
1423+ }
1424+ else
1425+ {
1426+ type = typeof ( IEnumerable < object > ) ;
1427+ }
1428+ }
1429+ else if ( TypeImplementsGenericInterface ( type , typeof ( IAsyncEnumerable < > ) ) )
1430+ {
1431+ type = typeof ( IAsyncEnumerable < object > ) ;
1432+ }
1433+
1434+ return type;
1435+ }
1436+
1437+ private static bool TypeImplementsGenericInterface( Type type , Type genericInterface )
1438+ {
1439+ if ( type . IsInterface && type . IsGenericType &&
1440+ type . GetGenericTypeDefinition ( ) == genericInterface )
1441+ {
1442+ return true;
1443+ }
1444+
1445+ foreach ( Type interfaceType in type . GetInterfaces ( ) )
1446+ {
1447+ if ( interfaceType . IsGenericType &&
1448+ interfaceType . GetGenericTypeDefinition ( ) == genericInterface )
1449+ {
1450+ return true;
1451+ }
1452+ }
1453+
1454+ return false;
1455+ }
1456+
13841457 /// <summary>
13851458 /// Gets overload information for a set of overloaded methods.
13861459 /// </summary>
@@ -2968,71 +3041,114 @@ private IEnumerable<Expression> BuildFromJSToCollectionInterfaceExpressions(
29683041 Type elementType = toType . GenericTypeArguments [ 0 ] ;
29693042 Type typeDefinition = toType . GetGenericTypeDefinition ( ) ;
29703043
2971- if ( typeDefinition == typeof ( IList < > ) ||
2972- typeDefinition == typeof ( ICollection < > ) ||
3044+ if ( typeDefinition == typeof ( IEnumerable < > ) ||
3045+ typeDefinition == typeof ( IAsyncEnumerable < > ) ||
3046+ typeDefinition == typeof ( ISet < > ) ||
29733047#if READONLY_SET
29743048 typeDefinition == typeof ( IReadOnlySet < > ) ||
29753049#else
29763050 // TODO: Support IReadOnlySet on .NET Framework / .NET Standard 2.0.
29773051#endif
2978- typeDefinition == typeof ( ISet < > ) )
3052+ typeDefinition == typeof ( IList < > ) ||
3053+ typeDefinition == typeof ( IReadOnlyList < > ) )
29793054 {
29803055 /*
2981- * value.TryUnwrap() as ICollection<T> ??
2982- * ((JSArray)value).AsCollection<T>(
2983- * (value) => (T)value,
2984- * (value) => (JSValue)value);
3056+ * value.TryUnwrap() as IList<T> ??
3057+ * ((JSArray)value).AsList<T>((value) => (T)value, (value) => (JSValue)value);
29853058 */
2986- Type jsCollectionType = typeDefinition. Name. Contains( "Set") ?
2987- typeof ( JSSet) : typeof ( JSArray) ;
3059+ Type jsCollectionType =
3060+ typeDefinition == typeof ( IEnumerable< > ) ? typeof ( JSIterable) :
3061+ typeDefinition == typeof ( IAsyncEnumerable< > ) ? typeof ( JSAsyncIterable) :
3062+ typeDefinition. Name. Contains( "Set") ? typeof ( JSSet) :
3063+ typeof ( JSArray) ;
3064+ bool isBidirectional = ( typeDefinition == typeof ( IList< > ) ||
3065+ #if READONLY_SET
3066+ typeDefinition == typeof ( IReadOnlySet< > ) ||
3067+ #endif
3068+ typeDefinition == typeof ( ISet< > ) ) ;
29883069 MethodInfo asCollectionMethod = typeof ( JSCollectionExtensions) . GetStaticMethod(
29893070#if ! STRING_AS_SPAN
29903071 "As" + typeDefinition. Name. Substring( 1 , typeDefinition . Name . IndexOf ( '`') - 1 ) ,
29913072#else
29923073 string . Concat ( "As ",
29933074 typeDefinition . Name . AsSpan ( 1 , typeDefinition . Name . IndexOf ( '`') - 1 ) ) ,
29943075#endif
2995- new [ ] { jsCollectionType , typeof ( JSValue . To < > ) , typeof ( JSValue . From < > ) } ,
3076+ isBidirectional ?
3077+ new [ ] { jsCollectionType , typeof ( JSValue . To < > ) , typeof ( JSValue . From < > ) } :
3078+ new [ ] { jsCollectionType, typeof ( JSValue . To < > ) } ,
29963079 elementType ) ;
29973080 MethodInfo asJSCollectionMethod = jsCollectionType . GetExplicitConversion (
29983081 typeof ( JSValue ) , jsCollectionType ) ;
3082+ Expression valueAsCollectionExpression =
3083+ Expression . Convert ( valueExpression , jsCollectionType , asJSCollectionMethod ) ;
3084+ Expression fromJSExpression = GetFromJSValueExpression ( elementType ) ;
3085+ Expression toJSExpression = GetToJSValueExpression ( elementType ) ;
29993086 yield return Expression . Coalesce (
30003087 Expression . TypeAs ( Expression . Call ( valueExpression , s_tryUnwrap ) , toType ) ,
3001- Expression . Call (
3002- asCollectionMethod ,
3003- Expression . Convert ( valueExpression , jsCollectionType , asJSCollectionMethod ) ,
3004- GetFromJSValueExpression ( elementType ) ,
3005- GetToJSValueExpression ( elementType ) ) ) ;
3088+ isBidirectional ?
3089+ Expression . Call(
3090+ asCollectionMethod,
3091+ valueAsCollectionExpression ,
3092+ fromJSExpression ,
3093+ toJSExpression ) :
3094+ Expression . Call (
3095+ asCollectionMethod ,
3096+ valueAsCollectionExpression ,
3097+ fromJSExpression ) ) ;
30063098 }
3007- else if ( typeDefinition == typeof ( IReadOnlyList < > ) ||
3008- typeDefinition == typeof ( IReadOnlyCollection < > ) ||
3009- typeDefinition == typeof ( IEnumerable < > ) ||
3010- typeDefinition == typeof ( IAsyncEnumerable < > ) )
3099+ else if ( typeDefinition == typeof ( ICollection< > ) ||
3100+ typeDefinition == typeof ( IReadOnlyCollection< > ) )
30113101 {
30123102 /*
3013- * value.TryUnwrap() as IReadOnlyCollection<T> ??
3014- * ((JSArray)value).AsReadOnlyCollection<T>((value) => (T)value);
3103+ * value.TryUnwrap() as ICollection<T> ?? value.IsArray() ?
3104+ * ((JSArray)value).AsCollection<T>((value) => (T)value) :
3105+ * ((JSSet)value).AsCollection<T>((value) => (T)value, (value) => (JSValue)value);
30153106 */
3016- Type jsCollectionType = typeDefinition == typeof ( IEnumerable< > ) ?
3017- typeof ( JSIterable) : typeDefinition == typeof ( IAsyncEnumerable< > ) ?
3018- typeof ( JSAsyncIterable) : typeof ( JSArray) ;
3019- MethodInfo asCollectionMethod = typeof ( JSCollectionExtensions) . GetStaticMethod(
3020- #if ! STRING_AS_SPAN
3021- "As" + typeDefinition. Name. Substring( 1 , typeDefinition . Name . IndexOf ( '`') - 1 ) ,
3022- #else
3023- string . Concat ( "As ",
3024- typeDefinition . Name . AsSpan ( 1 , typeDefinition . Name . IndexOf ( '`') - 1 ) ) ,
3025- #endif
3026- new [ ] { jsCollectionType , typeof ( JSValue . To < > ) } ,
3107+ MethodInfo isArrayMethod = typeof ( JSValue) . GetInstanceMethod( nameof( JSValue. IsArray) ) ;
3108+ bool isReadOnlyCollection = typeDefinition == typeof ( IReadOnlyCollection< > ) ;
3109+ string asCollectionMethodName = isReadOnlyCollection ?
3110+ nameof( JSCollectionExtensions. AsReadOnlyCollection) :
3111+ nameof( JSCollectionExtensions. AsCollection) ;
3112+ MethodInfo arrayAsCollectionMethod = typeof ( JSCollectionExtensions) . GetStaticMethod(
3113+ asCollectionMethodName,
3114+ isReadOnlyCollection ?
3115+ new [ ] { typeof ( JSArray ) , typeof ( JSValue . To < > ) } :
3116+ new [ ] { typeof ( JSArray) , typeof ( JSValue . To < > ) , typeof ( JSValue . From < > ) } ,
30273117 elementType ) ;
3028- MethodInfo asJSCollectionMethod = jsCollectionType . GetExplicitConversion (
3029- typeof ( JSValue ) , jsCollectionType ) ;
3118+ MethodInfo setAsCollectionMethod = typeof ( JSCollectionExtensions ) . GetStaticMethod(
3119+ asCollectionMethodName ,
3120+ isReadOnlyCollection ?
3121+ new [ ] { typeof ( JSSet ) , typeof ( JSValue . To < > ) } :
3122+ new [ ] { typeof ( JSSet) , typeof ( JSValue . To < > ) , typeof ( JSValue . From < > ) } ,
3123+ elementType ) ;
3124+ MethodInfo asJSArrayMethod = typeof ( JSArray ) . GetExplicitConversion(
3125+ typeof ( JSValue ) , typeof ( JSArray ) ) ;
3126+ MethodInfo asJSSetMethod = typeof ( JSSet ) . GetExplicitConversion(
3127+ typeof ( JSValue ) , typeof ( JSSet ) ) ;
30303128 yield return Expression. Coalesce(
30313129 Expression . TypeAs ( Expression . Call ( valueExpression , s_tryUnwrap ) , toType ) ,
3032- Expression . Call (
3033- asCollectionMethod ,
3034- Expression . Convert ( valueExpression , jsCollectionType , asJSCollectionMethod ) ,
3035- GetFromJSValueExpression ( elementType ) ) ) ;
3130+ Expression . Condition (
3131+ Expression . Call ( valueExpression , isArrayMethod ) ,
3132+ isReadOnlyCollection ?
3133+ Expression . Call(
3134+ arrayAsCollectionMethod,
3135+ Expression . Convert ( valueExpression , typeof ( JSArray ) , asJSArrayMethod ) ,
3136+ GetFromJSValueExpression ( elementType ) ) :
3137+ Expression . Call (
3138+ arrayAsCollectionMethod ,
3139+ Expression . Convert ( valueExpression , typeof ( JSArray ) , asJSArrayMethod ) ,
3140+ GetFromJSValueExpression ( elementType ) ,
3141+ GetToJSValueExpression ( elementType ) ) ,
3142+ isReadOnlyCollection ?
3143+ Expression . Call(
3144+ setAsCollectionMethod,
3145+ Expression . Convert ( valueExpression , typeof ( JSSet ) , asJSSetMethod ) ,
3146+ GetFromJSValueExpression ( elementType ) ) :
3147+ Expression . Call (
3148+ setAsCollectionMethod ,
3149+ Expression . Convert ( valueExpression , typeof ( JSSet ) , asJSSetMethod ) ,
3150+ GetFromJSValueExpression ( elementType ) ,
3151+ GetToJSValueExpression ( elementType ) ) ) ) ;
30363152 }
30373153 else if ( typeDefinition == typeof ( IDictionary< , > ) )
30383154 {
0 commit comments