@@ -13,14 +13,12 @@ namespace Microsoft.JSInterop.Infrastructure;
1313/// </summary>
1414internal readonly struct JSFunctionReference
1515{
16- private static readonly ConcurrentDictionary < Type , MethodInfo > _methodInfoCache = new ( ) ;
17-
18- private readonly IJSObjectReference _jsObjectReference ;
19-
2016 /// <summary>
2117 /// Caches previously constructed MethodInfo instances for various delegate types.
2218 /// </summary>
23- public static ConcurrentDictionary < Type , MethodInfo > MethodInfoCache => _methodInfoCache ;
19+ private static readonly ConcurrentDictionary < Type , MethodInfo > _methodInfoCache = new ( ) ;
20+
21+ private readonly IJSObjectReference _jsObjectReference ;
2422
2523 public JSFunctionReference ( IJSObjectReference jsObjectReference )
2624 {
@@ -34,84 +32,88 @@ public static T CreateInvocationDelegate<T>(IJSObjectReference jsObjectReference
3432 {
3533 Type delegateType = typeof ( T ) ;
3634
37- if ( MethodInfoCache . TryGetValue ( delegateType , out var wrapperMethod ) )
35+ if ( _methodInfoCache . TryGetValue ( delegateType , out var wrapperMethod ) )
3836 {
3937 var wrapper = new JSFunctionReference ( jsObjectReference ) ;
4038 return ( T ) Delegate . CreateDelegate ( delegateType , wrapper , wrapperMethod ) ;
4139 }
4240
4341 if ( ! delegateType . IsGenericType )
4442 {
45- throw new ArgumentException ( "The delegate type must be a Func." ) ;
43+ throw CreateInvalidTypeParameterException ( delegateType ) ;
4644 }
4745
4846 var returnTypeCandidate = delegateType . GenericTypeArguments [ ^ 1 ] ;
4947
5048 if ( returnTypeCandidate == typeof ( ValueTask ) )
5149 {
52- var methodName = GetVoidMethodName ( delegateType . GetGenericTypeDefinition ( ) ) ;
50+ var methodName = GetVoidMethodName ( delegateType ) ;
5351 return CreateVoidDelegate < T > ( delegateType , jsObjectReference , methodName ) ;
5452 }
5553 else if ( returnTypeCandidate == typeof ( Task ) )
5654 {
57- var methodName = GetVoidTaskMethodName ( delegateType . GetGenericTypeDefinition ( ) ) ;
55+ var methodName = GetVoidTaskMethodName ( delegateType ) ;
5856 return CreateVoidDelegate < T > ( delegateType , jsObjectReference , methodName ) ;
5957 }
60- else
58+ else if ( returnTypeCandidate . IsGenericType )
6159 {
6260 var returnTypeGenericTypeDefinition = returnTypeCandidate . GetGenericTypeDefinition ( ) ;
6361
6462 if ( returnTypeGenericTypeDefinition == typeof ( ValueTask < > ) )
6563 {
66- var methodName = GetMethodName ( delegateType . GetGenericTypeDefinition ( ) ) ;
64+ var methodName = GetMethodName ( delegateType ) ;
6765 var innerReturnType = returnTypeCandidate . GenericTypeArguments [ 0 ] ;
6866 return CreateDelegate < T > ( delegateType , innerReturnType , jsObjectReference , methodName ) ;
6967 }
7068
7169 else if ( returnTypeGenericTypeDefinition == typeof ( Task < > ) )
7270 {
73- var methodName = GetTaskMethodName ( delegateType . GetGenericTypeDefinition ( ) ) ;
71+ var methodName = GetTaskMethodName ( delegateType ) ;
7472 var innerReturnType = returnTypeCandidate . GenericTypeArguments [ 0 ] ;
7573 return CreateDelegate < T > ( delegateType , innerReturnType , jsObjectReference , methodName ) ;
7674 }
77- else
78- {
79- throw new ArgumentException ( "The delegate return type must be Task<TResult> or ValueTask<TResult>." ) ;
80- }
8175 }
76+
77+ throw CreateInvalidTypeParameterException ( delegateType ) ;
8278 }
8379
8480 private static T CreateDelegate < T > ( Type delegateType , Type returnType , IJSObjectReference jsObjectReference , string methodName ) where T : Delegate
8581 {
86- var wrapper = new JSFunctionReference ( jsObjectReference ) ;
8782 var wrapperMethod = typeof ( JSFunctionReference ) . GetMethod ( methodName , BindingFlags . Public | BindingFlags . Instance ) ! ;
8883 Type [ ] genericArguments = [ .. delegateType . GenericTypeArguments [ ..^ 1 ] , returnType ] ;
8984
9085#pragma warning disable IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
9186 var concreteWrapperMethod = wrapperMethod . MakeGenericMethod ( genericArguments ) ;
9287#pragma warning restore IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
9388
94- MethodInfoCache . TryAdd ( delegateType , concreteWrapperMethod ) ;
89+ _methodInfoCache . TryAdd ( delegateType , concreteWrapperMethod ) ;
9590
91+ var wrapper = new JSFunctionReference ( jsObjectReference ) ;
9692 return ( T ) Delegate . CreateDelegate ( delegateType , wrapper , concreteWrapperMethod ) ;
9793 }
9894
9995 private static T CreateVoidDelegate < T > ( Type delegateType , IJSObjectReference jsObjectReference , string methodName ) where T : Delegate
10096 {
101- var wrapper = new JSFunctionReference ( jsObjectReference ) ;
10297 var wrapperMethod = typeof ( JSFunctionReference ) . GetMethod ( methodName , BindingFlags . Public | BindingFlags . Instance ) ! ;
10398 Type [ ] genericArguments = delegateType . GenericTypeArguments [ ..^ 1 ] ;
10499
105100#pragma warning disable IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
106101 var concreteWrapperMethod = wrapperMethod . MakeGenericMethod ( genericArguments ) ;
107102#pragma warning restore IL2060 // Call to 'System.Reflection.MethodInfo.MakeGenericMethod' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method.
108103
109- MethodInfoCache . TryAdd ( delegateType , concreteWrapperMethod ) ;
104+ _methodInfoCache . TryAdd ( delegateType , concreteWrapperMethod ) ;
110105
106+ var wrapper = new JSFunctionReference ( jsObjectReference ) ;
111107 return ( T ) Delegate . CreateDelegate ( delegateType , wrapper , concreteWrapperMethod ) ;
112108 }
113109
114- private static string GetMethodName ( Type genericDelegateTypeDefiniton ) => genericDelegateTypeDefiniton switch
110+ private static InvalidOperationException CreateInvalidTypeParameterException ( Type delegateType )
111+ {
112+ return new InvalidOperationException (
113+ $ "The type { delegateType } is not supported as the type parameter of '{ nameof ( JSObjectReferenceExtensions . AsAsyncFunction ) } '. 'T' must be Func with the return type Task<TResult> or ValueTask<TResult>.") ;
114+ }
115+
116+ private static string GetMethodName ( Type delegateType ) => delegateType . GetGenericTypeDefinition ( ) switch
115117 {
116118 var gd when gd == typeof ( Func < > ) => nameof ( Invoke0 ) ,
117119 var gd when gd == typeof ( Func < , > ) => nameof ( Invoke1 ) ,
@@ -120,10 +122,10 @@ private static T CreateVoidDelegate<T>(Type delegateType, IJSObjectReference jsO
120122 var gd when gd == typeof ( Func < , , , , > ) => nameof ( Invoke4 ) ,
121123 var gd when gd == typeof ( Func < , , , , , > ) => nameof ( Invoke5 ) ,
122124 var gd when gd == typeof ( Func < , , , , , , > ) => nameof ( Invoke6 ) ,
123- _ => throw new NotSupportedException ( $ "The type { genericDelegateTypeDefiniton } is not supported." )
125+ _ => throw CreateInvalidTypeParameterException ( delegateType )
124126 } ;
125127
126- private static string GetTaskMethodName ( Type genericDelegateTypeDefiniton ) => genericDelegateTypeDefiniton switch
128+ private static string GetTaskMethodName ( Type delegateType ) => delegateType . GetGenericTypeDefinition ( ) switch
127129 {
128130 var gd when gd == typeof ( Func < > ) => nameof ( InvokeTask0 ) ,
129131 var gd when gd == typeof ( Func < , > ) => nameof ( InvokeTask1 ) ,
@@ -132,10 +134,10 @@ private static T CreateVoidDelegate<T>(Type delegateType, IJSObjectReference jsO
132134 var gd when gd == typeof ( Func < , , , , > ) => nameof ( InvokeTask4 ) ,
133135 var gd when gd == typeof ( Func < , , , , , > ) => nameof ( InvokeTask5 ) ,
134136 var gd when gd == typeof ( Func < , , , , , , > ) => nameof ( InvokeTask6 ) ,
135- _ => throw new NotSupportedException ( $ "The type { genericDelegateTypeDefiniton } is not supported." )
137+ _ => throw CreateInvalidTypeParameterException ( delegateType )
136138 } ;
137139
138- private static string GetVoidMethodName ( Type genericDelegateTypeDefiniton ) => genericDelegateTypeDefiniton switch
140+ private static string GetVoidMethodName ( Type delegateType ) => delegateType . GetGenericTypeDefinition ( ) switch
139141 {
140142 var gd when gd == typeof ( Func < > ) => nameof ( InvokeVoid0 ) ,
141143 var gd when gd == typeof ( Func < , > ) => nameof ( InvokeVoid1 ) ,
@@ -144,10 +146,10 @@ private static T CreateVoidDelegate<T>(Type delegateType, IJSObjectReference jsO
144146 var gd when gd == typeof ( Func < , , , , > ) => nameof ( InvokeVoid4 ) ,
145147 var gd when gd == typeof ( Func < , , , , , > ) => nameof ( InvokeVoid5 ) ,
146148 var gd when gd == typeof ( Func < , , , , , , > ) => nameof ( InvokeVoid6 ) ,
147- _ => throw new NotSupportedException ( $ "The type { genericDelegateTypeDefiniton } is not supported." )
149+ _ => throw CreateInvalidTypeParameterException ( delegateType )
148150 } ;
149151
150- private static string GetVoidTaskMethodName ( Type genericDelegateTypeDefiniton ) => genericDelegateTypeDefiniton switch
152+ private static string GetVoidTaskMethodName ( Type delegateType ) => delegateType . GetGenericTypeDefinition ( ) switch
151153 {
152154 var gd when gd == typeof ( Func < > ) => nameof ( InvokeVoidTask0 ) ,
153155 var gd when gd == typeof ( Func < , > ) => nameof ( InvokeVoidTask1 ) ,
@@ -156,7 +158,7 @@ private static T CreateVoidDelegate<T>(Type delegateType, IJSObjectReference jsO
156158 var gd when gd == typeof ( Func < , , , , > ) => nameof ( InvokeVoidTask4 ) ,
157159 var gd when gd == typeof ( Func < , , , , , > ) => nameof ( InvokeVoidTask5 ) ,
158160 var gd when gd == typeof ( Func < , , , , , , > ) => nameof ( InvokeVoidTask6 ) ,
159- _ => throw new NotSupportedException ( $ "The type { genericDelegateTypeDefiniton } is not supported." )
161+ _ => throw CreateInvalidTypeParameterException ( delegateType )
160162 } ;
161163
162164 // Variants returning ValueTask<T> using InvokeAsync
0 commit comments