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