CustomMethodSearcher.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #if REFLECTION_EMIT_COMPILER
  2. using System.Reflection;
  3. using System;
  4. namespace FastReport.Code.Expressions
  5. {
  6. internal static class CustomMethodSearcher
  7. {
  8. public static MethodInfo Search(Type onType, string methodName, Type[] requiredArguments)
  9. {
  10. // TODO: remove this default logic to work only with new searcher
  11. MethodInfo methodInfo = onType.GetMethod(methodName, requiredArguments);
  12. if (methodInfo == null)
  13. {
  14. // New searcher
  15. // TODO: CACHE
  16. // ['{methodName}_{onType}_{requiredArguments}'] = methodInfo
  17. MethodInfoExt candidate = MethodInfoExt.Empty;
  18. // searching compatible methods
  19. foreach (var method in onType.GetMethods())
  20. {
  21. if (method.Name == methodName)
  22. {
  23. if (ArgumentsAreEqual(requiredArguments, method, out MethodInfoExt methodInfoExt))
  24. {
  25. // comparison with a more suitable method
  26. if (candidate.CompareTo(methodInfoExt) > 0)
  27. candidate = methodInfoExt;
  28. }
  29. }
  30. }
  31. methodInfo = candidate.Method;
  32. }
  33. return methodInfo;
  34. }
  35. private readonly struct MethodInfoExt : IComparable<MethodInfoExt>
  36. {
  37. public readonly MethodInfo Method;
  38. public readonly int ParamLength;
  39. public readonly bool HasParamArray;
  40. public static readonly MethodInfoExt Empty = new MethodInfoExt(null, int.MaxValue, true);
  41. public MethodInfoExt(MethodInfo method, int paramLength, bool hasParamArray)
  42. {
  43. Method = method;
  44. ParamLength = paramLength;
  45. HasParamArray = hasParamArray;
  46. }
  47. public int CompareTo(MethodInfoExt other)
  48. {
  49. const int ItsGreater = -1;
  50. const int ItsLess = 1;
  51. if (!HasParamArray && other.HasParamArray)
  52. return ItsGreater;
  53. if (HasParamArray && !other.HasParamArray)
  54. return ItsLess;
  55. var otherParamLength = other.ParamLength;
  56. if (!HasParamArray)
  57. {
  58. if (ParamLength > otherParamLength)
  59. return ItsLess;
  60. if (ParamLength < otherParamLength)
  61. return ItsGreater;
  62. }
  63. else
  64. {
  65. if (ParamLength < otherParamLength)
  66. return ItsLess;
  67. if (ParamLength > otherParamLength)
  68. return ItsGreater;
  69. }
  70. return 0;
  71. }
  72. }
  73. private static bool ArgumentsAreEqual(Type[] requiredArguments, MethodInfo foundMethod, out MethodInfoExt methodInfoExt)
  74. {
  75. ParameterInfo[] parameters = foundMethod.GetParameters();
  76. methodInfoExt = MethodInfoExt.Empty;
  77. // if argumentTypes = Empty
  78. if (requiredArguments.Length == 0)
  79. {
  80. if (parameters.Length == 0)
  81. {
  82. methodInfoExt = new MethodInfoExt(
  83. method: foundMethod,
  84. paramLength: parameters.Length,
  85. hasParamArray: false);
  86. return true;
  87. }
  88. var parameter = parameters[0];
  89. return parameter.IsOptional();
  90. }
  91. // if parameters = Empty
  92. if (parameters.Length == 0)
  93. return false;
  94. bool hasParamArray = false;
  95. for (int argIndex = 0, paramIndex = 0; ;)
  96. {
  97. var argument = requiredArguments[argIndex];
  98. var parameter = parameters[paramIndex];
  99. if (!TypeCompatible(argument, parameter))
  100. return false;
  101. argIndex++;
  102. // parameter isn't 'params []'
  103. if (!parameter.IsParamArray())
  104. paramIndex++;
  105. else
  106. hasParamArray = true;
  107. if (argIndex < requiredArguments.Length)
  108. {
  109. // if params finished, but arguments aren't
  110. if (paramIndex >= parameters.Length)
  111. return false;
  112. }
  113. else
  114. {
  115. // arguments are over, check the rest of the parameters
  116. while (parameters.Length - paramIndex > 0)
  117. {
  118. parameter = parameters[paramIndex];
  119. if (parameter.IsParamArray())
  120. hasParamArray = true;
  121. if (!parameter.IsOptional())
  122. return false;
  123. paramIndex++;
  124. }
  125. methodInfoExt = new MethodInfoExt(
  126. method: foundMethod,
  127. paramLength: parameters.Length,
  128. hasParamArray: hasParamArray);
  129. return true;
  130. }
  131. }
  132. }
  133. private static bool TypeCompatible(Type type1, ParameterInfo parameter)
  134. {
  135. Type paramType = parameter.ParameterType;
  136. if (TypeCompatible(type1, paramType)) return true;
  137. // Is it `param T[] args` ?
  138. if (parameter.IsParamArray())
  139. {
  140. var elementType = paramType.GetElementType();
  141. if (TypeCompatible(type1, elementType)) return true;
  142. }
  143. return false;
  144. }
  145. private static bool TypeCompatible(Type type1, Type type2)
  146. {
  147. if (type1 == type2) return true;
  148. if (type2.IsAssignableFrom(type1)) return true;
  149. return false;
  150. }
  151. }
  152. internal static class ParameterInfoHelpers
  153. {
  154. internal static bool IsParamArray(this ParameterInfo parameter)
  155. {
  156. return parameter.GetCustomAttribute(typeof(ParamArrayAttribute), false) != null;
  157. }
  158. internal static bool IsOptional(this ParameterInfo parameter)
  159. {
  160. // parameter is 'params []'
  161. if (parameter.IsParamArray())
  162. return true;
  163. // parameter has default value
  164. if (parameter.HasDefaultValue) // TODO: IsOptional ?
  165. return true;
  166. return false;
  167. }
  168. }
  169. }
  170. #endif