Reflect shared code changes
[mono-project.git] / mcs / class / System.Private.CoreLib / System / Delegate.cs
blob71972044ac83fd03342712e4f047c535f37fdadf
1 using System.Reflection;
2 using System.Runtime.CompilerServices;
3 using System.Runtime.InteropServices;
4 using System.Runtime.Serialization;
6 namespace System
8 /* Contains the rarely used fields of Delegate */
9 sealed class DelegateData
11 public Type target_type;
12 public string method_name;
13 public bool curried_first_arg;
16 [StructLayout (LayoutKind.Sequential)]
17 partial class Delegate
19 #region Sync with object-internals.h
20 IntPtr method_ptr;
21 IntPtr invoke_impl;
22 object _target;
23 IntPtr method;
24 IntPtr delegate_trampoline;
25 IntPtr extra_arg;
26 IntPtr method_code;
27 IntPtr interp_method;
28 IntPtr interp_invoke_impl;
29 MethodInfo method_info;
31 // Keep a ref of the MethodInfo passed to CreateDelegate.
32 // Used to keep DynamicMethods alive.
33 MethodInfo original_method_info;
35 DelegateData data;
37 bool method_is_virtual;
38 #endregion
40 protected Delegate (object target, string method)
42 if (target == null)
43 throw new ArgumentNullException (nameof(target));
45 if (method == null)
46 throw new ArgumentNullException (nameof(method));
48 this._target = target;
49 this.data = new DelegateData () {
50 method_name = method
54 protected Delegate (Type target, string method)
56 if (target == null)
57 throw new ArgumentNullException (nameof (target));
59 if (target.ContainsGenericParameters)
60 throw new ArgumentException (SR.Arg_UnboundGenParam, nameof (target));
62 if (method == null)
63 throw new ArgumentNullException (nameof (method));
65 if (!target.IsRuntimeImplemented ())
66 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (target));
68 this.data = new DelegateData () {
69 method_name = method,
70 target_type = target
74 public object Target => _target;
76 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
78 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
81 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
83 return CreateDelegate (type, null, method, throwOnBindFailure, false);
86 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
88 if (type == null)
89 throw new ArgumentNullException (nameof (type));
90 if (method == null)
91 throw new ArgumentNullException (nameof (method));
93 RuntimeType rtType = type as RuntimeType;
94 if (rtType == null)
95 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (type));
96 if (!(method is RuntimeMethodInfo || method is System.Reflection.Emit.DynamicMethod))
97 throw new ArgumentException (SR.Argument_MustBeRuntimeMethodInfo, nameof (method));
99 if (!rtType.IsDelegate ())
100 throw new ArgumentException (SR.Arg_MustBeDelegate, nameof (type));
102 if (!IsMatchingCandidate (type, firstArgument, method, allowClosed, out DelegateData delegate_data)) {
103 if (throwOnBindFailure)
104 throw new ArgumentException (SR.Arg_DlgtTargMeth);
106 return null;
109 Delegate d = CreateDelegate_internal (type, firstArgument, method, throwOnBindFailure);
110 if (d != null)
111 d.original_method_info = method;
112 if (delegate_data != null)
113 d.data = delegate_data;
114 return d;
117 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
119 if (type == null)
120 throw new ArgumentNullException (nameof (type));
121 if (target == null)
122 throw new ArgumentNullException (nameof (target));
123 if (method == null)
124 throw new ArgumentNullException (nameof (method));
126 RuntimeType rtType = type as RuntimeType;
127 if (rtType == null)
128 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (type));
129 if (!rtType.IsDelegate ())
130 throw new ArgumentException (SR.Arg_MustBeDelegate, nameof (type));
132 MethodInfo info = GetCandidateMethod (type, target.GetType (), method, BindingFlags.Instance, ignoreCase);
133 if (info == null) {
134 if (throwOnBindFailure)
135 throw new ArgumentException (SR.Arg_DlgtTargMeth);
137 return null;
140 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
143 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
145 if (type == null)
146 throw new ArgumentNullException (nameof (type));
147 if (target == null)
148 throw new ArgumentNullException (nameof (target));
149 if (target.ContainsGenericParameters)
150 throw new ArgumentException (SR.Arg_UnboundGenParam, nameof (target));
151 if (method == null)
152 throw new ArgumentNullException (nameof (method));
154 RuntimeType rtType = type as RuntimeType;
155 RuntimeType rtTarget = target as RuntimeType;
156 if (rtType == null)
157 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (type));
158 if (rtTarget == null)
159 throw new ArgumentException (SR.Argument_MustBeRuntimeType, nameof (target));
160 if (!rtType.IsDelegate ())
161 throw new ArgumentException (SR.Arg_MustBeDelegate, nameof (type));
163 MethodInfo info = GetCandidateMethod (type, target, method, BindingFlags.Static, ignoreCase);
164 if (info == null) {
165 if (throwOnBindFailure)
166 throw new ArgumentException (SR.Arg_DlgtTargMeth);
168 return null;
171 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
174 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase)
176 MethodInfo invoke = type.GetMethod ("Invoke");
177 ParameterInfo [] delargs = invoke.GetParametersInternal ();
178 Type[] delargtypes = new Type [delargs.Length];
180 for (int i = 0; i < delargs.Length; i++)
181 delargtypes [i] = delargs [i].ParameterType;
184 * since we need to walk the inheritance chain anyway to
185 * find private methods, adjust the bindingflags to ignore
186 * inherited methods
188 BindingFlags flags = BindingFlags.ExactBinding |
189 BindingFlags.Public | BindingFlags.NonPublic |
190 BindingFlags.DeclaredOnly | bflags;
192 if (ignoreCase)
193 flags |= BindingFlags.IgnoreCase;
195 MethodInfo info = null;
197 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
198 MethodInfo mi = targetType.GetMethod (method, flags,
199 null, delargtypes, Array.Empty<ParameterModifier>());
200 if (mi != null && IsReturnTypeMatch (invoke.ReturnType, mi.ReturnType)) {
201 info = mi;
202 break;
206 return info;
209 static bool IsMatchingCandidate (Type type, object target, MethodInfo method, bool allowClosed, out DelegateData delegate_data)
211 MethodInfo invoke = type.GetMethod ("Invoke");
213 if (!IsReturnTypeMatch (invoke.ReturnType, method.ReturnType)) {
214 delegate_data = null;
215 return false;
218 ParameterInfo[] delargs = invoke.GetParametersInternal ();
219 ParameterInfo[] args = method.GetParametersInternal ();
221 bool argLengthMatch;
223 if (target != null) {
224 // delegate closed over target
225 if (!method.IsStatic)
226 // target is passed as this
227 argLengthMatch = (args.Length == delargs.Length);
228 else
229 // target is passed as the first argument to the static method
230 argLengthMatch = (args.Length == delargs.Length + 1);
231 } else {
232 if (!method.IsStatic) {
234 // Net 2.0 feature. The first argument of the delegate is passed
235 // as the 'this' argument to the method.
237 argLengthMatch = (args.Length + 1 == delargs.Length);
239 if (!argLengthMatch)
240 // closed over a null reference
241 argLengthMatch = (args.Length == delargs.Length);
242 } else {
243 argLengthMatch = (args.Length == delargs.Length);
245 if (!argLengthMatch)
246 // closed over a null reference
247 argLengthMatch = args.Length == delargs.Length + 1;
251 if (!argLengthMatch) {
252 delegate_data = null;
253 return false;
256 bool argsMatch;
257 delegate_data = new DelegateData ();
259 if (target != null) {
260 if (!method.IsStatic) {
261 argsMatch = IsArgumentTypeMatchWithThis (target.GetType (), method.DeclaringType, true);
262 for (int i = 0; i < args.Length; i++)
263 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i].ParameterType);
264 } else {
265 argsMatch = IsArgumentTypeMatch (target.GetType (), args [0].ParameterType);
266 for (int i = 1; i < args.Length; i++)
267 argsMatch &= IsArgumentTypeMatch (delargs [i - 1].ParameterType, args [i].ParameterType);
269 delegate_data.curried_first_arg = true;
271 } else {
272 if (!method.IsStatic) {
273 if (args.Length + 1 == delargs.Length) {
274 // The first argument should match this
275 argsMatch = IsArgumentTypeMatchWithThis (delargs [0].ParameterType, method.DeclaringType, false);
276 for (int i = 0; i < args.Length; i++)
277 argsMatch &= IsArgumentTypeMatch (delargs [i + 1].ParameterType, args [i].ParameterType);
278 } else {
279 // closed over a null reference
280 argsMatch = allowClosed;
281 for (int i = 0; i < args.Length; i++)
282 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i].ParameterType);
284 } else {
285 if (delargs.Length + 1 == args.Length) {
286 // closed over a null reference
287 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
288 for (int i = 0; i < delargs.Length; i++)
289 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i + 1].ParameterType);
291 delegate_data.curried_first_arg = true;
292 } else {
293 argsMatch = true;
294 for (int i = 0; i < args.Length; i++)
295 argsMatch &= IsArgumentTypeMatch (delargs [i].ParameterType, args [i].ParameterType);
300 return argsMatch;
303 static bool IsReturnTypeMatch (Type delReturnType, Type returnType)
305 bool returnMatch = returnType == delReturnType;
307 if (!returnMatch) {
308 // Delegate covariance
309 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
310 returnMatch = true;
313 return returnMatch;
316 static bool IsArgumentTypeMatch (Type delArgType, Type argType)
318 bool match = delArgType == argType;
320 // Delegate contravariance
321 if (!match) {
322 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
323 match = true;
325 // enum basetypes
326 if (!match) {
327 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
328 match = true;
329 else if (argType.IsEnum && Enum.GetUnderlyingType (argType) == delArgType)
330 match = true;
333 return match;
336 static bool IsArgumentTypeMatchWithThis (Type delArgType, Type argType, bool boxedThis)
338 bool match;
339 if (argType.IsValueType)
340 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
341 (boxedThis && delArgType == argType);
342 else
343 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
345 return match;
348 protected virtual object DynamicInvokeImpl (object[] args)
350 if (Method == null) {
351 Type[] mtypes = new Type [args.Length];
352 for (int i = 0; i < args.Length; ++i) {
353 mtypes [i] = args [i].GetType ();
355 method_info = _target.GetType ().GetMethod (data.method_name, mtypes);
358 var target = _target;
359 if (this.data == null)
360 InitializeDelegateData ();
362 if (Method.IsStatic) {
364 // The delegate is bound to _target
366 if (data.curried_first_arg) {
367 if (args == null) {
368 args = new [] { target };
369 } else {
370 Array.Resize (ref args, args.Length + 1);
371 Array.Copy (args, 0, args, 1, args.Length - 1);
372 args [0] = target;
375 target = null;
377 } else {
378 if (_target == null && args != null && args.Length > 0) {
379 target = args [0];
380 Array.Copy (args, 1, args, 0, args.Length - 1);
381 Array.Resize (ref args, args.Length - 1);
385 return Method.Invoke (target, args);
388 public override bool Equals (object obj)
390 Delegate d = obj as Delegate;
392 if (d == null)
393 return false;
395 // Do not compare method_ptr, since it can point to a trampoline
396 if (d._target == _target && d.Method == Method) {
397 if (d.data != null || data != null) {
398 /* Uncommon case */
399 if (d.data != null && data != null)
400 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
401 else {
402 if (d.data != null)
403 return d.data.target_type == null;
404 if (data != null)
405 return data.target_type == null;
406 return false;
409 return true;
412 return false;
415 public override int GetHashCode ()
417 MethodInfo m = Method;
419 return (m != null ? m.GetHashCode () : GetType ().GetHashCode ()) ^ RuntimeHelpers.GetHashCode (_target);
422 protected virtual MethodInfo GetMethodImpl ()
424 if (method_info != null)
425 return method_info;
427 if (method != IntPtr.Zero) {
428 if (!method_is_virtual)
429 method_info = (MethodInfo) RuntimeMethodInfo.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
430 else
431 method_info = GetVirtualMethod_internal ();
434 return method_info;
437 void InitializeDelegateData ()
439 DelegateData delegate_data = new DelegateData ();
440 if (method_info.IsStatic) {
441 if (_target != null) {
442 delegate_data.curried_first_arg = true;
443 } else {
444 MethodInfo invoke = GetType ().GetMethod ("Invoke");
445 if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
446 delegate_data.curried_first_arg = true;
449 this.data = delegate_data;
452 static bool InternalEqualTypes (object source, object value)
454 return source.GetType () == value.GetType ();
457 [MethodImplAttribute (MethodImplOptions.InternalCall)]
458 private protected extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);
460 [MethodImplAttribute (MethodImplOptions.InternalCall)]
461 static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
463 [MethodImplAttribute (MethodImplOptions.InternalCall)]
464 extern MethodInfo GetVirtualMethod_internal ();