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