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;
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 if (method_info != null) {
108 if (method != IntPtr.Zero) {
109 if (!method_is_virtual)
110 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
112 method_info = GetVirtualMethod_internal ();
119 [MethodImplAttribute (MethodImplOptions.InternalCall)]
120 extern MethodInfo GetVirtualMethod_internal ();
122 public object Target {
132 [MethodImplAttribute (MethodImplOptions.InternalCall)]
133 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
135 private static bool arg_type_match (Type delArgType, Type argType) {
136 bool match = delArgType == argType;
138 // Delegate contravariance
140 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
145 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
152 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
154 if (argType.IsValueType)
155 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
156 (boxedThis && delArgType == argType);
158 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
162 private static bool return_type_match (Type delReturnType, Type returnType) {
163 bool returnMatch = returnType == delReturnType;
166 // Delegate covariance
167 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
174 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
176 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
179 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
181 // The name of the parameter changed in 2.0
182 object target = firstArgument;
185 throw new ArgumentNullException ("type");
188 throw new ArgumentNullException ("method");
190 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
191 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
193 MethodInfo invoke = type.GetMethod ("Invoke");
195 if (!return_type_match (invoke.ReturnType, method.ReturnType)) {
196 if (throwOnBindFailure)
197 throw new ArgumentException ("method return type is incompatible");
202 ParameterInfo[] delargs = invoke.GetParametersInternal ();
203 ParameterInfo[] args = method.GetParametersInternal ();
207 if (target != null) {
208 // delegate closed over target
209 if (!method.IsStatic)
210 // target is passed as this
211 argLengthMatch = (args.Length == delargs.Length);
213 // target is passed as the first argument to the static method
214 argLengthMatch = (args.Length == delargs.Length + 1);
216 if (!method.IsStatic) {
218 // Net 2.0 feature. The first argument of the delegate is passed
219 // as the 'this' argument to the method.
221 argLengthMatch = (args.Length + 1 == delargs.Length);
224 // closed over a null reference
225 argLengthMatch = (args.Length == delargs.Length);
227 argLengthMatch = (args.Length == delargs.Length);
230 // closed over a null reference
231 argLengthMatch = args.Length == delargs.Length + 1;
234 if (!argLengthMatch) {
235 if (throwOnBindFailure)
236 throw new ArgumentException ("method argument length mismatch");
242 DelegateData delegate_data = new DelegateData ();
244 if (target != null) {
245 if (!method.IsStatic) {
246 argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
247 for (int i = 0; i < args.Length; i++)
248 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
250 argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
251 for (int i = 1; i < args.Length; i++)
252 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
254 delegate_data.curried_first_arg = true;
257 if (!method.IsStatic) {
258 if (args.Length + 1 == delargs.Length) {
259 // The first argument should match this
260 argsMatch = arg_type_match_this (delargs [0].ParameterType, method.DeclaringType, false);
261 for (int i = 0; i < args.Length; i++)
262 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
264 // closed over a null reference
265 argsMatch = allowClosed;
266 for (int i = 0; i < args.Length; i++)
267 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
270 if (delargs.Length + 1 == args.Length) {
271 // closed over a null reference
272 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
273 for (int i = 0; i < delargs.Length; i++)
274 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
276 delegate_data.curried_first_arg = true;
279 for (int i = 0; i < args.Length; i++)
280 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
286 if (throwOnBindFailure)
287 throw new ArgumentException ("method arguments are incompatible");
292 Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
294 d.original_method_info = method;
295 if (delegate_data != null)
296 d.data = delegate_data;
300 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
302 return CreateDelegate (type, firstArgument, method, true, true);
305 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
307 return CreateDelegate (type, null, method, throwOnBindFailure, false);
310 public static Delegate CreateDelegate (Type type, MethodInfo method)
312 return CreateDelegate (type, method, true);
315 public static Delegate CreateDelegate (Type type, object target, string method)
317 return CreateDelegate (type, target, method, false);
320 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
323 throw new ArgumentNullException ("type");
326 throw new ArgumentNullException ("method");
328 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
329 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
331 MethodInfo invoke = type.GetMethod ("Invoke");
332 ParameterInfo [] delargs = invoke.GetParametersInternal ();
333 Type[] delargtypes = new Type [delargs.Length];
335 for (int i=0; i<delargs.Length; i++)
336 delargtypes [i] = delargs [i].ParameterType;
339 * FIXME: we should check the caller has reflection permission
340 * or if it lives in the same assembly...
344 * since we need to walk the inheritance chain anyway to
345 * find private methods, adjust the bindingflags to ignore
348 BindingFlags flags = BindingFlags.ExactBinding |
349 BindingFlags.Public | BindingFlags.NonPublic |
350 BindingFlags.DeclaredOnly | bflags;
353 flags |= BindingFlags.IgnoreCase;
355 MethodInfo info = null;
357 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
358 MethodInfo mi = targetType.GetMethod (method, flags,
359 null, delargtypes, EmptyArray<ParameterModifier>.Value);
360 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
367 if (throwOnBindFailure)
368 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
376 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
379 throw new ArgumentNullException ("target");
381 MethodInfo info = GetCandidateMethod (type, target, method,
382 BindingFlags.Static, ignoreCase, throwOnBindFailure);
386 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
389 public static Delegate CreateDelegate (Type type, Type target, string method) {
390 return CreateDelegate (type, target, method, false, true);
393 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
394 return CreateDelegate (type, target, method, ignoreCase, true);
397 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
400 throw new ArgumentNullException ("target");
402 MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
403 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
407 return CreateDelegate_internal (type, target, info, throwOnBindFailure);
410 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
411 return CreateDelegate (type, target, method, ignoreCase, true);
414 public object DynamicInvoke (params object[] args)
416 return DynamicInvokeImpl (args);
419 void InitializeDelegateData ()
421 DelegateData delegate_data = new DelegateData ();
422 if (method_info.IsStatic) {
423 if (m_target != null) {
424 delegate_data.curried_first_arg = true;
426 MethodInfo invoke = GetType ().GetMethod ("Invoke");
427 if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
428 delegate_data.curried_first_arg = true;
431 this.data = delegate_data;
434 protected virtual object DynamicInvokeImpl (object[] args)
436 if (Method == null) {
437 Type[] mtypes = new Type [args.Length];
438 for (int i = 0; i < args.Length; ++i) {
439 mtypes [i] = args [i].GetType ();
441 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
444 var target = m_target;
445 if (this.data == null)
446 InitializeDelegateData ();
448 if (Method.IsStatic) {
450 // The delegate is bound to m_target
452 if (data.curried_first_arg) {
454 args = new [] { target };
456 Array.Resize (ref args, args.Length + 1);
457 Array.Copy (args, 0, args, 1, args.Length - 1);
464 if (m_target == null && args != null && args.Length > 0) {
466 Array.Copy (args, 1, args, 0, args.Length - 1);
467 Array.Resize (ref args, args.Length - 1);
471 return Method.Invoke (target, args);
474 public virtual object Clone ()
476 return MemberwiseClone ();
479 public override bool Equals (object obj)
481 Delegate d = obj as Delegate;
486 // Do not compare method_ptr, since it can point to a trampoline
487 if (d.m_target == m_target && d.Method == Method) {
488 if (d.data != null || data != null) {
490 if (d.data != null && data != null)
491 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
494 return d.data.target_type == null;
496 return data.target_type == null;
506 public override int GetHashCode ()
508 /* same implementation as CoreCLR */
509 return GetType ().GetHashCode ();
512 protected virtual MethodInfo GetMethodImpl ()
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)
537 return a.CombineImpl (b);
541 /// Returns a new MulticastDelegate holding the
542 /// concatenated invocation lists of an Array of MulticastDelegates
544 [System.Runtime.InteropServices.ComVisible (true)]
545 public static Delegate Combine (params Delegate[] delegates)
547 if (delegates == null)
550 Delegate retval = null;
552 foreach (Delegate next in delegates)
553 retval = Combine (retval, next);
558 protected virtual Delegate CombineImpl (Delegate d)
560 throw new MulticastNotSupportedException (String.Empty);
563 public static Delegate Remove (Delegate source, Delegate value)
571 if (source.GetType () != value.GetType ())
572 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", source.GetType ().FullName, value.GetType ().FullName));
574 return source.RemoveImpl (value);
577 protected virtual Delegate RemoveImpl (Delegate d)
585 public static Delegate RemoveAll (Delegate source, Delegate value)
587 Delegate tmp = source;
588 while ((source = Delegate.Remove (source, value)) != tmp)
594 public static bool operator == (Delegate d1, Delegate d2)
596 if ((object)d1 == null) {
597 if ((object)d2 == null)
600 } else if ((object) d2 == null)
603 return d1.Equals (d2);
606 public static bool operator != (Delegate d1, Delegate d2)
611 internal bool IsTransparentProxy ()
616 return RemotingServices.IsTransparentProxy (m_target);
620 internal static Delegate CreateDelegateNoSecurityCheck (RuntimeType type, Object firstArgument, MethodInfo method)
622 return CreateDelegate_internal (type, firstArgument, method, true);
625 /* Internal call largely inspired from MS Delegate.InternalAllocLike */
626 [MethodImplAttribute(MethodImplOptions.InternalCall)]
627 internal extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);