summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/DefaultBinder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System/DefaultBinder.cs')
-rw-r--r--src/mscorlib/shared/System/DefaultBinder.cs1201
1 files changed, 1201 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/DefaultBinder.cs b/src/mscorlib/shared/System/DefaultBinder.cs
new file mode 100644
index 0000000000..3b46d5f4d3
--- /dev/null
+++ b/src/mscorlib/shared/System/DefaultBinder.cs
@@ -0,0 +1,1201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using System.Diagnostics;
+using CultureInfo = System.Globalization.CultureInfo;
+
+namespace System
+{
+ //Marked serializable even though it has no state.
+ [Serializable]
+#if CORECLR
+ internal
+#else
+ public sealed
+#endif
+ partial class DefaultBinder : Binder
+ {
+ // This method is passed a set of methods and must choose the best
+ // fit. The methods all have the same number of arguments and the object
+ // array args. On exit, this method will choice the best fit method
+ // and coerce the args to match that method. By match, we mean all primitive
+ // arguments are exact matchs and all object arguments are exact or subclasses
+ // of the target. If the target OR is an interface, the object must implement
+ // that interface. There are a couple of exceptions
+ // thrown when a method cannot be returned. If no method matchs the args and
+ // ArgumentException is thrown. If multiple methods match the args then
+ // an AmbiguousMatchException is thrown.
+ //
+ // The most specific match will be selected.
+ //
+ public sealed override MethodBase BindToMethod(
+ BindingFlags bindingAttr, MethodBase[] match, ref object[] args,
+ ParameterModifier[] modifiers, CultureInfo cultureInfo, string[] names, out object state)
+ {
+ if (match == null || match.Length == 0)
+ throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
+
+ MethodBase[] candidates = (MethodBase[])match.Clone();
+
+ int i;
+ int j;
+
+ state = null;
+
+#region Map named parameters to candidate parameter postions
+ // We are creating an paramOrder array to act as a mapping
+ // between the order of the args and the actual order of the
+ // parameters in the method. This order may differ because
+ // named parameters (names) may change the order. If names
+ // is not provided, then we assume the default mapping (0,1,...)
+ int[][] paramOrder = new int[candidates.Length][];
+
+ for (i = 0; i < candidates.Length; i++)
+ {
+ ParameterInfo[] par = candidates[i].GetParametersNoCopy();
+
+ // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted
+ paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length];
+
+ if (names == null)
+ {
+ // Default mapping
+ for (j = 0; j < args.Length; j++)
+ paramOrder[i][j] = j;
+ }
+ else
+ {
+ // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method
+ // doesn't have a name that matchs one of the named parameters so we don't consider it any further.
+ if (!CreateParamOrder(paramOrder[i], par, names))
+ candidates[i] = null;
+ }
+ }
+#endregion
+
+ Type[] paramArrayTypes = new Type[candidates.Length];
+
+ Type[] argTypes = new Type[args.Length];
+
+#region Cache the type of the provided arguments
+ // object that contain a null are treated as if they were typeless (but match either object
+ // references or value classes). We mark this condition by placing a null in the argTypes array.
+ for (i = 0; i < args.Length; i++)
+ {
+ if (args[i] != null)
+ {
+ argTypes[i] = args[i].GetType();
+ }
+ }
+#endregion
+
+
+ // Find the method that matches...
+ int CurIdx = 0;
+ bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0);
+
+ Type paramArrayType = null;
+
+#region Filter methods by parameter count and type
+ for (i = 0; i < candidates.Length; i++)
+ {
+ paramArrayType = null;
+
+ // If we have named parameters then we may have a hole in the candidates array.
+ if (candidates[i] == null)
+ continue;
+
+ // Validate the parameters.
+ ParameterInfo[] par = candidates[i].GetParametersNoCopy();
+
+#region Match method by parameter count
+ if (par.Length == 0)
+ {
+#region No formal parameters
+ if (args.Length != 0)
+ {
+ if ((candidates[i].CallingConvention & CallingConventions.VarArgs) == 0)
+ continue;
+ }
+
+ // This is a valid routine so we move it up the candidates list.
+ paramOrder[CurIdx] = paramOrder[i];
+ candidates[CurIdx++] = candidates[i];
+
+ continue;
+#endregion
+ }
+ else if (par.Length > args.Length)
+ {
+#region Shortage of provided parameters
+ // If the number of parameters is greater than the number of args then
+ // we are in the situation were we may be using default values.
+ for (j = args.Length; j < par.Length - 1; j++)
+ {
+ if (par[j].DefaultValue == System.DBNull.Value)
+ break;
+ }
+
+ if (j != par.Length - 1)
+ continue;
+
+ if (par[j].DefaultValue == System.DBNull.Value)
+ {
+ if (!par[j].ParameterType.IsArray)
+ continue;
+
+ if (!par[j].IsDefined(typeof(ParamArrayAttribute), true))
+ continue;
+
+ paramArrayType = par[j].ParameterType.GetElementType();
+ }
+#endregion
+ }
+ else if (par.Length < args.Length)
+ {
+#region Excess provided parameters
+ // test for the ParamArray case
+ int lastArgPos = par.Length - 1;
+
+ if (!par[lastArgPos].ParameterType.IsArray)
+ continue;
+
+ if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true))
+ continue;
+
+ if (paramOrder[i][lastArgPos] != lastArgPos)
+ continue;
+
+ paramArrayType = par[lastArgPos].ParameterType.GetElementType();
+#endregion
+ }
+ else
+ {
+#region Test for paramArray, save paramArray type
+ int lastArgPos = par.Length - 1;
+
+ if (par[lastArgPos].ParameterType.IsArray
+ && par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)
+ && paramOrder[i][lastArgPos] == lastArgPos)
+ {
+ if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos]))
+ paramArrayType = par[lastArgPos].ParameterType.GetElementType();
+ }
+#endregion
+ }
+#endregion
+
+ Type pCls = null;
+ int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length;
+
+#region Match method by parameter type
+ for (j = 0; j < argsToCheck; j++)
+ {
+#region Classic argument coersion checks
+ // get the formal type
+ pCls = par[j].ParameterType;
+
+ if (pCls.IsByRef)
+ pCls = pCls.GetElementType();
+
+ // the type is the same
+ if (pCls == argTypes[paramOrder[i][j]])
+ continue;
+
+ // a default value is available
+ if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing)
+ continue;
+
+ // the argument was null, so it matches with everything
+ if (args[paramOrder[i][j]] == null)
+ continue;
+
+ // the type is Object, so it will match everything
+ if (pCls == typeof(object))
+ continue;
+
+ // now do a "classic" type check
+ if (pCls.IsPrimitive)
+ {
+ if (argTypes[paramOrder[i][j]] == null || !CanChangePrimitiveObjectToType(args[paramOrder[i][j]], pCls))
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (argTypes[paramOrder[i][j]] == null)
+ continue;
+
+ if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]]))
+ {
+ if (argTypes[paramOrder[i][j]].IsCOMObject)
+ {
+ if (pCls.IsInstanceOfType(args[paramOrder[i][j]]))
+ continue;
+ }
+ break;
+ }
+ }
+#endregion
+ }
+
+ if (paramArrayType != null && j == par.Length - 1)
+ {
+#region Check that excess arguments can be placed in the param array
+ for (; j < args.Length; j++)
+ {
+ if (paramArrayType.IsPrimitive)
+ {
+ if (argTypes[j] == null || !CanChangePrimitiveObjectToType(args[j], paramArrayType))
+ break;
+ }
+ else
+ {
+ if (argTypes[j] == null)
+ continue;
+
+ if (!paramArrayType.IsAssignableFrom(argTypes[j]))
+ {
+ if (argTypes[j].IsCOMObject)
+ {
+ if (paramArrayType.IsInstanceOfType(args[j]))
+ continue;
+ }
+
+ break;
+ }
+ }
+ }
+#endregion
+ }
+#endregion
+
+ if (j == args.Length)
+ {
+#region This is a valid routine so we move it up the candidates list
+ paramOrder[CurIdx] = paramOrder[i];
+ paramArrayTypes[CurIdx] = paramArrayType;
+ candidates[CurIdx++] = candidates[i];
+#endregion
+ }
+ }
+#endregion
+
+ // If we didn't find a method
+ if (CurIdx == 0)
+ throw new MissingMethodException(SR.MissingMember);
+
+ if (CurIdx == 1)
+ {
+#region Found only one method
+ if (names != null)
+ {
+ state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null);
+ ReorderParams(paramOrder[0], args);
+ }
+
+ // If the parameters and the args are not the same length or there is a paramArray
+ // then we need to create a argument array.
+ ParameterInfo[] parms = candidates[0].GetParametersNoCopy();
+
+ if (parms.Length == args.Length)
+ {
+ if (paramArrayTypes[0] != null)
+ {
+ object[] objs = new object[parms.Length];
+ int lastPos = parms.Length - 1;
+ Array.Copy(args, 0, objs, 0, lastPos);
+ objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1);
+ ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
+ args = objs;
+ }
+ }
+ else if (parms.Length > args.Length)
+ {
+ object[] objs = new object[parms.Length];
+
+ for (i = 0; i < args.Length; i++)
+ objs[i] = args[i];
+
+ for (; i < parms.Length - 1; i++)
+ objs[i] = parms[i].DefaultValue;
+
+ if (paramArrayTypes[0] != null)
+ objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the
+
+ else
+ objs[i] = parms[i].DefaultValue;
+
+ args = objs;
+ }
+ else
+ {
+ if ((candidates[0].CallingConvention & CallingConventions.VarArgs) == 0)
+ {
+ object[] objs = new object[parms.Length];
+ int paramArrayPos = parms.Length - 1;
+ Array.Copy(args, 0, objs, 0, paramArrayPos);
+ objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos);
+ Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
+ args = objs;
+ }
+ }
+#endregion
+
+ return candidates[0];
+ }
+
+ int currentMin = 0;
+ bool ambig = false;
+ for (i = 1; i < CurIdx; i++)
+ {
+#region Walk all of the methods looking the most specific method to invoke
+ int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder[currentMin], paramArrayTypes[currentMin],
+ candidates[i], paramOrder[i], paramArrayTypes[i], argTypes, args);
+
+ if (newMin == 0)
+ {
+ ambig = true;
+ }
+ else if (newMin == 2)
+ {
+ currentMin = i;
+ ambig = false;
+ }
+#endregion
+ }
+
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+
+ // Reorder (if needed)
+ if (names != null)
+ {
+ state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null);
+ ReorderParams(paramOrder[currentMin], args);
+ }
+
+ // If the parameters and the args are not the same length or there is a paramArray
+ // then we need to create a argument array.
+ ParameterInfo[] parameters = candidates[currentMin].GetParametersNoCopy();
+ if (parameters.Length == args.Length)
+ {
+ if (paramArrayTypes[currentMin] != null)
+ {
+ object[] objs = new object[parameters.Length];
+ int lastPos = parameters.Length - 1;
+ Array.Copy(args, 0, objs, 0, lastPos);
+ objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1);
+ ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
+ args = objs;
+ }
+ }
+ else if (parameters.Length > args.Length)
+ {
+ object[] objs = new object[parameters.Length];
+
+ for (i = 0; i < args.Length; i++)
+ objs[i] = args[i];
+
+ for (; i < parameters.Length - 1; i++)
+ objs[i] = parameters[i].DefaultValue;
+
+ if (paramArrayTypes[currentMin] != null)
+ {
+ objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0);
+ }
+ else
+ {
+ objs[i] = parameters[i].DefaultValue;
+ }
+
+ args = objs;
+ }
+ else
+ {
+ if ((candidates[currentMin].CallingConvention & CallingConventions.VarArgs) == 0)
+ {
+ object[] objs = new object[parameters.Length];
+ int paramArrayPos = parameters.Length - 1;
+ Array.Copy(args, 0, objs, 0, paramArrayPos);
+ objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos);
+ Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
+ args = objs;
+ }
+ }
+
+ return candidates[currentMin];
+ }
+
+
+ // Given a set of fields that match the base criteria, select a field.
+ // if value is null then we have no way to select a field
+ public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo cultureInfo)
+ {
+ if (match == null)
+ {
+ throw new ArgumentNullException(nameof(match));
+ }
+
+ int i;
+ // Find the method that match...
+ int CurIdx = 0;
+
+ Type valueType = null;
+
+ FieldInfo[] candidates = (FieldInfo[])match.Clone();
+
+ // If we are a FieldSet, then use the value's type to disambiguate
+ if ((bindingAttr & BindingFlags.SetField) != 0)
+ {
+ valueType = value.GetType();
+
+ for (i = 0; i < candidates.Length; i++)
+ {
+ Type pCls = candidates[i].FieldType;
+ if (pCls == valueType)
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ if (value == Empty.Value)
+ {
+ // the object passed in was null which would match any non primitive non value type
+ if (pCls.IsClass)
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ }
+ if (pCls == typeof(object))
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ if (pCls.IsPrimitive)
+ {
+ if (CanChangePrimitiveObjectToType(value, pCls))
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ }
+ else
+ {
+ if (pCls.IsAssignableFrom(valueType))
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ }
+ }
+ if (CurIdx == 0)
+ throw new MissingFieldException(SR.MissingField);
+ if (CurIdx == 1)
+ return candidates[0];
+ }
+
+ // Walk all of the methods looking the most specific method to invoke
+ int currentMin = 0;
+ bool ambig = false;
+ for (i = 1; i < CurIdx; i++)
+ {
+ int newMin = FindMostSpecificField(candidates[currentMin], candidates[i]);
+ if (newMin == 0)
+ ambig = true;
+ else
+ {
+ if (newMin == 2)
+ {
+ currentMin = i;
+ ambig = false;
+ }
+ }
+ }
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ return candidates[currentMin];
+ }
+
+ // Given a set of methods that match the base criteria, select a method based
+ // upon an array of types. This method should return null if no method matchs
+ // the criteria.
+ public sealed override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
+ {
+ int i;
+ int j;
+
+ Type[] realTypes = new Type[types.Length];
+ for (i = 0; i < types.Length; i++)
+ {
+ realTypes[i] = types[i].UnderlyingSystemType;
+ if (!(realTypes[i].IsRuntimeImplemented()))
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(types));
+ }
+ types = realTypes;
+
+ // We don't automatically jump out on exact match.
+ if (match == null || match.Length == 0)
+ throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
+
+ MethodBase[] candidates = (MethodBase[])match.Clone();
+
+ // Find all the methods that can be described by the types parameter.
+ // Remove all of them that cannot.
+ int CurIdx = 0;
+ for (i = 0; i < candidates.Length; i++)
+ {
+ ParameterInfo[] par = candidates[i].GetParametersNoCopy();
+ if (par.Length != types.Length)
+ continue;
+ for (j = 0; j < types.Length; j++)
+ {
+ Type pCls = par[j].ParameterType;
+ if (pCls == types[j])
+ continue;
+ if (pCls == typeof(object))
+ continue;
+ if (pCls.IsPrimitive)
+ {
+ if (!(types[j].UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(types[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
+ break;
+ }
+ else
+ {
+ if (!pCls.IsAssignableFrom(types[j]))
+ break;
+ }
+ }
+ if (j == types.Length)
+ candidates[CurIdx++] = candidates[i];
+ }
+ if (CurIdx == 0)
+ return null;
+ if (CurIdx == 1)
+ return candidates[0];
+
+ // Walk all of the methods looking the most specific method to invoke
+ int currentMin = 0;
+ bool ambig = false;
+ int[] paramOrder = new int[types.Length];
+ for (i = 0; i < types.Length; i++)
+ paramOrder[i] = i;
+ for (i = 1; i < CurIdx; i++)
+ {
+ int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder, null, candidates[i], paramOrder, null, types, null);
+ if (newMin == 0)
+ ambig = true;
+ else
+ {
+ if (newMin == 2)
+ {
+ currentMin = i;
+ ambig = false;
+ currentMin = i;
+ }
+ }
+ }
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ return candidates[currentMin];
+ }
+
+ // Given a set of properties that match the base criteria, select one.
+ public sealed override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType,
+ Type[] indexes, ParameterModifier[] modifiers)
+ {
+ // Allow a null indexes array. But if it is not null, every element must be non-null as well.
+ if (indexes != null)
+ {
+ foreach (Type index in indexes)
+ {
+ if (index == null)
+ throw new ArgumentNullException(nameof(indexes));
+ }
+ }
+
+ if (match == null || match.Length == 0)
+ throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
+
+ PropertyInfo[] candidates = (PropertyInfo[])match.Clone();
+
+ int i, j = 0;
+
+ // Find all the properties that can be described by type indexes parameter
+ int CurIdx = 0;
+ int indexesLength = (indexes != null) ? indexes.Length : 0;
+ for (i = 0; i < candidates.Length; i++)
+ {
+ if (indexes != null)
+ {
+ ParameterInfo[] par = candidates[i].GetIndexParameters();
+ if (par.Length != indexesLength)
+ continue;
+
+ for (j = 0; j < indexesLength; j++)
+ {
+ Type pCls = par[j].ParameterType;
+
+ // If the classes exactly match continue
+ if (pCls == indexes[j])
+ continue;
+ if (pCls == typeof(object))
+ continue;
+
+ if (pCls.IsPrimitive)
+ {
+ if (!(indexes[j].UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(indexes[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
+ break;
+ }
+ else
+ {
+ if (!pCls.IsAssignableFrom(indexes[j]))
+ break;
+ }
+ }
+ }
+
+ if (j == indexesLength)
+ {
+ if (returnType != null)
+ {
+ if (candidates[i].PropertyType.IsPrimitive)
+ {
+ if (!(returnType.UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(returnType.UnderlyingSystemType, candidates[i].PropertyType.UnderlyingSystemType))
+ continue;
+ }
+ else
+ {
+ if (!candidates[i].PropertyType.IsAssignableFrom(returnType))
+ continue;
+ }
+ }
+ candidates[CurIdx++] = candidates[i];
+ }
+ }
+ if (CurIdx == 0)
+ return null;
+ if (CurIdx == 1)
+ return candidates[0];
+
+ // Walk all of the properties looking the most specific method to invoke
+ int currentMin = 0;
+ bool ambig = false;
+ int[] paramOrder = new int[indexesLength];
+ for (i = 0; i < indexesLength; i++)
+ paramOrder[i] = i;
+ for (i = 1; i < CurIdx; i++)
+ {
+ int newMin = FindMostSpecificType(candidates[currentMin].PropertyType, candidates[i].PropertyType, returnType);
+ if (newMin == 0 && indexes != null)
+ newMin = FindMostSpecific(candidates[currentMin].GetIndexParameters(),
+ paramOrder,
+ null,
+ candidates[i].GetIndexParameters(),
+ paramOrder,
+ null,
+ indexes,
+ null);
+ if (newMin == 0)
+ {
+ newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]);
+ if (newMin == 0)
+ ambig = true;
+ }
+ if (newMin == 2)
+ {
+ ambig = false;
+ currentMin = i;
+ }
+ }
+
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ return candidates[currentMin];
+ }
+
+ // ChangeType
+ // The default binder doesn't support any change type functionality.
+ // This is because the default is built into the low level invoke code.
+ public override object ChangeType(object value, Type type, CultureInfo cultureInfo)
+ {
+ throw new NotSupportedException(SR.NotSupported_ChangeType);
+ }
+
+ public sealed override void ReorderArgumentArray(ref object[] args, object state)
+ {
+ BinderState binderState = (BinderState)state;
+ ReorderParams(binderState._argsMap, args);
+ if (binderState._isParamArray)
+ {
+ int paramArrayPos = args.Length - 1;
+ if (args.Length == binderState._originalSize)
+ args[paramArrayPos] = ((object[])args[paramArrayPos])[0];
+ else
+ {
+ // must be args.Length < state.originalSize
+ object[] newArgs = new object[args.Length];
+ Array.Copy(args, 0, newArgs, 0, paramArrayPos);
+ for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++)
+ {
+ newArgs[i] = ((object[])args[paramArrayPos])[j];
+ }
+ args = newArgs;
+ }
+ }
+ else
+ {
+ if (args.Length > binderState._originalSize)
+ {
+ object[] newArgs = new object[binderState._originalSize];
+ Array.Copy(args, 0, newArgs, 0, binderState._originalSize);
+ args = newArgs;
+ }
+ }
+ }
+
+ // Return any exact bindings that may exist. (This method is not defined on the
+ // Binder and is used by RuntimeType.)
+ public static MethodBase ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (match == null)
+ throw new ArgumentNullException(nameof(match));
+
+ MethodBase[] aExactMatches = new MethodBase[match.Length];
+ int cExactMatches = 0;
+
+ for (int i = 0; i < match.Length; i++)
+ {
+ ParameterInfo[] par = match[i].GetParametersNoCopy();
+ if (par.Length == 0)
+ {
+ continue;
+ }
+ int j;
+ for (j = 0; j < types.Length; j++)
+ {
+ Type pCls = par[j].ParameterType;
+
+ // If the classes exactly match continue
+ if (!pCls.Equals(types[j]))
+ break;
+ }
+ if (j < types.Length)
+ continue;
+
+ // Add the exact match to the array of exact matches.
+ aExactMatches[cExactMatches] = match[i];
+ cExactMatches++;
+ }
+
+ if (cExactMatches == 0)
+ return null;
+
+ if (cExactMatches == 1)
+ return aExactMatches[0];
+
+ return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches);
+ }
+
+ // Return any exact bindings that may exist. (This method is not defined on the
+ // Binder and is used by RuntimeType.)
+ public static PropertyInfo ExactPropertyBinding(PropertyInfo[] match, Type returnType, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (match == null)
+ throw new ArgumentNullException(nameof(match));
+
+ PropertyInfo bestMatch = null;
+ int typesLength = (types != null) ? types.Length : 0;
+ for (int i = 0; i < match.Length; i++)
+ {
+ ParameterInfo[] par = match[i].GetIndexParameters();
+ int j;
+ for (j = 0; j < typesLength; j++)
+ {
+ Type pCls = par[j].ParameterType;
+
+ // If the classes exactly match continue
+ if (pCls != types[j])
+ break;
+ }
+ if (j < typesLength)
+ continue;
+ if (returnType != null && returnType != match[i].PropertyType)
+ continue;
+
+ if (bestMatch != null)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+
+ bestMatch = match[i];
+ }
+ return bestMatch;
+ }
+
+ private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type paramArrayType1,
+ ParameterInfo[] p2, int[] paramOrder2, Type paramArrayType2,
+ Type[] types, object[] args)
+ {
+ // A method using params is always less specific than one not using params
+ if (paramArrayType1 != null && paramArrayType2 == null) return 2;
+ if (paramArrayType2 != null && paramArrayType1 == null) return 1;
+
+ // now either p1 and p2 both use params or neither does.
+
+ bool p1Less = false;
+ bool p2Less = false;
+
+ for (int i = 0; i < types.Length; i++)
+ {
+ if (args != null && args[i] == Type.Missing)
+ continue;
+
+ Type c1, c2;
+
+ // If a param array is present, then either
+ // the user re-ordered the parameters in which case
+ // the argument to the param array is either an array
+ // in which case the params is conceptually ignored and so paramArrayType1 == null
+ // or the argument to the param array is a single element
+ // in which case paramOrder[i] == p1.Length - 1 for that element
+ // or the user did not re-order the parameters in which case
+ // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
+ // so any index >= p.Length - 1 is being put in the param array
+
+ if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1)
+ c1 = paramArrayType1;
+ else
+ c1 = p1[paramOrder1[i]].ParameterType;
+
+ if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1)
+ c2 = paramArrayType2;
+ else
+ c2 = p2[paramOrder2[i]].ParameterType;
+
+ if (c1 == c2) continue;
+
+ switch (FindMostSpecificType(c1, c2, types[i]))
+ {
+ case 0: return 0;
+ case 1: p1Less = true; break;
+ case 2: p2Less = true; break;
+ }
+ }
+
+ // Two way p1Less and p2Less can be equal. All the arguments are the
+ // same they both equal false, otherwise there were things that both
+ // were the most specific type on....
+ if (p1Less == p2Less)
+ {
+ // if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
+ // let's see which one has the most matches without using the params array (the longer one wins).
+ if (!p1Less && args != null)
+ {
+ if (p1.Length > p2.Length)
+ {
+ return 1;
+ }
+ else if (p2.Length > p1.Length)
+ {
+ return 2;
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ return (p1Less == true) ? 1 : 2;
+ }
+ }
+
+ private static int FindMostSpecificType(Type c1, Type c2, Type t)
+ {
+ // If the two types are exact move on...
+ if (c1 == c2)
+ return 0;
+
+ if (c1 == t)
+ return 1;
+
+ if (c2 == t)
+ return 2;
+
+ bool c1FromC2;
+ bool c2FromC1;
+
+ if (c1.IsByRef || c2.IsByRef)
+ {
+ if (c1.IsByRef && c2.IsByRef)
+ {
+ c1 = c1.GetElementType();
+ c2 = c2.GetElementType();
+ }
+ else if (c1.IsByRef)
+ {
+ if (c1.GetElementType() == c2)
+ return 2;
+
+ c1 = c1.GetElementType();
+ }
+ else
+ {
+ if (c2.GetElementType() == c1)
+ return 1;
+
+ c2 = c2.GetElementType();
+ }
+ }
+
+
+ if (c1.IsPrimitive && c2.IsPrimitive)
+ {
+ c1FromC2 = CanChangePrimitive(c2, c1);
+ c2FromC1 = CanChangePrimitive(c1, c2);
+ }
+ else
+ {
+ c1FromC2 = c1.IsAssignableFrom(c2);
+ c2FromC1 = c2.IsAssignableFrom(c1);
+ }
+
+ if (c1FromC2 == c2FromC1)
+ return 0;
+
+ if (c1FromC2)
+ {
+ return 2;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type paramArrayType1,
+ MethodBase m2, int[] paramOrder2, Type paramArrayType2,
+ Type[] types, object[] args)
+ {
+ // Find the most specific method based on the parameters.
+ int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1,
+ m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args);
+
+ // If the match was not ambigous then return the result.
+ if (res != 0)
+ return res;
+
+ // Check to see if the methods have the exact same name and signature.
+ if (CompareMethodSig(m1, m2))
+ {
+ // Determine the depth of the declaring types for both methods.
+ int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType);
+ int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType);
+
+ // The most derived method is the most specific one.
+ if (hierarchyDepth1 == hierarchyDepth2)
+ {
+ return 0;
+ }
+ else if (hierarchyDepth1 < hierarchyDepth2)
+ {
+ return 2;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ // The match is ambigous.
+ return 0;
+ }
+
+ private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2)
+ {
+ // Check to see if the fields have the same name.
+ if (cur1.Name == cur2.Name)
+ {
+ int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType);
+ int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType);
+
+ if (hierarchyDepth1 == hierarchyDepth2)
+ {
+ Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2");
+ return 0;
+ }
+ else if (hierarchyDepth1 < hierarchyDepth2)
+ return 2;
+ else
+ return 1;
+ }
+
+ // The match is ambigous.
+ return 0;
+ }
+
+ private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2)
+ {
+ // Check to see if the fields have the same name.
+ if (cur1.Name == cur2.Name)
+ {
+ int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType);
+ int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType);
+
+ if (hierarchyDepth1 == hierarchyDepth2)
+ {
+ return 0;
+ }
+ else if (hierarchyDepth1 < hierarchyDepth2)
+ return 2;
+ else
+ return 1;
+ }
+
+ // The match is ambigous.
+ return 0;
+ }
+
+ public static bool CompareMethodSig(MethodBase m1, MethodBase m2)
+ {
+ ParameterInfo[] params1 = m1.GetParametersNoCopy();
+ ParameterInfo[] params2 = m2.GetParametersNoCopy();
+
+ if (params1.Length != params2.Length)
+ return false;
+
+ int numParams = params1.Length;
+ for (int i = 0; i < numParams; i++)
+ {
+ if (params1[i].ParameterType != params2[i].ParameterType)
+ return false;
+ }
+
+ return true;
+ }
+
+ private static int GetHierarchyDepth(Type t)
+ {
+ int depth = 0;
+
+ Type currentType = t;
+ do
+ {
+ depth++;
+ currentType = currentType.BaseType;
+ } while (currentType != null);
+
+ return depth;
+ }
+
+ internal static MethodBase FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches)
+ {
+ int deepestHierarchy = 0;
+ MethodBase methWithDeepestHierarchy = null;
+
+ for (int i = 0; i < cMatches; i++)
+ {
+ // Calculate the depth of the hierarchy of the declaring type of the
+ // current method.
+ int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType);
+
+ // The two methods have the same name, signature, and hierarchy depth.
+ // This can only happen if at least one is vararg or generic.
+ if (currentHierarchyDepth == deepestHierarchy)
+ {
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ }
+
+ // Check to see if this method is on the most derived class.
+ if (currentHierarchyDepth > deepestHierarchy)
+ {
+ deepestHierarchy = currentHierarchyDepth;
+ methWithDeepestHierarchy = match[i];
+ }
+ }
+
+ return methWithDeepestHierarchy;
+ }
+
+ // This method will sort the vars array into the mapping order stored
+ // in the paramOrder array.
+ private static void ReorderParams(int[] paramOrder, object[] vars)
+ {
+ object[] varsCopy = new object[vars.Length];
+ for (int i = 0; i < vars.Length; i++)
+ varsCopy[i] = vars[i];
+
+ for (int i = 0; i < vars.Length; i++)
+ vars[i] = varsCopy[paramOrder[i]];
+ }
+
+ // This method will create the mapping between the Parameters and the underlying
+ // data based upon the names array. The names array is stored in the same order
+ // as the values and maps to the parameters of the method. We store the mapping
+ // from the parameters to the names in the paramOrder array. All parameters that
+ // don't have matching names are then stored in the array in order.
+ private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names)
+ {
+ bool[] used = new bool[pars.Length];
+
+ // Mark which parameters have not been found in the names list
+ for (int i = 0; i < pars.Length; i++)
+ paramOrder[i] = -1;
+ // Find the parameters with names.
+ for (int i = 0; i < names.Length; i++)
+ {
+ int j;
+ for (j = 0; j < pars.Length; j++)
+ {
+ if (names[i].Equals(pars[j].Name))
+ {
+ paramOrder[j] = i;
+ used[i] = true;
+ break;
+ }
+ }
+ // This is an error condition. The name was not found. This
+ // method must not match what we sent.
+ if (j == pars.Length)
+ return false;
+ }
+
+ // Now we fill in the holes with the parameters that are unused.
+ int pos = 0;
+ for (int i = 0; i < pars.Length; i++)
+ {
+ if (paramOrder[i] == -1)
+ {
+ for (; pos < pars.Length; pos++)
+ {
+ if (!used[pos])
+ {
+ paramOrder[i] = pos;
+ pos++;
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ internal class BinderState
+ {
+ internal readonly int[] _argsMap;
+ internal readonly int _originalSize;
+ internal readonly bool _isParamArray;
+
+ internal BinderState(int[] argsMap, int originalSize, bool isParamArray)
+ {
+ _argsMap = argsMap;
+ _originalSize = originalSize;
+ _isParamArray = isParamArray;
+ }
+ }
+ }
+}