PageRenderTime 46ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Runtime/Microsoft.Dynamic/Actions/DefaultBinder.MethodCalls.cs

http://github.com/IronLanguages/main
C# | 288 lines | 173 code | 37 blank | 78 comment | 19 complexity | 26f51d2428dcfe2cba52f9646b97bd6f MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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 Apache License, Version 2.0, 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 Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if FEATURE_CORE_DLR
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Diagnostics;
  24. using System.Reflection;
  25. using System.Dynamic;
  26. using Microsoft.Scripting.Actions.Calls;
  27. using Microsoft.Scripting.Generation;
  28. using Microsoft.Scripting.Runtime;
  29. using Microsoft.Scripting.Utils;
  30. using AstUtils = Microsoft.Scripting.Ast.Utils;
  31. namespace Microsoft.Scripting.Actions {
  32. using Ast = Expression;
  33. public partial class DefaultBinder : ActionBinder {
  34. #region Public APIs
  35. /// <summary>
  36. /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are
  37. /// consumed as specified by the CallSignature object.
  38. /// </summary>
  39. /// <param name="resolver">Overload resolver.</param>
  40. /// <param name="targets">The methods to be called</param>
  41. /// <returns>A meta object which results from the call.</returns>
  42. public DynamicMetaObject CallMethod(DefaultOverloadResolver resolver, IList<MethodBase> targets) {
  43. return CallMethod(resolver, targets, BindingRestrictions.Empty, null);
  44. }
  45. /// <summary>
  46. /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are
  47. /// consumed as specified by the CallSignature object.
  48. /// </summary>
  49. /// <param name="resolver">Overload resolver.</param>
  50. /// <param name="targets">The methods to be called</param>
  51. /// <param name="name">The name of the method or null to use the name from targets.</param>
  52. /// <returns>A meta object which results from the call.</returns>
  53. public DynamicMetaObject CallMethod(DefaultOverloadResolver resolver, IList<MethodBase> targets, string name) {
  54. return CallMethod(resolver, targets, BindingRestrictions.Empty, name);
  55. }
  56. /// <summary>
  57. /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are
  58. /// consumed as specified by the CallSignature object.
  59. /// </summary>
  60. /// <param name="resolver">Overload resolver.</param>
  61. /// <param name="targets">The methods to be called</param>
  62. /// <param name="restrictions">Additional restrictions which should be applied to the resulting MetaObject.</param>
  63. /// <returns>A meta object which results from the call.</returns>
  64. public DynamicMetaObject CallMethod(DefaultOverloadResolver resolver, IList<MethodBase> targets, BindingRestrictions restrictions) {
  65. return CallMethod(
  66. resolver,
  67. targets,
  68. restrictions,
  69. null
  70. );
  71. }
  72. /// <summary>
  73. /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are
  74. /// consumed as specified by the CallSignature object.
  75. /// </summary>
  76. /// <param name="resolver">Overload resolver.</param>
  77. /// <param name="targets">The methods to be called</param>
  78. /// <param name="restrictions">Additional restrictions which should be applied to the resulting MetaObject.</param>
  79. /// <param name="name">The name of the method or null to use the name from targets.</param>
  80. /// <returns>A meta object which results from the call.</returns>
  81. public DynamicMetaObject CallMethod(DefaultOverloadResolver resolver, IList<MethodBase> targets, BindingRestrictions restrictions, string name) {
  82. BindingTarget target;
  83. return CallMethod(
  84. resolver,
  85. targets,
  86. restrictions,
  87. name,
  88. NarrowingLevel.None,
  89. NarrowingLevel.All,
  90. out target
  91. );
  92. }
  93. /// <summary>
  94. /// Performs binding against a set of overloaded methods using the specified arguments. The arguments are
  95. /// consumed as specified by the CallSignature object.
  96. /// </summary>
  97. /// <param name="minLevel">TODO.</param>
  98. /// <param name="maxLevel">TODO.</param>
  99. /// <param name="resolver">Overload resolver.</param>
  100. /// <param name="targets">The methods to be called</param>
  101. /// <param name="restrictions">Additional restrictions which should be applied to the resulting MetaObject.</param>
  102. /// <param name="target">The resulting binding target which can be used for producing error information.</param>
  103. /// <param name="name">The name of the method or null to use the name from targets.</param>
  104. /// <returns>A meta object which results from the call.</returns>
  105. public DynamicMetaObject CallMethod(DefaultOverloadResolver resolver, IList<MethodBase> targets, BindingRestrictions restrictions, string name,
  106. NarrowingLevel minLevel, NarrowingLevel maxLevel, out BindingTarget target) {
  107. ContractUtils.RequiresNotNull(resolver, "resolver");
  108. ContractUtils.RequiresNotNullItems(targets, "targets");
  109. ContractUtils.RequiresNotNull(restrictions, "restrictions");
  110. // attempt to bind to an individual method
  111. target = resolver.ResolveOverload(name ?? GetTargetName(targets), targets, minLevel, maxLevel);
  112. if (target.Success) {
  113. // if we succeed make the target for the rule
  114. return new DynamicMetaObject(
  115. target.MakeExpression(),
  116. restrictions.Merge(
  117. MakeSplatTests(resolver.CallType, resolver.Signature, resolver.Arguments).
  118. Merge(target.RestrictedArguments.GetAllRestrictions())
  119. )
  120. );
  121. }
  122. // make an error rule
  123. return MakeInvalidParametersRule(resolver, restrictions, target);
  124. }
  125. internal static string GetTargetName(IList<MethodBase> targets) {
  126. return targets[0].IsConstructor ? targets[0].DeclaringType.Name : targets[0].Name;
  127. }
  128. // TODO: revisit
  129. private DynamicMetaObject MakeInvalidParametersRule(DefaultOverloadResolver binder, BindingRestrictions restrictions, BindingTarget bt) {
  130. var args = binder.Arguments;
  131. BindingRestrictions restriction = MakeSplatTests(binder.CallType, binder.Signature, true, args);
  132. // restrict to the exact type of all parameters for errors
  133. for (int i = 0; i < args.Count; i++) {
  134. args[i] = args[i].Restrict(args[i].GetLimitType());
  135. }
  136. return MakeError(
  137. binder.MakeInvalidParametersError(bt),
  138. restrictions.Merge(BindingRestrictions.Combine(args).Merge(restriction)),
  139. typeof(object)
  140. );
  141. }
  142. #endregion
  143. #region Restriction helpers (TODO: revisit)
  144. private static BindingRestrictions MakeSplatTests(CallTypes callType, CallSignature signature, IList<DynamicMetaObject> args) {
  145. return MakeSplatTests(callType, signature, false, args);
  146. }
  147. /// <summary>
  148. /// Makes test for param arrays and param dictionary parameters.
  149. /// </summary>
  150. private static BindingRestrictions MakeSplatTests(CallTypes callType, CallSignature signature, bool testTypes, IList<DynamicMetaObject> args) {
  151. BindingRestrictions res = BindingRestrictions.Empty;
  152. if (signature.HasListArgument()) {
  153. res = MakeParamsArrayTest(callType, signature, testTypes, args);
  154. }
  155. if (signature.HasDictionaryArgument()) {
  156. res = res.Merge(MakeParamsDictionaryTest(args, testTypes));
  157. }
  158. return res;
  159. }
  160. /// <summary>
  161. /// Pulls out the right argument to build the splat test. MakeParamsTest makes the actual test.
  162. /// </summary>
  163. private static BindingRestrictions MakeParamsArrayTest(CallTypes callType, CallSignature signature, bool testTypes, IList<DynamicMetaObject> args) {
  164. int listIndex = signature.IndexOf(ArgumentType.List);
  165. Debug.Assert(listIndex != -1);
  166. if (callType == CallTypes.ImplicitInstance) {
  167. listIndex++;
  168. }
  169. return MakeParamsTest(args[listIndex], testTypes);
  170. }
  171. /// <summary>
  172. /// Builds the restrictions for calling with a splatted argument array. Ensures that the
  173. /// argument is still an ICollection of object and that it has the same number of arguments.
  174. /// </summary>
  175. private static BindingRestrictions MakeParamsTest(DynamicMetaObject splattee, bool testTypes) {
  176. IList<object> list = splattee.Value as IList<object>;
  177. if (list == null) {
  178. if (splattee.Value == null) {
  179. return BindingRestrictions.GetExpressionRestriction(Ast.Equal(splattee.Expression, AstUtils.Constant(null)));
  180. } else {
  181. return BindingRestrictions.GetTypeRestriction(splattee.Expression, splattee.Value.GetType());
  182. }
  183. }
  184. BindingRestrictions res = BindingRestrictions.GetExpressionRestriction(
  185. Ast.AndAlso(
  186. Ast.TypeIs(splattee.Expression, typeof(IList<object>)),
  187. Ast.Equal(
  188. Ast.Property(
  189. Ast.Convert(splattee.Expression, typeof(IList<object>)),
  190. typeof(ICollection<object>).GetDeclaredProperty("Count")
  191. ),
  192. AstUtils.Constant(list.Count)
  193. )
  194. )
  195. );
  196. if (testTypes) {
  197. for (int i = 0; i < list.Count; i++) {
  198. res = res.Merge(
  199. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(
  200. Ast.Call(
  201. AstUtils.Convert(
  202. splattee.Expression,
  203. typeof(IList<object>)
  204. ),
  205. typeof(IList<object>).GetMethod("get_Item"),
  206. AstUtils.Constant(i)
  207. ),
  208. CompilerHelpers.GetType(list[i])
  209. )
  210. );
  211. }
  212. }
  213. return res;
  214. }
  215. /// <summary>
  216. /// Builds the restrictions for calling with keyword arguments. The restrictions include
  217. /// tests on the individual keys of the dictionary to ensure they have the same names.
  218. /// </summary>
  219. private static BindingRestrictions MakeParamsDictionaryTest(IList<DynamicMetaObject> args, bool testTypes) {
  220. IDictionary dict = (IDictionary)args[args.Count - 1].Value;
  221. IDictionaryEnumerator dictEnum = dict.GetEnumerator();
  222. // verify the dictionary has the same count and arguments.
  223. string[] names = new string[dict.Count];
  224. Type[] types = testTypes ? new Type[dict.Count] : null;
  225. int index = 0;
  226. while (dictEnum.MoveNext()) {
  227. string name = dictEnum.Entry.Key as string;
  228. if (name == null) {
  229. throw ScriptingRuntimeHelpers.SimpleTypeError(String.Format("expected string for dictionary argument got {0}", dictEnum.Entry.Key));
  230. }
  231. names[index] = name;
  232. if (types != null) {
  233. types[index] = CompilerHelpers.GetType(dictEnum.Entry.Value);
  234. }
  235. index++;
  236. }
  237. return BindingRestrictions.GetExpressionRestriction(
  238. Ast.AndAlso(
  239. Ast.TypeIs(args[args.Count - 1].Expression, typeof(IDictionary)),
  240. Ast.Call(
  241. typeof(BinderOps).GetMethod("CheckDictionaryMembers"),
  242. Ast.Convert(args[args.Count - 1].Expression, typeof(IDictionary)),
  243. AstUtils.Constant(names),
  244. testTypes ? AstUtils.Constant(types) : AstUtils.Constant(null, typeof(Type[]))
  245. )
  246. )
  247. );
  248. }
  249. #endregion
  250. }
  251. }