2010-03-12 Jb Evain <jbevain@novell.com>
[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.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         class DelegateData {
44                 public Type target_type;
45                 public string method_name;
46         }
47
48         [ClassInterface (ClassInterfaceType.AutoDual)]
49         [System.Runtime.InteropServices.ComVisible (true)]
50         [Serializable]
51         public abstract class Delegate : ICloneable, ISerializable
52         {
53                 #region Sync with object-internals.h
54 #pragma warning disable 169, 414, 649
55                 private IntPtr method_ptr;
56                 private IntPtr invoke_impl;
57                 private object m_target;
58                 private IntPtr method;
59                 private IntPtr delegate_trampoline;
60                 private IntPtr method_code;
61                 private MethodInfo method_info;
62
63                 // Keep a ref of the MethodInfo passed to CreateDelegate.
64                 // Used to keep DynamicMethods alive.
65                 private MethodInfo original_method_info;
66
67                 private DelegateData data;
68 #pragma warning restore 169, 414, 649
69                 #endregion
70
71                 protected Delegate (object target, string method)
72                 {
73                         if (target == null)
74                                 throw new ArgumentNullException ("target");
75
76                         if (method == null)
77                                 throw new ArgumentNullException ("method");
78
79                         this.m_target = target;
80                         this.data = new DelegateData ();
81                         this.data.method_name = method;
82                 }
83
84                 protected Delegate (Type target, string method)
85                 {
86                         if (target == null)
87                                 throw new ArgumentNullException ("target");
88
89                         if (method == null)
90                                 throw new ArgumentNullException ("method");
91
92                         this.data = new DelegateData ();
93                         this.data.method_name = method;
94                         this.data.target_type = target;
95                 }
96
97                 public MethodInfo Method {
98                         get {
99                                 if (method_info != null) {
100                                         return method_info;
101                                 } else {
102                                         if (method != IntPtr.Zero) {
103                                                 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
104                                         }
105                                         return method_info;
106                                 }
107                         }
108                 }
109
110                 public object Target {
111                         get {
112                                 return m_target;
113                         }
114                 }
115
116                 //
117                 // Methods
118                 //
119
120                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
121                 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
122
123                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
124                 internal extern void SetMulticastInvoke ();
125
126                 private static bool arg_type_match (Type delArgType, Type argType) {
127                         bool match = delArgType == argType;
128
129                         // Delegate contravariance
130                         if (!match) {
131                                 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
132                                         match = true;
133                         }
134
135                         return match;
136                 }
137
138                 private static bool return_type_match (Type delReturnType, Type returnType) {
139                         bool returnMatch = returnType == delReturnType;
140
141                         if (!returnMatch) {
142                                 // Delegate covariance
143                                 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
144                                         returnMatch = true;
145                         }
146
147                         return returnMatch;
148                 }
149
150                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
151                 {
152                         // The name of the parameter changed in 2.0
153                         object target = firstArgument;
154
155                         if (type == null)
156                                 throw new ArgumentNullException ("type");
157
158                         if (method == null)
159                                 throw new ArgumentNullException ("method");
160
161                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
162                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
163
164                         MethodInfo invoke = type.GetMethod ("Invoke");
165
166                         if (!return_type_match (invoke.ReturnType, method.ReturnType))
167                                 if (throwOnBindFailure)
168                                         throw new ArgumentException ("method return type is incompatible");
169                                 else
170                                         return null;
171
172                         ParameterInfo[] delargs = invoke.GetParameters ();
173                         ParameterInfo[] args = method.GetParameters ();
174
175                         bool argLengthMatch;
176
177                         if (target != null) {
178                                 // delegate closed over target
179                                 if (!method.IsStatic)
180                                         // target is passed as this
181                                         argLengthMatch = (args.Length == delargs.Length);
182                                 else
183                                         // target is passed as the first argument to the static method
184                                         argLengthMatch = (args.Length == delargs.Length + 1);
185                         } else {
186                                 if (!method.IsStatic)
187                                         //
188                                         // Net 2.0 feature. The first argument of the delegate is passed
189                                         // as the 'this' argument to the method.
190                                         //
191                                         argLengthMatch = (args.Length + 1 == delargs.Length);
192                                 else {
193                                         argLengthMatch = (args.Length == delargs.Length);
194
195                                         if (!argLengthMatch)
196                                                 // closed over a null reference
197                                                 argLengthMatch = args.Length == delargs.Length + 1;
198                                 }
199                         }
200                         if (!argLengthMatch)
201                                 if (throwOnBindFailure)
202                                         throw new ArgumentException ("method argument length mismatch");
203                                 else
204                                         return null;
205
206                         bool argsMatch;
207                         if (target != null) {
208                                 if (!method.IsStatic) {
209                                         argsMatch = arg_type_match (target.GetType (), method.DeclaringType);
210                                         for (int i = 0; i < args.Length; i++)
211                                                 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
212                                 } else {
213                                         argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
214                                         for (int i = 1; i < args.Length; i++)
215                                                 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);                                    
216                                 }
217                         } else {
218                                 if (!method.IsStatic) {
219                                         // The first argument should match this
220                                         argsMatch = arg_type_match (delargs [0].ParameterType, method.DeclaringType);
221                                         for (int i = 0; i < args.Length; i++)
222                                                 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
223                                 } else {
224                                         if (delargs.Length + 1 == args.Length) {
225                                                 // closed over a null reference
226                                                 argsMatch = !args [0].ParameterType.IsValueType;
227                                                 for (int i = 0; i < delargs.Length; i++)
228                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
229                                         } else {
230                                                 argsMatch = true;
231                                                 for (int i = 0; i < args.Length; i++)
232                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
233                                         }
234                                 }
235                         }
236
237                         if (!argsMatch)
238                                 if (throwOnBindFailure)
239                                         throw new ArgumentException ("method arguments are incompatible");
240                                 else
241                                         return null;
242
243                         Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
244                         if (d != null)
245                                 d.original_method_info = method;
246                         return d;
247                 }
248
249                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method) {
250                         return CreateDelegate (type, firstArgument, method, true);
251                 }
252
253                 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
254                 {
255                         return CreateDelegate (type, null, method, throwOnBindFailure);
256                 }
257
258                 public static Delegate CreateDelegate (Type type, MethodInfo method) {
259                         return CreateDelegate (type, method, true);
260                 }
261
262                 public static Delegate CreateDelegate (Type type, object target, string method)
263                 {
264                         return CreateDelegate(type, target, method, false);
265                 }
266
267                 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
268                 {
269                         if (type == null)
270                                 throw new ArgumentNullException ("type");
271
272                         if (method == null)
273                                 throw new ArgumentNullException ("method");
274
275                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
276                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
277
278                         MethodInfo invoke = type.GetMethod ("Invoke");
279                         ParameterInfo [] delargs = invoke.GetParameters ();
280                         Type[] delargtypes = new Type [delargs.Length];
281
282                         for (int i=0; i<delargs.Length; i++)
283                                 delargtypes [i] = delargs [i].ParameterType;
284
285                         /* 
286                          * FIXME: we should check the caller has reflection permission
287                          * or if it lives in the same assembly...
288                          */
289
290                         /*
291                          * since we need to walk the inheritance chain anyway to
292                          * find private methods, adjust the bindingflags to ignore
293                          * inherited methods
294                          */
295                         BindingFlags flags = BindingFlags.ExactBinding |
296                                 BindingFlags.Public | BindingFlags.NonPublic |
297                                 BindingFlags.DeclaredOnly | bflags;
298
299                         if (ignoreCase)
300                                 flags |= BindingFlags.IgnoreCase;
301
302                         MethodInfo info = null;
303
304                         for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
305                                 MethodInfo mi = targetType.GetMethod (method, flags,
306                                         null, delargtypes, new ParameterModifier [0]);
307                                 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
308                                         info = mi;
309                                         break;
310                                 }
311                         }
312
313                         if (info == null) {
314                                 if (throwOnBindFailure)
315                                         throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
316                                 else
317                                         return null;
318                         }
319
320                         return info;
321                 }
322
323                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
324                 {
325                         if (target == null)
326                                 throw new ArgumentNullException ("target");
327
328                         MethodInfo info = GetCandidateMethod (type, target, method,
329                                 BindingFlags.Static, ignoreCase, throwOnBindFailure);
330                         if (info == null)
331                                 return null;
332
333                         return CreateDelegate_internal (type, null, info, throwOnBindFailure);
334                 }
335
336                 public static Delegate CreateDelegate (Type type, Type target, string method) {
337                         return CreateDelegate (type, target, method, false, true);
338                 }
339
340                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
341                         return CreateDelegate (type, target, method, ignoreCase, true);
342                 }
343
344                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
345                 {
346                         if (target == null)
347                                 throw new ArgumentNullException ("target");
348
349                         MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
350                                 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
351                         if (info == null)
352                                 return null;
353
354                         return CreateDelegate_internal (type, target, info, throwOnBindFailure);
355                 }
356
357                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
358                         return CreateDelegate (type, target, method, ignoreCase, true);
359                 }
360
361                 public object DynamicInvoke (params object[] args)
362                 {
363                         return DynamicInvokeImpl (args);
364                 }
365
366                 protected virtual object DynamicInvokeImpl (object[] args)
367                 {
368                         if (Method == null) {
369                                 Type[] mtypes = new Type [args.Length];
370                                 for (int i = 0; i < args.Length; ++i) {
371                                         mtypes [i] = args [i].GetType ();
372                                 }
373                                 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
374                         }
375
376                         if ((m_target != null) && Method.IsStatic) {
377                                 // The delegate is bound to m_target
378                                 if (args != null) {
379                                         object[] newArgs = new object [args.Length + 1];
380                                         args.CopyTo (newArgs, 1);
381                                         newArgs [0] = m_target;
382                                         args = newArgs;
383                                 } else {
384                                         args = new object [] { m_target };
385                                 }
386                                 return Method.Invoke (null, args);
387                         }
388
389                         return Method.Invoke (m_target, args);
390                 }
391
392                 public virtual object Clone ()
393                 {
394                         return MemberwiseClone ();
395                 }
396
397                 public override bool Equals (object obj)
398                 {
399                         Delegate d = obj as Delegate;
400                         
401                         if (d == null)
402                                 return false;
403                         
404                         // Do not compare method_ptr, since it can point to a trampoline
405                         if (d.m_target == m_target && d.method == method) {
406                                 if (d.data != null || data != null) {
407                                         /* Uncommon case */
408                                         if (d.data != null && data != null)
409                                                 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
410                                         else
411                                                 return false;
412                                 }
413                                 return true;
414                         }
415
416                         return false;
417                 }
418
419                 public override int GetHashCode ()
420                 {
421                         return method.GetHashCode () ^ (m_target != null ? m_target.GetHashCode () : 0);
422                 }
423
424                 protected virtual MethodInfo GetMethodImpl ()
425                 {
426                         return Method;
427                 }
428
429                 // This is from ISerializable
430                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
431                 {
432                         DelegateSerializationHolder.GetDelegateData (this, info, context);
433                 }
434
435                 public virtual Delegate[] GetInvocationList()
436                 {
437                         return new Delegate[] { this };
438                 }
439
440                 /// <symmary>
441                 ///   Returns a new MulticastDelegate holding the
442                 ///   concatenated invocation lists of MulticastDelegates a and b
443                 /// </symmary>
444                 public static Delegate Combine (Delegate a, Delegate b)
445                 {
446                         if (a == null) {
447                                 if (b == null)
448                                         return null;
449                                 return b;
450                         } else 
451                                 if (b == null)
452                                         return a;
453
454                         if (a.GetType () != b.GetType ())
455                                 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types."));
456                         
457                         return a.CombineImpl (b);
458                 }
459
460                 /// <symmary>
461                 ///   Returns a new MulticastDelegate holding the
462                 ///   concatenated invocation lists of an Array of MulticastDelegates
463                 /// </symmary>
464                 [System.Runtime.InteropServices.ComVisible (true)]
465                 public static Delegate Combine (params Delegate[] delegates)
466                 {
467                         if (delegates == null)
468                                 return null;
469
470                         Delegate retval = null;
471
472                         foreach (Delegate next in delegates)
473                                 retval = Combine (retval, next);
474
475                         return retval;
476                 }
477
478                 protected virtual Delegate CombineImpl (Delegate d)
479                 {
480                         throw new MulticastNotSupportedException (String.Empty);
481                 }
482
483                 public static Delegate Remove (Delegate source, Delegate value) 
484                 {
485                         if (source == null)
486                                 return null;
487
488                         return source.RemoveImpl (value);
489                 }
490
491                 protected virtual Delegate RemoveImpl (Delegate d)
492                 {
493                         if (this.Equals (d))
494                                 return null;
495
496                         return this;
497                 }
498
499                 public static Delegate RemoveAll (Delegate source, Delegate value)
500                 {
501                         Delegate tmp = source;
502                         while ((source = Delegate.Remove (source, value)) != tmp)
503                                 tmp = source;
504
505                         return tmp;
506                 }
507
508                 public static bool operator == (Delegate d1, Delegate d2)
509                 {
510                         if ((object)d1 == null) {
511                                 if ((object)d2 == null)
512                                         return true;
513                                 return false;
514                         } else if ((object) d2 == null)
515                                 return false;
516                         
517                         return d1.Equals (d2);
518                 }
519
520                 public static bool operator != (Delegate d1, Delegate d2)
521                 {
522                         return !(d1 == d2);
523                 }
524         }
525 }