Removed Convert.ChangeType calls because of poor performance (DynamicReflectionDelegateFactory now uses IConvertible.ToTypeName, while ExpressionReflectionDelegateFactory uses Convert.ToTypeName). Method binder moved to a separate file and documented, + code style fixes.
This commit is contained in:
@@ -233,6 +233,7 @@
|
||||
<Compile Include="Utilities\LateBoundReflectionDelegateFactory.cs" />
|
||||
<Compile Include="Utilities\LinqBridge.cs" />
|
||||
<Compile Include="Utilities\MathUtils.cs" />
|
||||
<Compile Include="Utilities\MethodBinder.cs" />
|
||||
<Compile Include="Utilities\MethodCall.cs" />
|
||||
<Compile Include="Utilities\MiscellaneousUtils.cs" />
|
||||
<Compile Include="Utilities\PropertyNameTable.cs" />
|
||||
|
||||
@@ -238,6 +238,7 @@
|
||||
<Compile Include="Utilities\LateBoundReflectionDelegateFactory.cs" />
|
||||
<Compile Include="Utilities\LinqBridge.cs" />
|
||||
<Compile Include="Utilities\MathUtils.cs" />
|
||||
<Compile Include="Utilities\MethodBinder.cs" />
|
||||
<Compile Include="Utilities\MethodCall.cs" />
|
||||
<Compile Include="Utilities\MiscellaneousUtils.cs" />
|
||||
<Compile Include="Utilities\PropertyNameTable.cs" />
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
<Compile Include="Utilities\DynamicReflectionDelegateFactory.cs" />
|
||||
<Compile Include="Utilities\DynamicUtils.cs" />
|
||||
<Compile Include="Utilities\JsonTokenUtils.cs" />
|
||||
<Compile Include="Utilities\MethodBinder.cs" />
|
||||
<Compile Include="Utilities\PropertyNameTable.cs" />
|
||||
<Compile Include="Utilities\ReflectionObject.cs" />
|
||||
<Compile Include="Utilities\EnumUtils.cs" />
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
ILGenerator generator = dynamicMethod.GetILGenerator();
|
||||
|
||||
GenerateCreateMethodCallIL(method, generator, 0);
|
||||
|
||||
|
||||
return (ObjectConstructor<object>)dynamicMethod.CreateDelegate(typeof(ObjectConstructor<object>));
|
||||
}
|
||||
|
||||
@@ -90,7 +90,9 @@ namespace Newtonsoft.Json.Utilities
|
||||
generator.PushInstance(method.DeclaringType);
|
||||
}
|
||||
|
||||
int localVariableCount = 0;
|
||||
LocalBuilder localConvertible = generator.DeclareLocal(typeof(IConvertible));
|
||||
LocalBuilder localObject = generator.DeclareLocal(typeof(object));
|
||||
int localVariableCount = 2;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
@@ -125,7 +127,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
generator.MarkLabel(skipSettingDefault);
|
||||
generator.PushArrayInstance(argsIndex, i);
|
||||
generator.UnboxIfNeeded(parameterType);
|
||||
generator.Emit(OpCodes.Stloc, localVariableCount);
|
||||
generator.Emit(OpCodes.Stloc_S, localVariable);
|
||||
|
||||
// parameter finished, we out!
|
||||
generator.MarkLabel(finishedProcessingParameter);
|
||||
@@ -133,7 +135,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
else
|
||||
{
|
||||
generator.UnboxIfNeeded(parameterType);
|
||||
generator.Emit(OpCodes.Stloc, localVariableCount);
|
||||
generator.Emit(OpCodes.Stloc_S, localVariable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +144,9 @@ namespace Newtonsoft.Json.Utilities
|
||||
localVariableCount++;
|
||||
}
|
||||
else if (parameterType.IsValueType())
|
||||
{
|
||||
{
|
||||
generator.PushArrayInstance(argsIndex, i);
|
||||
generator.Emit(OpCodes.Stloc_S, localObject);
|
||||
|
||||
// have to check that value type parameters aren't null
|
||||
// otherwise they will error when unboxed
|
||||
@@ -151,34 +154,59 @@ namespace Newtonsoft.Json.Utilities
|
||||
Label finishedProcessingParameter = generator.DefineLabel();
|
||||
|
||||
// check if parameter is not null
|
||||
generator.Emit(OpCodes.Ldloc_S, localObject);
|
||||
generator.Emit(OpCodes.Brtrue_S, skipSettingDefault);
|
||||
|
||||
// parameter has no value, initialize to default
|
||||
LocalBuilder localVariable = generator.DeclareLocal(parameterType);
|
||||
generator.Emit(OpCodes.Ldloca_S, localVariable);
|
||||
generator.Emit(OpCodes.Initobj, parameterType);
|
||||
generator.Emit(OpCodes.Ldloc, localVariableCount);
|
||||
generator.Emit(OpCodes.Ldloc_S, localVariable);
|
||||
generator.Emit(OpCodes.Br_S, finishedProcessingParameter);
|
||||
|
||||
// parameter has value, get value from array again and unbox
|
||||
// argument has value, try to convert it to parameter type
|
||||
generator.MarkLabel(skipSettingDefault);
|
||||
generator.PushArrayInstance(argsIndex, i);
|
||||
|
||||
|
||||
if (parameterType.IsPrimitive())
|
||||
{
|
||||
// for primitive types we need to call Convert.ChangeType() to correctly
|
||||
// convert value from other compatible primitive types (e.g. short -> int)
|
||||
// for primitive types we need to handle type widening (e.g. short -> int)
|
||||
|
||||
generator.Emit(OpCodes.Ldtoken, parameterType);
|
||||
generator.Emit(OpCodes.Call, typeof(Type).GetMethod(
|
||||
"GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }));
|
||||
|
||||
generator.Emit(OpCodes.Call, typeof(Convert).GetMethod(
|
||||
"ChangeType", new[] { typeof(object), typeof(Type) }));
|
||||
MethodInfo toParameterTypeMethod = typeof(IConvertible)
|
||||
.GetMethod("To" + parameterType.Name, new[] { typeof(IFormatProvider) });
|
||||
|
||||
if (toParameterTypeMethod != null)
|
||||
{
|
||||
Label skipConvertible = generator.DefineLabel();
|
||||
|
||||
// check if argument type is an exact match for parameter type
|
||||
// in this case we may use cheap unboxing instead
|
||||
generator.Emit(OpCodes.Ldloc_S, localObject);
|
||||
generator.Emit(OpCodes.Isinst, parameterType);
|
||||
generator.Emit(OpCodes.Brtrue_S, skipConvertible);
|
||||
|
||||
// types don't match, check if argument implements IConvertible
|
||||
generator.Emit(OpCodes.Ldloc_S, localObject);
|
||||
generator.Emit(OpCodes.Isinst, typeof(IConvertible));
|
||||
generator.Emit(OpCodes.Stloc_S, localConvertible);
|
||||
generator.Emit(OpCodes.Ldloc_S, localConvertible);
|
||||
generator.Emit(OpCodes.Brfalse_S, skipConvertible);
|
||||
|
||||
// convert argument to parameter type
|
||||
generator.Emit(OpCodes.Ldloc_S, localConvertible);
|
||||
generator.Emit(OpCodes.Ldnull);
|
||||
generator.Emit(OpCodes.Callvirt, toParameterTypeMethod);
|
||||
generator.Emit(OpCodes.Br_S, finishedProcessingParameter);
|
||||
|
||||
generator.MarkLabel(skipConvertible);
|
||||
}
|
||||
}
|
||||
|
||||
generator.UnboxIfNeeded(parameterType);
|
||||
// we got here because either argument type matches parameter (conversion will succeed),
|
||||
// or argument type doesn't match parameter, but we're out of options (conversion will fail)
|
||||
generator.Emit(OpCodes.Ldloc_S, localObject);
|
||||
|
||||
generator.UnboxIfNeeded(parameterType);
|
||||
|
||||
// parameter finished, we out!
|
||||
generator.MarkLabel(finishedProcessingParameter);
|
||||
localVariableCount++;
|
||||
|
||||
@@ -362,9 +362,13 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
if (targetType.IsPrimitive())
|
||||
{
|
||||
expression = Expression.Call(
|
||||
typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }),
|
||||
expression, Expression.Constant(targetType));
|
||||
MethodInfo toTargetTypeMethod = typeof(Convert)
|
||||
.GetMethod("To" + targetType.Name, new[] { typeof(object) });
|
||||
|
||||
if (toTargetTypeMethod != null)
|
||||
{
|
||||
expression = Expression.Call(toTargetTypeMethod, expression);
|
||||
}
|
||||
}
|
||||
|
||||
return Expression.Convert(expression, targetType);
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
#if PORTABLE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
internal static class MethodBinder
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// List of primitive types which can be widened.
|
||||
/// </summary>
|
||||
private static readonly Type[] PrimitiveTypes = new Type[]
|
||||
{
|
||||
typeof(bool), typeof(char), typeof(sbyte), typeof(byte),
|
||||
typeof(short), typeof(ushort), typeof(int), typeof(uint),
|
||||
typeof(long), typeof(ulong), typeof(float), typeof(double)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Widening masks for primitive types above.
|
||||
/// Index of the value in this array defines a type we're widening,
|
||||
/// while the bits in mask define types it can be widened to (including itself).
|
||||
///
|
||||
/// For example, value at index 0 defines a bool type, and it only has bit 0 set,
|
||||
/// i.e. bool values can be assigned only to bool.
|
||||
/// </summary>
|
||||
private static readonly int[] WideningMasks = new int[]
|
||||
{
|
||||
0x0001, 0x0FE2, 0x0D54, 0x0FFA,
|
||||
0x0D50, 0x0FE2, 0x0D40, 0x0F80,
|
||||
0x0D00, 0x0E00, 0x0C00, 0x0800
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if value of primitive type <paramref name="from"/> can be
|
||||
/// assigned to parameter of primitive type <paramref name="to"/>.
|
||||
/// </summary>
|
||||
/// <param name="from">Source primitive type</param>
|
||||
/// <param name="to">Target primitive type</param>
|
||||
/// <returns>True if source type can be widened to target type, false otherwise</returns>
|
||||
internal static bool CanConvertPrimitive(Type from, Type to)
|
||||
{
|
||||
if (from == to)
|
||||
{
|
||||
// same type
|
||||
return true;
|
||||
}
|
||||
|
||||
int fromMask = 0, toMask = 0;
|
||||
|
||||
for (int i = 0; i < PrimitiveTypes.Length && (fromMask == 0 || toMask == 0); i++)
|
||||
{
|
||||
if (PrimitiveTypes[i] == from)
|
||||
{
|
||||
fromMask = WideningMasks[i];
|
||||
}
|
||||
else if (PrimitiveTypes[i] == to)
|
||||
{
|
||||
toMask = 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return (fromMask & toMask) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a set of values with given <paramref name="types"/> can be used
|
||||
/// to invoke a method with specified <paramref name="parameters"/>.
|
||||
/// </summary>
|
||||
/// <param name="parameters">Method parameters</param>
|
||||
/// <param name="types">Argument types</param>
|
||||
/// <param name="enableParamArray">Try to pack extra arguments into ParamArray</param>
|
||||
/// <returns>True if method can be called with given arguments, false otherwise</returns>
|
||||
private static bool FilterParameters(ParameterInfo[] parameters, IList<Type> types, bool enableParamArray)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameters));
|
||||
}
|
||||
if (types == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
}
|
||||
if (parameters.Length == 0)
|
||||
{
|
||||
// fast check for parameterless methods
|
||||
return types.Count == 0;
|
||||
}
|
||||
if (parameters.Length > types.Count)
|
||||
{
|
||||
// not all declared parameters were specified (optional parameters are not supported)
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the last parameter is ParamArray
|
||||
Type paramArrayType = null;
|
||||
|
||||
if (enableParamArray)
|
||||
{
|
||||
ParameterInfo lastParam = parameters[parameters.Length - 1];
|
||||
if (lastParam.ParameterType.IsArray && lastParam.IsDefined(typeof(ParamArrayAttribute)))
|
||||
{
|
||||
paramArrayType = lastParam.ParameterType.GetElementType();
|
||||
}
|
||||
}
|
||||
|
||||
if (paramArrayType == null && parameters.Length != types.Count)
|
||||
{
|
||||
// when there's no ParamArray, number of parameters should match
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
{
|
||||
Type paramType = (paramArrayType != null && i >= parameters.Length - 1) ? paramArrayType : parameters[i].ParameterType;
|
||||
|
||||
if (paramType == types[i])
|
||||
{
|
||||
// exact match with provided type
|
||||
continue;
|
||||
}
|
||||
|
||||
if (paramType == typeof(object))
|
||||
{
|
||||
// parameter of type object matches anything
|
||||
continue;
|
||||
}
|
||||
|
||||
if (paramType.IsPrimitive())
|
||||
{
|
||||
if (!types[i].IsPrimitive() || !CanConvertPrimitive(types[i], paramType))
|
||||
{
|
||||
// primitive parameter can only be assigned from compatible primitive type
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!paramType.IsAssignableFrom(types[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two sets of parameters to determine
|
||||
/// which one suits better for given argument types.
|
||||
/// </summary>
|
||||
private class ParametersMatchComparer : IComparer<ParameterInfo[]>
|
||||
{
|
||||
private readonly IList<Type> _types;
|
||||
private readonly bool _enableParamArray;
|
||||
|
||||
public ParametersMatchComparer(IList<Type> types, bool enableParamArray)
|
||||
{
|
||||
if (types == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
}
|
||||
|
||||
_types = types;
|
||||
_enableParamArray = enableParamArray;
|
||||
}
|
||||
|
||||
public int Compare(ParameterInfo[] parameters1, ParameterInfo[] parameters2)
|
||||
{
|
||||
if (parameters1 == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameters1));
|
||||
}
|
||||
if (parameters2 == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameters2));
|
||||
}
|
||||
|
||||
// parameterless method wins
|
||||
if (parameters1.Length == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (parameters2.Length == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
Type paramArrayType1 = null, paramArrayType2 = null;
|
||||
|
||||
if (_enableParamArray)
|
||||
{
|
||||
ParameterInfo lastParam1 = parameters1[parameters1.Length - 1];
|
||||
if (lastParam1.ParameterType.IsArray && lastParam1.IsDefined(typeof(ParamArrayAttribute)))
|
||||
{
|
||||
paramArrayType1 = lastParam1.ParameterType.GetElementType();
|
||||
}
|
||||
|
||||
ParameterInfo lastParam2 = parameters2[parameters2.Length - 1];
|
||||
if (lastParam2.ParameterType.IsArray && lastParam2.IsDefined(typeof(ParamArrayAttribute)))
|
||||
{
|
||||
paramArrayType2 = lastParam2.ParameterType.GetElementType();
|
||||
}
|
||||
|
||||
// A method using params always loses to one not using params
|
||||
if (paramArrayType1 != null && paramArrayType2 == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (paramArrayType2 != null && paramArrayType1 == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _types.Count; i++)
|
||||
{
|
||||
Type type1 = (paramArrayType1 != null && i >= parameters1.Length - 1) ? paramArrayType1 : parameters1[i].ParameterType;
|
||||
Type type2 = (paramArrayType2 != null && i >= parameters2.Length - 1) ? paramArrayType2 : parameters2[i].ParameterType;
|
||||
|
||||
if (type1 == type2)
|
||||
{
|
||||
// exact match between parameter types doesn't change score
|
||||
continue;
|
||||
}
|
||||
|
||||
// exact match with source type decides winner immediately
|
||||
if (type1 == _types[i])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (type2 == _types[i])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int r = ChooseMorePreciseType(type1, type2);
|
||||
if (r != 0)
|
||||
{
|
||||
// winner decided
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
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 (type1 == type2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type2 = type2.GetElementType();
|
||||
if (type2 == type1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool c1FromC2, c2FromC1;
|
||||
|
||||
if (type1.IsPrimitive() && type2.IsPrimitive())
|
||||
{
|
||||
c1FromC2 = CanConvertPrimitive(type2, type1);
|
||||
c2FromC1 = CanConvertPrimitive(type1, type2);
|
||||
}
|
||||
else
|
||||
{
|
||||
c1FromC2 = type1.IsAssignableFrom(type2);
|
||||
c2FromC1 = type2.IsAssignableFrom(type1);
|
||||
}
|
||||
|
||||
if (c1FromC2 == c2FromC1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return c1FromC2 ? 1 : -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retunrs a best method overload for given argument <paramref name="types"/>.
|
||||
/// </summary>
|
||||
/// <param name="candidates">List of method candidates</param>
|
||||
/// <param name="types">Argument types</param>
|
||||
/// <returns>Best method overload, or <c>null</c> if none matched</returns>
|
||||
public static TMethod SelectMethod<TMethod>(IEnumerable<TMethod> candidates, IList<Type> types) where TMethod : MethodBase
|
||||
{
|
||||
if (candidates == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(candidates));
|
||||
}
|
||||
if (types == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
}
|
||||
|
||||
// ParamArrays are not supported by ReflectionDelegateFactory
|
||||
// They will be treated like ordinary array arguments
|
||||
const bool enableParamArray = false;
|
||||
|
||||
return candidates
|
||||
.Where(m => FilterParameters(m.GetParameters(), types, enableParamArray))
|
||||
.OrderBy(m => m.GetParameters(), new ParametersMatchComparer(types, enableParamArray))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -274,225 +274,6 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
#if !DOTNET
|
||||
|
||||
private static readonly Type[] PrimitiveTypes = new Type[]
|
||||
{
|
||||
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[]
|
||||
{
|
||||
0x0001, 0x0FE2, 0x0D54, 0x0FFA,
|
||||
0x0D50, 0x0FE2, 0x0D40, 0x0F80,
|
||||
0x0D00, 0x0E00, 0x0C00, 0x0800
|
||||
};
|
||||
|
||||
internal static bool CanConvertPrimitive(Type from, Type to)
|
||||
{
|
||||
if (from == to) return true;
|
||||
|
||||
int fromMask = 0, toMask = 0;
|
||||
|
||||
for (int i = 0; i < PrimitiveTypes.Length && (fromMask == 0 || toMask == 0); i++)
|
||||
{
|
||||
if (PrimitiveTypes[i] == from)
|
||||
{
|
||||
fromMask = WideningMasks[i];
|
||||
}
|
||||
else if (PrimitiveTypes[i] == to)
|
||||
{
|
||||
toMask = 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return (fromMask & toMask) != 0;
|
||||
}
|
||||
|
||||
private static bool FilterParameters(ParameterInfo[] parameters, IList<Type> types, bool enableParamArray)
|
||||
{
|
||||
if (parameters == null)
|
||||
throw new ArgumentNullException(nameof(parameters));
|
||||
if (types == null)
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
|
||||
if (parameters.Length == 0)
|
||||
{
|
||||
// fast check for parameterless methods
|
||||
return types.Count == 0;
|
||||
}
|
||||
|
||||
if (parameters.Length > types.Count)
|
||||
{
|
||||
// not all declared parameters were specified (optional parameters are not supported)
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the last parameter is ParamArray
|
||||
Type paramArrayType = null;
|
||||
|
||||
if (enableParamArray &&
|
||||
parameters[parameters.Length - 1].ParameterType.IsArray &&
|
||||
parameters[parameters.Length - 1].IsDefined(typeof(ParamArrayAttribute)))
|
||||
{
|
||||
paramArrayType = parameters[parameters.Length - 1].ParameterType.GetElementType();
|
||||
}
|
||||
|
||||
if (paramArrayType == null && parameters.Length != types.Count)
|
||||
{
|
||||
// when there's no ParamArray, number of parameters should match
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
{
|
||||
var paramType = (paramArrayType != null && i >= parameters.Length - 1) ? paramArrayType : parameters[i].ParameterType;
|
||||
|
||||
// exact match with provided type
|
||||
if (paramType == types[i]) continue;
|
||||
|
||||
// parameter of type object matches anything
|
||||
if (paramType == typeof(object)) continue;
|
||||
|
||||
if (paramType.IsPrimitive())
|
||||
{
|
||||
// primitive parameter can only be assigned from compatible primitive type
|
||||
if (!types[i].IsPrimitive() || !CanConvertPrimitive(types[i], paramType))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!paramType.IsAssignableFrom(types[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class ParametersMatchComparer : IComparer<ParameterInfo[]>
|
||||
{
|
||||
private readonly IList<Type> _types;
|
||||
private readonly bool _enableParamArray;
|
||||
|
||||
public ParametersMatchComparer(IList<Type> types, bool enableParamArray)
|
||||
{
|
||||
if (types == null)
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
|
||||
_types = types;
|
||||
_enableParamArray = enableParamArray;
|
||||
}
|
||||
|
||||
public int Compare(ParameterInfo[] parameters1, ParameterInfo[] parameters2)
|
||||
{
|
||||
if (parameters1 == null)
|
||||
throw new ArgumentNullException(nameof(parameters1));
|
||||
if (parameters2 == null)
|
||||
throw new ArgumentNullException(nameof(parameters2));
|
||||
|
||||
// parameterless method wins
|
||||
if (parameters1.Length == 0) return -1;
|
||||
if (parameters2.Length == 0) return 1;
|
||||
|
||||
Type paramArrayType1 = null, paramArrayType2 = null;
|
||||
|
||||
if (_enableParamArray)
|
||||
{
|
||||
if (parameters1[parameters1.Length - 1].ParameterType.IsArray &&
|
||||
parameters1[parameters1.Length - 1].IsDefined(typeof(ParamArrayAttribute)))
|
||||
{
|
||||
paramArrayType1 = parameters1[parameters1.Length - 1].ParameterType.GetElementType();
|
||||
}
|
||||
|
||||
if (parameters2[parameters2.Length - 1].ParameterType.IsArray &&
|
||||
parameters2[parameters2.Length - 1].IsDefined(typeof(ParamArrayAttribute)))
|
||||
{
|
||||
paramArrayType2 = parameters2[parameters2.Length - 1].ParameterType.GetElementType();
|
||||
}
|
||||
|
||||
// A method using params always loses to one not using params
|
||||
if (paramArrayType1 != null && paramArrayType2 == null) return 1;
|
||||
if (paramArrayType2 != null && paramArrayType1 == null) return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _types.Count; i++)
|
||||
{
|
||||
var type1 = (paramArrayType1 != null && i >= parameters1.Length - 1) ? paramArrayType1 : parameters1[i].ParameterType;
|
||||
var type2 = (paramArrayType2 != null && i >= parameters2.Length - 1) ? paramArrayType2 : parameters2[i].ParameterType;
|
||||
|
||||
// exact match between parameter types doesn't change score
|
||||
if (type1 == type2) continue;
|
||||
|
||||
// exact match with source type decides winner immediately
|
||||
if (type1 == _types[i]) return -1;
|
||||
if (type2 == _types[i]) return 1;
|
||||
|
||||
var r = ChooseMorePreciseType(type1, type2);
|
||||
if (r != 0) return r;
|
||||
}
|
||||
|
||||
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 (type1 == type2) return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
type2 = type2.GetElementType();
|
||||
if (type2 == type1) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool c1FromC2, c2FromC1;
|
||||
|
||||
if (type1.IsPrimitive() && type2.IsPrimitive())
|
||||
{
|
||||
c1FromC2 = CanConvertPrimitive(type2, type1);
|
||||
c2FromC1 = CanConvertPrimitive(type1, type2);
|
||||
}
|
||||
else
|
||||
{
|
||||
c1FromC2 = type1.IsAssignableFrom(type2);
|
||||
c2FromC1 = type2.IsAssignableFrom(type1);
|
||||
}
|
||||
|
||||
if (c1FromC2 == c2FromC1) return 0;
|
||||
|
||||
return c1FromC2 ? 1 : -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static MethodBase SelectMethod(IEnumerable<MethodBase> candidates, BindingFlags bindingFlags, IList<Type> types)
|
||||
{
|
||||
if (candidates == null)
|
||||
throw new ArgumentNullException(nameof(candidates));
|
||||
if (types == null)
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
|
||||
// ParamArrays are not supported by ReflectionDelegateFactory
|
||||
// They will be treated like ordinary array arguments
|
||||
const bool enableParamArray = false;
|
||||
|
||||
return candidates
|
||||
.Where(m => TestAccessibility(m, bindingFlags) && FilterParameters(m.GetParameters(), types, enableParamArray))
|
||||
.OrderBy(m => m.GetParameters(), new ParametersMatchComparer(types, enableParamArray))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public static MethodInfo GetMethod(this Type type, string name)
|
||||
{
|
||||
return type.GetMethod(name, DefaultFlags);
|
||||
@@ -515,7 +296,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags, object placeHolder1, IList<Type> parameterTypes, object placeHolder2)
|
||||
{
|
||||
return (MethodInfo)SelectMethod(type.GetTypeInfo().DeclaredMethods.Where(m => name == null || m.Name == name), bindingFlags, parameterTypes);
|
||||
return MethodBinder.SelectMethod(type.GetTypeInfo().DeclaredMethods.Where(m => (name == null || m.Name == name) && TestAccessibility(m, bindingFlags)), parameterTypes);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConstructorInfo> GetConstructors(this Type type)
|
||||
@@ -535,7 +316,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
public static ConstructorInfo GetConstructor(this Type type, BindingFlags bindingFlags, object placeholder1, IList<Type> parameterTypes, object placeholder2)
|
||||
{
|
||||
return (ConstructorInfo)SelectMethod(type.GetTypeInfo().DeclaredConstructors, bindingFlags, parameterTypes);
|
||||
return MethodBinder.SelectMethod(type.GetConstructors(bindingFlags), parameterTypes);
|
||||
}
|
||||
|
||||
public static MemberInfo[] GetMember(this Type type, string member)
|
||||
|
||||
Reference in New Issue
Block a user