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;
50 [ClassInterface (ClassInterfaceType.AutoDual)]
51 [System.Runtime.InteropServices.ComVisible (true)]
53 [StructLayout (LayoutKind.Sequential)]
54 public abstract class Delegate : ICloneable, ISerializable
56 #region Sync with object-internals.h
57 #pragma warning disable 169, 414, 649
58 private IntPtr method_ptr;
59 private IntPtr invoke_impl;
60 private object m_target;
61 private IntPtr method;
62 private IntPtr delegate_trampoline;
63 private IntPtr extra_arg;
64 private IntPtr method_code;
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 {
122 [MethodImplAttribute (MethodImplOptions.InternalCall)]
123 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
125 private static bool arg_type_match (Type delArgType, Type argType) {
126 bool match = delArgType == argType;
128 // Delegate contravariance
130 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
135 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
142 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
144 if (argType.IsValueType)
145 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
146 (boxedThis && delArgType == argType);
148 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
152 private static bool return_type_match (Type delReturnType, Type returnType) {
153 bool returnMatch = returnType == delReturnType;
156 // Delegate covariance
157 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
164 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
166 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
169 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
171 // The name of the parameter changed in 2.0
172 object target = firstArgument;
175 throw new ArgumentNullException ("type");
178 throw new ArgumentNullException ("method");
180 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
181 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
183 MethodInfo invoke = type.GetMethod ("Invoke");
185 if (!return_type_match (invoke.ReturnType, method.ReturnType)) {
186 if (throwOnBindFailure)
187 throw new ArgumentException ("method return type is incompatible");
192 ParameterInfo[] delargs = invoke.GetParametersInternal ();
193 ParameterInfo[] args = method.GetParametersInternal ();
197 if (target != null) {
198 // delegate closed over target
199 if (!method.IsStatic)
200 // target is passed as this
201 argLengthMatch = (args.Length == delargs.Length);
203 // target is passed as the first argument to the static method
204 argLengthMatch = (args.Length == delargs.Length + 1);
206 if (!method.IsStatic) {
208 // Net 2.0 feature. The first argument of the delegate is passed
209 // as the 'this' argument to the method.
211 argLengthMatch = (args.Length + 1 == delargs.Length);
214 // closed over a null reference
215 argLengthMatch = (args.Length == delargs.Length);
217 argLengthMatch = (args.Length == delargs.Length);
220 // closed over a null reference
221 argLengthMatch = args.Length == delargs.Length + 1;
224 if (!argLengthMatch) {
225 if (throwOnBindFailure)
226 throw new ArgumentException ("method argument length mismatch");
232 DelegateData delegate_data = new DelegateData ();
234 if (target != null) {
235 if (!method.IsStatic) {
236 argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
237 for (int i = 0; i < args.Length; i++)
238 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
240 argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
241 for (int i = 1; i < args.Length; i++)
242 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
244 delegate_data.curried_first_arg = true;
247 if (!method.IsStatic) {
248 if (args.Length + 1 == delargs.Length) {
249 // The first argument should match this
250 argsMatch = arg_type_match_this (delargs [0].ParameterType, method.DeclaringType, false);
251 for (int i = 0; i < args.Length; i++)
252 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
254 // closed over a null reference
255 argsMatch = allowClosed;
256 for (int i = 0; i < args.Length; i++)
257 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
260 if (delargs.Length + 1 == args.Length) {
261 // closed over a null reference
262 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
263 for (int i = 0; i < delargs.Length; i++)
264 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
266 delegate_data.curried_first_arg = true;
269 for (int i = 0; i < args.Length; i++)
270 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
276 if (throwOnBindFailure)
277 throw new ArgumentException ("method arguments are incompatible");
282 Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
284 d.original_method_info = method;
285 if (delegate_data != null)
286 d.data = delegate_data;
290 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
292 return CreateDelegate (type, firstArgument, method, true, true);
295 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
297 return CreateDelegate (type, null, method, throwOnBindFailure, false);
300 public static Delegate CreateDelegate (Type type, MethodInfo method)
302 return CreateDelegate (type, method, true);
305 public static Delegate CreateDelegate (Type type, object target, string method)
307 return CreateDelegate (type, target, method, false);
310 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
313 throw new ArgumentNullException ("type");
316 throw new ArgumentNullException ("method");
318 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
319 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
321 MethodInfo invoke = type.GetMethod ("Invoke");
322 ParameterInfo [] delargs = invoke.GetParametersInternal ();
323 Type[] delargtypes = new Type [delargs.Length];
325 for (int i=0; i<delargs.Length; i++)
326 delargtypes [i] = delargs [i].ParameterType;
329 * FIXME: we should check the caller has reflection permission
330 * or if it lives in the same assembly...
334 * since we need to walk the inheritance chain anyway to
335 * find private methods, adjust the bindingflags to ignore
338 BindingFlags flags = BindingFlags.ExactBinding |
339 BindingFlags.Public | BindingFlags.NonPublic |
340 BindingFlags.DeclaredOnly | bflags;
343 flags |= BindingFlags.IgnoreCase;
345 MethodInfo info = null;
347 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
348 MethodInfo mi = targetType.GetMethod (method, flags,
349 null, delargtypes, EmptyArray<ParameterModifier>.Value);
350 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
357 if (throwOnBindFailure)
358 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
366 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
369 throw new ArgumentNullException ("target");
371 MethodInfo info = GetCandidateMethod (type, target, method,
372 BindingFlags.Static, ignoreCase, throwOnBindFailure);
376 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
379 public static Delegate CreateDelegate (Type type, Type target, string method) {
380 return CreateDelegate (type, target, method, false, true);
383 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
384 return CreateDelegate (type, target, method, ignoreCase, true);
387 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
390 throw new ArgumentNullException ("target");
392 MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
393 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
397 return CreateDelegate_internal (type, target, info, throwOnBindFailure);
400 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
401 return CreateDelegate (type, target, method, ignoreCase, true);
404 public object DynamicInvoke (params object[] args)
406 return DynamicInvokeImpl (args);
409 void InitializeDelegateData ()
411 DelegateData delegate_data = new DelegateData ();
412 if (method_info.IsStatic) {
413 if (m_target != null) {
414 delegate_data.curried_first_arg = true;
416 MethodInfo invoke = GetType ().GetMethod ("Invoke");
417 if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
418 delegate_data.curried_first_arg = true;
421 this.data = delegate_data;
424 protected virtual object DynamicInvokeImpl (object[] args)
426 if (Method == null) {
427 Type[] mtypes = new Type [args.Length];
428 for (int i = 0; i < args.Length; ++i) {
429 mtypes [i] = args [i].GetType ();
431 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
434 var target = m_target;
435 if (this.data == null)
436 InitializeDelegateData ();
438 if (Method.IsStatic) {
440 // The delegate is bound to m_target
442 if (data.curried_first_arg) {
444 args = new [] { target };
446 Array.Resize (ref args, args.Length + 1);
447 Array.Copy (args, 0, args, 1, args.Length - 1);
454 if (m_target == null && args != null && args.Length > 0) {
456 Array.Copy (args, 1, args, 0, args.Length - 1);
457 Array.Resize (ref args, args.Length - 1);
461 return Method.Invoke (target, args);
464 public virtual object Clone ()
466 return MemberwiseClone ();
469 public override bool Equals (object obj)
471 Delegate d = obj as Delegate;
476 // Do not compare method_ptr, since it can point to a trampoline
477 if (d.m_target == m_target && d.Method == Method) {
478 if (d.data != null || data != null) {
480 if (d.data != null && data != null)
481 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
484 return d.data.target_type == null;
486 return data.target_type == null;
496 public override int GetHashCode ()
498 /* same implementation as CoreCLR */
499 return GetType ().GetHashCode ();
502 protected virtual MethodInfo GetMethodImpl ()
504 if (method_info != null) {
507 if (method != IntPtr.Zero) {
508 if (!method_is_virtual)
509 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
511 method_info = GetVirtualMethod_internal ();
517 // This is from ISerializable
518 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
520 DelegateSerializationHolder.GetDelegateData (this, info, context);
523 public virtual Delegate[] GetInvocationList()
525 return new Delegate[] { this };
529 /// Returns a new MulticastDelegate holding the
530 /// concatenated invocation lists of MulticastDelegates a and b
532 public static Delegate Combine (Delegate a, Delegate b)
540 if (a.GetType () != b.GetType ())
541 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", a.GetType ().FullName, b.GetType ().FullName));
543 return a.CombineImpl (b);
547 /// Returns a new MulticastDelegate holding the
548 /// concatenated invocation lists of an Array of MulticastDelegates
550 [System.Runtime.InteropServices.ComVisible (true)]
551 public static Delegate Combine (params Delegate[] delegates)
553 if (delegates == null)
556 Delegate retval = null;
558 foreach (Delegate next in delegates)
559 retval = Combine (retval, next);
564 protected virtual Delegate CombineImpl (Delegate d)
566 throw new MulticastNotSupportedException (String.Empty);
569 public static Delegate Remove (Delegate source, Delegate value)
577 if (source.GetType () != value.GetType ())
578 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", source.GetType ().FullName, value.GetType ().FullName));
580 return source.RemoveImpl (value);
583 protected virtual Delegate RemoveImpl (Delegate d)
591 public static Delegate RemoveAll (Delegate source, Delegate value)
593 Delegate tmp = source;
594 while ((source = Delegate.Remove (source, value)) != tmp)
600 public static bool operator == (Delegate d1, Delegate d2)
602 if ((object)d1 == null) {
603 if ((object)d2 == null)
606 } else if ((object) d2 == null)
609 return d1.Equals (d2);
612 public static bool operator != (Delegate d1, Delegate d2)
617 internal bool IsTransparentProxy ()
622 return RemotingServices.IsTransparentProxy (m_target);
626 internal static Delegate CreateDelegateNoSecurityCheck (RuntimeType type, Object firstArgument, MethodInfo method)
628 return CreateDelegate_internal (type, firstArgument, method, true);
631 /* Internal call largely inspired from MS Delegate.InternalAllocLike */
632 [MethodImplAttribute(MethodImplOptions.InternalCall)]
633 internal extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);