PageRenderTime 54ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/Dependencies/boo/src/Boo.Lang/Runtime/MethodResolver.cs

https://github.com/w4x/boolangstudio
C# | 260 lines | 199 code | 34 blank | 27 comment | 46 complexity | d7dd92f9f210d6c88689e8574d825181 MD5 | raw file
Possible License(s): GPL-2.0
  1. #region license
  2. // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without modification,
  6. // are permitted provided that the following conditions are met:
  7. //
  8. // * Redistributions of source code must retain the above copyright notice,
  9. // this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above copyright notice,
  11. // this list of conditions and the following disclaimer in the documentation
  12. // and/or other materials provided with the distribution.
  13. // * Neither the name of Rodrigo B. de Oliveira nor the names of its
  14. // contributors may be used to endorse or promote products derived from this
  15. // software without specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  21. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  25. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. #endregion
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. namespace Boo.Lang.Runtime
  32. {
  33. public class MethodResolver
  34. {
  35. public static Type[] GetArgumentTypes(object[] arguments)
  36. {
  37. if (arguments.Length == 0) return DispatcherKey.NoArguments;
  38. Type[] types = new Type[arguments.Length];
  39. for (int i = 0; i < types.Length; ++i)
  40. {
  41. types[i] = GetObjectTypeOrNull(arguments[i]);
  42. }
  43. return types;
  44. }
  45. private static Type GetObjectTypeOrNull(object arg)
  46. {
  47. if (null == arg) return null;
  48. return arg.GetType();
  49. }
  50. private readonly Type[] _arguments;
  51. public MethodResolver(params Type[] argumentTypes)
  52. {
  53. _arguments = argumentTypes;
  54. }
  55. public CandidateMethod ResolveMethod(IEnumerable<MethodInfo> candidates)
  56. {
  57. List<CandidateMethod> applicable = FindApplicableMethods(candidates);
  58. if (applicable.Count == 0) return null;
  59. if (applicable.Count == 1) return applicable[0];
  60. List<CandidateMethod> dataPreserving = applicable.FindAll(DoesNotRequireConversions);
  61. if (dataPreserving.Count > 0) return BestMethod(dataPreserving);
  62. return BestMethod(applicable);
  63. }
  64. private static bool DoesNotRequireConversions(CandidateMethod candidate)
  65. {
  66. return candidate.DoesNotRequireConversions;
  67. }
  68. private CandidateMethod BestMethod(List<CandidateMethod> applicable)
  69. {
  70. applicable.Sort(BetterCandidate);
  71. return applicable[applicable.Count - 1];
  72. }
  73. private static int TotalScore(CandidateMethod c1)
  74. {
  75. int total = 0;
  76. foreach (int score in c1.ArgumentScores)
  77. {
  78. total += score;
  79. }
  80. return total;
  81. }
  82. private int BetterCandidate(CandidateMethod c1, CandidateMethod c2)
  83. {
  84. int result = Math.Sign(TotalScore(c1) - TotalScore(c2));
  85. if (result != 0) return result;
  86. if (c1.VarArgs && !c2.VarArgs) return -1;
  87. if (c2.VarArgs && !c1.VarArgs) return 1;
  88. int minArgumentCount = Math.Min(c1.MinimumArgumentCount, c2.MinimumArgumentCount);
  89. for (int i = 0; i < minArgumentCount; ++i)
  90. {
  91. result += MoreSpecificType(c1.GetParameterType(i), c2.GetParameterType(i));
  92. }
  93. if (result != 0) return result;
  94. if (c1.VarArgs && c2.VarArgs)
  95. {
  96. return MoreSpecificType(c1.VarArgsParameterType, c2.VarArgsParameterType);
  97. }
  98. return 0;
  99. }
  100. private static int MoreSpecificType(Type t1, Type t2)
  101. {
  102. // The less-generic type is more specific
  103. int result = GetTypeGenerity(t2) - GetTypeGenerity(t1);
  104. if (result != 0) return result;
  105. // If both types have the same generity, the deeper-nested type is more specific
  106. return GetLogicalTypeDepth(t1) - GetLogicalTypeDepth(t2);
  107. }
  108. private static int GetTypeGenerity(Type type)
  109. {
  110. if (!type.ContainsGenericParameters) return 0;
  111. return type.GetGenericArguments().Length;
  112. }
  113. private static int GetLogicalTypeDepth(Type type)
  114. {
  115. int depth = GetTypeDepth(type);
  116. return (type.IsValueType) ? depth - 1 : depth;
  117. }
  118. private static int GetTypeDepth(Type type)
  119. {
  120. if (type.IsByRef)
  121. {
  122. return GetTypeDepth(type.GetElementType());
  123. }
  124. else if (type.IsInterface)
  125. {
  126. return GetInterfaceDepth(type);
  127. }
  128. return GetClassDepth(type);
  129. }
  130. private static int GetClassDepth(Type type)
  131. {
  132. int depth = 0;
  133. Type objectType = typeof(object);
  134. while (type != objectType)
  135. {
  136. type = type.BaseType;
  137. ++depth;
  138. }
  139. return depth;
  140. }
  141. private static int GetInterfaceDepth(Type type)
  142. {
  143. Type[] interfaces = type.GetInterfaces();
  144. if (interfaces.Length > 0)
  145. {
  146. int current = 0;
  147. foreach (Type i in interfaces)
  148. {
  149. int depth = GetInterfaceDepth(i);
  150. if (depth > current) current = depth;
  151. }
  152. return 1+current;
  153. }
  154. return 1;
  155. }
  156. private List<CandidateMethod> FindApplicableMethods(IEnumerable<MethodInfo> candidates)
  157. {
  158. List<CandidateMethod> applicable = new List<CandidateMethod>();
  159. foreach (MethodInfo method in candidates)
  160. {
  161. CandidateMethod candidateMethod = IsApplicableMethod(method);
  162. if (null == candidateMethod) continue;
  163. applicable.Add(candidateMethod);
  164. }
  165. return applicable;
  166. }
  167. private CandidateMethod IsApplicableMethod(MethodInfo method)
  168. {
  169. ParameterInfo[] parameters = method.GetParameters();
  170. bool varargs = IsVarArgs(parameters);
  171. if (!ValidArgumentCount(parameters, varargs)) return null;
  172. CandidateMethod candidateMethod = new CandidateMethod(method, _arguments.Length, varargs);
  173. if (CalculateCandidateScore(candidateMethod)) return candidateMethod;
  174. return null;
  175. }
  176. private bool ValidArgumentCount(ParameterInfo[] parameters, bool varargs)
  177. {
  178. if (varargs)
  179. {
  180. int minArgumentCount = parameters.Length - 1;
  181. return _arguments.Length >= minArgumentCount;
  182. }
  183. return _arguments.Length == parameters.Length;
  184. }
  185. private bool IsVarArgs(ParameterInfo[] parameters)
  186. {
  187. if (parameters.Length == 0) return false;
  188. return HasParamArrayAttribute(parameters[parameters.Length - 1]);
  189. }
  190. private bool HasParamArrayAttribute(ParameterInfo info)
  191. {
  192. return info.IsDefined(typeof(ParamArrayAttribute), true);
  193. }
  194. private bool CalculateCandidateScore(CandidateMethod candidateMethod)
  195. {
  196. ParameterInfo[] parameters = candidateMethod.Parameters;
  197. for (int i = 0; i < candidateMethod.MinimumArgumentCount; ++i)
  198. {
  199. if (parameters[i].IsOut) return false;
  200. if (!CalculateCandidateArgumentScore(candidateMethod, i, parameters[i].ParameterType))
  201. {
  202. return false;
  203. }
  204. }
  205. if (candidateMethod.VarArgs)
  206. {
  207. Type varArgItemType = candidateMethod.VarArgsParameterType;
  208. for (int i = candidateMethod.MinimumArgumentCount; i < _arguments.Length; ++i)
  209. {
  210. if (!CalculateCandidateArgumentScore(candidateMethod, i, varArgItemType))
  211. {
  212. return false;
  213. }
  214. }
  215. }
  216. return true;
  217. }
  218. private bool CalculateCandidateArgumentScore(CandidateMethod candidateMethod, int argumentIndex, Type paramType)
  219. {
  220. int score = CandidateMethod.CalculateArgumentScore(paramType, _arguments[argumentIndex]);
  221. if (score < 0) return false;
  222. candidateMethod.ArgumentScores[argumentIndex] = score;
  223. return true;
  224. }
  225. }
  226. }