⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memberlookuphelper.cs

📁 SharpDevelop2.0.0 c#开发免费工具
💻 CS
📖 第 1 页 / 共 2 页
字号:
// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision: 915 $</version>
// </file>

using System;
using System.Collections.Generic;
using ICSharpCode.Core;

namespace ICSharpCode.SharpDevelop.Dom
{
	/// <summary>
	/// Class with methods to help finding the correct overload for a member.
	/// </summary>
	/// <remarks>
	/// This class tries to do member lookup as close to the C# spec (ECMA-334, § 14.3) as possible.
	/// Other languages might need custom lookup methods.
	/// </remarks>
	public static class MemberLookupHelper
	{
		#region FindOverload
		public static IMethod FindOverload(IList<IMethod> methods, IReturnType[] typeParameters, IReturnType[] arguments)
		{
			if (methods.Count == 0)
				return null;
			bool tmp;
			int[] ranking = RankOverloads(methods, typeParameters, arguments, false, out tmp);
			int bestRanking = -1;
			int best = 0;
			for (int i = 0; i < ranking.Length; i++) {
				if (ranking[i] > bestRanking) {
					bestRanking = ranking[i];
					best = i;
				}
			}
			return methods[best];
		}
		
		public static IProperty FindOverload(IList<IProperty> properties, IReturnType[] arguments)
		{
			if (properties.Count == 0)
				return null;
			bool tmp1; IReturnType[][] tmp2;
			List<IMethodOrProperty> newList = new List<IMethodOrProperty>(properties.Count);
			foreach (IProperty p in properties) newList.Add(p);
			int[] ranking = RankOverloads(newList, arguments, false, out tmp1, out tmp2);
			int bestRanking = -1;
			int best = 0;
			for (int i = 0; i < ranking.Length; i++) {
				if (ranking[i] > bestRanking) {
					bestRanking = ranking[i];
					best = i;
				}
			}
			return properties[best];
		}
		#endregion
		
		#region Rank method overloads
		/// <summary>
		/// Assigns a ranking score to each method in the <paramref name="list"/>.
		/// </summary>
		/// <param name="list">Link with the methods to check.<br/>
		/// <b>Generic methods in the input type are replaced by methods with have the types substituted!</b>
		/// </param>
		/// <param name="typeParameters">List with the type parameters passed to the method.
		/// Can be null (=no type parameters)</param>
		/// <param name="arguments">The types of the arguments passed to the method.
		/// A null return type means any argument type is allowed.</param>
		/// <param name="allowAdditionalArguments">Specifies whether the method can have
		/// more parameters than specified here. Useful for method insight scenarios.</param>
		/// <param name="acceptableMatch">Out parameter that is true when the best ranked
		/// method is acceptable for a method call (no invalid casts)</param>
		/// <returns>Integer array. Each value in the array </returns>
		public static int[] RankOverloads(IList<IMethod> list,
		                                  IReturnType[] typeParameters,
		                                  IReturnType[] arguments,
		                                  bool allowAdditionalArguments,
		                                  out bool acceptableMatch)
		{
			acceptableMatch = false;
			if (list.Count == 0) return new int[] {};
			
			List<IMethodOrProperty> l2 = new List<IMethodOrProperty>(list.Count);
			IReturnType[][] inferredTypeParameters;
			// See ECMA-334, § 14.3
			
			// If type parameters are specified, remove all methods from the list that do not
			// use the specified number of parameters.
			if (typeParameters != null && typeParameters.Length > 0) {
				for (int i = 0; i < list.Count; i++) {
					IMethod m = list[i];
					if (m.TypeParameters.Count == typeParameters.Length) {
						m = (IMethod)m.Clone();
						m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, typeParameters, true);
						for (int j = 0; j < m.Parameters.Count; ++j) {
							m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, typeParameters, true);
						}
						list[i] = m;
						l2.Add(m);
					}
				}
				
