Merge pull request #1971 from angeloc/master
[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 rgctx;
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                                 if (method_info != null) {
106                                         return method_info;
107                                 } else {
108                                         if (method != IntPtr.Zero) {
109                                                 if (!method_is_virtual)
110                                                         method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
111                                                 else
112                                                         method_info = GetVirtualMethod_internal ();
113                                         }
114                                         return method_info;
115                                 }
116                         }
117                 }
118
119                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
120                 extern MethodInfo GetVirtualMethod_internal ();
121
122                 public object Target {
123                         get {
124                                 return m_target;
125                         }
126                 }
127
128                 //
129                 // Methods
130                 //
131
132                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
133                 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
134
135                 private static bool arg_type_match (Type delArgType, Type argType) {
136                         bool match = delArgType == argType;
137
138                         // Delegate contravariance
139                         if (!match) {
140                                 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
141                                         match = true;
142                         }
143                         // enum basetypes
144                         if (!match) {
145                                 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
146                                         match = true;
147                         }
148
149                         return match;
150                 }
151
152                 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
153                         bool match;
154                         if (argType.IsValueType)
155                                 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
156                                                 (boxedThis && delArgType == argType);
157                         else
158                                 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
159
160                         return match;
161                 }
162                 private static bool return_type_match (Type delReturnType, Type returnType) {
163                         bool returnMatch = returnType == delReturnType;
164
165                         if (!returnMatch) {
166                                 // Delegate covariance
167                                 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
168                                         returnMatch = true;
169                         }
170
171                         return returnMatch;
172                 }
173
174                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
175                 {
176                         return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
177                 }
178
179                 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
180                 {
181                         // The name of the parameter changed in 2.0
182                         object target = firstArgument;
183
184                         if (type == null)
185                                 throw new ArgumentNullException ("type");
186
187                         if (method == null)
188                                 throw new ArgumentNullException ("method");
189
190                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
191                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
192
193                         MethodInfo invoke = type.GetMethod ("Invoke");
194
195                         if (!return_type_match (invoke.ReturnType, method.ReturnType)) {
196                                 if (throwOnBindFailure)
197                                         throw new ArgumentException ("method return type is incompatible");
198                                 else
199                                         return null;
200                         }
201
202                         ParameterInfo[] delargs = invoke.GetParametersInternal ();
203                         ParameterInfo[] args = method.GetParametersInternal ();
204
205                         bool argLengthMatch;
206
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);
212                                 else
213                                         // target is passed as the first argument to the static method
214                                         argLengthMatch = (args.Length == delargs.Length + 1);
215                         } else {
216                                 if (!method.IsStatic) {
217                                         //
218                                         // Net 2.0 feature. The first argument of the delegate is passed
219                                         // as the 'this' argument to the method.
220                                         //
221                                         argLengthMatch = (args.Length + 1 == delargs.Length);
222
223                                         if (!argLengthMatch)
224                                                 // closed over a null reference
225                                                 argLengthMatch = (args.Length == delargs.Length);
226                                 } else {
227                                         argLengthMatch = (args.Length == delargs.Length);
228
229                                         if (!argLengthMatch)
230                                                 // closed over a null reference
231                                                 argLengthMatch = args.Length == delargs.Length + 1;
232                                 }
233                         }
234                         if (!argLengthMatch) {
235                                 if (throwOnBindFailure)
236                                         throw new ArgumentException ("method argument length mismatch");
237                                 else
238                                         return null;
239                         }
240
241                         bool argsMatch;
242                         DelegateData delegate_data = new DelegateData ();
243
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);
249                                 } else {
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);
253
254                                         delegate_data.curried_first_arg = true;
255                                 }
256                         } else {
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);
263                                         } else {
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);
268                                         }
269                                 } else {
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);
275
276                                                 delegate_data.curried_first_arg = true;
277                                         } else {
278                                                 argsMatch = true;
279                                                 for (int i = 0; i < args.Length; i++)
280                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
281                                         }
282                                 }
283                         }
284
285                         if (!argsMatch) {
286                                 if (throwOnBindFailure)
287                                         throw new ArgumentException ("method arguments are incompatible");
288                                 else
289                                         return null;
290                         }
291
292                         Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
293                         if (d != null)
294                                 d.original_method_info = method;
295                         if (delegate_data != null)
296                                 d.data = delegate_data;
297                         return d;
298                 }
299
300                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
301                 {
302                         return CreateDelegate (type, firstArgument, method, true, true);
303                 }
304
305                 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
306                 {
307                         return CreateDelegate (type, null, method, throwOnBindFailure, false);
308                 }
309
310                 public static Delegate CreateDelegate (Type type, MethodInfo method)
311                 {
312                         return CreateDelegate (type, method, true);
313                 }
314
315                 public static Delegate CreateDelegate (Type type, object target, string method)
316                 {
317                         return CreateDelegate (type, target, method, false);
318                 }
319
320                 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
321                 {
322                         if (type == null)
323                                 throw new ArgumentNullException ("type");
324
325                         if (method == null)
326                                 throw new ArgumentNullException ("method");
327
328                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
329                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
330
331                         MethodInfo invoke = type.GetMethod ("Invoke");
332                         ParameterInfo [] delargs = invoke.GetParametersInternal ();
333                         Type[] delargtypes = new Type [delargs.Length];
334
335                         for (int i=0; i<delargs.Length; i++)
336                                 delargtypes [i] = delargs [i].ParameterType;
337
338                         /* 
339                          * FIXME: we should check the caller has reflection permission
340                          * or if it lives in the same assembly...
341                          */
342
343                         /*
344                          * since we need to walk the inheritance chain anyway to
345                          * find private methods, adjust the bindingflags to ignore
346                          * inherited methods
347                          */
348                         BindingFlags flags = BindingFlags.ExactBinding |
349                                 BindingFlags.Public | BindingFlags.NonPublic |
350                                 BindingFlags.DeclaredOnly | bflags;
351
352                         if (ignoreCase)
353                                 flags |= BindingFlags.IgnoreCase;
354
355                         MethodInfo info = null;
356
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)) {
361                                         info = mi;
362                                         break;
363                                 }
364                         }
365
366                         if (info == null) {
367                                 if (throwOnBindFailure)
368                                         throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
369                                 else
370                                         return null;
371                         }
372
373                         return info;
374                 }
375
376                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
377                 {
378                         if (target == null)
379                                 throw new ArgumentNullException ("target");
380
381                         MethodInfo info = GetCandidateMethod (type, target, method,
382                                 BindingFlags.Static, ignoreCase, throwOnBindFailure);
383                         if (info == null)
384                                 return null;
385
386                         return CreateDelegate_internal (type, null, info, throwOnBindFailure);
387                 }
388
389                 public static Delegate CreateDelegate (Type type, Type target, string method) {
390                         return CreateDelegate (type, target, method, false, true);
391                 }
392
393                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
394                         return CreateDelegate (type, target, method, ignoreCase, true);
395                 }
396
397                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
398                 {
399                         if (target == null)
400                                 throw new ArgumentNullException ("target");
401
402                         MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
403                                 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
404                         if (info == null)
405                                 return null;
406
407                         return CreateDelegate_internal (type, target, info, throwOnBindFailure);
408                 }
409
410                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
411                         return CreateDelegate (type, target, method, ignoreCase, true);
412                 }
413
414                 public object DynamicInvoke (params object[] args)
415                 {
416                         return DynamicInvokeImpl (args);
417                 }
418
419                 void InitializeDelegateData ()
420                 {
421                         DelegateData delegate_data = new DelegateData ();
422                         if (method_info.IsStatic) {
423                                 if (m_target != null) {
424                                         delegate_data.curried_first_arg = true;
425                                 } else {
426                                         MethodInfo invoke = GetType ().GetMethod ("Invoke");
427                                         if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
428                                                 delegate_data.curried_first_arg = true;
429                                 }
430                         }
431                         this.data = delegate_data;
432                 }
433
434                 protected virtual object DynamicInvokeImpl (object[] args)
435                 {
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 ();
440                                 }
441                                 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
442                         }
443
444                         var target = m_target;
445                         if (this.data == null)
446                                 InitializeDelegateData ();
447
448                         if (Method.IsStatic) {
449                                 //
450                                 // The delegate is bound to m_target
451                                 //
452                                 if (data.curried_first_arg) {
453                                         if (args == null) {
454                                                 args = new [] { target };
455                                         } else {
456                                                 Array.Resize (ref args, args.Length + 1);
457                                                 Array.Copy (args, 0, args, 1, args.Length - 1);
458                                                 args [0] = target;
459                                         }
460
461                                         target = null;
462                                 }
463                         } else {
464                                 if (m_target == null && args != null && args.Length > 0) {
465                                         target = args [0];
466                                         Array.Copy (args, 1, args, 0, args.Length - 1);
467                                         Array.Resize (ref args, args.Length - 1);
468                                 }
469                         }
470
471                         return Method.Invoke (target, args);
472                 }
473
474                 public virtual object Clone ()
475                 {
476                         return MemberwiseClone ();
477                 }
478
479                 public override bool Equals (object obj)
480                 {
481                         Delegate d = obj as Delegate;
482
483                         if (d == null)
484                                 return false;
485
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) {
489                                         /* Uncommon case */
490                                         if (d.data != null && data != null)
491                                                 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
492                                         else {
493                                                 if (d.data != null)
494                                                         return d.data.target_type == null;
495                                                 if (data != null)
496                                                         return data.target_type == null;
497                                                 return false;
498                                         }
499                                 }
500                                 return true;
501                         }
502
503                         return false;
504                 }
505
506                 public override int GetHashCode ()
507                 {
508                         /* same implementation as CoreCLR */
509                         return GetType ().GetHashCode ();
510                 }
511
512                 protected virtual MethodInfo GetMethodImpl ()
513                 {
514                         return Method;
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                         return a.CombineImpl (b);
538                 }
539
540                 /// <symmary>
541                 ///   Returns a new MulticastDelegate holding the
542                 ///   concatenated invocation lists of an Array of MulticastDelegates
543                 /// </symmary>
544                 [System.Runtime.InteropServices.ComVisible (true)]
545                 public static Delegate Combine (params Delegate[] delegates)
546                 {
547                         if (delegates == null)
548                                 return null;
549
550                         Delegate retval = null;
551
552                         foreach (Delegate next in delegates)
553                                 retval = Combine (retval, next);
554
555                         return retval;
556                 }
557
558                 protected virtual Delegate CombineImpl (Delegate d)
559                 {
560                         throw new MulticastNotSupportedException (String.Empty);
561                 }
562
563                 public static Delegate Remove (Delegate source, Delegate value) 
564                 {
565                         if (source == null)
566                                 return null;
567
568                         if (value == null)
569                                 return source;
570
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));
573
574                         return source.RemoveImpl (value);
575                 }
576
577                 protected virtual Delegate RemoveImpl (Delegate d)
578                 {
579                         if (this.Equals (d))
580                                 return null;
581
582                         return this;
583                 }
584
585                 public static Delegate RemoveAll (Delegate source, Delegate value)
586                 {
587                         Delegate tmp = source;
588                         while ((source = Delegate.Remove (source, value)) != tmp)
589                                 tmp = source;
590
591                         return tmp;
592                 }
593
594                 public static bool operator == (Delegate d1, Delegate d2)
595                 {
596                         if ((object)d1 == null) {
597                                 if ((object)d2 == null)
598                                         return true;
599                                 return false;
600                         } else if ((object) d2 == null)
601                                 return false;
602                         
603                         return d1.Equals (d2);
604                 }
605
606                 public static bool operator != (Delegate d1, Delegate d2)
607                 {
608                         return !(d1 == d2);
609                 }
610
611                 internal bool IsTransparentProxy ()
612                 {
613 #if DISABLE_REMOTING
614                         return false;
615 #else
616                         return RemotingServices.IsTransparentProxy (m_target);
617 #endif
618                 }
619
620                 internal static Delegate CreateDelegateNoSecurityCheck (RuntimeType type, Object firstArgument, MethodInfo method)
621                 {
622                         return CreateDelegate_internal (type, firstArgument, method, true);
623                 }
624
625                 /* Internal call largely inspired from MS Delegate.InternalAllocLike */
626                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
627                 internal extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);
628         }
629 }