/Microsoft.Scripting/Actions/Calls/MethodCandidate.cs

https://bitbucket.org/stefanrusek/xronos · C# · 307 lines · 216 code · 48 blank · 43 comment · 64 complexity · 70a8bf82b85e3d89b15298c664b9fc34 MD5 · raw file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. using System.Diagnostics;
  22. #if CODEPLEX_40
  23. using System.Dynamic;
  24. #else
  25. using Microsoft.Scripting;
  26. #endif
  27. using System.Text;
  28. using Microsoft.Contracts;
  29. using Microsoft.Scripting.Actions;
  30. using Microsoft.Scripting.Runtime;
  31. using Microsoft.Scripting.Generation;
  32. namespace Microsoft.Scripting.Actions.Calls {
  33. /// <summary>
  34. /// MethodCandidate represents the different possible ways of calling a method or a set of method overloads.
  35. /// A single method can result in multiple MethodCandidates. Some reasons include:
  36. /// - Every optional parameter or parameter with a default value will result in a candidate
  37. /// - The presence of ref and out parameters will add a candidate for languages which want to return the updated values as return values.
  38. /// - ArgumentKind.List and ArgumentKind.Dictionary can result in a new candidate per invocation since the list might be different every time.
  39. ///
  40. /// Each MethodCandidate represents the parameter type for the candidate using ParameterWrapper.
  41. ///
  42. /// Contrast this with MethodTarget which represents the real physical invocation of a method
  43. /// </summary>
  44. class MethodCandidate {
  45. private MethodTarget _target;
  46. private List<ParameterWrapper> _parameters;
  47. private NarrowingLevel _narrowingLevel;
  48. internal MethodCandidate(MethodCandidate previous, NarrowingLevel narrowingLevel) {
  49. this._target = previous.Target;
  50. this._parameters = previous._parameters;
  51. _narrowingLevel = narrowingLevel;
  52. }
  53. internal MethodCandidate(MethodTarget target, List<ParameterWrapper> parameters) {
  54. Debug.Assert(target != null);
  55. _target = target;
  56. _parameters = parameters;
  57. _narrowingLevel = NarrowingLevel.None;
  58. parameters.TrimExcess();
  59. }
  60. public MethodTarget Target {
  61. get { return _target; }
  62. }
  63. public NarrowingLevel NarrowingLevel {
  64. get {
  65. return _narrowingLevel;
  66. }
  67. }
  68. [Confined]
  69. public override string ToString() {
  70. return string.Format("MethodCandidate({0})", Target);
  71. }
  72. internal bool IsApplicable(Type[] types, NarrowingLevel narrowingLevel, List<ConversionResult> conversionResults) {
  73. // attempt to convert each parameter
  74. bool res = true;
  75. for (int i = 0; i < types.Length; i++) {
  76. bool success = _parameters[i].HasConversionFrom(types[i], narrowingLevel);
  77. conversionResults.Add(new ConversionResult(types[i], _parameters[i].Type, i, !success));
  78. res &= success;
  79. }
  80. return res;
  81. }
  82. internal bool IsApplicable(DynamicMetaObject[] objects, NarrowingLevel narrowingLevel, List<ConversionResult> conversionResults) {
  83. // attempt to convert each parameter
  84. bool res = true;
  85. for (int i = 0; i < objects.Length; i++) {
  86. /*if (objects[i].NeedsDeferral) {
  87. conversionResults.Add(new ConversionResult(typeof(Dynamic), _parameters[i].Type, i, false));
  88. } else*/ {
  89. bool success = _parameters[i].HasConversionFrom(objects[i].GetLimitType(), narrowingLevel);
  90. conversionResults.Add(new ConversionResult(objects[i].GetLimitType(), _parameters[i].Type, i, !success));
  91. res &= success;
  92. }
  93. }
  94. return res;
  95. }
  96. internal static Candidate GetPreferredCandidate(MethodCandidate one, MethodCandidate two, CallTypes callType, Type[] actualTypes) {
  97. Candidate cmpParams = ParameterWrapper.GetPreferredParameters(one.Parameters, two.Parameters, actualTypes);
  98. if (cmpParams.Chosen()) {
  99. return cmpParams;
  100. }
  101. Candidate ret = MethodTarget.CompareEquivalentParameters(one.Target, two.Target);
  102. if (ret.Chosen()) {
  103. return ret;
  104. }
  105. if (CompilerHelpers.IsStatic(one.Target.Method) && !CompilerHelpers.IsStatic(two.Target.Method)) {
  106. return callType == CallTypes.ImplicitInstance ? Candidate.Two : Candidate.One;
  107. } else if (!CompilerHelpers.IsStatic(one.Target.Method) && CompilerHelpers.IsStatic(two.Target.Method)) {
  108. return callType == CallTypes.ImplicitInstance ? Candidate.One : Candidate.Two;
  109. }
  110. return Candidate.Equivalent;
  111. }
  112. internal static Candidate GetPreferredCandidate(MethodCandidate one, MethodCandidate two, CallTypes callType, DynamicMetaObject[] actualTypes) {
  113. Candidate cmpParams = ParameterWrapper.GetPreferredParameters(one.Parameters, two.Parameters, actualTypes);
  114. if (cmpParams.Chosen()) {
  115. return cmpParams;
  116. }
  117. Candidate ret = MethodTarget.CompareEquivalentParameters(one.Target, two.Target);
  118. if (ret.Chosen()) {
  119. return ret;
  120. }
  121. if (CompilerHelpers.IsStatic(one.Target.Method) && !CompilerHelpers.IsStatic(two.Target.Method)) {
  122. return callType == CallTypes.ImplicitInstance ? Candidate.Two : Candidate.One;
  123. } else if (!CompilerHelpers.IsStatic(one.Target.Method) && CompilerHelpers.IsStatic(two.Target.Method)) {
  124. return callType == CallTypes.ImplicitInstance ? Candidate.One : Candidate.Two;
  125. }
  126. return Candidate.Equivalent;
  127. }
  128. /// <summary>
  129. /// Builds a new MethodCandidate which takes count arguments and the provided list of keyword arguments.
  130. ///
  131. /// The basic idea here is to figure out which parameters map to params or a dictionary params and
  132. /// fill in those spots w/ extra ParameterWrapper's.
  133. /// </summary>
  134. internal MethodCandidate MakeParamsExtended(ActionBinder binder, int count, string[] names) {
  135. Debug.Assert(BinderHelpers.IsParamsMethod(_target.Method));
  136. List<ParameterWrapper> newParameters = new List<ParameterWrapper>(count);
  137. // keep track of which kw args map to a real argument, and which ones
  138. // map to the params dictionary.
  139. List<string> unusedNames = new List<string>(names);
  140. List<int> unusedNameIndexes = new List<int>();
  141. for (int i = 0; i < unusedNames.Count; i++) {
  142. unusedNameIndexes.Add(i);
  143. }
  144. // if we don't have a param array we'll have a param dict which is type object
  145. ParameterWrapper paramsArrayParameter = null, paramsDictParameter = null;
  146. int paramsArrayIndex = -1, paramsDictIndex = -1;
  147. for (int i = 0; i < _parameters.Count; i++) {
  148. ParameterWrapper parameter = _parameters[i];
  149. if (parameter.IsParamsDict) {
  150. paramsDictParameter = parameter;
  151. paramsDictIndex = i;
  152. } else if (parameter.IsParamsArray) {
  153. paramsArrayParameter = parameter;
  154. paramsArrayIndex = i;
  155. } else {
  156. int j = unusedNames.IndexOf(parameter.Name);
  157. if (j != -1) {
  158. unusedNames.RemoveAt(j);
  159. unusedNameIndexes.RemoveAt(j);
  160. }
  161. newParameters.Add(parameter);
  162. }
  163. }
  164. if (paramsArrayIndex != -1) {
  165. Type elementType = paramsArrayParameter.Type.GetElementType();
  166. bool nonNullItems = CompilerHelpers.ProhibitsNullItems(paramsArrayParameter.ParameterInfo);
  167. while (newParameters.Count < (count - unusedNames.Count)) {
  168. ParameterWrapper param = new ParameterWrapper(binder, paramsArrayParameter.ParameterInfo, elementType, null, nonNullItems, false, false);
  169. newParameters.Insert(System.Math.Min(paramsArrayIndex, newParameters.Count), param);
  170. }
  171. }
  172. if (paramsDictIndex != -1) {
  173. Type elementType = paramsDictParameter.Type.GetElementType();
  174. bool nonNullItems = CompilerHelpers.ProhibitsNullItems(paramsDictParameter.ParameterInfo);
  175. foreach (string si in unusedNames) {
  176. newParameters.Add(new ParameterWrapper(binder, paramsDictParameter.ParameterInfo, typeof(object), si, nonNullItems, false, false));
  177. }
  178. } else if (unusedNames.Count != 0) {
  179. // unbound kw args and no where to put them, can't call...
  180. // TODO: We could do better here because this results in an incorrect arg # error message.
  181. return null;
  182. }
  183. // if we have too many or too few args we also can't call
  184. if (count != newParameters.Count) {
  185. return null;
  186. }
  187. return new MethodCandidate(_target.MakeParamsExtended(count, unusedNames.ToArray(), unusedNameIndexes.ToArray()), newParameters);
  188. }
  189. internal string ToSignatureString(string name, CallTypes callType) {
  190. StringBuilder buf = new StringBuilder(name);
  191. buf.Append("(");
  192. bool isFirstArg = true;
  193. int i = 0;
  194. if (callType == CallTypes.ImplicitInstance) i = 1;
  195. for (; i < _parameters.Count; i++) {
  196. if (isFirstArg) isFirstArg = false;
  197. else buf.Append(", ");
  198. buf.Append(_parameters[i].ToSignatureString());
  199. }
  200. buf.Append(")");
  201. return buf.ToString(); //@todo add helper info for more interesting signatures
  202. }
  203. internal IList<ParameterWrapper> Parameters {
  204. get {
  205. return _parameters;
  206. }
  207. }
  208. internal bool HasParamsDictionary() {
  209. foreach (ParameterWrapper pw in _parameters) {
  210. // can't bind to methods that are params dictionaries, only to their extended forms.
  211. if (pw.IsParamsDict) return true;
  212. }
  213. return false;
  214. }
  215. internal bool TryGetNormalizedArguments<T>(T[] argTypes, string[] names, out T[] args, out CallFailure failure) {
  216. if (names.Length == 0) {
  217. // no named arguments, success!
  218. args = argTypes;
  219. failure = null;
  220. return true;
  221. }
  222. T[] res = new T[argTypes.Length];
  223. Array.Copy(argTypes, res, argTypes.Length - names.Length);
  224. List<string> unboundNames = null;
  225. List<string> duppedNames = null;
  226. for (int i = 0; i < names.Length; i++) {
  227. bool found = false;
  228. for (int j = 0; j < _parameters.Count; j++) {
  229. if (_parameters[j].Name == names[i]) {
  230. found = true;
  231. if (res[j] != null) {
  232. if (duppedNames == null) duppedNames = new List<string>();
  233. duppedNames.Add(names[i]);
  234. } else {
  235. res[j] = argTypes[i + argTypes.Length - names.Length];
  236. }
  237. break;
  238. }
  239. }
  240. if (!found) {
  241. if (unboundNames == null) unboundNames = new List<string>();
  242. unboundNames.Add(names[i]);
  243. }
  244. }
  245. if (unboundNames != null) {
  246. failure = new CallFailure(Target, unboundNames.ToArray(), true);
  247. args = null;
  248. return false;
  249. } else if (duppedNames != null) {
  250. failure = new CallFailure(Target, duppedNames.ToArray(), false);
  251. args = null;
  252. return false;
  253. }
  254. failure = null;
  255. args = res;
  256. return true;
  257. }
  258. }
  259. }