4064ed9c90185ac0eec46c390977a61b4340a0f9
[mono.git] / mcs / class / corlib / System / Delegate.cs
1 //
2 // System.Delegate.cs
3 //
4 // Authors:
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)
9 //
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)
13 //
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:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
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.
32 //
33
34 using System.Reflection;
35 using System.Runtime.Remoting;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39
40 namespace System
41 {
42         /* Contains the rarely used fields of Delegate */
43         sealed class DelegateData
44         {
45                 public Type target_type;
46                 public string method_name;
47                 public bool curried_first_arg;
48         }
49
50         [ClassInterface (ClassInterfaceType.AutoDual)]
51         [System.Runtime.InteropServices.ComVisible (true)]
52         [Serializable]
53         [StructLayout (LayoutKind.Sequential)]
54         public abstract class Delegate : ICloneable, ISerializable
55         {
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;
66
67                 // Keep a ref of the MethodInfo passed to CreateDelegate.
68                 // Used to keep DynamicMethods alive.
69                 private MethodInfo original_method_info;
70
71                 private DelegateData data;
72
73                 private bool method_is_virtual;
74 #pragma warning restore 169, 414, 649
75                 #endregion
76
77                 protected Delegate (object target, string method)
78                 {
79                         if (target == null)
80                                 throw new ArgumentNullException ("target");
81
82                         if (method == null)
83                                 throw new ArgumentNullException ("method");
84
85                         this.m_target = target;
86                         this.data = new DelegateData ();
87                         this.data.method_name = method;
88                 }
89
90                 protected Delegate (Type target, string method)
91                 {
92                         if (target == null)
93                                 throw new ArgumentNullException ("target");
94
95                         if (method == null)
96                                 throw new ArgumentNullException ("method");
97
98                         this.data = new DelegateData ();
99                         this.data.method_name = method;
100                         this.data.target_type = target;
101                 }
102
103                 public MethodInfo Method {
104                         get {
105                                 return GetMethodImpl ();
106                         }
107                 }
108
109                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
110                 extern MethodInfo GetVirtualMethod_internal ();
111
112                 public object Target {
113                         get {
114                                 return m_target;
115                         }
116                 }
117
118                 //
119                 // Methods
120                 //
121
122                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
123                 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
124
125                 private static bool arg_type_match (Type delArgType, Type argType) {
126                         bool match = delArgType == argType;
127
128                         // Delegate contravariance
129                         if (!match) {
130                                 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
131                                         match = true;
132                         }
133                         // enum basetypes
134                         if (!match) {
135                                 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
136                                         match = true;
137                         }
138
139                         return match;
140                 }
141
142                 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
143                         bool match;
144                         if (argType.IsValueType)
145                                 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
146                                                 (boxedThis && delArgType == argType);
147                         else
148                                 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
149
150                         return match;
151                 }
152                 private static bool return_type_match (Type delReturnType, Type returnType) {
153                         bool returnMatch = returnType == delReturnType;
154
155                         if (!returnMatch) {
156                                 // Delegate covariance
157                                 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
158                                         returnMatch = true;
159                         }
160
161                         return returnMatch;
162                 }
163
164                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
165                 {
166                         return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
167                 }
168
169                 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
170                 {
171                         // The name of the parameter changed in 2.0
172                         object target = firstArgument;
173
174                         if (type == null)
175                                 throw new ArgumentNullException ("type");
176
177                         if (method == null)
178                                 throw new ArgumentNullException ("method");
179
180                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
181                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
182
183                         MethodInfo invoke = type.GetMethod ("Invoke");
184
185                         if (!return_type_match (invoke.ReturnType, method.ReturnType)) {
186                                 if (throwOnBindFailure)
187                                         throw new ArgumentException ("method return type is incompatible");
188                                 else
189                                         return null;
190                         }
191
192                         ParameterInfo[] delargs = invoke.GetParametersInternal ();
193                         ParameterInfo[] args = method.GetParametersInternal ();
194
195                         bool argLengthMatch;
196
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);
202                                 else
203                                         // target is passed as the first argument to the static method
204                                         argLengthMatch = (args.Length == delargs.Length + 1);
205                         } else {
206                                 if (!method.IsStatic) {
207                                         //
208                                         // Net 2.0 feature. The first argument of the delegate is passed
209                                         // as the 'this' argument to the method.
210                                         //
211                                         argLengthMatch = (args.Length + 1 == delargs.Length);
212
213                                         if (!argLengthMatch)
214                                                 // closed over a null reference
215                                                 argLengthMatch = (args.Length == delargs.Length);
216                                 } else {
217                                         argLengthMatch = (args.Length == delargs.Length);
218
219                                         if (!argLengthMatch)
220                                                 // closed over a null reference
221                                                 argLengthMatch = args.Length == delargs.Length + 1;
222                                 }
223                         }
224                         if (!argLengthMatch) {
225                                 if (throwOnBindFailure)
226                                         throw new ArgumentException ("method argument length mismatch");
227                                 else
228                                         return null;
229                         }
230
231                         bool argsMatch;
232                         DelegateData delegate_data = new DelegateData ();
233
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);
239                                 } else {
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);
243
244                                         delegate_data.curried_first_arg = true;
245                                 }
246                         } else {
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);
253                                         } else {
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);
258                                         }
259                                 } else {
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);
265
266                                                 delegate_data.curried_first_arg = true;
267                                         } else {
268                                                 argsMatch = true;
269                                                 for (int i = 0; i < args.Length; i++)
270                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
271                                         }
272                                 }
273                         }
274
275                         if (!argsMatch) {
276                                 if (throwOnBindFailure)
277                                         throw new ArgumentException ("method arguments are incompatible");
278                                 else
279                                         return null;
280                         }
281
282                         Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
283                         if (d != null)
284                                 d.original_method_info = method;
285                         if (delegate_data != null)
286                                 d.data = delegate_data;
287                         return d;
288                 }
289
290                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
291                 {
292                         return CreateDelegate (type, firstArgument, method, true, true);
293                 }
294
295                 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
296                 {
297                         return CreateDelegate (type, null, method, throwOnBindFailure, false);
298                 }
299
300                 public static Delegate CreateDelegate (Type type, MethodInfo method)
301                 {
302                         return CreateDelegate (type, method, true);
303                 }
304
305                 public static Delegate CreateDelegate (Type type, object target, string method)
306                 {
307                         return CreateDelegate (type, target, method, false);
308                 }
309
310                 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
311                 {
312                         if (type == null)
313                                 throw new ArgumentNullException ("type");
314
315                         if (method == null)
316                                 throw new ArgumentNullException ("method");
317
318                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
319                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
320
321                         MethodInfo invoke = type.GetMethod ("Invoke");
322                         ParameterInfo [] delargs = invoke.GetParametersInternal ();
323                         Type[] delargtypes = new Type [delargs.Length];
324
325                         for (int i=0; i<delargs.Length; i++)
326                                 delargtypes [i] = delargs [i].ParameterType;
327
328                         /* 
329                          * FIXME: we should check the caller has reflection permission
330                          * or if it lives in the same assembly...
331                          */
332
333                         /*
334                          * since we need to walk the inheritance chain anyway to
335                          * find private methods, adjust the bindingflags to ignore
336                          * inherited methods
337                          */
338                         BindingFlags flags = BindingFlags.ExactBinding |
339                                 BindingFlags.Public | BindingFlags.NonPublic |
340                                 BindingFlags.DeclaredOnly | bflags;
341
342                         if (ignoreCase)
343                                 flags |= BindingFlags.IgnoreCase;
344
345                         MethodInfo info = null;
346
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)) {
351                                         info = mi;
352                                         break;
353                                 }
354                         }
355
356                         if (info == null) {
357                                 if (throwOnBindFailure)
358                                         throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
359                                 else
360                                         return null;
361                         }
362
363                         return info;
364                 }
365
366                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
367                 {
368                         if (target == null)
369                                 throw new ArgumentNullException ("target");
370
371                         MethodInfo info = GetCandidateMethod (type, target, method,
372                                 BindingFlags.Static, ignoreCase, throwOnBindFailure);
373                         if (info == null)
374                                 return null;
375
376                         return CreateDelegate_internal (type, null, info, throwOnBindFailure);
377                 }
378
379                 public static Delegate CreateDelegate (Type type, Type target, string method) {
380                         return CreateDelegate (type, target, method, false, true);
381                 }
382
383                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
384                         return CreateDelegate (type, target, method, ignoreCase, true);
385                 }
386
387                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
388                 {
389                         if (target == null)
390                                 throw new ArgumentNullException ("target");
391
392                         MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
393                                 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
394                         if (info == null)
395                                 return null;
396
397                         return CreateDelegate_internal (type, target, info, throwOnBindFailure);
398                 }
399
400                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
401                         return CreateDelegate (type, target, method, ignoreCase, true);
402                 }
403
404                 public object DynamicInvoke (params object[] args)
405                 {
406                         return DynamicInvokeImpl (args);
407                 }
408
409                 void InitializeDelegateData ()
410                 {
411                         DelegateData delegate_data = new DelegateData ();
412                         if (method_info.IsStatic) {
413                                 if (m_target != null) {
414                                         delegate_data.curried_first_arg = true;
415                                 } else {
416                                         MethodInfo invoke = GetType ().GetMethod ("Invoke");
417                                         if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
418                                                 delegate_data.curried_first_arg = true;
419                                 }
420                         }
421                         this.data = delegate_data;
422                 }
423
424                 protected virtual object DynamicInvokeImpl (object[] args)
425                 {
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 ();
430                                 }
431                                 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
432                         }
433
434                         var target = m_target;
435                         if (this.data == null)
436                                 InitializeDelegateData ();
437
438                         if (Method.IsStatic) {
439                                 //
440                                 // The delegate is bound to m_target
441                                 //
442                                 if (data.curried_first_arg) {
443                                         if (args == null) {
444                                                 args = new [] { target };
445                                         } else {
446                                                 Array.Resize (ref args, args.Length + 1);
447                                                 Array.Copy (args, 0, args, 1, args.Length - 1);
448                                                 args [0] = target;
449                                         }
450
451                                         target = null;
452                                 }
453                         } else {
454                                 if (m_target == null && args != null && args.Length > 0) {
455                                         target = args [0];
456                                         Array.Copy (args, 1, args, 0, args.Length - 1);
457                                         Array.Resize (ref args, args.Length - 1);
458                                 }
459                         }
460
461                         return Method.Invoke (target, args);
462                 }
463
464                 public virtual object Clone ()
465                 {
466                         return MemberwiseClone ();
467                 }
468
469                 public override bool Equals (object obj)
470                 {
471                         Delegate d = obj as Delegate;
472
473                         if (d == null)
474                                 return false;
475
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) {
479                                         /* Uncommon case */
480                                         if (d.data != null && data != null)
481                                                 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
482                                         else {
483                                                 if (d.data != null)
484                                                         return d.data.target_type == null;
485                                                 if (data != null)
486                                                         return data.target_type == null;
487                                                 return false;
488                                         }
489                                 }
490                                 return true;
491                         }
492
493                         return false;
494                 }
495
496                 public override int GetHashCode ()
497                 {
498                         /* same implementation as CoreCLR */
499                         return GetType ().GetHashCode ();
500                 }
501
502                 protected virtual MethodInfo GetMethodImpl ()
503                 {
504                         if (method_info != null) {
505                                 return method_info;
506                         } else {
507                                 if (method != IntPtr.Zero) {
508                                         if (!method_is_virtual)
509                                                 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
510                                         else
511                                                 method_info = GetVirtualMethod_internal ();
512                                 }
513                                 return method_info;
514                         }
515                 }
516
517                 // This is from ISerializable
518                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
519                 {
520                         DelegateSerializationHolder.GetDelegateData (this, info, context);
521                 }
522
523                 public virtual Delegate[] GetInvocationList()
524                 {
525                         return new Delegate[] { this };
526                 }
527
528                 /// <symmary>
529                 ///   Returns a new MulticastDelegate holding the
530                 ///   concatenated invocation lists of MulticastDelegates a and b
531                 /// </symmary>
532                 public static Delegate Combine (Delegate a, Delegate b)
533                 {
534                         if (a == null)
535                                 return b;
536
537                         if (b == null)
538                                 return a;
539
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));
542
543                         return a.CombineImpl (b);
544                 }
545
546                 /// <symmary>
547                 ///   Returns a new MulticastDelegate holding the
548                 ///   concatenated invocation lists of an Array of MulticastDelegates
549                 /// </symmary>
550                 [System.Runtime.InteropServices.ComVisible (true)]
551                 public static Delegate Combine (params Delegate[] delegates)
552                 {
553                         if (delegates == null)
554                                 return null;
555
556                         Delegate retval = null;
557
558                         foreach (Delegate next in delegates)
559                                 retval = Combine (retval, next);
560
561                         return retval;
562                 }
563
564                 protected virtual Delegate CombineImpl (Delegate d)
565                 {
566                         throw new MulticastNotSupportedException (String.Empty);
567                 }
568
569                 public static Delegate Remove (Delegate source, Delegate value) 
570                 {
571                         if (source == null)
572                                 return null;
573
574                         if (value == null)
575                                 return source;
576
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));
579
580                         return source.RemoveImpl (value);
581                 }
582
583                 protected virtual Delegate RemoveImpl (Delegate d)
584                 {
585                         if (this.Equals (d))
586                                 return null;
587
588                         return this;
589                 }
590
591                 public static Delegate RemoveAll (Delegate source, Delegate value)
592                 {
593                         Delegate tmp = source;
594                         while ((source = Delegate.Remove (source, value)) != tmp)
595                                 tmp = source;
596
597                         return tmp;
598                 }
599
600                 public static bool operator == (Delegate d1, Delegate d2)
601                 {
602                         if ((object)d1 == null) {
603                                 if ((object)d2 == null)
604                                         return true;
605                                 return false;
606                         } else if ((object) d2 == null)
607                                 return false;
608                         
609                         return d1.Equals (d2);
610                 }
611
612                 public static bool operator != (Delegate d1, Delegate d2)
613                 {
614                         return !(d1 == d2);
615                 }
616
617                 internal bool IsTransparentProxy ()
618                 {
619 #if DISABLE_REMOTING
620                         return false;
621 #else
622                         return RemotingServices.IsTransparentProxy (m_target);
623 #endif
624                 }
625
626                 internal static Delegate CreateDelegateNoSecurityCheck (RuntimeType type, Object firstArgument, MethodInfo method)
627                 {
628                         return CreateDelegate_internal (type, firstArgument, method, true);
629                 }
630
631                 /* Internal call largely inspired from MS Delegate.InternalAllocLike */
632                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
633                 internal extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);
634         }
635 }