3 // Miguel de Icaza (miguel@ximian.com)
4 // Daniel Stodden (stodden@in.tum.de)
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // (C) Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Reflection
;
33 using System
.Runtime
.CompilerServices
;
34 using System
.Runtime
.InteropServices
;
35 using System
.Runtime
.Serialization
;
39 /* Contains the rarely used fields of Delegate */
40 sealed class DelegateData
42 public Type
? target_type
;
43 public string? method_name
;
44 public bool curried_first_arg
;
47 [StructLayout (LayoutKind
.Sequential
)]
48 partial class Delegate
50 #region Sync with object-internals.h
55 IntPtr delegate_trampoline
;
59 IntPtr interp_invoke_impl
;
60 MethodInfo
? method_info
;
62 // Keep a ref of the MethodInfo passed to CreateDelegate.
63 // Used to keep DynamicMethods alive.
64 MethodInfo
? original_method_info
;
68 bool method_is_virtual
;
71 protected Delegate (object target
, string method
)
74 throw new ArgumentNullException (nameof (target
));
77 throw new ArgumentNullException (nameof (method
));
79 this._target
= target
;
80 this.data
= new DelegateData () {
85 protected Delegate (Type target
, string method
)
88 throw new ArgumentNullException (nameof (target
));
90 if (target
.ContainsGenericParameters
)
91 throw new ArgumentException (SR
.Arg_UnboundGenParam
, nameof (target
));
94 throw new ArgumentNullException (nameof (method
));
96 if (!target
.IsRuntimeImplemented ())
97 throw new ArgumentException (SR
.Argument_MustBeRuntimeType
, nameof (target
));
99 this.data
= new DelegateData () {
100 method_name
= method
,
105 public object? Target
=> GetTarget ();
107 internal virtual object? GetTarget () => _target
;
109 public static Delegate
CreateDelegate (Type type
, object? firstArgument
, MethodInfo method
, bool throwOnBindFailure
)
111 return CreateDelegate (type
, firstArgument
, method
, throwOnBindFailure
, true)!;
114 public static Delegate
? CreateDelegate (Type type
, MethodInfo method
, bool throwOnBindFailure
)
116 return CreateDelegate (type
, null, method
, throwOnBindFailure
, false);
119 static Delegate
? CreateDelegate (Type type
, object? firstArgument
, MethodInfo method
, bool throwOnBindFailure
, bool allowClosed
)
122 throw new ArgumentNullException (nameof (type
));
124 throw new ArgumentNullException (nameof (method
));
126 if (!(type
is RuntimeType rtType
))
127 throw new ArgumentException (SR
.Argument_MustBeRuntimeType
, nameof (type
));
128 if (!(method
is RuntimeMethodInfo
|| method
is System
.Reflection
.Emit
.DynamicMethod
))
129 throw new ArgumentException (SR
.Argument_MustBeRuntimeMethodInfo
, nameof (method
));
131 if (!rtType
.IsDelegate ())
132 throw new ArgumentException (SR
.Arg_MustBeDelegate
, nameof (type
));
134 if (!IsMatchingCandidate (type
, firstArgument
, method
, allowClosed
, out DelegateData
? delegate_data
)) {
135 if (throwOnBindFailure
)
136 throw new ArgumentException (SR
.Arg_DlgtTargMeth
);
141 Delegate
? d
= CreateDelegate_internal (type
, firstArgument
, method
, throwOnBindFailure
);
143 d
.original_method_info
= method
;
144 d
.data
= delegate_data
!;
150 public static Delegate
? CreateDelegate (Type type
, object target
, string method
, bool ignoreCase
, bool throwOnBindFailure
)
153 throw new ArgumentNullException (nameof (type
));
155 throw new ArgumentNullException (nameof (target
));
157 throw new ArgumentNullException (nameof (method
));
159 if (!(type
is RuntimeType rtType
))
160 throw new ArgumentException (SR
.Argument_MustBeRuntimeType
, nameof (type
));
161 if (!rtType
.IsDelegate ())
162 throw new ArgumentException (SR
.Arg_MustBeDelegate
, nameof (type
));
164 MethodInfo
? info
= GetCandidateMethod (type
, target
.GetType (), method
, BindingFlags
.Instance
, ignoreCase
);
166 if (throwOnBindFailure
)
167 throw new ArgumentException (SR
.Arg_DlgtTargMeth
);
172 return CreateDelegate_internal (type
, null, info
, throwOnBindFailure
);
175 public static Delegate
? CreateDelegate (Type type
, Type target
, string method
, bool ignoreCase
, bool throwOnBindFailure
)
178 throw new ArgumentNullException (nameof (type
));
180 throw new ArgumentNullException (nameof (target
));
181 if (target
.ContainsGenericParameters
)
182 throw new ArgumentException (SR
.Arg_UnboundGenParam
, nameof (target
));
184 throw new ArgumentNullException (nameof (method
));
186 if (!(type
is RuntimeType rtType
))
187 throw new ArgumentException (SR
.Argument_MustBeRuntimeType
, nameof (type
));
189 if (!target
.IsRuntimeImplemented ())
190 throw new ArgumentException (SR
.Argument_MustBeRuntimeType
, nameof (target
));
191 if (!rtType
.IsDelegate ())
192 throw new ArgumentException (SR
.Arg_MustBeDelegate
, nameof (type
));
194 MethodInfo
? info
= GetCandidateMethod (type
, target
, method
, BindingFlags
.Static
, ignoreCase
);
196 if (throwOnBindFailure
)
197 throw new ArgumentException (SR
.Arg_DlgtTargMeth
);
202 return CreateDelegate_internal (type
, null, info
, throwOnBindFailure
);
205 static MethodInfo
? GetCandidateMethod (Type type
, Type target
, string method
, BindingFlags bflags
, bool ignoreCase
)
207 MethodInfo
? invoke
= type
.GetMethod ("Invoke");
211 ParameterInfo
[] delargs
= invoke
.GetParametersInternal ();
212 Type
[] delargtypes
= new Type
[delargs
.Length
];
214 for (int i
= 0; i
< delargs
.Length
; i
++)
215 delargtypes
[i
] = delargs
[i
].ParameterType
;
218 * since we need to walk the inheritance chain anyway to
219 * find private methods, adjust the bindingflags to ignore
222 BindingFlags flags
= BindingFlags
.ExactBinding
|
223 BindingFlags
.Public
| BindingFlags
.NonPublic
|
224 BindingFlags
.DeclaredOnly
| bflags
;
227 flags
|= BindingFlags
.IgnoreCase
;
229 for (Type
? targetType
= target
; targetType
!= null; targetType
= targetType
.BaseType
) {
230 MethodInfo
? mi
= targetType
.GetMethod (method
, flags
, null, delargtypes
, Array
.Empty
<ParameterModifier
>());
232 if (mi
!= null && IsReturnTypeMatch (invoke
.ReturnType
!, mi
.ReturnType
!)) {
240 static bool IsMatchingCandidate (Type type
, object? target
, MethodInfo method
, bool allowClosed
, out DelegateData
? delegateData
)
242 MethodInfo
? invoke
= type
.GetMethod ("Invoke");
244 if (invoke
== null || !IsReturnTypeMatch (invoke
.ReturnType
!, method
.ReturnType
!)) {
249 ParameterInfo
[] delargs
= invoke
.GetParametersInternal ();
250 ParameterInfo
[] args
= method
.GetParametersInternal ();
254 if (target
!= null) {
255 // delegate closed over target
256 if (!method
.IsStatic
)
257 // target is passed as this
258 argLengthMatch
= (args
.Length
== delargs
.Length
);
260 // target is passed as the first argument to the static method
261 argLengthMatch
= (args
.Length
== delargs
.Length
+ 1);
263 if (!method
.IsStatic
) {
265 // Net 2.0 feature. The first argument of the delegate is passed
266 // as the 'this' argument to the method.
268 argLengthMatch
= (args
.Length
+ 1 == delargs
.Length
);
271 // closed over a null reference
272 argLengthMatch
= (args
.Length
== delargs
.Length
);
274 argLengthMatch
= (args
.Length
== delargs
.Length
);
277 // closed over a null reference
278 argLengthMatch
= args
.Length
== delargs
.Length
+ 1;
282 if (!argLengthMatch
) {
288 delegateData
= new DelegateData ();
290 if (target
!= null) {
291 if (!method
.IsStatic
) {
292 argsMatch
= IsArgumentTypeMatchWithThis (target
.GetType (), method
.DeclaringType
!, true);
293 for (int i
= 0; i
< args
.Length
; i
++)
294 argsMatch
&= IsArgumentTypeMatch (delargs
[i
].ParameterType
, args
[i
].ParameterType
);
296 argsMatch
= IsArgumentTypeMatch (target
.GetType (), args
[0].ParameterType
);
297 for (int i
= 1; i
< args
.Length
; i
++)
298 argsMatch
&= IsArgumentTypeMatch (delargs
[i
- 1].ParameterType
, args
[i
].ParameterType
);
300 delegateData
.curried_first_arg
= true;
303 if (!method
.IsStatic
) {
304 if (args
.Length
+ 1 == delargs
.Length
) {
305 // The first argument should match this
306 argsMatch
= IsArgumentTypeMatchWithThis (delargs
[0].ParameterType
, method
.DeclaringType
!, false);
307 for (int i
= 0; i
< args
.Length
; i
++)
308 argsMatch
&= IsArgumentTypeMatch (delargs
[i
+ 1].ParameterType
, args
[i
].ParameterType
);
310 // closed over a null reference
311 argsMatch
= allowClosed
;
312 for (int i
= 0; i
< args
.Length
; i
++)
313 argsMatch
&= IsArgumentTypeMatch (delargs
[i
].ParameterType
, args
[i
].ParameterType
);
316 if (delargs
.Length
+ 1 == args
.Length
) {
317 // closed over a null reference
318 argsMatch
= !(args
[0].ParameterType
.IsValueType
|| args
[0].ParameterType
.IsByRef
) && allowClosed
;
319 for (int i
= 0; i
< delargs
.Length
; i
++)
320 argsMatch
&= IsArgumentTypeMatch (delargs
[i
].ParameterType
, args
[i
+ 1].ParameterType
);
322 delegateData
.curried_first_arg
= true;
325 for (int i
= 0; i
< args
.Length
; i
++)
326 argsMatch
&= IsArgumentTypeMatch (delargs
[i
].ParameterType
, args
[i
].ParameterType
);
334 static bool IsReturnTypeMatch (Type delReturnType
, Type returnType
)
336 bool returnMatch
= returnType
== delReturnType
;
339 // Delegate covariance
340 if (!returnType
.IsValueType
&& delReturnType
.IsAssignableFrom (returnType
))
344 bool isDelArgEnum
= delReturnType
.IsEnum
;
345 bool isArgEnum
= returnType
.IsEnum
;
346 if (isArgEnum
&& isDelArgEnum
)
347 returnMatch
= Enum
.GetUnderlyingType (delReturnType
) == Enum
.GetUnderlyingType (returnType
);
348 else if (isDelArgEnum
&& Enum
.GetUnderlyingType (delReturnType
) == returnType
)
350 else if (isArgEnum
&& Enum
.GetUnderlyingType (returnType
) == delReturnType
)
358 static bool IsArgumentTypeMatch (Type delArgType
, Type argType
)
360 bool match
= delArgType
== argType
;
362 // Delegate contravariance
364 if (!argType
.IsValueType
&& argType
.IsAssignableFrom (delArgType
))
369 if (delArgType
.IsEnum
&& Enum
.GetUnderlyingType (delArgType
) == argType
)
371 else if (argType
.IsEnum
&& Enum
.GetUnderlyingType (argType
) == delArgType
)
378 static bool IsArgumentTypeMatchWithThis (Type delArgType
, Type argType
, bool boxedThis
)
381 if (argType
.IsValueType
)
382 match
= delArgType
.IsByRef
&& delArgType
.GetElementType () == argType
||
383 (boxedThis
&& delArgType
== argType
);
385 match
= delArgType
== argType
|| argType
.IsAssignableFrom (delArgType
);
390 protected virtual object? DynamicInvokeImpl (object?[]? args
)
392 if (Method
is null) {
394 // FIXME: This code cannot handle null argument values
395 Type
[] mtypes
= new Type
[args
.Length
];
396 for (int i
= 0; i
< args
.Length
; ++i
) {
397 mtypes
[i
] = args
[i
].GetType ();
399 method_info
= _target
.GetType ().GetMethod (data
.method_name
, mtypes
);
403 var target
= _target
;
406 data
= CreateDelegateData ();
408 // replace all Type.Missing with default values defined on parameters of the delegate if any
409 MethodInfo
? invoke
= GetType ().GetMethod ("Invoke");
410 if (invoke
!= null && args
!= null) {
411 ParameterInfo
[] delegateParameters
= invoke
.GetParameters ();
412 for (int i
= 0; i
< args
.Length
; i
++) {
413 if (args
[i
] == Type
.Missing
) {
414 ParameterInfo dlgParam
= delegateParameters
[i
];
415 if (dlgParam
.HasDefaultValue
) {
416 args
[i
] = dlgParam
.DefaultValue
;
422 if (Method
.IsStatic
) {
424 // The delegate is bound to _target
426 if (data
.curried_first_arg
) {
428 args
= new object?[] { target }
;
430 Array
.Resize (ref args
, args
.Length
+ 1);
431 Array
.Copy (args
, 0, args
, 1, args
.Length
- 1);
438 if (_target
is null && args
?.Length
> 0) {
440 Array
.Copy (args
, 1, args
, 0, args
.Length
- 1);
441 Array
.Resize (ref args
, args
.Length
- 1);
445 return Method
.Invoke (target
, args
);
448 public override bool Equals (object? obj
)
450 if (!(obj
is Delegate d
) || !InternalEqualTypes (this, obj
))
453 // Do not compare method_ptr, since it can point to a trampoline
454 if (d
._target
== _target
&& d
.Method
== Method
) {
455 if (d
.data
!= null || data
!= null) {
457 if (d
.data
!= null && data
!= null)
458 return (d
.data
.target_type
== data
.target_type
&& d
.data
.method_name
== data
.method_name
);
461 return d
.data
.target_type
is null;
463 return data
.target_type
is null;
473 public override int GetHashCode ()
475 MethodInfo
? m
= Method
;
477 return (m
!= null ? m
.GetHashCode () : GetType ().GetHashCode ()) ^ RuntimeHelpers
.GetHashCode (_target
);
480 protected virtual MethodInfo
GetMethodImpl ()
482 if (method_info
!= null)
485 if (method
!= IntPtr
.Zero
) {
486 if (!method_is_virtual
)
487 method_info
= (MethodInfo
) RuntimeMethodInfo
.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method
));
489 method_info
= GetVirtualMethod_internal ();
495 DelegateData
CreateDelegateData ()
497 DelegateData delegate_data
= new DelegateData ();
498 if (method_info
.IsStatic
) {
499 if (_target
!= null) {
500 delegate_data
.curried_first_arg
= true;
502 MethodInfo
? invoke
= GetType ().GetMethod ("Invoke");
503 if (invoke
!= null && invoke
.GetParametersCount () + 1 == method_info
.GetParametersCount ())
504 delegate_data
.curried_first_arg
= true;
508 return delegate_data
;
511 static bool InternalEqualTypes (object source
, object value)
513 return source
.GetType () == value.GetType ();
516 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
517 private protected extern static MulticastDelegate
AllocDelegateLike_internal (Delegate d
);
519 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
520 static extern Delegate
? CreateDelegate_internal (Type type
, object? target
, MethodInfo info
, bool throwOnBindFailure
);
522 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
523 extern MethodInfo
GetVirtualMethod_internal ();