Make TypeNameParser consistently use tabs
[mono-project.git] / netcore / System.Private.CoreLib / src / System / Delegate.cs
blob09fec9904ae423d1c764913d7e620384789bcf2b
1 //
2 // Authors:
3 // Miguel de Icaza (miguel@ximian.com)
4 // Daniel Stodden (stodden@in.tum.de)
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
7 //
8 // (C) Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Reflection;
33 using System.Runtime.CompilerServices;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Serialization;
37 namespace System
39 /* Contains the rarely used fields of Delegate */
40 sealed class DelegateData
42 public Type? target_type;
43 public string? method_name;
44 public bool curried_first_arg;
47 [StructLayout (LayoutKind.Sequential)]
48 partial class Delegate
50 #region Sync with object-internals.h
51 IntPtr method_ptr;
52 IntPtr invoke_impl;
53 object? _target;
54 IntPtr method;
55 IntPtr delegate_trampoline;
56 IntPtr extra_arg;
57 IntPtr method_code;
58 IntPtr interp_method;
59 IntPtr interp_invoke_impl;
60 MethodInfo? method_info;
62 // Keep a ref of the MethodInfo passed to CreateDelegate.
63 // Used to keep DynamicMethods alive.
64 MethodInfo? original_method_info;
66 DelegateData data;
68 bool method_is_virtual;
69 #endregion
71 protected Delegate (object target, string method)
73 if (target is null)
74 throw new ArgumentNullException (nameof (target));
76 if (method is null)
77 throw new ArgumentNullException (nameof (method));
79 this._target = target;
80 this.data = new DelegateData () {
81 method_name = method
85 protected Delegate (Type target, string method)
87 if (target is null)
88 throw new ArgumentNullException (nameof (target));
90 if (target.ContainsGenericParameters)
91 throw new ArgumentException (SR.Arg_UnboundGenParam, nameof (target));
93 if (method is null)
94 throw new ArgumentNullException (nameof (method));
96 if (!target.IsRuntimeImplemented ())
97 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (target));
99 this.data = new DelegateData () {
100 method_name = method,
101 target_type = target
105 public object? Target => GetTarget ();
107 internal virtual object? GetTarget () => _target;
109 public static Delegate CreateDelegate (Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure)
111 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true)!;
114 public static Delegate? CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
116 return CreateDelegate (type, null, method, throwOnBindFailure, false);
119 static Delegate? CreateDelegate (Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
121 if (type is null)
122 throw new ArgumentNullException (nameof (type));
123 if (method is null)
124 throw new ArgumentNullException (nameof (method));
126 if (!(type is RuntimeType rtType))
127 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (type));
128 if (!(method is RuntimeMethodInfo || method is System.Reflection.Emit.DynamicMethod))
129 throw new ArgumentException (SR.Argument_MustBeRuntimeMethodInfo, nameof (method));
131 if (!rtType.IsDelegate ())
132 throw new ArgumentException (SR.Arg_MustBeDelegate, nameof (type));
134 if (!IsMatchingCandidate (type, firstArgument, method, allowClosed, out DelegateData? delegate_data)) {
135 if (throwOnBindFailure)
136 throw new ArgumentException (SR.Arg_DlgtTargMeth);
138 return null;
141 Delegate? d = CreateDelegate_internal (type, firstArgument, method, throwOnBindFailure);
142 if (d != null) {
143 d.original_method_info = method;
144 d.data = delegate_data!;
147 return d;
150 public static Delegate? CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
152 if (type is null)
153 throw new ArgumentNullException (nameof (type));
154 if (target is null)
155 throw new ArgumentNullException (nameof (target));
156 if (method is null)
157 throw new ArgumentNullException (nameof (method));
159 if (!(type is RuntimeType rtType))
160 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (type));
161 if (!rtType.IsDelegate ())
162 throw new ArgumentException (SR.Arg_MustBeDelegate, nameof (type));
164 MethodInfo? info = GetCandidateMethod (type, target.GetType (), method, BindingFlags.Instance, ignoreCase);
165 if (info is null) {
166 if (throwOnBindFailure)
167 throw new ArgumentException (SR.Arg_DlgtTargMeth);
169 return null;
172 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
175 public static Delegate? CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
177 if (type is null)
178 throw new ArgumentNullException (nameof (type));
179 if (target is null)
180 throw new ArgumentNullException (nameof (target));
181 if (target.ContainsGenericParameters)
182 throw new ArgumentException (SR.Arg_UnboundGenParam, nameof (target));
183 if (method is null)
184 throw new ArgumentNullException (nameof (method));
186 if (!(type is RuntimeType rtType))
187 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (type));
189 if (!target.IsRuntimeImplemented ())
190 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (target));
191 if (!rtType.IsDelegate ())
192 throw new ArgumentException (SR.Arg_MustBeDelegate, nameof (type));
194 MethodInfo? info = GetCandidateMethod (type, target, method, BindingFlags.Static, ignoreCase);
195 if (info is null) {
196 if (throwOnBindFailure)
197 throw new ArgumentException (SR.Arg_DlgtTargMeth);
199 return null;
202 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
205 static MethodInfo? GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase)
207 MethodInfo? invoke = type.GetMethod ("Invoke");
208 if (invoke is null)
209 return null;
211 ParameterInfo [] delargs = invoke.GetParametersInternal ();
212 Type[] delargtypes = new Type [delargs.Length];
214 for (int i = 0; i < delargs.Length; i++)
215 delargtypes [i] = delargs [i].ParameterType;
218 * since we need to walk the inheritance chain anyway to
219 * find private methods, adjust the bindingflags to ignore
220 * inherited methods
222 BindingFlags flags = BindingFlags.ExactBinding |
223 BindingFlags.Public | BindingFlags.NonPublic |
224 BindingFlags.DeclaredOnly | bflags;
226 if (ignoreCase)
227 flags |= BindingFlags.IgnoreCase;
229 for (Type? targetType = target; targetType != null; targetType = targetType.BaseType) {
230 MethodInfo? mi = targetType.GetMethod (method, flags, null, delargtypes, Array.Empty<ParameterModifier>());
232 if (mi != null && IsReturnTypeMatch (invoke.ReturnType!, mi.ReturnType!)) {
233 return mi;
237 return null;
240 static bool IsMatchingCandidate (Type type, object? target, MethodInfo method, bool allowClosed, out DelegateData? delegateData)
242 MethodInfo? invoke = type.GetMethod ("Invoke");
244 if (invoke == null || !IsReturnTypeMatch (invoke.ReturnType!, method.ReturnType!)) {
245 delegateData = null;
246 return false;
249 ParameterInfo[] delargs = invoke.GetParametersInternal ();
250 ParameterInfo[] args = method.GetParametersInternal ();
252 bool argLengthMatch;
254 if (target != null) {
255 // delegate closed over target
256 if (!method.IsStatic)
257 // target is passed as this
258 argLengthMatch = (args.Length == delargs.Length);
259 else
260 // target is passed as the first argument to the static method
261 argLengthMatch = (args.Length == delargs.Length + 1);
262 } else {
263 if (!method.IsStatic) {
265 // Net 2.0 feature. The first argument of the delegate is passed
266 // as the 'this' argument to the method.
268 argLengthMatch = (args.Length + 1 == delargs.Length);
270 if (!argLengthMatch)
271 // closed over a null reference
272 argLengthMatch = (args.Length == delargs.Length);
273 } else {
274 argLengthMatch = (args.Length == delargs.Length);
276 if (!argLengthMatch)
277 // closed over a null reference
278 argLengthMatch = args.Length == delargs.Length + 1;
282 if (!argLengthMatch) {
283 delegateData = null;
284 return false;
287 bool argsMatch;
288 delegateData = new DelegateData ();
290 if (target != null) {
291 if (!method.IsStatic) {
292 argsMatch = IsArgumentTypeMatchWithThis (target.GetType (), method.DeclaringType!, true);
293 for (int i = 0; i < args.Length; i++)
294 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i].ParameterType);
295 } else {
296 argsMatch = IsArgumentTypeMatch (target.GetType (), args [0].ParameterType);
297 for (int i = 1; i < args.Length; i++)
298 argsMatch &= IsArgumentTypeMatch (delargs [i - 1].ParameterType, args [i].ParameterType);
300 delegateData.curried_first_arg = true;
302 } else {
303 if (!method.IsStatic) {
304 if (args.Length + 1 == delargs.Length) {
305 // The first argument should match this
306 argsMatch = IsArgumentTypeMatchWithThis (delargs [0].ParameterType, method.DeclaringType!, false);
307 for (int i = 0; i < args.Length; i++)
308 argsMatch &= IsArgumentTypeMatch (delargs [i + 1].ParameterType, args [i].ParameterType);
309 } else {
310 // closed over a null reference
311 argsMatch = allowClosed;
312 for (int i = 0; i < args.Length; i++)
313 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i].ParameterType);
315 } else {
316 if (delargs.Length + 1 == args.Length) {
317 // closed over a null reference
318 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
319 for (int i = 0; i < delargs.Length; i++)
320 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i + 1].ParameterType);
322 delegateData.curried_first_arg = true;
323 } else {
324 argsMatch = true;
325 for (int i = 0; i < args.Length; i++)
326 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i].ParameterType);
331 return argsMatch;
334 static bool IsReturnTypeMatch (Type delReturnType, Type returnType)
336 bool returnMatch = returnType == delReturnType;
338 if (!returnMatch) {
339 // Delegate covariance
340 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
341 returnMatch = true;
342 else
344 bool isDelArgEnum = delReturnType.IsEnum;
345 bool isArgEnum = returnType.IsEnum;
346 if (isArgEnum && isDelArgEnum)
347 returnMatch = Enum.GetUnderlyingType (delReturnType) == Enum.GetUnderlyingType (returnType);
348 else if (isDelArgEnum && Enum.GetUnderlyingType (delReturnType) == returnType)
349 returnMatch = true;
350 else if (isArgEnum && Enum.GetUnderlyingType (returnType) == delReturnType)
351 returnMatch = true;
355 return returnMatch;
358 static bool IsArgumentTypeMatch (Type delArgType, Type argType)
360 bool match = delArgType == argType;
362 // Delegate contravariance
363 if (!match) {
364 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
365 match = true;
367 // enum basetypes
368 if (!match) {
369 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
370 match = true;
371 else if (argType.IsEnum && Enum.GetUnderlyingType (argType) == delArgType)
372 match = true;
375 return match;
378 static bool IsArgumentTypeMatchWithThis (Type delArgType, Type argType, bool boxedThis)
380 bool match;
381 if (argType.IsValueType)
382 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
383 (boxedThis && delArgType == argType);
384 else
385 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
387 return match;
390 protected virtual object? DynamicInvokeImpl (object?[]? args)
392 if (Method is null) {
393 #nullable disable
394 // FIXME: This code cannot handle null argument values
395 Type[] mtypes = new Type [args.Length];
396 for (int i = 0; i < args.Length; ++i) {
397 mtypes [i] = args [i].GetType ();
399 method_info = _target.GetType ().GetMethod (data.method_name, mtypes);
400 #nullable restore
403 var target = _target;
405 if (data is null)
406 data = CreateDelegateData ();
408 // replace all Type.Missing with default values defined on parameters of the delegate if any
409 MethodInfo? invoke = GetType ().GetMethod ("Invoke");
410 if (invoke != null && args != null) {
411 ParameterInfo[] delegateParameters = invoke.GetParameters ();
412 for (int i = 0; i < args.Length; i++) {
413 if (args [i] == Type.Missing) {
414 ParameterInfo dlgParam = delegateParameters [i];
415 if (dlgParam.HasDefaultValue) {
416 args [i] = dlgParam.DefaultValue;
422 if (Method.IsStatic) {
424 // The delegate is bound to _target
426 if (data.curried_first_arg) {
427 if (args is null) {
428 args = new object?[] { target };
429 } else {
430 Array.Resize (ref args, args.Length + 1);
431 Array.Copy (args, 0, args, 1, args.Length - 1);
432 args [0] = target;
435 target = null;
437 } else {
438 if (_target is null && args?.Length > 0) {
439 target = args [0];
440 Array.Copy (args, 1, args, 0, args.Length - 1);
441 Array.Resize (ref args, args.Length - 1);
445 return Method.Invoke (target, args);
448 public override bool Equals (object? obj)
450 if (!(obj is Delegate d) || !InternalEqualTypes (this, obj))
451 return false;
453 // Do not compare method_ptr, since it can point to a trampoline
454 if (d._target == _target && d.Method == Method) {
455 if (d.data != null || data != null) {
456 /* Uncommon case */
457 if (d.data != null && data != null)
458 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
459 else {
460 if (d.data != null)
461 return d.data.target_type is null;
462 if (data != null)
463 return data.target_type is null;
464 return false;
467 return true;
470 return false;
473 public override int GetHashCode ()
475 MethodInfo? m = Method;
477 return (m != null ? m.GetHashCode () : GetType ().GetHashCode ()) ^ RuntimeHelpers.GetHashCode (_target);
480 protected virtual MethodInfo GetMethodImpl ()
482 if (method_info != null)
483 return method_info;
485 if (method != IntPtr.Zero) {
486 if (!method_is_virtual)
487 method_info = (MethodInfo) RuntimeMethodInfo.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
488 else
489 method_info = GetVirtualMethod_internal ();
492 return method_info;
495 DelegateData CreateDelegateData ()
497 DelegateData delegate_data = new DelegateData ();
498 if (method_info.IsStatic) {
499 if (_target != null) {
500 delegate_data.curried_first_arg = true;
501 } else {
502 MethodInfo? invoke = GetType ().GetMethod ("Invoke");
503 if (invoke != null && invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
504 delegate_data.curried_first_arg = true;
508 return delegate_data;
511 static bool InternalEqualTypes (object source, object value)
513 return source.GetType () == value.GetType ();
516 [MethodImplAttribute (MethodImplOptions.InternalCall)]
517 private protected extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);
519 [MethodImplAttribute (MethodImplOptions.InternalCall)]
520 static extern Delegate? CreateDelegate_internal (Type type, object? target, MethodInfo info, bool throwOnBindFailure);
522 [MethodImplAttribute (MethodImplOptions.InternalCall)]
523 extern MethodInfo GetVirtualMethod_internal ();