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