Merge pull request #487 from mayerwin/patch-1
[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 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 //
11
12 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System.Reflection;
36 using System.Runtime.Remoting;
37 using System.Runtime.Serialization;
38 using System.Runtime.CompilerServices;
39 using System.Runtime.InteropServices;
40
41 namespace System
42 {
43         /* Contains the rarely used fields of Delegate */
44         class DelegateData {
45                 public Type target_type;
46                 public string method_name;
47         }
48
49         [ClassInterface (ClassInterfaceType.AutoDual)]
50         [System.Runtime.InteropServices.ComVisible (true)]
51         [Serializable]
52         [StructLayout (LayoutKind.Sequential)]
53         public abstract class Delegate : ICloneable, ISerializable
54         {
55                 #region Sync with object-internals.h
56 #pragma warning disable 169, 414, 649
57                 private IntPtr method_ptr;
58                 private IntPtr invoke_impl;
59                 private object m_target;
60                 private IntPtr method;
61                 private IntPtr delegate_trampoline;
62                 private IntPtr method_code;
63                 private MethodInfo method_info;
64
65                 // Keep a ref of the MethodInfo passed to CreateDelegate.
66                 // Used to keep DynamicMethods alive.
67                 private MethodInfo original_method_info;
68
69                 private DelegateData data;
70 #pragma warning restore 169, 414, 649
71                 #endregion
72
73                 protected Delegate (object target, string method)
74                 {
75                         if (target == null)
76                                 throw new ArgumentNullException ("target");
77
78                         if (method == null)
79                                 throw new ArgumentNullException ("method");
80
81                         this.m_target = target;
82                         this.data = new DelegateData ();
83                         this.data.method_name = method;
84                 }
85
86                 protected Delegate (Type target, string method)
87                 {
88                         if (target == null)
89                                 throw new ArgumentNullException ("target");
90
91                         if (method == null)
92                                 throw new ArgumentNullException ("method");
93
94                         this.data = new DelegateData ();
95                         this.data.method_name = method;
96                         this.data.target_type = target;
97                 }
98
99                 public MethodInfo Method {
100                         get {
101                                 if (method_info != null) {
102                                         return method_info;
103                                 } else {
104                                         if (method != IntPtr.Zero) {
105                                                 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
106                                         }
107                                         return method_info;
108                                 }
109                         }
110                 }
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                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
126                 internal extern void SetMulticastInvoke ();
127
128                 private static bool arg_type_match (Type delArgType, Type argType) {
129                         bool match = delArgType == argType;
130
131                         // Delegate contravariance
132                         if (!match) {
133                                 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
134                                         match = true;
135                         }
136                         // enum basetypes
137                         if (!match) {
138                                 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
139                                         match = true;
140                         }
141
142                         return match;
143                 }
144
145                 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
146                         bool match;
147                         if (argType.IsValueType)
148                                 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
149                                                 (boxedThis && delArgType == argType);
150                         else
151                                 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
152
153                         return match;
154                 }
155                 private static bool return_type_match (Type delReturnType, Type returnType) {
156                         bool returnMatch = returnType == delReturnType;
157
158                         if (!returnMatch) {
159                                 // Delegate covariance
160                                 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
161                                         returnMatch = true;
162                         }
163
164                         return returnMatch;
165                 }
166
167                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
168                 {
169                         return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
170                 }
171
172                 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
173                 {
174                         // The name of the parameter changed in 2.0
175                         object target = firstArgument;
176
177                         if (type == null)
178                                 throw new ArgumentNullException ("type");
179
180                         if (method == null)
181                                 throw new ArgumentNullException ("method");
182
183                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
184                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
185
186                         MethodInfo invoke = type.GetMethod ("Invoke");
187
188                         if (!return_type_match (invoke.ReturnType, method.ReturnType))
189                                 if (throwOnBindFailure)
190                                         throw new ArgumentException ("method return type is incompatible");
191                                 else
192                                         return null;
193
194                         ParameterInfo[] delargs = invoke.GetParameters ();
195                         ParameterInfo[] args = method.GetParameters ();
196
197                         bool argLengthMatch;
198
199                         if (target != null) {
200                                 // delegate closed over target
201                                 if (!method.IsStatic)
202                                         // target is passed as this
203                                         argLengthMatch = (args.Length == delargs.Length);
204                                 else
205                                         // target is passed as the first argument to the static method
206                                         argLengthMatch = (args.Length == delargs.Length + 1);
207                         } else {
208                                 if (!method.IsStatic) {
209                                         //
210                                         // Net 2.0 feature. The first argument of the delegate is passed
211                                         // as the 'this' argument to the method.
212                                         //
213                                         argLengthMatch = (args.Length + 1 == delargs.Length);
214
215                                         if (!argLengthMatch)
216                                                 // closed over a null reference
217                                                 argLengthMatch = (args.Length == delargs.Length);
218                                 } else {
219                                         argLengthMatch = (args.Length == delargs.Length);
220
221                                         if (!argLengthMatch)
222                                                 // closed over a null reference
223                                                 argLengthMatch = args.Length == delargs.Length + 1;
224                                 }
225                         }
226                         if (!argLengthMatch)
227                                 if (throwOnBindFailure)
228                                         throw new ArgumentException ("method argument length mismatch");
229                                 else
230                                         return null;
231
232                         bool argsMatch;
233                         if (target != null) {
234                                 if (!method.IsStatic) {
235                                         argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
236                                         for (int i = 0; i < args.Length; i++)
237                                                 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
238                                 } else {
239                                         argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
240                                         for (int i = 1; i < args.Length; i++)
241                                                 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);                                    
242                                 }
243                         } else {
244                                 if (!method.IsStatic) {
245                                         if (args.Length + 1 == delargs.Length) {
246                                                 // The first argument should match this
247                                                 argsMatch = arg_type_match_this (delargs [0].ParameterType, method.DeclaringType, false);
248                                                 for (int i = 0; i < args.Length; i++)
249                                                         argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
250                                         } else {
251                                                 // closed over a null reference
252                                                 argsMatch = allowClosed;
253                                                 for (int i = 0; i < args.Length; i++)
254                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
255                                         }
256                                 } else {
257                                         if (delargs.Length + 1 == args.Length) {
258                                                 // closed over a null reference
259                                                 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
260                                                 for (int i = 0; i < delargs.Length; i++)
261                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
262                                         } else {
263                                                 argsMatch = true;
264                                                 for (int i = 0; i < args.Length; i++)
265                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
266                                         }
267                                 }
268                         }
269
270                         if (!argsMatch)
271                                 if (throwOnBindFailure)
272                                         throw new ArgumentException ("method arguments are incompatible");
273                                 else
274                                         return null;
275
276                         Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
277                         if (d != null)
278                                 d.original_method_info = method;
279                         return d;
280                 }
281
282                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
283                 {
284                         return CreateDelegate (type, firstArgument, method, true, true);
285                 }
286
287                 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
288                 {
289                         return CreateDelegate (type, null, method, throwOnBindFailure, false);
290                 }
291
292                 public static Delegate CreateDelegate (Type type, MethodInfo method)
293                 {
294                         return CreateDelegate (type, method, true);
295                 }
296
297                 public static Delegate CreateDelegate (Type type, object target, string method)
298                 {
299                         return CreateDelegate (type, target, method, false);
300                 }
301
302                 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
303                 {
304                         if (type == null)
305                                 throw new ArgumentNullException ("type");
306
307                         if (method == null)
308                                 throw new ArgumentNullException ("method");
309
310                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
311                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
312
313                         MethodInfo invoke = type.GetMethod ("Invoke");
314                         ParameterInfo [] delargs = invoke.GetParameters ();
315                         Type[] delargtypes = new Type [delargs.Length];
316
317                         for (int i=0; i<delargs.Length; i++)
318                                 delargtypes [i] = delargs [i].ParameterType;
319
320                         /* 
321                          * FIXME: we should check the caller has reflection permission
322                          * or if it lives in the same assembly...
323                          */
324
325                         /*
326                          * since we need to walk the inheritance chain anyway to
327                          * find private methods, adjust the bindingflags to ignore
328                          * inherited methods
329                          */
330                         BindingFlags flags = BindingFlags.ExactBinding |
331                                 BindingFlags.Public | BindingFlags.NonPublic |
332                                 BindingFlags.DeclaredOnly | bflags;
333
334                         if (ignoreCase)
335                                 flags |= BindingFlags.IgnoreCase;
336
337                         MethodInfo info = null;
338
339                         for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
340                                 MethodInfo mi = targetType.GetMethod (method, flags,
341                                         null, delargtypes, new ParameterModifier [0]);
342                                 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
343                                         info = mi;
344                                         break;
345                                 }
346                         }
347
348                         if (info == null) {
349                                 if (throwOnBindFailure)
350                                         throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
351                                 else
352                                         return null;
353                         }
354
355                         return info;
356                 }
357
358                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
359                 {
360                         if (target == null)
361                                 throw new ArgumentNullException ("target");
362
363                         MethodInfo info = GetCandidateMethod (type, target, method,
364                                 BindingFlags.Static, ignoreCase, throwOnBindFailure);
365                         if (info == null)
366                                 return null;
367
368                         return CreateDelegate_internal (type, null, info, throwOnBindFailure);
369                 }
370
371                 public static Delegate CreateDelegate (Type type, Type target, string method) {
372                         return CreateDelegate (type, target, method, false, true);
373                 }
374
375                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
376                         return CreateDelegate (type, target, method, ignoreCase, true);
377                 }
378
379                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
380                 {
381                         if (target == null)
382                                 throw new ArgumentNullException ("target");
383
384                         MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
385                                 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
386                         if (info == null)
387                                 return null;
388
389                         return CreateDelegate_internal (type, target, info, throwOnBindFailure);
390                 }
391
392                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
393                         return CreateDelegate (type, target, method, ignoreCase, true);
394                 }
395
396                 public object DynamicInvoke (params object[] args)
397                 {
398                         return DynamicInvokeImpl (args);
399                 }
400
401                 protected virtual object DynamicInvokeImpl (object[] args)
402                 {
403                         if (Method == null) {
404                                 Type[] mtypes = new Type [args.Length];
405                                 for (int i = 0; i < args.Length; ++i) {
406                                         mtypes [i] = args [i].GetType ();
407                                 }
408                                 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
409                         }
410
411                         if (Method.IsStatic && (args != null ? args.Length : 0) == Method.GetParameters ().Length - 1) {
412                                 // The delegate is bound to m_target
413                                 if (args != null) {
414                                         object[] newArgs = new object [args.Length + 1];
415                                         args.CopyTo (newArgs, 1);
416                                         newArgs [0] = m_target;
417                                         args = newArgs;
418                                 } else {
419                                         args = new object [] { m_target };
420                                 }
421                                 return Method.Invoke (null, args);
422                         }
423
424                         return Method.Invoke (m_target, args);
425                 }
426
427                 public virtual object Clone ()
428                 {
429                         return MemberwiseClone ();
430                 }
431
432                 public override bool Equals (object obj)
433                 {
434                         Delegate d = obj as Delegate;
435                         
436                         if (d == null)
437                                 return false;
438                         
439                         // Do not compare method_ptr, since it can point to a trampoline
440                         if (d.m_target == m_target && d.method == method) {
441                                 if (d.data != null || data != null) {
442                                         /* Uncommon case */
443                                         if (d.data != null && data != null)
444                                                 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
445                                         else
446                                                 return false;
447                                 }
448                                 return true;
449                         }
450
451                         return false;
452                 }
453
454                 public override int GetHashCode ()
455                 {
456                         return method.GetHashCode () ^ (m_target != null ? m_target.GetHashCode () : 0);
457                 }
458
459                 protected virtual MethodInfo GetMethodImpl ()
460                 {
461                         return Method;
462                 }
463
464                 // This is from ISerializable
465                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
466                 {
467                         DelegateSerializationHolder.GetDelegateData (this, info, context);
468                 }
469
470                 public virtual Delegate[] GetInvocationList()
471                 {
472                         return new Delegate[] { this };
473                 }
474
475                 /// <symmary>
476                 ///   Returns a new MulticastDelegate holding the
477                 ///   concatenated invocation lists of MulticastDelegates a and b
478                 /// </symmary>
479                 public static Delegate Combine (Delegate a, Delegate b)
480                 {
481                         if (a == null) {
482                                 if (b == null)
483                                         return null;
484                                 return b;
485                         } else 
486                                 if (b == null)
487                                         return a;
488
489                         if (a.GetType () != b.GetType ())
490                                 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", a.GetType ().FullName, b.GetType ().FullName));
491                         
492                         return a.CombineImpl (b);
493                 }
494
495                 /// <symmary>
496                 ///   Returns a new MulticastDelegate holding the
497                 ///   concatenated invocation lists of an Array of MulticastDelegates
498                 /// </symmary>
499                 [System.Runtime.InteropServices.ComVisible (true)]
500                 public static Delegate Combine (params Delegate[] delegates)
501                 {
502                         if (delegates == null)
503                                 return null;
504
505                         Delegate retval = null;
506
507                         foreach (Delegate next in delegates)
508                                 retval = Combine (retval, next);
509
510                         return retval;
511                 }
512
513                 protected virtual Delegate CombineImpl (Delegate d)
514                 {
515                         throw new MulticastNotSupportedException (String.Empty);
516                 }
517
518                 public static Delegate Remove (Delegate source, Delegate value) 
519                 {
520                         if (source == null)
521                                 return null;
522
523                         if (value == null)
524                                 return source;
525
526                         if (source.GetType () != value.GetType ())
527                                 throw new ArgumentException ("Delegate type mismatch");
528
529                         return source.RemoveImpl (value);
530                 }
531
532                 protected virtual Delegate RemoveImpl (Delegate d)
533                 {
534                         if (this.Equals (d))
535                                 return null;
536
537                         return this;
538                 }
539
540                 public static Delegate RemoveAll (Delegate source, Delegate value)
541                 {
542                         Delegate tmp = source;
543                         while ((source = Delegate.Remove (source, value)) != tmp)
544                                 tmp = source;
545
546                         return tmp;
547                 }
548
549                 public static bool operator == (Delegate d1, Delegate d2)
550                 {
551                         if ((object)d1 == null) {
552                                 if ((object)d2 == null)
553                                         return true;
554                                 return false;
555                         } else if ((object) d2 == null)
556                                 return false;
557                         
558                         return d1.Equals (d2);
559                 }
560
561                 public static bool operator != (Delegate d1, Delegate d2)
562                 {
563                         return !(d1 == d2);
564                 }
565
566                 internal bool IsTransparentProxy ()
567                 {
568 #if MONOTOUCH
569                         return false;
570 #else
571                         return RemotingServices.IsTransparentProxy (m_target);
572 #endif
573                 }
574         }
575 }