				int[] innerRanking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch, out inferredTypeParameters);
				int[] ranking = new int[list.Count];
				int innerIndex = 0;
				for (int i = 0; i < ranking.Length; i++) {
					if (list[i].TypeParameters.Count == typeParameters.Length) {
						ranking[i] = innerRanking[innerIndex++];
					} else {
						ranking[i] = 0;
					}
				}
				return ranking;
			} else {
				// Note that when there are no type parameters, methods having type parameters
				// are not removed, since the type inference process might be able to infer the
				// type arguments.
				foreach (IMethod m in list) l2.Add(m);
				
				int[] ranking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch, out inferredTypeParameters);
				ApplyInferredTypeParameters(list, inferredTypeParameters);
				return ranking;
			}
		}
		
		static void ApplyInferredTypeParameters(IList<IMethod> list, IReturnType[][] inferredTypeParameters)
		{
			if (inferredTypeParameters == null)
				return;
			for (int i = 0; i < list.Count; i++) {
				IReturnType[] inferred = inferredTypeParameters[i];
				if (inferred != null && inferred.Length > 0) {
					IMethod m = (IMethod)list[i].Clone();
					m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, inferred, true);
					for (int j = 0; j < m.Parameters.Count; ++j) {
						m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, inferred, true);
					}
					list[i] = m;
				}
			}
		}
		#endregion
		
		#region Main ranking algorithm
		/// <summary>
		/// The inner ranking engine. Works on both methods and properties.
		/// For parameter documentation, read the comments on the above method.
		/// </summary>
		public static int[] RankOverloads(IList<IMethodOrProperty> list,
		                                  IReturnType[] arguments,
		                                  bool allowAdditionalArguments,
		                                  out bool acceptableMatch,
		                                  out IReturnType[][] inferredTypeParameters)
		{
			// § 14.4.2 Overload resolution
			acceptableMatch = false;
			inferredTypeParameters = null;
			if (list.Count == 0) return new int[] {};
			
			int[] ranking = new int[list.Count];
			bool[] needToExpand = new bool[list.Count];
			int maxScore = 0;
			int baseScore = 0;
			int score;
			bool expanded;
			for (int i = 0; i < list.Count; i++) {
				if (IsApplicable(list[i].Parameters, arguments, allowAdditionalArguments, out score, out expanded)) {
					acceptableMatch = true;
					score = int.MaxValue;
				} else {
					baseScore = Math.Max(baseScore, score);
				}
				needToExpand[i] = expanded;
				ranking[i] = score;
				maxScore = Math.Max(maxScore, score);
			}
			// all overloads that have maxScore (normally those that are applicable)
			// have to be rescored.
			// The new scala starts with baseScore + 1 to ensure that all applicable members have
			// a higher score than non-applicable members
			
			// The first step is to expand the methods and do type argument substitution
			IReturnType[][] expandedParameters = ExpandParametersAndSubstitute(list, arguments, maxScore, ranking, needToExpand, out inferredTypeParameters);
			
			// find the best function member
			
			score = baseScore + 2;
			int bestIndex = -1;
			for (int i = 0; i < ranking.Length; i++) {
				if (ranking[i] == maxScore) {
					// the best function member is the one that is better than all other function members
					if (bestIndex < 0) {
						ranking[i] = score;
						bestIndex = i;
					} else {
						switch (GetBetterFunctionMember(arguments,
						                                list[i], expandedParameters[i], needToExpand[i],
						                                list[bestIndex], expandedParameters[bestIndex], needToExpand[bestIndex]))
						{
							case 0:
								// neither member is better
								ranking[i] = score;
								break;
							case 1:
								// the new member is better
								ranking[i] = ++score;
								bestIndex = i;
								break;
							case 2:
								// the old member is better
								ranking[i] = score - 1;
								// this is not really correct, normally we would need to
								// compare the member with other members
								break;
						}
					}
				}
			}
			
			return ranking;
		}
		
		static IReturnType[][] ExpandParametersAndSubstitute(IList<IMethodOrProperty> list,
		                                                     IReturnType[] arguments,
		                                                     int maxScore, int[] ranking, bool[] needToExpand,
		                                                     out IReturnType[][] inferredTypeParameters)
		{
			IReturnType[][] expandedParameters = new IReturnType[list.Count][];
			inferredTypeParameters = new IReturnType[list.Count][];
			for (int i = 0; i < ranking.Length; i++) {
				if (ranking[i] == maxScore) {
					IList<IParameter> parameters = list[i].Parameters;
					IReturnType[] typeParameters = (list[i] is IMethod) ? InferTypeArguments((IMethod)list[i], arguments) : null;
					inferredTypeParameters[i] = typeParameters;
					IReturnType paramsType = null;
					expandedParameters[i] = new IReturnType[arguments.Length];
					for (int j = 0; j < arguments.Length; j++) {
						if (j < parameters.Count) {
							IParameter parameter = parameters[j];
							if (parameter.IsParams && needToExpand[i]) {
								if (parameter.ReturnType.ArrayDimensions > 0) {
									paramsType = parameter.ReturnType.ArrayElementType;
									paramsType = ConstructedReturnType.TranslateType(paramsType, typeParameters, true);
								}
								expandedParameters[i][j] = paramsType;
							} else {
								expandedParameters[i][j] = ConstructedReturnType.TranslateType(parameter.ReturnType, typeParameters, true);
							}
						} else {
							expandedParameters[i][j] = paramsType;
						}
					}
				}
			}
			return expandedParameters;
		}
		
		/// <summary>
		/// Gets which function member is better. (§ 14.4.2.2)
		/// </summary>
		/// <param name="arguments">The arguments passed to the function</param>
		/// <param name="m1">The first method</param>
		/// <param name="parameters1">The expanded and substituted parameters of the first method</param>
		/// <param name="m2">The second method</param>
		/// <param name="parameters2">The expanded and substituted parameters of the second method</param>
		/// <returns>0 if neither method is better. 1 if m1 is better. 2 if m2 is better.</returns>
		static int GetBetterFunctionMember(IReturnType[] arguments,
		                                   IMethodOrProperty m1, IReturnType[] parameters1, bool isExpanded1,
		                                   IMethodOrProperty m2, IReturnType[] parameters2, bool isExpanded2)
		{
			int length = Math.Min(Math.Min(parameters1.Length, parameters2.Length), arguments.Length);
			bool foundBetterParamIn1 = false;
			bool foundBetterParamIn2 = false;
			for (int i = 0; i < length; i++) {
				if (arguments[i] == null)
					continue;
				int res = GetBetterConversion(arguments[i], parameters1[i], parameters2[i]);
				if (res == 1) foundBetterParamIn1 = true;
				if (res == 2) foundBetterParamIn2 = true;
			}
			if (foundBetterParamIn1 && !foundBetterParamIn2)
				return 1;
			if (foundBetterParamIn2 && !foundBetterParamIn1)
				return 2;
			if (foundBetterParamIn1 && foundBetterParamIn2)
				return 0; // ambigous
			// If none conversion is better than any other, it is possible that the
			// expanded parameter lists are the same:
			for (int i = 0; i < length; i++) {
				if (!object.Equals(parameters1[i], parameters2[i])) {
					// if expanded parameters are not the same, neither function member is better
					return 0;
				}
			}
			
			// the expanded parameters are the same, apply the tie-breaking rules from the spec:
			
			// if one method is generic and the other non-generic, the non-generic is better
			bool m1IsGeneric = (m1 is IMethod) ? ((IMethod)m1).TypeParameters.Count > 0 : false;
			bool m2IsGeneric = (m2 is IMethod) ? ((IMethod)m2).TypeParameters.Count > 0 : false;
			if (m1IsGeneric && !m2IsGeneric) return 2;
			if (m2IsGeneric && !m1IsGeneric) return 1;
			
			// for params parameters: non-expanded calls are better
			if (isExpanded1 && !isExpanded2) return 2;
			if (isExpanded2 && !isExpanded1) return 1;
			
			// if the number of parameters is different, the one with more parameters is better
			// this occurs when only when both methods are expanded
			if (m1.Parameters.Count > m2.Parameters.Count) return 1;
			if (m2.Parameters.Count > m1.Parameters.Count) return 2;
			
			IReturnType[] m1ParamTypes = new IReturnType[m1.Parameters.Count];
			IReturnType[] m2ParamTypes = new IReturnType[m2.Parameters.Count];
			for (int i = 0; i < m1ParamTypes.Length; i++) {
				m1ParamTypes[i] = m1.Parameters[i].ReturnType;
				m2ParamTypes[i] = m2.Parameters[i].ReturnType;
			}
			return GetMoreSpecific(m1ParamTypes, m2ParamTypes);
		}
		
		/// <summary>
		/// Gets which return type list is more specific.
		/// § 14.4.2.2: types with generic arguments are less specific than types with fixed arguments
		/// </summary>
		/// <returns>0 if both are equally specific, 1 if <paramref name="r"/> is more specific,
		/// 2 if <paramref name="s"/> is more specific.</returns>
		static int GetMoreSpecific(IList<IReturnType> r, IList<IReturnType> s)
		{
			bool foundMoreSpecificParamIn1 = false;
			bool foundMoreSpecificParamIn2 = false;
			int length = Math.Min(r.Count, s.Count);
			for (int i = 0; i < length; i++) {
				int res = GetMoreSpecific(r[i], s[i]);
				if (res == 1) foundMoreSpecificParamIn1 = true;
				if (res == 2) foundMoreSpecificParamIn2 = true;
			}
			if (foundMoreSpecificParamIn1 && !foundMoreSpecificParamIn2)
				return 1;
			if (foundMoreSpecificParamIn2 && !foundMoreSpecificParamIn1)
				return 2;
			return 0;
		}
		
		static int GetMoreSpecific(IReturnType r, IReturnType s)
		{
			if (r == null && s == null) return 0;
			if (r == null) return 2;
			if (s == null) return 1;
			if (r is GenericReturnType && !(s is GenericReturnType))
				return 2;
			if (s is GenericReturnType && !(r is GenericReturnType))
				return 1;
			if (r.ArrayDimensions > 0 && s.ArrayDimensions > 0)
				return GetMoreSpecific(r.ArrayElementType, s.ArrayElementType);
			if (r.TypeArguments != null && s.TypeArguments != null)
				return GetMoreSpecific(r.TypeArguments, s.TypeArguments);
			return 0;
		}
		#endregion
		
		#region Type Argument Inference
		static IReturnType[] InferTypeArguments(IMethod method, IReturnType[] arguments)
		{
			// §25.6.4 Inference of type arguments
			int count = method.TypeParameters.Count;
			if (count == 0) return null;
			IReturnType[] result = new IReturnType[count];
			IList<IParameter> parameters = method.Parameters;
			for (int i = 0; i < arguments.Length; i++) {
				if (i >= parameters.Count)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -