MethodBinder
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Newtonsoft.Json.Utilities
{
internal static class MethodBinder
{
private class ParametersMatchComparer : IComparer<ParameterInfo[]>
{
private readonly IList<Type> _types;
private readonly bool _enableParamArray;
public ParametersMatchComparer(IList<Type> types, bool enableParamArray)
{
ValidationUtils.ArgumentNotNull(types, "types");
_types = types;
_enableParamArray = enableParamArray;
}
public int Compare(ParameterInfo[] parameters1, ParameterInfo[] parameters2)
{
ValidationUtils.ArgumentNotNull(parameters1, "parameters1");
ValidationUtils.ArgumentNotNull(parameters2, "parameters2");
if (parameters1.Length == 0)
return -1;
if (parameters2.Length == 0)
return 1;
Type type = null;
Type type2 = null;
if (_enableParamArray) {
ParameterInfo parameterInfo = parameters1[parameters1.Length - 1];
if (parameterInfo.ParameterType.IsArray && CustomAttributeExtensions.IsDefined(parameterInfo, typeof(ParamArrayAttribute)))
type = parameterInfo.ParameterType.GetElementType();
ParameterInfo parameterInfo2 = parameters2[parameters2.Length - 1];
if (parameterInfo2.ParameterType.IsArray && CustomAttributeExtensions.IsDefined(parameterInfo2, typeof(ParamArrayAttribute)))
type2 = parameterInfo2.ParameterType.GetElementType();
if ((object)type != null && (object)type2 == null)
return 1;
if ((object)type2 != null && (object)type == null)
return -1;
}
for (int i = 0; i < _types.Count; i++) {
Type type3 = ((object)type != null && i >= parameters1.Length - 1) ? type : parameters1[i].ParameterType;
Type type4 = ((object)type2 != null && i >= parameters2.Length - 1) ? type2 : parameters2[i].ParameterType;
if ((object)type3 != type4) {
if ((object)type3 == _types[i])
return -1;
if ((object)type4 == _types[i])
return 1;
int num = ChooseMorePreciseType(type3, type4);
if (num != 0)
return num;
}
}
return 0;
}
private static int ChooseMorePreciseType(Type type1, Type type2)
{
if (type1.IsByRef || type2.IsByRef) {
if (type1.IsByRef && type2.IsByRef) {
type1 = type1.GetElementType();
type2 = type2.GetElementType();
} else if (type1.IsByRef) {
type1 = type1.GetElementType();
if ((object)type1 == type2)
return 1;
} else {
type2 = type2.GetElementType();
if ((object)type2 == type1)
return -1;
}
}
bool flag;
bool flag2;
if (type1.IsPrimitive() && type2.IsPrimitive()) {
flag = CanConvertPrimitive(type2, type1);
flag2 = CanConvertPrimitive(type1, type2);
} else {
flag = TypeExtensions.IsAssignableFrom(type1, type2);
flag2 = TypeExtensions.IsAssignableFrom(type2, type1);
}
if (flag == flag2)
return 0;
if (!flag)
return -1;
return 1;
}
}
private static readonly Type[] PrimitiveTypes = new Type[12] {
typeof(bool),
typeof(char),
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double)
};
private static readonly int[] WideningMasks = new int[12] {
1,
4066,
3412,
4090,
3408,
4066,
3392,
3968,
3328,
3584,
3072,
2048
};
private static bool CanConvertPrimitive(Type from, Type to)
{
if ((object)from == to)
return true;
int num = 0;
int num2 = 0;
for (int i = 0; i < PrimitiveTypes.Length; i++) {
if ((object)PrimitiveTypes[i] == from)
num = WideningMasks[i];
else if ((object)PrimitiveTypes[i] == to) {
num2 = 1 << i;
}
if (num != 0 && num2 != 0)
break;
}
return (num & num2) != 0;
}
private static bool FilterParameters(ParameterInfo[] parameters, IList<Type> types, bool enableParamArray)
{
ValidationUtils.ArgumentNotNull(parameters, "parameters");
ValidationUtils.ArgumentNotNull(types, "types");
if (parameters.Length == 0)
return types.Count == 0;
if (parameters.Length > types.Count)
return false;
Type type = null;
if (enableParamArray) {
ParameterInfo parameterInfo = parameters[parameters.Length - 1];
if (parameterInfo.ParameterType.IsArray && CustomAttributeExtensions.IsDefined(parameterInfo, typeof(ParamArrayAttribute)))
type = parameterInfo.ParameterType.GetElementType();
}
if ((object)type == null && parameters.Length != types.Count)
return false;
for (int i = 0; i < types.Count; i++) {
Type type2 = ((object)type != null && i >= parameters.Length - 1) ? type : parameters[i].ParameterType;
if ((object)type2 != types[i] && (object)type2 != typeof(object)) {
if (type2.IsPrimitive()) {
if (!types[i].IsPrimitive() || !CanConvertPrimitive(types[i], type2))
return false;
} else if (!TypeExtensions.IsAssignableFrom(type2, types[i])) {
return false;
}
}
}
return true;
}
public static TMethod SelectMethod<TMethod>(IEnumerable<TMethod> candidates, IList<Type> types) where TMethod : MethodBase
{
ValidationUtils.ArgumentNotNull(candidates, "candidates");
ValidationUtils.ArgumentNotNull(types, "types");
return (from m in candidates
where FilterParameters(m.GetParameters(), types, false)
select m).OrderBy((TMethod m) => m.GetParameters(), new ParametersMatchComparer(types, false)).FirstOrDefault();
}
}
}