Use simple Array.Copy overload where possible (dotnet/coreclr#27641)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / DefaultBinder.cs
blobfed9a020fd8721b69fb291bfe26ed15b46a7461a
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Reflection;
6 using System.Diagnostics;
7 using CultureInfo = System.Globalization.CultureInfo;
9 namespace System
11 #if CORERT
12 public sealed
13 #else
14 internal
15 #endif
16 partial class DefaultBinder : Binder
18 // This method is passed a set of methods and must choose the best
19 // fit. The methods all have the same number of arguments and the object
20 // array args. On exit, this method will choice the best fit method
21 // and coerce the args to match that method. By match, we mean all primitive
22 // arguments are exact matchs and all object arguments are exact or subclasses
23 // of the target. If the target OR is an interface, the object must implement
24 // that interface. There are a couple of exceptions
25 // thrown when a method cannot be returned. If no method matchs the args and
26 // ArgumentException is thrown. If multiple methods match the args then
27 // an AmbiguousMatchException is thrown.
29 // The most specific match will be selected.
31 public sealed override MethodBase BindToMethod(
32 BindingFlags bindingAttr, MethodBase[] match, ref object?[] args,
33 ParameterModifier[]? modifiers, CultureInfo? cultureInfo, string[]? names, out object? state)
35 if (match == null || match.Length == 0)
36 throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
38 MethodBase?[] candidates = (MethodBase[])match.Clone();
40 int i;
41 int j;
43 state = null;
45 #region Map named parameters to candidate parameter positions
46 // We are creating an paramOrder array to act as a mapping
47 // between the order of the args and the actual order of the
48 // parameters in the method. This order may differ because
49 // named parameters (names) may change the order. If names
50 // is not provided, then we assume the default mapping (0,1,...)
51 int[][] paramOrder = new int[candidates.Length][];
53 for (i = 0; i < candidates.Length; i++)
55 ParameterInfo[] par = candidates[i]!.GetParametersNoCopy();
57 // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted
58 paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length];
60 if (names == null)
62 // Default mapping
63 for (j = 0; j < args.Length; j++)
64 paramOrder[i][j] = j;
66 else
68 // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method
69 // doesn't have a name that matchs one of the named parameters so we don't consider it any further.
70 if (!CreateParamOrder(paramOrder[i], par, names))
71 candidates[i] = null;
74 #endregion
76 Type[] paramArrayTypes = new Type[candidates.Length];
78 Type[] argTypes = new Type[args.Length];
80 #region Cache the type of the provided arguments
81 // object that contain a null are treated as if they were typeless (but match either object
82 // references or value classes). We mark this condition by placing a null in the argTypes array.
83 for (i = 0; i < args.Length; i++)
85 if (args[i] != null)
87 argTypes[i] = args[i]!.GetType();
90 #endregion
92 // Find the method that matches...
93 int CurIdx = 0;
94 bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0);
96 Type? paramArrayType;
98 #region Filter methods by parameter count and type
99 for (i = 0; i < candidates.Length; i++)
101 paramArrayType = null;
103 // If we have named parameters then we may have a hole in the candidates array.
104 if (candidates[i] == null)
105 continue;
107 // Validate the parameters.
108 ParameterInfo[] par = candidates[i]!.GetParametersNoCopy(); // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
110 #region Match method by parameter count
111 if (par.Length == 0)
113 #region No formal parameters
114 if (args.Length != 0)
116 if ((candidates[i]!.CallingConvention & CallingConventions.VarArgs) == 0) // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
117 continue;
120 // This is a valid routine so we move it up the candidates list.
121 paramOrder[CurIdx] = paramOrder[i];
122 candidates[CurIdx++] = candidates[i];
124 continue;
125 #endregion
127 else if (par.Length > args.Length)
129 #region Shortage of provided parameters
130 // If the number of parameters is greater than the number of args then
131 // we are in the situation were we may be using default values.
132 for (j = args.Length; j < par.Length - 1; j++)
134 if (par[j].DefaultValue == System.DBNull.Value)
135 break;
138 if (j != par.Length - 1)
139 continue;
141 if (par[j].DefaultValue == System.DBNull.Value)
143 if (!par[j].ParameterType.IsArray)
144 continue;
146 if (!par[j].IsDefined(typeof(ParamArrayAttribute), true))
147 continue;
149 paramArrayType = par[j].ParameterType.GetElementType();
151 #endregion
153 else if (par.Length < args.Length)
155 #region Excess provided parameters
156 // test for the ParamArray case
157 int lastArgPos = par.Length - 1;
159 if (!par[lastArgPos].ParameterType.IsArray)
160 continue;
162 if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true))
163 continue;
165 if (paramOrder[i][lastArgPos] != lastArgPos)
166 continue;
168 paramArrayType = par[lastArgPos].ParameterType.GetElementType();
169 #endregion
171 else
173 #region Test for paramArray, save paramArray type
174 int lastArgPos = par.Length - 1;
176 if (par[lastArgPos].ParameterType.IsArray
177 && par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)
178 && paramOrder[i][lastArgPos] == lastArgPos)
180 if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos]))
181 paramArrayType = par[lastArgPos].ParameterType.GetElementType();
183 #endregion
185 #endregion
187 Type pCls;
188 int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length;
190 #region Match method by parameter type
191 for (j = 0; j < argsToCheck; j++)
193 #region Classic argument coersion checks
194 // get the formal type
195 pCls = par[j].ParameterType;
197 if (pCls.IsByRef)
198 pCls = pCls.GetElementType()!;
200 // the type is the same
201 if (pCls == argTypes[paramOrder[i][j]])
202 continue;
204 // a default value is available
205 if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing)
206 continue;
208 // the argument was null, so it matches with everything
209 if (args[paramOrder[i][j]] == null)
210 continue;
212 // the type is Object, so it will match everything
213 if (pCls == typeof(object))
214 continue;
216 // now do a "classic" type check
217 if (pCls.IsPrimitive)
219 if (argTypes[paramOrder[i][j]] == null || !CanChangePrimitive(args[paramOrder[i][j]]?.GetType(), pCls))
221 break;
224 else
226 if (argTypes[paramOrder[i][j]] == null)
227 continue;
229 if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]]))
231 if (argTypes[paramOrder[i][j]].IsCOMObject)
233 if (pCls.IsInstanceOfType(args[paramOrder[i][j]]))
234 continue;
236 break;
239 #endregion
242 if (paramArrayType != null && j == par.Length - 1)
244 #region Check that excess arguments can be placed in the param array
245 for (; j < args.Length; j++)
247 if (paramArrayType.IsPrimitive)
249 if (argTypes[j] == null || !CanChangePrimitive(args[j]?.GetType(), paramArrayType))
250 break;
252 else
254 if (argTypes[j] == null)
255 continue;
257 if (!paramArrayType.IsAssignableFrom(argTypes[j]))
259 if (argTypes[j].IsCOMObject)
261 if (paramArrayType.IsInstanceOfType(args[j]))
262 continue;
265 break;
269 #endregion
271 #endregion
273 if (j == args.Length)
275 #region This is a valid routine so we move it up the candidates list
276 paramOrder[CurIdx] = paramOrder[i];
277 paramArrayTypes[CurIdx] = paramArrayType!;
278 candidates[CurIdx++] = candidates[i];
279 #endregion
282 #endregion
284 // If we didn't find a method
285 if (CurIdx == 0)
286 throw new MissingMethodException(SR.MissingMember);
288 if (CurIdx == 1)
290 #region Found only one method
291 if (names != null)
293 state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null);
294 ReorderParams(paramOrder[0], args);
297 // If the parameters and the args are not the same length or there is a paramArray
298 // then we need to create a argument array.
299 ParameterInfo[] parms = candidates[0]!.GetParametersNoCopy();
301 if (parms.Length == args.Length)
303 if (paramArrayTypes[0] != null)
305 object[] objs = new object[parms.Length];
306 int lastPos = parms.Length - 1;
307 Array.Copy(args, objs, lastPos);
308 objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1);
309 ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
310 args = objs;
313 else if (parms.Length > args.Length)
315 object?[] objs = new object[parms.Length];
317 for (i = 0; i < args.Length; i++)
318 objs[i] = args[i];
320 for (; i < parms.Length - 1; i++)
321 objs[i] = parms[i].DefaultValue;
323 if (paramArrayTypes[0] != null)
324 objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the
326 else
327 objs[i] = parms[i].DefaultValue;
329 args = objs;
331 else
333 if ((candidates[0]!.CallingConvention & CallingConventions.VarArgs) == 0)
335 object[] objs = new object[parms.Length];
336 int paramArrayPos = parms.Length - 1;
337 Array.Copy(args, objs, paramArrayPos);
338 objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos);
339 Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
340 args = objs;
343 #endregion
345 return candidates[0]!;
348 int currentMin = 0;
349 bool ambig = false;
350 for (i = 1; i < CurIdx; i++)
352 #region Walk all of the methods looking the most specific method to invoke
353 int newMin = FindMostSpecificMethod(candidates[currentMin]!, paramOrder[currentMin], paramArrayTypes[currentMin],
354 candidates[i]!, paramOrder[i], paramArrayTypes[i], argTypes, args);
356 if (newMin == 0)
358 ambig = true;
360 else if (newMin == 2)
362 currentMin = i;
363 ambig = false;
365 #endregion
368 if (ambig)
369 throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
371 // Reorder (if needed)
372 if (names != null)
374 state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null);
375 ReorderParams(paramOrder[currentMin], args);
378 // If the parameters and the args are not the same length or there is a paramArray
379 // then we need to create a argument array.
380 ParameterInfo[] parameters = candidates[currentMin]!.GetParametersNoCopy();
381 if (parameters.Length == args.Length)
383 if (paramArrayTypes[currentMin] != null)
385 object[] objs = new object[parameters.Length];
386 int lastPos = parameters.Length - 1;
387 Array.Copy(args, objs, lastPos);
388 objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1);
389 ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
390 args = objs;
393 else if (parameters.Length > args.Length)
395 object?[] objs = new object[parameters.Length];
397 for (i = 0; i < args.Length; i++)
398 objs[i] = args[i];
400 for (; i < parameters.Length - 1; i++)
401 objs[i] = parameters[i].DefaultValue;
403 if (paramArrayTypes[currentMin] != null)
405 objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0);
407 else
409 objs[i] = parameters[i].DefaultValue;
412 args = objs;
414 else
416 if ((candidates[currentMin]!.CallingConvention & CallingConventions.VarArgs) == 0)
418 object[] objs = new object[parameters.Length];
419 int paramArrayPos = parameters.Length - 1;
420 Array.Copy(args, objs, paramArrayPos);
421 objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos);
422 Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
423 args = objs;
427 return candidates[currentMin]!;
430 // Given a set of fields that match the base criteria, select a field.
431 // if value is null then we have no way to select a field
432 public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo? cultureInfo)
434 if (match == null)
436 throw new ArgumentNullException(nameof(match));
439 int i;
440 // Find the method that match...
441 int CurIdx = 0;
443 Type valueType;
445 FieldInfo[] candidates = (FieldInfo[])match.Clone();
447 // If we are a FieldSet, then use the value's type to disambiguate
448 if ((bindingAttr & BindingFlags.SetField) != 0)
450 valueType = value.GetType();
452 for (i = 0; i < candidates.Length; i++)
454 Type pCls = candidates[i].FieldType;
455 if (pCls == valueType)
457 candidates[CurIdx++] = candidates[i];
458 continue;
460 if (value == Empty.Value)
462 // the object passed in was null which would match any non primitive non value type
463 if (pCls.IsClass)
465 candidates[CurIdx++] = candidates[i];
466 continue;
469 if (pCls == typeof(object))
471 candidates[CurIdx++] = candidates[i];
472 continue;
474 if (pCls.IsPrimitive)
476 if (CanChangePrimitive(valueType, pCls))
478 candidates[CurIdx++] = candidates[i];
479 continue;
482 else
484 if (pCls.IsAssignableFrom(valueType))
486 candidates[CurIdx++] = candidates[i];
487 continue;
491 if (CurIdx == 0)
492 throw new MissingFieldException(SR.MissingField);
493 if (CurIdx == 1)
494 return candidates[0];
497 // Walk all of the methods looking the most specific method to invoke
498 int currentMin = 0;
499 bool ambig = false;
500 for (i = 1; i < CurIdx; i++)
502 int newMin = FindMostSpecificField(candidates[currentMin], candidates[i]);
503 if (newMin == 0)
504 ambig = true;
505 else
507 if (newMin == 2)
509 currentMin = i;
510 ambig = false;
514 if (ambig)
515 throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
516 return candidates[currentMin];
519 // Given a set of methods that match the base criteria, select a method based
520 // upon an array of types. This method should return null if no method matchs
521 // the criteria.
522 public sealed override MethodBase? SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[]? modifiers)
524 int i;
525 int j;
527 Type[] realTypes = new Type[types.Length];
528 for (i = 0; i < types.Length; i++)
530 realTypes[i] = types[i].UnderlyingSystemType;
531 if (!(realTypes[i].IsRuntimeImplemented() || realTypes[i] is SignatureType))
532 throw new ArgumentException(SR.Arg_MustBeType, nameof(types));
534 types = realTypes;
536 // We don't automatically jump out on exact match.
537 if (match == null || match.Length == 0)
538 throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
540 MethodBase[] candidates = (MethodBase[])match.Clone();
542 // Find all the methods that can be described by the types parameter.
543 // Remove all of them that cannot.
544 int CurIdx = 0;
545 for (i = 0; i < candidates.Length; i++)
547 ParameterInfo[] par = candidates[i].GetParametersNoCopy();
548 if (par.Length != types.Length)
549 continue;
550 for (j = 0; j < types.Length; j++)
552 Type pCls = par[j].ParameterType;
553 if (types[j].MatchesParameterTypeExactly(par[j]))
554 continue;
555 if (pCls == typeof(object))
556 continue;
558 Type? type = types[j];
559 if (type is SignatureType signatureType)
561 if (!(candidates[i] is MethodInfo methodInfo))
562 break;
563 type = signatureType.TryResolveAgainstGenericMethod(methodInfo);
564 if (type == null)
565 break;
568 if (pCls.IsPrimitive)
570 if (!type.UnderlyingSystemType.IsRuntimeImplemented() ||
571 !CanChangePrimitive(type.UnderlyingSystemType, pCls.UnderlyingSystemType))
572 break;
574 else
576 if (!pCls.IsAssignableFrom(type))
577 break;
580 if (j == types.Length)
581 candidates[CurIdx++] = candidates[i];
583 if (CurIdx == 0)
584 return null;
585 if (CurIdx == 1)
586 return candidates[0];
588 // Walk all of the methods looking the most specific method to invoke
589 int currentMin = 0;
590 bool ambig = false;
591 int[] paramOrder = new int[types.Length];
592 for (i = 0; i < types.Length; i++)
593 paramOrder[i] = i;
594 for (i = 1; i < CurIdx; i++)
596 int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder, null, candidates[i], paramOrder, null, types, null);
597 if (newMin == 0)
598 ambig = true;
599 else
601 if (newMin == 2)
603 currentMin = i;
604 ambig = false;
605 currentMin = i;
609 if (ambig)
610 throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
611 return candidates[currentMin];
614 // Given a set of properties that match the base criteria, select one.
615 public sealed override PropertyInfo? SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type? returnType,
616 Type[]? indexes, ParameterModifier[]? modifiers)
618 // Allow a null indexes array. But if it is not null, every element must be non-null as well.
619 if (indexes != null)
621 foreach (Type index in indexes)
623 if (index == null)
624 throw new ArgumentNullException(nameof(indexes));
628 if (match == null || match.Length == 0)
629 throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
631 PropertyInfo[] candidates = (PropertyInfo[])match.Clone();
633 int i, j = 0;
635 // Find all the properties that can be described by type indexes parameter
636 int CurIdx = 0;
637 int indexesLength = (indexes != null) ? indexes.Length : 0;
638 for (i = 0; i < candidates.Length; i++)
640 if (indexes != null)
642 ParameterInfo[] par = candidates[i].GetIndexParameters();
643 if (par.Length != indexesLength)
644 continue;
646 for (j = 0; j < indexesLength; j++)
648 Type pCls = par[j].ParameterType;
650 // If the classes exactly match continue
651 if (pCls == indexes[j])
652 continue;
653 if (pCls == typeof(object))
654 continue;
656 if (pCls.IsPrimitive)
658 if (!indexes[j].UnderlyingSystemType.IsRuntimeImplemented() ||
659 !CanChangePrimitive(indexes[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
660 break;
662 else
664 if (!pCls.IsAssignableFrom(indexes[j]))
665 break;
670 if (j == indexesLength)
672 if (returnType != null)
674 if (candidates[i].PropertyType.IsPrimitive)
676 if (!returnType.UnderlyingSystemType.IsRuntimeImplemented() ||
677 !CanChangePrimitive(returnType.UnderlyingSystemType, candidates[i].PropertyType.UnderlyingSystemType))
678 continue;
680 else
682 if (!candidates[i].PropertyType.IsAssignableFrom(returnType))
683 continue;
686 candidates[CurIdx++] = candidates[i];
689 if (CurIdx == 0)
690 return null;
691 if (CurIdx == 1)
692 return candidates[0];
694 // Walk all of the properties looking the most specific method to invoke
695 int currentMin = 0;
696 bool ambig = false;
697 int[] paramOrder = new int[indexesLength];
698 for (i = 0; i < indexesLength; i++)
699 paramOrder[i] = i;
700 for (i = 1; i < CurIdx; i++)
702 int newMin = FindMostSpecificType(candidates[currentMin].PropertyType, candidates[i].PropertyType, returnType);
703 if (newMin == 0 && indexes != null)
704 newMin = FindMostSpecific(candidates[currentMin].GetIndexParameters(),
705 paramOrder,
706 null,
707 candidates[i].GetIndexParameters(),
708 paramOrder,
709 null,
710 indexes,
711 null);
712 if (newMin == 0)
714 newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]);
715 if (newMin == 0)
716 ambig = true;
718 if (newMin == 2)
720 ambig = false;
721 currentMin = i;
725 if (ambig)
726 throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
727 return candidates[currentMin];
730 // ChangeType
731 // The default binder doesn't support any change type functionality.
732 // This is because the default is built into the low level invoke code.
733 public override object ChangeType(object value, Type type, CultureInfo? cultureInfo)
735 throw new NotSupportedException(SR.NotSupported_ChangeType);
738 public sealed override void ReorderArgumentArray(ref object?[] args, object state)
740 BinderState binderState = (BinderState)state;
741 ReorderParams(binderState._argsMap, args);
742 if (binderState._isParamArray)
744 int paramArrayPos = args.Length - 1;
745 if (args.Length == binderState._originalSize)
747 args[paramArrayPos] = ((object[])args[paramArrayPos]!)[0];
749 else
751 // must be args.Length < state.originalSize
752 object[] newArgs = new object[args.Length];
753 Array.Copy(args, newArgs, paramArrayPos);
754 for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++)
756 newArgs[i] = ((object[])args[paramArrayPos]!)[j];
758 args = newArgs;
761 else
763 if (args.Length > binderState._originalSize)
765 object[] newArgs = new object[binderState._originalSize];
766 Array.Copy(args, newArgs, binderState._originalSize);
767 args = newArgs;
772 // Return any exact bindings that may exist. (This method is not defined on the
773 // Binder and is used by RuntimeType.)
774 public static MethodBase? ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[]? modifiers)
776 if (match == null)
777 throw new ArgumentNullException(nameof(match));
779 MethodBase[] aExactMatches = new MethodBase[match.Length];
780 int cExactMatches = 0;
782 for (int i = 0; i < match.Length; i++)
784 ParameterInfo[] par = match[i].GetParametersNoCopy();
785 if (par.Length == 0)
787 continue;
789 int j;
790 for (j = 0; j < types.Length; j++)
792 Type pCls = par[j].ParameterType;
794 // If the classes exactly match continue
795 if (!pCls.Equals(types[j]))
796 break;
798 if (j < types.Length)
799 continue;
801 // Add the exact match to the array of exact matches.
802 aExactMatches[cExactMatches] = match[i];
803 cExactMatches++;
806 if (cExactMatches == 0)
807 return null;
809 if (cExactMatches == 1)
810 return aExactMatches[0];
812 return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches);
815 // Return any exact bindings that may exist. (This method is not defined on the
816 // Binder and is used by RuntimeType.)
817 public static PropertyInfo? ExactPropertyBinding(PropertyInfo[] match, Type? returnType, Type[]? types, ParameterModifier[]? modifiers)
819 if (match == null)
820 throw new ArgumentNullException(nameof(match));
822 PropertyInfo? bestMatch = null;
823 int typesLength = (types != null) ? types.Length : 0;
824 for (int i = 0; i < match.Length; i++)
826 ParameterInfo[] par = match[i].GetIndexParameters();
827 int j;
828 for (j = 0; j < typesLength; j++)
830 Type pCls = par[j].ParameterType;
832 // If the classes exactly match continue
833 if (pCls != types![j])
834 break;
836 if (j < typesLength)
837 continue;
838 if (returnType != null && returnType != match[i].PropertyType)
839 continue;
841 if (bestMatch != null)
842 throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
844 bestMatch = match[i];
846 return bestMatch;
849 private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type? paramArrayType1,
850 ParameterInfo[] p2, int[] paramOrder2, Type? paramArrayType2,
851 Type[] types, object?[]? args)
853 // A method using params is always less specific than one not using params
854 if (paramArrayType1 != null && paramArrayType2 == null) return 2;
855 if (paramArrayType2 != null && paramArrayType1 == null) return 1;
857 // now either p1 and p2 both use params or neither does.
859 bool p1Less = false;
860 bool p2Less = false;
862 for (int i = 0; i < types.Length; i++)
864 if (args != null && args[i] == Type.Missing)
865 continue;
867 Type c1, c2;
869 // If a param array is present, then either
870 // the user re-ordered the parameters in which case
871 // the argument to the param array is either an array
872 // in which case the params is conceptually ignored and so paramArrayType1 == null
873 // or the argument to the param array is a single element
874 // in which case paramOrder[i] == p1.Length - 1 for that element
875 // or the user did not re-order the parameters in which case
876 // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
877 // so any index >= p.Length - 1 is being put in the param array
879 if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1)
880 c1 = paramArrayType1;
881 else
882 c1 = p1[paramOrder1[i]].ParameterType;
884 if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1)
885 c2 = paramArrayType2;
886 else
887 c2 = p2[paramOrder2[i]].ParameterType;
889 if (c1 == c2) continue;
891 switch (FindMostSpecificType(c1, c2, types[i]))
893 case 0: return 0;
894 case 1: p1Less = true; break;
895 case 2: p2Less = true; break;
899 // Two way p1Less and p2Less can be equal. All the arguments are the
900 // same they both equal false, otherwise there were things that both
901 // were the most specific type on....
902 if (p1Less == p2Less)
904 // if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
905 // let's see which one has the most matches without using the params array (the longer one wins).
906 if (!p1Less && args != null)
908 if (p1.Length > p2.Length)
910 return 1;
912 else if (p2.Length > p1.Length)
914 return 2;
918 return 0;
920 else
922 return p1Less ? 1 : 2;
926 private static int FindMostSpecificType(Type c1, Type c2, Type? t)
928 // If the two types are exact move on...
929 if (c1 == c2)
930 return 0;
932 if (t is SignatureType signatureType)
934 if (signatureType.MatchesExactly(c1))
935 return 1;
937 if (signatureType.MatchesExactly(c2))
938 return 2;
940 else
942 if (c1 == t)
943 return 1;
945 if (c2 == t)
946 return 2;
949 bool c1FromC2;
950 bool c2FromC1;
952 if (c1.IsByRef || c2.IsByRef)
954 if (c1.IsByRef && c2.IsByRef)
956 c1 = c1.GetElementType()!;
957 c2 = c2.GetElementType()!;
959 else if (c1.IsByRef)
961 if (c1.GetElementType() == c2)
962 return 2;
964 c1 = c1.GetElementType()!;
966 else // if (c2.IsByRef)
968 if (c2.GetElementType() == c1)
969 return 1;
971 c2 = c2.GetElementType()!;
975 if (c1.IsPrimitive && c2.IsPrimitive)
977 c1FromC2 = CanChangePrimitive(c2, c1);
978 c2FromC1 = CanChangePrimitive(c1, c2);
980 else
982 c1FromC2 = c1.IsAssignableFrom(c2);
983 c2FromC1 = c2.IsAssignableFrom(c1);
986 if (c1FromC2 == c2FromC1)
987 return 0;
989 if (c1FromC2)
991 return 2;
993 else
995 return 1;
999 private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type? paramArrayType1,
1000 MethodBase m2, int[] paramOrder2, Type? paramArrayType2,
1001 Type[] types, object?[]? args)
1003 // Find the most specific method based on the parameters.
1004 int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1,
1005 m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args);
1007 // If the match was not ambigous then return the result.
1008 if (res != 0)
1009 return res;
1011 // Check to see if the methods have the exact same name and signature.
1012 if (CompareMethodSig(m1, m2))
1014 // Determine the depth of the declaring types for both methods.
1015 int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType!);
1016 int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType!);
1018 // The most derived method is the most specific one.
1019 if (hierarchyDepth1 == hierarchyDepth2)
1021 return 0;
1023 else if (hierarchyDepth1 < hierarchyDepth2)
1025 return 2;
1027 else
1029 return 1;
1033 // The match is ambigous.
1034 return 0;
1037 private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2)
1039 // Check to see if the fields have the same name.
1040 if (cur1.Name == cur2.Name)
1042 int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType!);
1043 int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType!);
1045 if (hierarchyDepth1 == hierarchyDepth2)
1047 Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2");
1048 return 0;
1050 else if (hierarchyDepth1 < hierarchyDepth2)
1051 return 2;
1052 else
1053 return 1;
1056 // The match is ambigous.
1057 return 0;
1060 private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2)
1062 // Check to see if the fields have the same name.
1063 if (cur1.Name == cur2.Name)
1065 int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType!);
1066 int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType!);
1068 if (hierarchyDepth1 == hierarchyDepth2)
1070 return 0;
1072 else if (hierarchyDepth1 < hierarchyDepth2)
1073 return 2;
1074 else
1075 return 1;
1078 // The match is ambigous.
1079 return 0;
1082 public static bool CompareMethodSig(MethodBase m1, MethodBase m2)
1084 ParameterInfo[] params1 = m1.GetParametersNoCopy();
1085 ParameterInfo[] params2 = m2.GetParametersNoCopy();
1087 if (params1.Length != params2.Length)
1088 return false;
1090 int numParams = params1.Length;
1091 for (int i = 0; i < numParams; i++)
1093 if (params1[i].ParameterType != params2[i].ParameterType)
1094 return false;
1097 return true;
1100 private static int GetHierarchyDepth(Type t)
1102 int depth = 0;
1104 Type? currentType = t;
1107 depth++;
1108 currentType = currentType.BaseType;
1109 } while (currentType != null);
1111 return depth;
1114 internal static MethodBase? FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches)
1116 int deepestHierarchy = 0;
1117 MethodBase? methWithDeepestHierarchy = null;
1119 for (int i = 0; i < cMatches; i++)
1121 // Calculate the depth of the hierarchy of the declaring type of the
1122 // current method.
1123 int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType!);
1125 // The two methods have the same name, signature, and hierarchy depth.
1126 // This can only happen if at least one is vararg or generic.
1127 if (currentHierarchyDepth == deepestHierarchy)
1129 throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
1132 // Check to see if this method is on the most derived class.
1133 if (currentHierarchyDepth > deepestHierarchy)
1135 deepestHierarchy = currentHierarchyDepth;
1136 methWithDeepestHierarchy = match[i];
1140 return methWithDeepestHierarchy;
1143 // This method will sort the vars array into the mapping order stored
1144 // in the paramOrder array.
1145 private static void ReorderParams(int[] paramOrder, object?[] vars)
1147 object?[] varsCopy = new object[vars.Length];
1148 for (int i = 0; i < vars.Length; i++)
1149 varsCopy[i] = vars[i];
1151 for (int i = 0; i < vars.Length; i++)
1152 vars[i] = varsCopy[paramOrder[i]];
1155 // This method will create the mapping between the Parameters and the underlying
1156 // data based upon the names array. The names array is stored in the same order
1157 // as the values and maps to the parameters of the method. We store the mapping
1158 // from the parameters to the names in the paramOrder array. All parameters that
1159 // don't have matching names are then stored in the array in order.
1160 private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names)
1162 bool[] used = new bool[pars.Length];
1164 // Mark which parameters have not been found in the names list
1165 for (int i = 0; i < pars.Length; i++)
1166 paramOrder[i] = -1;
1167 // Find the parameters with names.
1168 for (int i = 0; i < names.Length; i++)
1170 int j;
1171 for (j = 0; j < pars.Length; j++)
1173 if (names[i].Equals(pars[j].Name))
1175 paramOrder[j] = i;
1176 used[i] = true;
1177 break;
1180 // This is an error condition. The name was not found. This
1181 // method must not match what we sent.
1182 if (j == pars.Length)
1183 return false;
1186 // Now we fill in the holes with the parameters that are unused.
1187 int pos = 0;
1188 for (int i = 0; i < pars.Length; i++)
1190 if (paramOrder[i] == -1)
1192 for (; pos < pars.Length; pos++)
1194 if (!used[pos])
1196 paramOrder[i] = pos;
1197 pos++;
1198 break;
1203 return true;
1206 // CanChangePrimitive
1207 // This will determine if the source can be converted to the target type
1208 internal static bool CanChangePrimitive(Type? source, Type? target)
1210 if ((source == typeof(IntPtr) && target == typeof(IntPtr)) ||
1211 (source == typeof(UIntPtr) && target == typeof(UIntPtr)))
1212 return true;
1214 Primitives widerCodes = s_primitiveConversions[(int)(Type.GetTypeCode(source))];
1215 Primitives targetCode = (Primitives)(1 << (int)(Type.GetTypeCode(target)));
1217 return (widerCodes & targetCode) != 0;
1220 private static readonly Primitives[] s_primitiveConversions = {
1221 /* Empty */ 0, // not primitive
1222 /* Object */ 0, // not primitive
1223 /* DBNull */ 0, // not primitive
1224 /* Boolean */ Primitives.Boolean,
1225 /* Char */ Primitives.Char | Primitives.UInt16 | Primitives.UInt32 | Primitives.Int32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1226 /* SByte */ Primitives.SByte | Primitives.Int16 | Primitives.Int32 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1227 /* Byte */ Primitives.Byte | Primitives.Char | Primitives.UInt16 | Primitives.Int16 | Primitives.UInt32 | Primitives.Int32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1228 /* Int16 */ Primitives.Int16 | Primitives.Int32 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1229 /* UInt16 */ Primitives.UInt16 | Primitives.UInt32 | Primitives.Int32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1230 /* Int32 */ Primitives.Int32 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1231 /* UInt32 */ Primitives.UInt32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
1232 /* Int64 */ Primitives.Int64 | Primitives.Single | Primitives.Double,
1233 /* UInt64 */ Primitives.UInt64 | Primitives.Single | Primitives.Double,
1234 /* Single */ Primitives.Single | Primitives.Double,
1235 /* Double */ Primitives.Double,
1236 /* Decimal */ Primitives.Decimal,
1237 /* DateTime */ Primitives.DateTime,
1238 /* [Unused] */ 0,
1239 /* String */ Primitives.String,
1242 [Flags]
1243 private enum Primitives
1245 Boolean = 1 << TypeCode.Boolean,
1246 Char = 1 << TypeCode.Char,
1247 SByte = 1 << TypeCode.SByte,
1248 Byte = 1 << TypeCode.Byte,
1249 Int16 = 1 << TypeCode.Int16,
1250 UInt16 = 1 << TypeCode.UInt16,
1251 Int32 = 1 << TypeCode.Int32,
1252 UInt32 = 1 << TypeCode.UInt32,
1253 Int64 = 1 << TypeCode.Int64,
1254 UInt64 = 1 << TypeCode.UInt64,
1255 Single = 1 << TypeCode.Single,
1256 Double = 1 << TypeCode.Double,
1257 Decimal = 1 << TypeCode.Decimal,
1258 DateTime = 1 << TypeCode.DateTime,
1259 String = 1 << TypeCode.String,
1262 internal class BinderState
1264 internal readonly int[] _argsMap;
1265 internal readonly int _originalSize;
1266 internal readonly bool _isParamArray;
1268 internal BinderState(int[] argsMap, int originalSize, bool isParamArray)
1270 _argsMap = argsMap;
1271 _originalSize = originalSize;
1272 _isParamArray = isParamArray;