2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
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.
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.
31 using System
.Collections
.Generic
;
32 using System
.Reflection
;
34 namespace Boo
.Lang
.Runtime
36 public class MethodResolver
38 public static Type
[] GetArgumentTypes(object[] arguments
)
40 if (arguments
.Length
== 0) return DispatcherKey
.NoArguments
;
42 Type
[] types
= new Type
[arguments
.Length
];
43 for (int i
= 0; i
< types
.Length
; ++i
)
45 types
[i
] = GetObjectTypeOrNull(arguments
[i
]);
50 private static Type
GetObjectTypeOrNull(object arg
)
52 if (null == arg
) return null;
56 private readonly Type
[] _arguments
;
58 public MethodResolver(params Type
[] argumentTypes
)
60 _arguments
= argumentTypes
;
63 public CandidateMethod
ResolveMethod(IEnumerable
<MethodInfo
> candidates
)
65 List
<CandidateMethod
> applicable
= FindApplicableMethods(candidates
);
66 if (applicable
.Count
== 0) return null;
67 if (applicable
.Count
== 1) return applicable
[0];
69 List
<CandidateMethod
> dataPreserving
= applicable
.FindAll(DoesNotRequireConversions
);
70 if (dataPreserving
.Count
> 0) return BestMethod(dataPreserving
);
71 return BestMethod(applicable
);
74 private static bool DoesNotRequireConversions(CandidateMethod candidate
)
76 return candidate
.DoesNotRequireConversions
;
79 private CandidateMethod
BestMethod(List
<CandidateMethod
> applicable
)
81 applicable
.Sort(BetterCandidate
);
82 return applicable
[applicable
.Count
- 1];
85 private static int TotalScore(CandidateMethod c1
)
88 foreach (int score
in c1
.ArgumentScores
)
95 private int BetterCandidate(CandidateMethod c1
, CandidateMethod c2
)
97 int result
= Math
.Sign(TotalScore(c1
) - TotalScore(c2
));
98 if (result
!= 0) return result
;
99 if (c1
.VarArgs
&& !c2
.VarArgs
) return -1;
100 if (c2
.VarArgs
&& !c1
.VarArgs
) return 1;
102 int minArgumentCount
= Math
.Min(c1
.MinimumArgumentCount
, c2
.MinimumArgumentCount
);
103 for (int i
= 0; i
< minArgumentCount
; ++i
)
105 result
+= MoreSpecificType(c1
.GetParameterType(i
), c2
.GetParameterType(i
));
107 if (result
!= 0) return result
;
109 if (c1
.VarArgs
&& c2
.VarArgs
)
111 return MoreSpecificType(c1
.VarArgsParameterType
, c2
.VarArgsParameterType
);
116 private static int MoreSpecificType(Type t1
, Type t2
)
118 // The less-generic type is more specific
119 int result
= GetTypeGenerity(t2
) - GetTypeGenerity(t1
);
120 if (result
!= 0) return result
;
122 // If both types have the same generity, the deeper-nested type is more specific
123 return GetLogicalTypeDepth(t1
) - GetLogicalTypeDepth(t2
);
126 private static int GetTypeGenerity(Type type
)
128 if (!type
.ContainsGenericParameters
) return 0;
129 return type
.GetGenericArguments().Length
;
132 private static int GetLogicalTypeDepth(Type type
)
134 int depth
= GetTypeDepth(type
);
135 return (type
.IsValueType
) ? depth
- 1 : depth
;
138 private static int GetTypeDepth(Type type
)
142 return GetTypeDepth(type
.GetElementType());
144 else if (type
.IsInterface
)
146 return GetInterfaceDepth(type
);
148 return GetClassDepth(type
);
151 private static int GetClassDepth(Type type
)
154 Type objectType
= typeof(object);
155 while (type
!= objectType
)
157 type
= type
.BaseType
;
163 private static int GetInterfaceDepth(Type type
)
165 Type
[] interfaces
= type
.GetInterfaces();
166 if (interfaces
.Length
> 0)
169 foreach (Type i
in interfaces
)
171 int depth
= GetInterfaceDepth(i
);
172 if (depth
> current
) current
= depth
;
179 private List
<CandidateMethod
> FindApplicableMethods(IEnumerable
<MethodInfo
> candidates
)
181 List
<CandidateMethod
> applicable
= new List
<CandidateMethod
>();
182 foreach (MethodInfo method
in candidates
)
184 CandidateMethod candidateMethod
= IsApplicableMethod(method
);
185 if (null == candidateMethod
) continue;
186 applicable
.Add(candidateMethod
);
191 private CandidateMethod
IsApplicableMethod(MethodInfo method
)
193 ParameterInfo
[] parameters
= method
.GetParameters();
194 bool varargs
= IsVarArgs(parameters
);
195 if (!ValidArgumentCount(parameters
, varargs
)) return null;
197 CandidateMethod candidateMethod
= new CandidateMethod(method
, _arguments
.Length
, varargs
);
198 if (CalculateCandidateScore(candidateMethod
)) return candidateMethod
;
203 private bool ValidArgumentCount(ParameterInfo
[] parameters
, bool varargs
)
207 int minArgumentCount
= parameters
.Length
- 1;
208 return _arguments
.Length
>= minArgumentCount
;
210 return _arguments
.Length
== parameters
.Length
;
213 private bool IsVarArgs(ParameterInfo
[] parameters
)
215 if (parameters
.Length
== 0) return false;
216 return HasParamArrayAttribute(parameters
[parameters
.Length
- 1]);
219 private bool HasParamArrayAttribute(ParameterInfo info
)
221 return info
.IsDefined(typeof(ParamArrayAttribute
), true);
224 private bool CalculateCandidateScore(CandidateMethod candidateMethod
)
226 ParameterInfo
[] parameters
= candidateMethod
.Parameters
;
227 for (int i
= 0; i
< candidateMethod
.MinimumArgumentCount
; ++i
)
229 if (parameters
[i
].IsOut
) return false;
231 if (!CalculateCandidateArgumentScore(candidateMethod
, i
, parameters
[i
].ParameterType
))
237 if (candidateMethod
.VarArgs
)
239 Type varArgItemType
= candidateMethod
.VarArgsParameterType
;
240 for (int i
= candidateMethod
.MinimumArgumentCount
; i
< _arguments
.Length
; ++i
)
242 if (!CalculateCandidateArgumentScore(candidateMethod
, i
, varArgItemType
))
251 private bool CalculateCandidateArgumentScore(CandidateMethod candidateMethod
, int argumentIndex
, Type paramType
)
253 int score
= CandidateMethod
.CalculateArgumentScore(paramType
, _arguments
[argumentIndex
]);
254 if (score
< 0) return false;
256 candidateMethod
.ArgumentScores
[argumentIndex
] = score
;