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