[corlib] Fixed StringBuilder construction bugs in marshalling caused by changes to...
[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 //   Marek Safar (marek.safar@gmail.com)
9 //
10 // (C) Ximian, Inc.  http://www.ximian.com
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Reflection;
35 using System.Runtime.Remoting;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39
40 namespace System
41 {
42         /* Contains the rarely used fields of Delegate */
43         sealed class DelegateData
44         {
45                 public Type target_type;
46                 public string method_name;
47                 public bool curried_first_arg;
48         }
49
50         [ClassInterface (ClassInterfaceType.AutoDual)]
51         [System.Runtime.InteropServices.ComVisible (true)]
52         [Serializable]
53         [StructLayout (LayoutKind.Sequential)]
54         public abstract class Delegate : ICloneable, ISerializable
55         {
56                 #region Sync with object-internals.h
57 #pragma warning disable 169, 414, 649
58                 private IntPtr method_ptr;
59                 private IntPtr invoke_impl;
60                 private object m_target;
61                 private IntPtr method;
62                 private IntPtr delegate_trampoline;
63                 private IntPtr method_code;
64                 private MethodInfo method_info;
65
66                 // Keep a ref of the MethodInfo passed to CreateDelegate.
67                 // Used to keep DynamicMethods alive.
68                 private MethodInfo original_method_info;
69
70                 private DelegateData data;
71 #pragma warning restore 169, 414, 649
72                 #endregion
73
74                 protected Delegate (object target, string method)
75                 {
76                         if (target == null)
77                                 throw new ArgumentNullException ("target");
78
79                         if (method == null)
80                                 throw new ArgumentNullException ("method");
81
82                         this.m_target = target;
83                         this.data = new DelegateData ();
84                         this.data.method_name = method;
85                 }
86
87                 protected Delegate (Type target, string method)
88                 {
89                         if (target == null)
90                                 throw new ArgumentNullException ("target");
91
92                         if (method == null)
93                                 throw new ArgumentNullException ("method");
94
95                         this.data = new DelegateData ();
96                         this.data.method_name = method;
97                         this.data.target_type = target;
98                 }
99
100                 public MethodInfo Method {
101                         get {
102                                 if (method_info != null) {
103                                         return method_info;
104                                 } else {
105                                         if (method != IntPtr.Zero) {
106                                                 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
107                                         }
108                                         return method_info;
109                                 }
110                         }
111                 }
112
113                 public object Target {
114                         get {
115                                 return m_target;
116                         }
117                 }
118
119                 //
120                 // Methods
121                 //
122
123                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
124                 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
125
126                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
127                 internal extern void SetMulticastInvoke ();
128
129                 private static bool arg_type_match (Type delArgType, Type argType) {
130                         bool match = delArgType == argType;
131
132                         // Delegate contravariance
133                         if (!match) {
134                                 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
135                                         match = true;
136                         }
137                         // enum basetypes
138                         if (!match) {
139                                 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
140                                         match = true;
141                         }
142
143                         return match;
144                 }
145
146                 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
147                         bool match;
148                         if (argType.IsValueType)
149                                 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
150                                                 (boxedThis && delArgType == argType);
151                         else
152                                 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
153
154                         return match;
155                 }
156                 private static bool return_type_match (Type delReturnType, Type returnType) {
157                         bool returnMatch = returnType == delReturnType;
158
159                         if (!returnMatch) {
160                                 // Delegate covariance
161                                 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
162                                         returnMatch = true;
163                         }
164
165                         return returnMatch;
166                 }
167
168                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
169                 {
170                         return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
171                 }
172
173                 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
174                 {
175                         // The name of the parameter changed in 2.0
176                         object target = firstArgument;
177
178                         if (type == null)
179                                 throw new ArgumentNullException ("type");
180
181                         if (method == null)
182                                 throw new ArgumentNullException ("method");
183
184                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
185                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
186
187                         MethodInfo invoke = type.GetMethod ("Invoke");
188
189                         if (!return_type_match (invoke.ReturnType, method.ReturnType))
190                                 if (throwOnBindFailure)
191                                         throw new ArgumentException ("method return type is incompatible");
192                                 else
193                                         return null;
194
195                         ParameterInfo[] delargs = invoke.GetParametersInternal ();
196                         ParameterInfo[] args = method.GetParametersInternal ();
197
198                         bool argLengthMatch;
199
200                         if (target != null) {
201                                 // delegate closed over target
202                                 if (!method.IsStatic)
203                                         // target is passed as this
204                                         argLengthMatch = (args.Length == delargs.Length);
205                                 else
206                                         // target is passed as the first argument to the static method
207                                         argLengthMatch = (args.Length == delargs.Length + 1);
208                         } else {
209                                 if (!method.IsStatic) {
210                                         //
211                                         // Net 2.0 feature. The first argument of the delegate is passed
212                                         // as the 'this' argument to the method.
213                                         //
214                                         argLengthMatch = (args.Length + 1 == delargs.Length);
215
216                                         if (!argLengthMatch)
217                                                 // closed over a null reference
218                                                 argLengthMatch = (args.Length == delargs.Length);
219                                 } else {
220                                         argLengthMatch = (args.Length == delargs.Length);
221
222                                         if (!argLengthMatch)
223                                                 // closed over a null reference
224                                                 argLengthMatch = args.Length == delargs.Length + 1;
225                                 }
226                         }
227                         if (!argLengthMatch)
228                                 if (throwOnBindFailure)
229                                         throw new ArgumentException ("method argument length mismatch");
230                                 else
231                                         return null;
232
233                         bool argsMatch;
234                         DelegateData delegate_data = new DelegateData ();
235
236                         if (target != null) {
237                                 if (!method.IsStatic) {
238                                         argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
239                                         for (int i = 0; i < args.Length; i++)
240                                                 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
241                                 } else {
242                                         argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
243                                         for (int i = 1; i < args.Length; i++)
244                                                 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
245
246                                         delegate_data.curried_first_arg = true;
247                                 }
248                         } else {
249                                 if (!method.IsStatic) {
250                                         if (args.Length + 1 == delargs.Length) {
251                                                 // The first argument should match this
252                                                 argsMatch = arg_type_match_this (delargs [0].ParameterType, method.DeclaringType, false);
253                                                 for (int i = 0; i < args.Length; i++)
254                                                         argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
255                                         } else {
256                                                 // closed over a null reference
257                                                 argsMatch = allowClosed;
258                                                 for (int i = 0; i < args.Length; i++)
259                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
260                                         }
261                                 } else {
262                                         if (delargs.Length + 1 == args.Length) {
263                                                 // closed over a null reference
264                                                 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
265                                                 for (int i = 0; i < delargs.Length; i++)
266                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
267
268                                                 delegate_data.curried_first_arg = true;
269                                         } else {
270                                                 argsMatch = true;
271                                                 for (int i = 0; i < args.Length; i++)
272                                                         argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
273                                         }
274                                 }
275                         }
276
277                         if (!argsMatch)
278                                 if (throwOnBindFailure)
279                                         throw new ArgumentException ("method arguments are incompatible");
280                                 else
281                                         return null;
282
283                         Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
284                         if (d != null)
285                                 d.original_method_info = method;
286                         if (delegate_data != null)
287                                 d.data = delegate_data;
288                         return d;
289                 }
290
291                 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
292                 {
293                         return CreateDelegate (type, firstArgument, method, true, true);
294                 }
295
296                 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
297                 {
298                         return CreateDelegate (type, null, method, throwOnBindFailure, false);
299                 }
300
301                 public static Delegate CreateDelegate (Type type, MethodInfo method)
302                 {
303                         return CreateDelegate (type, method, true);
304                 }
305
306                 public static Delegate CreateDelegate (Type type, object target, string method)
307                 {
308                         return CreateDelegate (type, target, method, false);
309                 }
310
311                 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
312                 {
313                         if (type == null)
314                                 throw new ArgumentNullException ("type");
315
316                         if (method == null)
317                                 throw new ArgumentNullException ("method");
318
319                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
320                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
321
322                         MethodInfo invoke = type.GetMethod ("Invoke");
323                         ParameterInfo [] delargs = invoke.GetParametersInternal ();
324                         Type[] delargtypes = new Type [delargs.Length];
325
326                         for (int i=0; i<delargs.Length; i++)
327                                 delargtypes [i] = delargs [i].ParameterType;
328
329                         /* 
330                          * FIXME: we should check the caller has reflection permission
331                          * or if it lives in the same assembly...
332                          */
333
334                         /*
335                          * since we need to walk the inheritance chain anyway to
336                          * find private methods, adjust the bindingflags to ignore
337                          * inherited methods
338                          */
339                         BindingFlags flags = BindingFlags.ExactBinding |
340                                 BindingFlags.Public | BindingFlags.NonPublic |
341                                 BindingFlags.DeclaredOnly | bflags;
342
343                         if (ignoreCase)
344                                 flags |= BindingFlags.IgnoreCase;
345
346                         MethodInfo info = null;
347
348                         for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
349                                 MethodInfo mi = targetType.GetMethod (method, flags,
350                                         null, delargtypes, EmptyArray<ParameterModifier>.Value);
351                                 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
352                                         info = mi;
353                                         break;
354                                 }
355                         }
356
357                         if (info == null) {
358                                 if (throwOnBindFailure)
359                                         throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
360                                 else
361                                         return null;
362                         }
363
364                         return info;
365                 }
366
367                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
368                 {
369                         if (target == null)
370                                 throw new ArgumentNullException ("target");
371
372                         MethodInfo info = GetCandidateMethod (type, target, method,
373                                 BindingFlags.Static, ignoreCase, throwOnBindFailure);
374                         if (info == null)
375                                 return null;
376
377                         return CreateDelegate_internal (type, null, info, throwOnBindFailure);
378                 }
379
380                 public static Delegate CreateDelegate (Type type, Type target, string method) {
381                         return CreateDelegate (type, target, method, false, true);
382                 }
383
384                 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
385                         return CreateDelegate (type, target, method, ignoreCase, true);
386                 }
387
388                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
389                 {
390                         if (target == null)
391                                 throw new ArgumentNullException ("target");
392
393                         MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
394                                 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
395                         if (info == null)
396                                 return null;
397
398                         return CreateDelegate_internal (type, target, info, throwOnBindFailure);
399                 }
400
401                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
402                         return CreateDelegate (type, target, method, ignoreCase, true);
403                 }
404
405                 public object DynamicInvoke (params object[] args)
406                 {
407                         return DynamicInvokeImpl (args);
408                 }
409
410                 void InitializeDelegateData ()
411                 {
412                         DelegateData delegate_data = new DelegateData ();
413                         if (method_info.IsStatic) {
414                                 if (m_target != null) {
415                                         delegate_data.curried_first_arg = true;
416                                 } else {
417                                         MethodInfo invoke = GetType ().GetMethod ("Invoke");
418                                         if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
419                                                 delegate_data.curried_first_arg = true;
420                                 }
421                         }
422                         this.data = delegate_data;
423                 }
424
425                 protected virtual object DynamicInvokeImpl (object[] args)
426                 {
427                         if (Method == null) {
428                                 Type[] mtypes = new Type [args.Length];
429                                 for (int i = 0; i < args.Length; ++i) {
430                                         mtypes [i] = args [i].GetType ();
431                                 }
432                                 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
433                         }
434
435                         var target = m_target;
436                         if (this.data == null)
437                                 InitializeDelegateData ();
438
439                         if (Method.IsStatic) {
440                                 //
441                                 // The delegate is bound to m_target
442                                 //
443                                 if (data.curried_first_arg) {
444                                         if (args == null) {
445                                                 args = new [] { target };
446                                         } else {
447                                                 Array.Resize (ref args, args.Length + 1);
448                                                 Array.Copy (args, 0, args, 1, args.Length - 1);
449                                                 args [0] = target;
450                                         }
451
452                                         target = null;
453                                 }
454                         } else {
455                                 if (m_target == null && args != null && args.Length > 0) {
456                                         target = args [0];
457                                         Array.Copy (args, 1, args, 0, args.Length - 1);
458                                         Array.Resize (ref args, args.Length - 1);
459                                 }
460                         }
461
462                         return Method.Invoke (target, args);
463                 }
464
465                 public virtual object Clone ()
466                 {
467                         return MemberwiseClone ();
468                 }
469
470                 internal bool Compare (Delegate d)
471                 {
472                         if (d == null)
473                                 return false;
474                         
475                         // Do not compare method_ptr, since it can point to a trampoline
476                         if (d.m_target == m_target && d.method == method) {
477                                 if (d.data != null || data != null) {
478                                         /* Uncommon case */
479                                         if (d.data != null && data != null)
480                                                 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
481                                         else {
482                                                 if (d.data != null)
483                                                         return d.data.target_type == null;
484                                                 if (data != null)
485                                                         return data.target_type == null;
486                                                 return false;
487                                         }
488                                 }
489                                 return true;
490                         }
491
492                         return false;
493                 }
494
495                 public override bool Equals (object obj)
496                 {
497                         return Compare (obj as Delegate);
498                 }
499
500                 public override int GetHashCode ()
501                 {
502                         return method.GetHashCode () ^ (m_target != null ? m_target.GetHashCode () : 0);
503                 }
504
505                 protected virtual MethodInfo GetMethodImpl ()
506                 {
507                         return Method;
508                 }
509
510                 // This is from ISerializable
511                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
512                 {
513                         DelegateSerializationHolder.GetDelegateData (this, info, context);
514                 }
515
516                 public virtual Delegate[] GetInvocationList()
517                 {
518                         return new Delegate[] { this };
519                 }
520
521                 /// <symmary>
522                 ///   Returns a new MulticastDelegate holding the
523                 ///   concatenated invocation lists of MulticastDelegates a and b
524                 /// </symmary>
525                 public static Delegate Combine (Delegate a, Delegate b)
526                 {
527                         if (a == null)
528                                 return b;
529
530                         return a.CombineImpl (b);
531                 }
532
533                 /// <symmary>
534                 ///   Returns a new MulticastDelegate holding the
535                 ///   concatenated invocation lists of an Array of MulticastDelegates
536                 /// </symmary>
537                 [System.Runtime.InteropServices.ComVisible (true)]
538                 public static Delegate Combine (params Delegate[] delegates)
539                 {
540                         if (delegates == null)
541                                 return null;
542
543                         Delegate retval = null;
544
545                         foreach (Delegate next in delegates)
546                                 retval = Combine (retval, next);
547
548                         return retval;
549                 }
550
551                 protected virtual Delegate CombineImpl (Delegate d)
552                 {
553                         throw new MulticastNotSupportedException (String.Empty);
554                 }
555
556                 public static Delegate Remove (Delegate source, Delegate value) 
557                 {
558                         if (source == null)
559                                 return null;
560
561                         if (value == null)
562                                 return source;
563
564                         if (source.GetType () != value.GetType ())
565                                 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", source.GetType ().FullName, value.GetType ().FullName));
566
567                         return source.RemoveImpl (value);
568                 }
569
570                 protected virtual Delegate RemoveImpl (Delegate d)
571                 {
572                         if (this.Equals (d))
573                                 return null;
574
575                         return this;
576                 }
577
578                 public static Delegate RemoveAll (Delegate source, Delegate value)
579                 {
580                         Delegate tmp = source;
581                         while ((source = Delegate.Remove (source, value)) != tmp)
582                                 tmp = source;
583
584                         return tmp;
585                 }
586
587                 public static bool operator == (Delegate d1, Delegate d2)
588                 {
589                         if ((object)d1 == null) {
590                                 if ((object)d2 == null)
591                                         return true;
592                                 return false;
593                         } else if ((object) d2 == null)
594                                 return false;
595                         
596                         return d1.Equals (d2);
597                 }
598
599                 public static bool operator != (Delegate d1, Delegate d2)
600                 {
601                         return !(d1 == d2);
602                 }
603
604                 internal bool IsTransparentProxy ()
605                 {
606 #if DISABLE_REMOTING
607                         return false;
608 #else
609                         return RemotingServices.IsTransparentProxy (m_target);
610 #endif
611                 }
612         }
613 }