PageRenderTime 772ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMethodGroupBase.cs

https://github.com/Hank923/ironruby
C# | 299 lines | 210 code | 50 blank | 39 comment | 56 complexity | ee3e435b1611f1ac3d30ed7d068c1271 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1
  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. * ironruby@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. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Linq.Expressions;
  20. using System.Reflection;
  21. using IronRuby.Builtins;
  22. using IronRuby.Compiler;
  23. using Microsoft.Scripting.Actions.Calls;
  24. using Microsoft.Scripting.Generation;
  25. using Microsoft.Scripting.Utils;
  26. using Ast = System.Linq.Expressions.Expression;
  27. using AstFactory = IronRuby.Compiler.Ast.AstFactory;
  28. using AstUtils = Microsoft.Scripting.Ast.Utils;
  29. namespace IronRuby.Runtime.Calls {
  30. public enum SelfCallConvention {
  31. SelfIsInstance,
  32. SelfIsParameter,
  33. NoSelf
  34. }
  35. /// <summary>
  36. /// Performs method binding for calling CLR methods.
  37. /// Currently this is used for all builtin libary methods and interop calls to CLR methods
  38. /// </summary>
  39. public abstract class RubyMethodGroupBase : RubyMemberInfo {
  40. // Not protected by a lock. Immutable after initialized.
  41. private MethodBase/*!*/[] _methodBases;
  42. protected RubyMethodGroupBase(MethodBase/*!*/[] methods, RubyMemberFlags flags, RubyModule/*!*/ declaringModule)
  43. : base(flags, declaringModule) {
  44. if (methods != null) {
  45. SetMethodBasesNoLock(methods);
  46. }
  47. }
  48. protected abstract RubyMemberInfo/*!*/ Copy(MethodBase/*!*/[]/*!*/ methods);
  49. internal protected virtual MethodBase/*!*/[]/*!*/ MethodBases {
  50. get { return _methodBases; }
  51. }
  52. internal MethodBase/*!*/[]/*!*/ SetMethodBasesNoLock(MethodBase/*!*/[]/*!*/ methods) {
  53. Debug.Assert(
  54. CollectionUtils.TrueForAll(methods, (method) => method.IsStatic || method.DeclaringType == typeof(Object)) ||
  55. CollectionUtils.TrueForAll(methods, (method) => !method.IsStatic || CompilerHelpers.IsExtension(method) || RubyClass.IsOperator(method))
  56. );
  57. return _methodBases = methods;
  58. }
  59. public override MemberInfo/*!*/[]/*!*/ GetMembers() {
  60. return ArrayUtils.MakeArray(MethodBases);
  61. }
  62. internal abstract SelfCallConvention CallConvention { get; }
  63. internal abstract bool ImplicitProtocolConversions { get; }
  64. public override int GetArity() {
  65. int minParameters = Int32.MaxValue;
  66. int maxParameters = 0;
  67. bool hasOptional = false;
  68. foreach (MethodBase method in MethodBases) {
  69. int mandatory, optional;
  70. RubyOverloadResolver.GetParameterCount(method, method.GetParameters(), CallConvention, out mandatory, out optional);
  71. if (mandatory < minParameters) {
  72. minParameters = mandatory;
  73. }
  74. if (mandatory > maxParameters) {
  75. maxParameters = mandatory;
  76. }
  77. if (!hasOptional && optional > 0) {
  78. hasOptional = true;
  79. }
  80. }
  81. if (hasOptional || maxParameters > minParameters) {
  82. return -minParameters - 1;
  83. } else {
  84. return minParameters;
  85. }
  86. }
  87. #region Generic Parameters, Overloads Selection
  88. public override RubyMemberInfo TryBindGenericParameters(Type/*!*/[]/*!*/ typeArguments) {
  89. var boundMethods = new List<MethodBase>();
  90. foreach (var method in MethodBases) {
  91. if (method.IsGenericMethodDefinition) {
  92. if (typeArguments.Length == method.GetGenericArguments().Length) {
  93. Debug.Assert(!(method is ConstructorInfo));
  94. boundMethods.Add(((MethodInfo)method).MakeGenericMethod(typeArguments));
  95. }
  96. } else if (typeArguments.Length == 0) {
  97. boundMethods.Add(method);
  98. }
  99. }
  100. if (boundMethods.Count == 0) {
  101. return null;
  102. }
  103. return Copy(boundMethods.ToArray());
  104. }
  105. /// <summary>
  106. /// Filters out methods that don't exactly match parameter types except for hidden parameters (RubyContext, RubyScope, site local storage).
  107. /// </summary>
  108. public override RubyMemberInfo TrySelectOverload(Type/*!*/[]/*!*/ parameterTypes) {
  109. var boundMethods = new List<MethodBase>();
  110. foreach (var method in MethodBases) {
  111. if (IsOverloadSignature(method, parameterTypes)) {
  112. boundMethods.Add(method);
  113. }
  114. }
  115. if (boundMethods.Count == 0) {
  116. return null;
  117. }
  118. return Copy(boundMethods.ToArray());
  119. }
  120. private bool IsOverloadSignature(MethodBase/*!*/ method, Type/*!*/[]/*!*/ parameterTypes) {
  121. var infos = method.GetParameters();
  122. int firstInfo = RubyOverloadResolver.GetHiddenParameterCount(method, infos, CallConvention);
  123. if (infos.Length - firstInfo != parameterTypes.Length) {
  124. return false;
  125. }
  126. for (int i = 0; i < parameterTypes.Length; i++) {
  127. if (infos[firstInfo + i].ParameterType != parameterTypes[i]) {
  128. return false;
  129. }
  130. }
  131. return true;
  132. }
  133. #endregion
  134. #region Dynamic Sites
  135. private static Type/*!*/ GetAssociatedSystemType(RubyModule/*!*/ module) {
  136. if (module.IsClass) {
  137. Type type = ((RubyClass)module).GetUnderlyingSystemType();
  138. if (type != null) {
  139. return type;
  140. }
  141. }
  142. return typeof(SuperCallAction);
  143. }
  144. protected virtual MethodBase/*!*/[]/*!*/ GetStaticDispatchMethods(Type/*!*/ baseType, string/*!*/ name) {
  145. return MethodBases;
  146. }
  147. internal override void BuildSuperCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, RubyModule/*!*/ declaringModule) {
  148. Assert.NotNull(declaringModule, metaBuilder, args);
  149. IList<MethodBase> methods;
  150. if (!declaringModule.IsSingletonClass) {
  151. Type associatedType = GetAssociatedSystemType(declaringModule);
  152. methods = GetStaticDispatchMethods(associatedType, name);
  153. } else {
  154. methods = MethodBases;
  155. }
  156. BuildCallNoFlow(metaBuilder, args, name, methods, CallConvention, ImplicitProtocolConversions);
  157. }
  158. internal static BindingTarget/*!*/ ResolveOverload(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name,
  159. IList<MethodBase>/*!*/ overloads, SelfCallConvention callConvention, bool implicitProtocolConversions,
  160. out RubyOverloadResolver/*!*/ resolver) {
  161. resolver = new RubyOverloadResolver(metaBuilder, args, callConvention, implicitProtocolConversions);
  162. var bindingTarget = resolver.ResolveOverload(name, overloads, NarrowingLevel.None, NarrowingLevel.All);
  163. bool calleeHasBlockParam = bindingTarget.Success && HasBlockParameter(bindingTarget.Method);
  164. // At runtime the BlockParam is created with a new RFC instance that identifies the library method frame as
  165. // a proc-converter target of a method unwinder triggered by break from a block.
  166. if (args.Signature.HasBlock) {
  167. var metaBlock = args.GetMetaBlock();
  168. if (metaBlock.Value != null && calleeHasBlockParam) {
  169. Debug.Assert(metaBuilder.BfcVariable != null);
  170. metaBuilder.ControlFlowBuilder = RuleControlFlowBuilder;
  171. }
  172. // Overload resolution might not need to distinguish between nil and non-nil block.
  173. // However, we still do since we construct CF only for non-nil blocks.
  174. if (metaBlock.Value == null) {
  175. metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null)));
  176. } else {
  177. // don't need to test the exact type of the Proc since the code is subclass agnostic:
  178. metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null)));
  179. }
  180. }
  181. // add restrictions used for overload resolution:
  182. resolver.AddArgumentRestrictions(metaBuilder, bindingTarget);
  183. return bindingTarget;
  184. }
  185. /// <summary>
  186. /// Resolves an library method overload and builds call expression.
  187. /// The resulting expression on meta-builder doesn't handle block control flow yet.
  188. /// </summary>
  189. internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name,
  190. IList<MethodBase>/*!*/ overloads, SelfCallConvention callConvention, bool implicitProtocolConversions) {
  191. RubyOverloadResolver resolver;
  192. var bindingTarget = ResolveOverload(metaBuilder, args, name, overloads, callConvention, implicitProtocolConversions, out resolver);
  193. if (bindingTarget.Success) {
  194. metaBuilder.Result = bindingTarget.MakeExpression();
  195. } else {
  196. metaBuilder.SetError(resolver.MakeInvalidParametersError(bindingTarget).Expression);
  197. }
  198. }
  199. /// <summary>
  200. /// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for
  201. /// library method calls with block given in bfcVariable (BlockParam).
  202. /// </summary>
  203. public static void RuleControlFlowBuilder(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) {
  204. if (metaBuilder.Error) {
  205. return;
  206. }
  207. Expression expression = metaBuilder.Result;
  208. Expression bfcVariable = metaBuilder.BfcVariable;
  209. // Method call with proc can invoke control flow that returns an arbitrary value from the call, so we need to type result to Object.
  210. // Otherwise, the result could only be result of targetExpression unless its return type is void.
  211. Type resultType = (bfcVariable != null) ? typeof(object) : expression.Type;
  212. Expression resultVariable;
  213. if (resultType != typeof(void)) {
  214. resultVariable = metaBuilder.GetTemporary(resultType, "#result");
  215. } else {
  216. resultVariable = AstUtils.Empty();
  217. }
  218. if (expression.Type != typeof(void)) {
  219. expression = Ast.Assign(resultVariable, AstUtils.Convert(expression, resultType));
  220. }
  221. // a non-null proc is being passed to the callee:
  222. if (bfcVariable != null) {
  223. ParameterExpression methodUnwinder = metaBuilder.GetTemporary(typeof(MethodUnwinder), "#unwinder");
  224. expression = AstFactory.Block(
  225. Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))),
  226. AstUtils.Try(
  227. expression
  228. ).Filter(methodUnwinder, Methods.IsProcConverterTarget.OpCall(bfcVariable, methodUnwinder),
  229. Ast.Assign(resultVariable, Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField)),
  230. AstUtils.Default(expression.Type)
  231. ).Finally(
  232. Methods.LeaveProcConverter.OpCall(bfcVariable)
  233. ),
  234. resultVariable
  235. );
  236. }
  237. metaBuilder.Result = expression;
  238. }
  239. private static bool HasBlockParameter(MethodBase/*!*/ method) {
  240. foreach (ParameterInfo param in method.GetParameters()) {
  241. if (param.ParameterType == typeof(BlockParam)) {
  242. return true;
  243. }
  244. }
  245. return false;
  246. }
  247. #endregion
  248. }
  249. }