5 // Miguel de Icaza (miguel@ximian.com)
6 // Daniel Stodden (stodden@in.tum.de)
7 // Dietmar Maurer (dietmar@ximian.com)
8 // Marek Safar (marek.safar@gmail.com)
10 // (C) Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Reflection
;
35 using System
.Runtime
.Remoting
;
36 using System
.Runtime
.Serialization
;
37 using System
.Runtime
.CompilerServices
;
38 using System
.Runtime
.InteropServices
;
42 /* Contains the rarely used fields of Delegate */
43 sealed class DelegateData
45 public Type target_type
;
46 public string method_name
;
47 public bool curried_first_arg
;
51 [StructLayout (LayoutKind
.Sequential
)]
52 public abstract class Delegate
: ICloneable
, ISerializable
54 #region Sync with object-internals.h
55 #pragma warning disable 169, 414, 649
56 private IntPtr method_ptr
;
57 private IntPtr invoke_impl
;
58 private object m_target
;
59 private IntPtr method
;
60 private IntPtr delegate_trampoline
;
61 private IntPtr extra_arg
;
62 private IntPtr method_code
;
63 private IntPtr interp_method
;
64 private IntPtr interp_invoke_impl
;
65 private MethodInfo method_info
;
67 // Keep a ref of the MethodInfo passed to CreateDelegate.
68 // Used to keep DynamicMethods alive.
69 private MethodInfo original_method_info
;
71 private DelegateData data
;
73 private bool method_is_virtual
;
74 #pragma warning restore 169, 414, 649
77 protected Delegate (object target
, string method
)
80 throw new ArgumentNullException ("target");
83 throw new ArgumentNullException ("method");
85 this.m_target
= target
;
86 this.data
= new DelegateData ();
87 this.data
.method_name
= method
;
90 protected Delegate (Type target
, string method
)
93 throw new ArgumentNullException ("target");
96 throw new ArgumentNullException ("method");
98 this.data
= new DelegateData ();
99 this.data
.method_name
= method
;
100 this.data
.target_type
= target
;
103 public MethodInfo Method
{
105 return GetMethodImpl ();
109 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
110 extern MethodInfo
GetVirtualMethod_internal ();
112 public object Target
{
118 internal IntPtr
GetNativeFunctionPointer () => method_ptr
;
124 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
125 internal static extern Delegate
CreateDelegate_internal (Type type
, object target
, MethodInfo info
, bool throwOnBindFailure
);
127 private static bool arg_type_match (Type delArgType
, Type argType
) {
128 bool match
= delArgType
== argType
;
130 // Delegate contravariance
132 if (!argType
.IsValueType
&& argType
.IsAssignableFrom (delArgType
))
137 if (delArgType
.IsEnum
&& Enum
.GetUnderlyingType (delArgType
) == argType
)
139 else if (argType
.IsEnum
&& Enum
.GetUnderlyingType (argType
) == delArgType
)
146 private static bool arg_type_match_this (Type delArgType
, Type argType
, bool boxedThis
) {
148 if (argType
.IsValueType
)
149 match
= delArgType
.IsByRef
&& delArgType
.GetElementType () == argType
||
150 (boxedThis
&& delArgType
== argType
);
152 match
= delArgType
== argType
|| argType
.IsAssignableFrom (delArgType
);
156 private static bool return_type_match (Type delReturnType
, Type returnType
) {
157 bool returnMatch
= returnType
== delReturnType
;
160 // Delegate covariance
161 if (!returnType
.IsValueType
&& delReturnType
.IsAssignableFrom (returnType
))
168 public static Delegate
CreateDelegate (Type type
, object firstArgument
, MethodInfo method
, bool throwOnBindFailure
)
170 return CreateDelegate (type
, firstArgument
, method
, throwOnBindFailure
, true);
173 static Delegate
CreateDelegate (Type type
, object firstArgument
, MethodInfo method
, bool throwOnBindFailure
, bool allowClosed
)
175 // The name of the parameter changed in 2.0
176 object target
= firstArgument
;
179 throw new ArgumentNullException ("type");
182 throw new ArgumentNullException ("method");
184 if (!type
.IsSubclassOf (typeof (MulticastDelegate
)))
185 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
187 MethodInfo invoke
= type
.GetMethod ("Invoke");
189 if (!return_type_match (invoke
.ReturnType
, method
.ReturnType
)) {
190 if (throwOnBindFailure
)
191 throw new ArgumentException ("method return type is incompatible");
196 ParameterInfo
[] delargs
= invoke
.GetParametersInternal ();
197 ParameterInfo
[] args
= method
.GetParametersInternal ();
201 if (target
!= null) {
202 // delegate closed over target
203 if (!method
.IsStatic
)
204 // target is passed as this
205 argLengthMatch
= (args
.Length
== delargs
.Length
);
207 // target is passed as the first argument to the static method
208 argLengthMatch
= (args
.Length
== delargs
.Length
+ 1);
210 if (!method
.IsStatic
) {
212 // Net 2.0 feature. The first argument of the delegate is passed
213 // as the 'this' argument to the method.
215 argLengthMatch
= (args
.Length
+ 1 == delargs
.Length
);
218 // closed over a null reference
219 argLengthMatch
= (args
.Length
== delargs
.Length
);
221 argLengthMatch
= (args
.Length
== delargs
.Length
);
224 // closed over a null reference
225 argLengthMatch
= args
.Length
== delargs
.Length
+ 1;
228 if (!argLengthMatch
) {
229 if (throwOnBindFailure
)
230 throw new TargetParameterCountException ("Parameter count mismatch.");
236 DelegateData delegate_data
= new DelegateData ();
238 if (target
!= null) {
239 if (!method
.IsStatic
) {
240 argsMatch
= arg_type_match_this (target
.GetType (), method
.DeclaringType
, true);
241 for (int i
= 0; i
< args
.Length
; i
++)
242 argsMatch
&= arg_type_match (delargs
[i
].ParameterType
, args
[i
].ParameterType
);
244 argsMatch
= arg_type_match (target
.GetType (), args
[0].ParameterType
);
245 for (int i
= 1; i
< args
.Length
; i
++)
246 argsMatch
&= arg_type_match (delargs
[i
- 1].ParameterType
, args
[i
].ParameterType
);
248 delegate_data
.curried_first_arg
= true;
251 if (!method
.IsStatic
) {
252 if (args
.Length
+ 1 == delargs
.Length
) {
253 // The first argument should match this
254 argsMatch
= arg_type_match_this (delargs
[0].ParameterType
, method
.DeclaringType
, false);
255 for (int i
= 0; i
< args
.Length
; i
++)
256 argsMatch
&= arg_type_match (delargs
[i
+ 1].ParameterType
, args
[i
].ParameterType
);
258 // closed over a null reference
259 argsMatch
= allowClosed
;
260 for (int i
= 0; i
< args
.Length
; i
++)
261 argsMatch
&= arg_type_match (delargs
[i
].ParameterType
, args
[i
].ParameterType
);
264 if (delargs
.Length
+ 1 == args
.Length
) {
265 // closed over a null reference
266 argsMatch
= !(args
[0].ParameterType
.IsValueType
|| args
[0].ParameterType
.IsByRef
) && allowClosed
;
267 for (int i
= 0; i
< delargs
.Length
; i
++)
268 argsMatch
&= arg_type_match (delargs
[i
].ParameterType
, args
[i
+ 1].ParameterType
);
270 delegate_data
.curried_first_arg
= true;
273 for (int i
= 0; i
< args
.Length
; i
++)
274 argsMatch
&= arg_type_match (delargs
[i
].ParameterType
, args
[i
].ParameterType
);
280 if (throwOnBindFailure
)
281 throw new ArgumentException ("method arguments are incompatible");
286 Delegate d
= CreateDelegate_internal (type
, target
, method
, throwOnBindFailure
);
288 d
.original_method_info
= method
;
289 if (delegate_data
!= null)
290 d
.data
= delegate_data
;
294 public static Delegate
CreateDelegate (Type type
, object firstArgument
, MethodInfo method
)
296 return CreateDelegate (type
, firstArgument
, method
, true, true);
299 public static Delegate
CreateDelegate (Type type
, MethodInfo method
, bool throwOnBindFailure
)
301 return CreateDelegate (type
, null, method
, throwOnBindFailure
, false);
304 public static Delegate
CreateDelegate (Type type
, MethodInfo method
)
306 return CreateDelegate (type
, method
, true);
309 public static Delegate
CreateDelegate (Type type
, object target
, string method
)
311 return CreateDelegate (type
, target
, method
, false);
314 static MethodInfo
GetCandidateMethod (Type type
, Type target
, string method
, BindingFlags bflags
, bool ignoreCase
, bool throwOnBindFailure
)
317 throw new ArgumentNullException ("type");
320 throw new ArgumentNullException ("method");
322 if (!type
.IsSubclassOf (typeof (MulticastDelegate
)))
323 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
325 MethodInfo invoke
= type
.GetMethod ("Invoke");
326 ParameterInfo
[] delargs
= invoke
.GetParametersInternal ();
327 Type
[] delargtypes
= new Type
[delargs
.Length
];
329 for (int i
=0; i
<delargs
.Length
; i
++)
330 delargtypes
[i
] = delargs
[i
].ParameterType
;
333 * FIXME: we should check the caller has reflection permission
334 * or if it lives in the same assembly...
338 * since we need to walk the inheritance chain anyway to
339 * find private methods, adjust the bindingflags to ignore
342 BindingFlags flags
= BindingFlags
.ExactBinding
|
343 BindingFlags
.Public
| BindingFlags
.NonPublic
|
344 BindingFlags
.DeclaredOnly
| bflags
;
347 flags
|= BindingFlags
.IgnoreCase
;
349 MethodInfo info
= null;
351 for (Type targetType
= target
; targetType
!= null; targetType
= targetType
.BaseType
) {
352 MethodInfo mi
= targetType
.GetMethod (method
, flags
,
353 null, delargtypes
, Array
.Empty
<ParameterModifier
>());
354 if (mi
!= null && return_type_match (invoke
.ReturnType
, mi
.ReturnType
)) {
361 if (throwOnBindFailure
)
362 throw new ArgumentException ("Couldn't bind to method '" + method
+ "'.");
370 public static Delegate
CreateDelegate (Type type
, Type target
, string method
, bool ignoreCase
, bool throwOnBindFailure
)
373 throw new ArgumentNullException ("target");
375 MethodInfo info
= GetCandidateMethod (type
, target
, method
,
376 BindingFlags
.Static
, ignoreCase
, throwOnBindFailure
);
380 return CreateDelegate_internal (type
, null, info
, throwOnBindFailure
);
383 public static Delegate
CreateDelegate (Type type
, Type target
, string method
) {
384 return CreateDelegate (type
, target
, method
, false, true);
387 public static Delegate
CreateDelegate (Type type
, Type target
, string method
, bool ignoreCase
) {
388 return CreateDelegate (type
, target
, method
, ignoreCase
, true);
391 public static Delegate
CreateDelegate (Type type
, object target
, string method
, bool ignoreCase
, bool throwOnBindFailure
)
394 throw new ArgumentNullException ("target");
396 MethodInfo info
= GetCandidateMethod (type
, target
.GetType (), method
,
397 BindingFlags
.Instance
, ignoreCase
, throwOnBindFailure
);
401 return CreateDelegate_internal (type
, target
, info
, throwOnBindFailure
);
404 public static Delegate
CreateDelegate (Type type
, object target
, string method
, bool ignoreCase
) {
405 return CreateDelegate (type
, target
, method
, ignoreCase
, true);
408 public object DynamicInvoke (params object[] args
)
410 return DynamicInvokeImpl (args
);
413 void InitializeDelegateData ()
415 DelegateData delegate_data
= new DelegateData ();
416 if (method_info
.IsStatic
) {
417 if (m_target
!= null) {
418 delegate_data
.curried_first_arg
= true;
420 MethodInfo invoke
= GetType ().GetMethod ("Invoke");
421 if (invoke
.GetParametersCount () + 1 == method_info
.GetParametersCount ())
422 delegate_data
.curried_first_arg
= true;
425 this.data
= delegate_data
;
428 protected virtual object DynamicInvokeImpl (object[] args
)
430 if (Method
== null) {
431 Type
[] mtypes
= new Type
[args
.Length
];
432 for (int i
= 0; i
< args
.Length
; ++i
) {
433 mtypes
[i
] = args
[i
].GetType ();
435 method_info
= m_target
.GetType ().GetMethod (data
.method_name
, mtypes
);
438 var target
= m_target
;
439 if (this.data
== null)
440 InitializeDelegateData ();
442 if (Method
.IsStatic
) {
444 // The delegate is bound to m_target
446 if (data
.curried_first_arg
) {
448 args
= new [] { target }
;
450 Array
.Resize (ref args
, args
.Length
+ 1);
451 Array
.Copy (args
, 0, args
, 1, args
.Length
- 1);
458 if (m_target
== null && args
!= null && args
.Length
> 0) {
460 Array
.Copy (args
, 1, args
, 0, args
.Length
- 1);
461 Array
.Resize (ref args
, args
.Length
- 1);
465 return Method
.Invoke (target
, args
);
468 public virtual object Clone ()
470 return MemberwiseClone ();
473 public override bool Equals (object obj
)
475 Delegate d
= obj
as Delegate
;
480 // Do not compare method_ptr, since it can point to a trampoline
481 if (d
.m_target
== m_target
&& d
.Method
== Method
) {
482 if (d
.data
!= null || data
!= null) {
484 if (d
.data
!= null && data
!= null)
485 return (d
.data
.target_type
== data
.target_type
&& d
.data
.method_name
== data
.method_name
);
488 return d
.data
.target_type
== null;
490 return data
.target_type
== null;
500 public override int GetHashCode ()
506 return (m
!= null ? m
.GetHashCode () : GetType ().GetHashCode ()) ^ RuntimeHelpers
.GetHashCode (m_target
);
509 protected virtual MethodInfo
GetMethodImpl ()
511 if (method_info
!= null) {
514 if (method
!= IntPtr
.Zero
) {
515 if (!method_is_virtual
)
516 method_info
= (MethodInfo
)RuntimeMethodInfo
.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method
));
518 method_info
= GetVirtualMethod_internal ();
524 // This is from ISerializable
525 public virtual void GetObjectData (SerializationInfo info
, StreamingContext context
)
527 DelegateSerializationHolder
.GetDelegateData (this, info
, context
);
530 public virtual Delegate
[] GetInvocationList()
532 return new Delegate
[] { this }
;
536 /// Returns a new MulticastDelegate holding the
537 /// concatenated invocation lists of MulticastDelegates a and b
539 public static Delegate
Combine (Delegate a
, Delegate b
)
547 if (a
.GetType () != b
.GetType ())
548 throw new ArgumentException (string.Format ("Incompatible Delegate Types. First is {0} second is {1}.", a
.GetType ().FullName
, b
.GetType ().FullName
));
550 return a
.CombineImpl (b
);
554 /// Returns a new MulticastDelegate holding the
555 /// concatenated invocation lists of an Array of MulticastDelegates
557 [System
.Runtime
.InteropServices
.ComVisible (true)]
558 public static Delegate
Combine (params Delegate
[] delegates
)
560 if (delegates
== null)
563 Delegate retval
= null;
565 foreach (Delegate next
in delegates
)
566 retval
= Combine (retval
, next
);
571 protected virtual Delegate
CombineImpl (Delegate d
)
573 throw new MulticastNotSupportedException (String
.Empty
);
576 public static Delegate
Remove (Delegate source
, Delegate
value)
584 if (source
.GetType () != value.GetType ())
585 throw new ArgumentException (string.Format ("Incompatible Delegate Types. First is {0} second is {1}.", source
.GetType ().FullName
, value.GetType ().FullName
));
587 return source
.RemoveImpl (value);
590 protected virtual Delegate
RemoveImpl (Delegate d
)
598 public static Delegate
RemoveAll (Delegate source
, Delegate
value)
600 Delegate tmp
= source
;
601 while ((source
= Delegate
.Remove (source
, value)) != tmp
)
607 public static bool operator == (Delegate d1
, Delegate d2
)
609 if ((object)d1
== null) {
610 if ((object)d2
== null)
613 } else if ((object) d2
== null)
616 return d1
.Equals (d2
);
619 public static bool operator != (Delegate d1
, Delegate d2
)
624 internal bool IsTransparentProxy ()
626 #if !FEATURE_REMOTING
629 return RemotingServices
.IsTransparentProxy (m_target
);
633 internal static Delegate
CreateDelegateNoSecurityCheck (RuntimeType type
, Object firstArgument
, MethodInfo method
)
635 return CreateDelegate_internal (type
, firstArgument
, method
, true);
638 /* Internal call largely inspired from MS Delegate.InternalAllocLike */
639 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
640 internal extern static MulticastDelegate
AllocDelegateLike_internal (Delegate d
);