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