PageRenderTime 39ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_Main/Languages/Ruby/Ruby/Runtime/Calls/RubyOverloadResolver.cs

#
C# | 824 lines | 590 code | 146 blank | 88 comment | 172 complexity | 05848169a1ceb75df0c68f91463a310d MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  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. * ironruby@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 !CLR2
  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.Dynamic;
  25. using System.Reflection;
  26. using System.Text;
  27. using IronRuby.Builtins;
  28. using IronRuby.Compiler;
  29. using IronRuby.Runtime.Conversions;
  30. using Microsoft.Scripting.Actions.Calls;
  31. using Microsoft.Scripting.Generation;
  32. using Microsoft.Scripting.Runtime;
  33. using Microsoft.Scripting.Utils;
  34. using AstUtils = Microsoft.Scripting.Ast.Utils;
  35. namespace IronRuby.Runtime.Calls {
  36. using Ast = Expression;
  37. public sealed class RubyOverloadResolver : OverloadResolver {
  38. private readonly CallArguments/*!*/ _args;
  39. private readonly MetaObjectBuilder/*!*/ _metaBuilder;
  40. private readonly SelfCallConvention _callConvention;
  41. //
  42. // We want to perform Ruby protocol conversions when binding to CLR methods.
  43. // However, some Ruby libraries don't allow protocol conversions on their parameters.
  44. // Hence in libraries protocol conversions should only be performed when DefaultProtocol attribute is present.
  45. // This flag is set if protocol conversions should be applied on all parameters regardless of DefaultProtocol attribute.
  46. // This also implies a different narrowing level of the protocol conversions for library methods and other CLR methods
  47. // (see Converter.CanConvertFrom).
  48. //
  49. private readonly bool _implicitProtocolConversions;
  50. private int _firstRestrictedArg;
  51. private int _lastSplattedArg;
  52. private ParameterExpression _listVariable;
  53. private IList _list;
  54. // An optional list of assumptions upon arguments that were made during resolution.
  55. private List<Key<int, NarrowingLevel, Expression>> _argumentAssumptions;
  56. internal RubyContext/*!*/ Context {
  57. get { return _args.RubyContext; }
  58. }
  59. internal Expression/*!*/ ScopeExpression {
  60. get { return _args.MetaScope.Expression; }
  61. }
  62. internal Expression/*!*/ ContextExpression {
  63. get { return _args.MetaContext.Expression; }
  64. }
  65. internal RubyOverloadResolver(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, SelfCallConvention callConvention,
  66. bool implicitProtocolConversions)
  67. : base(args.RubyContext.Binder) {
  68. _args = args;
  69. _metaBuilder = metaBuilder;
  70. _callConvention = callConvention;
  71. _implicitProtocolConversions = implicitProtocolConversions;
  72. }
  73. #region Step 1: Special Parameters
  74. protected override bool AllowMemberInitialization(OverloadInfo method) {
  75. return false;
  76. }
  77. /// <summary>
  78. /// We expand params arrays for library methods. Splat operator needs to be used to pass content of an array/list into params array method.
  79. /// </summary>
  80. protected override bool BindToUnexpandedParams(MethodCandidate/*!*/ candidate) {
  81. // TODO: separate flag?
  82. return _implicitProtocolConversions;
  83. }
  84. protected override BitArray MapSpecialParameters(ParameterMapping/*!*/ mapping) {
  85. var method = mapping.Overload;
  86. var infos = method.Parameters;
  87. var special = new BitArray(infos.Count);
  88. // Method signatures SelfCallConvention
  89. // RubyMethod/RubyCtor: [(CallSiteStorage)*, (RubyContext|RubyScope)?, (BlockParam)?, self, args] SelfIsParameter
  90. // static: [(CallSiteStorage)*, (RubyContext|RubyScope)?, (BlockParam)?, args] NoSelf
  91. // instance/extension/op: [self, (CallSiteStorage)*, (RubyContext|RubyScope)?, (BlockParam)?, args] SelfIsInstace
  92. var i = 0;
  93. if (_callConvention == SelfCallConvention.SelfIsInstance) {
  94. if (method.IsStatic) {
  95. Debug.Assert(RubyUtils.IsOperator(method) || method.IsExtension);
  96. // receiver maps to the first parameter:
  97. AddSimpleHiddenMapping(mapping, infos[i], true);
  98. special[i++] = true;
  99. } else {
  100. // receiver maps to the instance (no parameter info represents it):
  101. mapping.AddParameter(new ParameterWrapper(null, method.DeclaringType, null, ParameterBindingFlags.ProhibitNull | ParameterBindingFlags.IsHidden));
  102. mapping.AddInstanceBuilder(new InstanceBuilder(mapping.ArgIndex));
  103. }
  104. } else if (_callConvention == SelfCallConvention.NoSelf) {
  105. // instance methods on Object can be called with arbitrary receiver object including classes (static call):
  106. if (!method.IsStatic && method.DeclaringType == typeof(Object)) {
  107. // insert an InstanceBuilder that doesn't consume any arguments, only inserts the target expression as instance:
  108. mapping.AddInstanceBuilder(new ImplicitInstanceBuilder());
  109. }
  110. }
  111. while (i < infos.Count && infos[i].ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage))) {
  112. mapping.AddBuilder(new RubyCallSiteStorageBuilder(infos[i]));
  113. special[i++] = true;
  114. }
  115. if (i < infos.Count) {
  116. var info = infos[i];
  117. if (info.ParameterType == typeof(RubyScope)) {
  118. mapping.AddBuilder(new RubyScopeArgBuilder(info));
  119. special[i++] = true;
  120. } else if (info.ParameterType == typeof(RubyContext)) {
  121. mapping.AddBuilder(new RubyContextArgBuilder(info));
  122. special[i++] = true;
  123. } else if (method.IsConstructor && info.ParameterType == typeof(RubyClass)) {
  124. mapping.AddBuilder(new RubyClassCtorArgBuilder(info));
  125. special[i++] = true;
  126. }
  127. }
  128. // If the method overload doesn't have a BlockParam parameter, we inject MissingBlockParam parameter and arg builder.
  129. // The parameter is treated as a regular explicit mandatory parameter.
  130. //
  131. // The argument builder provides no value for the actual argument expression, which makes the default binder to skip it
  132. // when emitting a tree for the actual method call (this is necessary since the method doesn't in fact have the parameter).
  133. //
  134. // By injecting the missing block parameter we achieve that all overloads have either BlockParam, [NotNull]BlockParam or
  135. // MissingBlockParam parameter. MissingBlockParam and BlockParam are convertible to each other. Default binder prefers
  136. // those overloads where no conversion needs to happen, which ensures the desired semantics:
  137. //
  138. // conversions with desired priority (the less number the higher priority)
  139. // Parameters: call w/o block call with non-null block call with null block
  140. // (implicit, MBP, ... ) MBP -> MBP (1) BP -> MBP (3) BP -> MBP (2)
  141. // (implicit, BP, ... ) MBP -> BP (2) BP -> BP (2) BP -> BP (1)
  142. // (implicit, BP!, ... ) N/A BP -> BP! (1) N/A
  143. //
  144. if (i < infos.Count && infos[i].ParameterType == typeof(BlockParam)) {
  145. AddSimpleHiddenMapping(mapping, infos[i], mapping.Overload.ProhibitsNull(i));
  146. special[i++] = true;
  147. } else if (i >= infos.Count || infos[i].ParameterType != typeof(BlockParam)) {
  148. mapping.AddBuilder(new MissingBlockArgBuilder(mapping.ArgIndex));
  149. mapping.AddParameter(new ParameterWrapper(null, typeof(MissingBlockParam), null, ParameterBindingFlags.IsHidden));
  150. }
  151. if (_callConvention == SelfCallConvention.SelfIsParameter) {
  152. // Ruby library methods only:
  153. Debug.Assert(method.IsStatic);
  154. Debug.Assert(i < infos.Count);
  155. // receiver maps to the first visible parameter:
  156. AddSimpleHiddenMapping(mapping, infos[i], mapping.Overload.ProhibitsNull(i));
  157. special[i++] = true;
  158. }
  159. return special;
  160. }
  161. private void AddSimpleHiddenMapping(ParameterMapping mapping, ParameterInfo info, bool prohibitNull) {
  162. mapping.AddBuilder(new SimpleArgBuilder(info, info.ParameterType, mapping.ArgIndex, false, false));
  163. mapping.AddParameter(new ParameterWrapper(info, info.ParameterType, null,
  164. ParameterBindingFlags.IsHidden | (prohibitNull ? ParameterBindingFlags.ProhibitNull : 0)
  165. ));
  166. }
  167. internal static int GetHiddenParameterCount(OverloadInfo/*!*/ method, SelfCallConvention callConvention) {
  168. int i = 0;
  169. var infos = method.Parameters;
  170. if (callConvention == SelfCallConvention.SelfIsInstance) {
  171. if (method.IsStatic) {
  172. Debug.Assert(RubyUtils.IsOperator(method) || method.IsExtension);
  173. i++;
  174. }
  175. }
  176. while (i < infos.Count && infos[i].ParameterType.IsSubclassOf(typeof(RubyCallSiteStorage))) {
  177. i++;
  178. }
  179. if (i < infos.Count) {
  180. var info = infos[i];
  181. if (info.ParameterType == typeof(RubyScope)) {
  182. i++;
  183. } else if (info.ParameterType == typeof(RubyContext)) {
  184. i++;
  185. } else if (method.IsConstructor && info.ParameterType == typeof(RubyClass)) {
  186. i++;
  187. }
  188. }
  189. if (i < infos.Count && infos[i].ParameterType == typeof(BlockParam)) {
  190. i++;
  191. }
  192. if (callConvention == SelfCallConvention.SelfIsParameter) {
  193. Debug.Assert(i < infos.Count);
  194. Debug.Assert(method.IsStatic);
  195. i++;
  196. }
  197. return i;
  198. }
  199. internal static void GetParameterCount(OverloadInfo/*!*/ method, SelfCallConvention callConvention, out int mandatory, out int optional) {
  200. mandatory = 0;
  201. optional = 0;
  202. for (int i = GetHiddenParameterCount(method, callConvention); i < method.ParameterCount; i++) {
  203. var info = method.Parameters[i];
  204. if (method.IsParamArray(i)) {
  205. // TODO: indicate splat args separately?
  206. optional++;
  207. } else if (info.IsOutParameter()) {
  208. // Python allows passing of optional "clr.Reference" to capture out parameters
  209. // Ruby should allow similar
  210. optional++;
  211. } else if (info.IsMandatory()) {
  212. mandatory++;
  213. } else {
  214. optional++;
  215. }
  216. }
  217. }
  218. #endregion
  219. #region Step 2: Actual Arguments
  220. private static readonly DynamicMetaObject NullMetaBlockParam =
  221. new DynamicMetaObject(
  222. AstUtils.Constant(null, typeof(BlockParam)),
  223. BindingRestrictions.Empty,
  224. null
  225. );
  226. // Creates actual/normalized arguments: inserts self, expands splats, and inserts rhs arg.
  227. // Adds any restrictions/conditions applied to the arguments to the given meta-builder.
  228. protected override ActualArguments CreateActualArguments(IList<DynamicMetaObject> namedArgs, IList<string> argNames, int preSplatLimit, int postSplatLimit) {
  229. var result = new List<DynamicMetaObject>();
  230. // self (instance):
  231. if (_callConvention == SelfCallConvention.SelfIsInstance) {
  232. result.Add(_args.MetaTarget);
  233. }
  234. if (_args.Signature.HasBlock) {
  235. if (_args.GetBlock() == null) {
  236. // the user explicitly passed nil as a block arg:
  237. result.Add(NullMetaBlockParam);
  238. } else {
  239. // pass BlockParam:
  240. if (_metaBuilder.BfcVariable == null) {
  241. // we add temporary even though we might not us it if the calee doesn't have block param arg:
  242. _metaBuilder.BfcVariable = _metaBuilder.GetTemporary(typeof(BlockParam), "#bfc");
  243. }
  244. result.Add(new DynamicMetaObject(_metaBuilder.BfcVariable, BindingRestrictions.Empty));
  245. }
  246. } else {
  247. // no block passed into a method with a BlockParam:
  248. result.Add(MissingBlockParam.Meta.Instance);
  249. }
  250. // self (parameter):
  251. if (_callConvention == SelfCallConvention.SelfIsParameter) {
  252. result.Add(_args.MetaTarget);
  253. }
  254. // the next argument is the first one for which we use restrictions coming from overload resolution:
  255. _firstRestrictedArg = result.Count;
  256. // hidden args: block, self
  257. int hidden = _callConvention == SelfCallConvention.NoSelf ? 1 : 2;
  258. return CreateActualArguments(result, _metaBuilder, _args, hidden, preSplatLimit, postSplatLimit, out _lastSplattedArg, out _list, out _listVariable);
  259. }
  260. public static IList<DynamicMetaObject/*!*/> NormalizeArguments(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, int minCount, int maxCount) {
  261. int lastSplattedArg;
  262. IList list;
  263. ParameterExpression listVariable;
  264. // 2 hidden arguments: block and self
  265. var actualArgs = CreateActualArguments(new List<DynamicMetaObject>(), metaBuilder, args, 2, maxCount, maxCount,
  266. out lastSplattedArg, out list, out listVariable);
  267. int actualCount = actualArgs.Count + actualArgs.CollapsedCount;
  268. if (actualCount < minCount) {
  269. metaBuilder.SetWrongNumberOfArgumentsError(actualCount, minCount);
  270. return null;
  271. } else if (actualCount > maxCount) {
  272. metaBuilder.SetWrongNumberOfArgumentsError(actualCount, maxCount);
  273. return null;
  274. }
  275. // any collapsed args are out of limits:
  276. return actualArgs.Arguments;
  277. }
  278. private static ActualArguments/*!*/ CreateActualArguments(List<DynamicMetaObject>/*!*/ normalized, MetaObjectBuilder/*!*/ metaBuilder,
  279. CallArguments/*!*/ args, int hidden, int preSplatLimit, int postSplatLimit, out int lastSplattedArg, out IList list, out ParameterExpression listVariable) {
  280. int firstSplattedArg, splatIndex, collapsedArgCount;
  281. // simple arguments:
  282. for (int i = 0; i < args.SimpleArgumentCount; i++) {
  283. normalized.Add(args.GetSimpleMetaArgument(i));
  284. }
  285. // splat argument:
  286. list = null;
  287. listVariable = null;
  288. if (args.Signature.HasSplattedArgument) {
  289. firstSplattedArg = normalized.Count;
  290. int listLength;
  291. var splatted = args.GetSplattedMetaArgument();
  292. list = (IList)splatted.Value;
  293. metaBuilder.AddSplattedArgumentTest(list, splatted.Expression, out listLength, out listVariable);
  294. int i = 0;
  295. while (i < Math.Min(listLength, preSplatLimit - firstSplattedArg)) {
  296. normalized.Add(MakeSplattedItem(list, listVariable, i));
  297. i++;
  298. }
  299. // skip items that are not needed for overload resolution
  300. splatIndex = normalized.Count;
  301. i = Math.Max(i, listLength - (postSplatLimit - (args.Signature.HasRhsArgument ? 1 : 0)));
  302. while (i < listLength) {
  303. normalized.Add(MakeSplattedItem(list, listVariable, i));
  304. i++;
  305. }
  306. collapsedArgCount = listLength - (normalized.Count - firstSplattedArg);
  307. lastSplattedArg = normalized.Count - 1;
  308. } else {
  309. splatIndex = firstSplattedArg = lastSplattedArg = -1;
  310. collapsedArgCount = 0;
  311. }
  312. Debug.Assert(collapsedArgCount >= 0);
  313. // rhs argument:
  314. if (args.Signature.HasRhsArgument) {
  315. normalized.Add(args.GetRhsMetaArgument());
  316. }
  317. return new ActualArguments(
  318. normalized.ToArray(),
  319. DynamicMetaObject.EmptyMetaObjects,
  320. ArrayUtils.EmptyStrings,
  321. hidden,
  322. collapsedArgCount,
  323. firstSplattedArg,
  324. splatIndex
  325. );
  326. }
  327. internal static DynamicMetaObject/*!*/ MakeSplattedItem(IList/*!*/ list, Expression/*!*/ listVariable, int index) {
  328. return DynamicMetaObject.Create(
  329. list[index],
  330. Ast.Call(listVariable, typeof(IList).GetMethod("get_Item"), AstUtils.Constant(index))
  331. );
  332. }
  333. #endregion
  334. #region Step 3: Restrictions
  335. internal void AddArgumentRestrictions(MetaObjectBuilder/*!*/ metaBuilder, BindingTarget/*!*/ bindingTarget) {
  336. var args = GetActualArguments();
  337. var restrictedArgs = bindingTarget.Success ? bindingTarget.RestrictedArguments.GetObjects() : args.Arguments;
  338. for (int i = _firstRestrictedArg; i < restrictedArgs.Count; i++) {
  339. var arg = (bindingTarget.Success ? restrictedArgs[i] : restrictedArgs[i].Restrict(restrictedArgs[i].GetLimitType()));
  340. if (i >= args.FirstSplattedArg && i <= _lastSplattedArg) {
  341. metaBuilder.AddCondition(arg.Restrictions.ToExpression());
  342. } else {
  343. metaBuilder.AddRestriction(arg.Restrictions);
  344. }
  345. }
  346. // Adds condition for collapsed arguments - it is the same whether we succeed or not:
  347. var splatCondition = GetCollapsedArgsCondition();
  348. if (splatCondition != null) {
  349. metaBuilder.AddCondition(splatCondition);
  350. }
  351. if (_argumentAssumptions != null) {
  352. foreach (var assumption in _argumentAssumptions) {
  353. if (assumption.Second == bindingTarget.NarrowingLevel) {
  354. metaBuilder.AddCondition(assumption.Third);
  355. }
  356. }
  357. }
  358. }
  359. #endregion
  360. #region Step 4: Argument Building, Conversions
  361. /// <summary>
  362. /// Returns true if fromArg of type fromType can be assigned to toParameter with a conversion on given narrowing level.
  363. /// </summary>
  364. public override bool CanConvertFrom(Type/*!*/ fromType, DynamicMetaObject fromArg, ParameterWrapper/*!*/ toParameter, NarrowingLevel level) {
  365. var result = Converter.CanConvertFrom(fromArg, fromType, toParameter.Type, toParameter.ProhibitNull, level,
  366. HasExplicitProtocolConversion(toParameter), _implicitProtocolConversions
  367. );
  368. if (result.Assumption != null) {
  369. if (_argumentAssumptions == null) {
  370. _argumentAssumptions = new List<Key<int, NarrowingLevel, Ast>>();
  371. }
  372. if (_argumentAssumptions.FindIndex((k) => k.First == toParameter.ParameterInfo.Position && k.Second == level) < 0) {
  373. _argumentAssumptions.Add(Key.Create(toParameter.ParameterInfo.Position, level, result.Assumption));
  374. }
  375. }
  376. return result.IsConvertible;
  377. }
  378. public override bool CanConvertFrom(ParameterWrapper/*!*/ fromParameter, ParameterWrapper/*!*/ toParameter) {
  379. return Converter.CanConvertFrom(null, fromParameter.Type, toParameter.Type, toParameter.ProhibitNull, NarrowingLevel.None, false, false).IsConvertible;
  380. }
  381. private bool HasExplicitProtocolConversion(ParameterWrapper/*!*/ parameter) {
  382. return
  383. parameter.ParameterInfo != null &&
  384. parameter.ParameterInfo.IsDefined(typeof(DefaultProtocolAttribute), false) &&
  385. !parameter.IsParamsArray; // default protocol doesn't apply on param-array/dict itself, only on the expanded parameters
  386. }
  387. public override Candidate SelectBestConversionFor(DynamicMetaObject/*!*/ arg, ParameterWrapper/*!*/ candidateOne,
  388. ParameterWrapper/*!*/ candidateTwo, NarrowingLevel level) {
  389. Type typeOne = candidateOne.Type;
  390. Type typeTwo = candidateTwo.Type;
  391. Type actualType = arg.GetLimitType();
  392. if (actualType == typeof(DynamicNull)) {
  393. // if nil is passed as a block argument prefers BlockParam over a missing block:
  394. if (typeOne == typeof(BlockParam) && typeTwo == typeof(MissingBlockParam)) {
  395. Debug.Assert(!candidateOne.ProhibitNull);
  396. return Candidate.One;
  397. }
  398. if (typeOne == typeof(MissingBlockParam) && typeTwo == typeof(BlockParam)) {
  399. Debug.Assert(!candidateTwo.ProhibitNull);
  400. return Candidate.Two;
  401. }
  402. } else {
  403. if (typeOne == actualType) {
  404. if (typeTwo == actualType) {
  405. // prefer non-nullable reference type over nullable:
  406. if (!actualType.IsValueType) {
  407. if (candidateOne.ProhibitNull) {
  408. return Candidate.One;
  409. } else if (candidateTwo.ProhibitNull) {
  410. return Candidate.Two;
  411. }
  412. }
  413. } else {
  414. return Candidate.One;
  415. }
  416. } else if (typeTwo == actualType) {
  417. return Candidate.Two;
  418. }
  419. }
  420. // prefer integer type over enum:
  421. if (typeOne.IsEnum && Enum.GetUnderlyingType(typeOne) == typeTwo) {
  422. return Candidate.Two;
  423. }
  424. if (typeTwo.IsEnum && Enum.GetUnderlyingType(typeTwo) == typeOne) {
  425. return Candidate.One;
  426. }
  427. return base.SelectBestConversionFor(arg, candidateOne, candidateTwo, level);
  428. }
  429. public override Expression/*!*/ Convert(DynamicMetaObject/*!*/ metaObject, Type restrictedType, ParameterInfo info, Type/*!*/ toType) {
  430. Expression expr = metaObject.Expression;
  431. Type fromType = restrictedType ?? expr.Type;
  432. // block:
  433. if (fromType == typeof(MissingBlockParam)) {
  434. Debug.Assert(toType == typeof(BlockParam) || toType == typeof(MissingBlockParam));
  435. return AstUtils.Constant(null);
  436. }
  437. if (fromType == typeof(BlockParam) && toType == typeof(MissingBlockParam)) {
  438. return AstUtils.Constant(null);
  439. }
  440. // protocol conversions:
  441. if (info != null && info.IsDefined(typeof(DefaultProtocolAttribute), false)) {
  442. var action = RubyConversionAction.TryGetDefaultConversionAction(Context, toType);
  443. if (action != null) {
  444. // TODO: inline implicit conversions:
  445. return AstUtils.LightDynamic(action, toType, expr);
  446. }
  447. // Do not throw an exception here to allow generic type parameters to be used with D.P. attribute.
  448. // The semantics should be to use DP if available for the current instantiation and ignore it otherwise.
  449. }
  450. if (restrictedType != null) {
  451. if (restrictedType == typeof(DynamicNull)) {
  452. if (!toType.IsValueType || toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
  453. return AstUtils.Constant(null, toType);
  454. } else if (toType == typeof(bool)) {
  455. return AstUtils.Constant(false);
  456. }
  457. }
  458. if (toType.IsAssignableFrom(restrictedType)) {
  459. // expr can be converted to restrictedType, which can be converted toType => we can convert expr to toType:
  460. return AstUtils.Convert(expr, CompilerHelpers.GetVisibleType(toType));
  461. }
  462. // if there is a simple conversion from restricted type, convert the expression to the restricted type and use that conversion:
  463. Type visibleRestrictedType = CompilerHelpers.GetVisibleType(restrictedType);
  464. if (Converter.CanConvertFrom(metaObject, visibleRestrictedType, toType, false, NarrowingLevel.None, false, false).IsConvertible) {
  465. expr = AstUtils.Convert(expr, visibleRestrictedType);
  466. }
  467. }
  468. return Converter.ConvertExpression(expr, toType, _args.RubyContext, _args.MetaContext.Expression, _implicitProtocolConversions);
  469. }
  470. protected override Expression/*!*/ GetSplattedExpression() {
  471. return _listVariable;
  472. }
  473. protected override object GetSplattedItem(int index) {
  474. return _list[index];
  475. }
  476. internal sealed class RubyContextArgBuilder : ArgBuilder {
  477. public RubyContextArgBuilder(ParameterInfo/*!*/ info)
  478. : base(info) {
  479. }
  480. public override int Priority {
  481. get { return -1; }
  482. }
  483. public override int ConsumedArgumentCount {
  484. get { return 0; }
  485. }
  486. protected override Expression ToExpression(OverloadResolver/*!*/ resolver, RestrictedArguments/*!*/ args, bool[]/*!*/ hasBeenUsed) {
  487. return ((RubyOverloadResolver)resolver).ContextExpression;
  488. }
  489. }
  490. internal sealed class RubyCallSiteStorageBuilder : ArgBuilder {
  491. public RubyCallSiteStorageBuilder(ParameterInfo/*!*/ info)
  492. : base(info) {
  493. }
  494. public override int Priority {
  495. get { return -1; }
  496. }
  497. public override int ConsumedArgumentCount {
  498. get { return 0; }
  499. }
  500. protected override Expression ToExpression(OverloadResolver/*!*/ resolver, RestrictedArguments/*!*/ args, bool[]/*!*/ hasBeenUsed) {
  501. return AstUtils.Constant(Activator.CreateInstance(ParameterInfo.ParameterType, ((RubyOverloadResolver)resolver).Context));
  502. }
  503. }
  504. internal sealed class RubyScopeArgBuilder : ArgBuilder {
  505. public RubyScopeArgBuilder(ParameterInfo/*!*/ info)
  506. : base(info) {
  507. }
  508. public override int Priority {
  509. get { return -1; }
  510. }
  511. public override int ConsumedArgumentCount {
  512. get { return 0; }
  513. }
  514. protected override Expression ToExpression(OverloadResolver/*!*/ resolver, RestrictedArguments/*!*/ args, bool[]/*!*/ hasBeenUsed) {
  515. return ((RubyOverloadResolver)resolver).ScopeExpression;
  516. }
  517. }
  518. internal sealed class RubyClassCtorArgBuilder : ArgBuilder {
  519. public RubyClassCtorArgBuilder(ParameterInfo/*!*/ info)
  520. : base(info) {
  521. }
  522. public override int Priority {
  523. get { return -1; }
  524. }
  525. public override int ConsumedArgumentCount {
  526. get { return 0; }
  527. }
  528. protected override Expression ToExpression(OverloadResolver/*!*/ resolver, RestrictedArguments/*!*/ args, bool[]/*!*/ hasBeenUsed) {
  529. return ((RubyOverloadResolver)resolver)._args.TargetExpression;
  530. }
  531. }
  532. internal sealed class ImplicitInstanceBuilder : InstanceBuilder {
  533. public ImplicitInstanceBuilder()
  534. : base(-1) {
  535. }
  536. public override bool HasValue {
  537. get { return true; }
  538. }
  539. public override int ConsumedArgumentCount {
  540. get { return 0; }
  541. }
  542. protected override Expression/*!*/ ToExpression(ref MethodInfo/*!*/ method, OverloadResolver/*!*/ resolver, RestrictedArguments/*!*/ args, bool[]/*!*/ hasBeenUsed) {
  543. return ((RubyOverloadResolver)resolver)._args.TargetExpression;
  544. }
  545. }
  546. internal sealed class MissingBlockArgBuilder : SimpleArgBuilder {
  547. public MissingBlockArgBuilder(int index)
  548. : base(typeof(MissingBlockParam), index, false, false) {
  549. }
  550. public override int Priority {
  551. get { return -1; }
  552. }
  553. public override int ConsumedArgumentCount {
  554. get { return 1; }
  555. }
  556. protected override SimpleArgBuilder/*!*/ Copy(int newIndex) {
  557. return new MissingBlockArgBuilder(newIndex);
  558. }
  559. protected override Expression ToExpression(OverloadResolver/*!*/ resolver, RestrictedArguments/*!*/ args, bool[]/*!*/ hasBeenUsed) {
  560. Debug.Assert(Index < args.Length);
  561. Debug.Assert(Index < hasBeenUsed.Length);
  562. hasBeenUsed[Index] = true;
  563. return null;
  564. }
  565. }
  566. #endregion
  567. #region Step 5: Errors
  568. public override Microsoft.Scripting.Actions.ErrorInfo MakeInvalidParametersError(BindingTarget target) {
  569. Expression exceptionValue;
  570. switch (target.Result) {
  571. case BindingResult.AmbiguousMatch:
  572. exceptionValue = MakeAmbiguousCallError(target);
  573. break;
  574. case BindingResult.IncorrectArgumentCount:
  575. exceptionValue = MakeIncorrectArgumentCountError(target);
  576. break;
  577. case BindingResult.CallFailure:
  578. exceptionValue = MakeCallFailureError(target);
  579. break;
  580. case BindingResult.NoCallableMethod:
  581. exceptionValue = Methods.CreateArgumentsError.OpCall(
  582. AstUtils.Constant(String.Format("Method '{0}' is not callable", target.Name))
  583. );
  584. break;
  585. default:
  586. throw new InvalidOperationException();
  587. }
  588. return Microsoft.Scripting.Actions.ErrorInfo.FromException(exceptionValue);
  589. }
  590. private Expression MakeAmbiguousCallError(BindingTarget target) {
  591. StringBuilder sb = new StringBuilder(string.Format("Found multiple methods for '{0}': ", target.Name));
  592. string outerComma = "";
  593. foreach (MethodCandidate candidate in target.AmbiguousMatches) {
  594. IList<ParameterWrapper> parameters = candidate.GetParameters();
  595. string innerComma = "";
  596. sb.Append(outerComma);
  597. sb.Append(target.Name);
  598. sb.Append('(');
  599. foreach (var param in parameters) {
  600. if (!param.IsHidden) {
  601. sb.Append(innerComma);
  602. sb.Append(Binder.GetTypeName(param.Type));
  603. if (param.ProhibitNull) {
  604. sb.Append('!');
  605. }
  606. innerComma = ", ";
  607. }
  608. }
  609. sb.Append(')');
  610. outerComma = ", ";
  611. }
  612. return Methods.MakeAmbiguousMatchError.OpCall(AstUtils.Constant(sb.ToString()));
  613. }
  614. private Expression MakeIncorrectArgumentCountError(BindingTarget target) {
  615. IList<int> available = target.ExpectedArgumentCount;
  616. int expected;
  617. if (available.Count > 0) {
  618. int minGreater = Int32.MaxValue;
  619. int maxLesser = Int32.MinValue;
  620. int max = Int32.MinValue;
  621. foreach (int arity in available) {
  622. if (arity > target.ActualArgumentCount) {
  623. minGreater = Math.Min(minGreater, arity);
  624. } else {
  625. maxLesser = Math.Max(maxLesser, arity);
  626. }
  627. max = Math.Max(max, arity);
  628. }
  629. expected = (target.ActualArgumentCount < maxLesser ? maxLesser : Math.Min(minGreater, max));
  630. } else {
  631. // no overload is callable:
  632. expected = 0;
  633. }
  634. return Methods.MakeWrongNumberOfArgumentsError.OpCall(AstUtils.Constant(target.ActualArgumentCount), AstUtils.Constant(expected));
  635. }
  636. private Expression MakeCallFailureError(BindingTarget target) {
  637. foreach (CallFailure cf in target.CallFailures) {
  638. switch (cf.Reason) {
  639. case CallFailureReason.ConversionFailure:
  640. foreach (ConversionResult cr in cf.ConversionResults) {
  641. if (cr.Failed) {
  642. if (typeof(Proc).IsAssignableFrom(cr.To)) {
  643. return Methods.CreateArgumentsErrorForProc.OpCall(AstUtils.Constant(cr.GetArgumentTypeName(Binder)));
  644. }
  645. Debug.Assert(typeof(BlockParam).IsSealed);
  646. if (cr.To == typeof(BlockParam)) {
  647. return Methods.CreateArgumentsErrorForMissingBlock.OpCall();
  648. }
  649. string toType;
  650. if (cr.To.IsGenericType && cr.To.GetGenericTypeDefinition() == typeof(Union<,>)) {
  651. var g = cr.To.GetGenericArguments();
  652. toType = Binder.GetTypeName(g[0]) + " or " + Binder.GetTypeName(g[1]);
  653. } else {
  654. toType = Binder.GetTypeName(cr.To);
  655. }
  656. return Methods.CreateTypeConversionError.OpCall(
  657. AstUtils.Constant(cr.GetArgumentTypeName(Binder)),
  658. AstUtils.Constant(toType)
  659. );
  660. }
  661. }
  662. break;
  663. case CallFailureReason.TypeInference:
  664. // TODO: Display generic parameters so it's clear what we couldn't infer.
  665. return Methods.CreateArgumentsError.OpCall(
  666. AstUtils.Constant(String.Format("generic arguments could not be infered for method '{0}'", target.Name))
  667. );
  668. case CallFailureReason.DuplicateKeyword:
  669. case CallFailureReason.UnassignableKeyword:
  670. default:
  671. throw new InvalidOperationException();
  672. }
  673. }
  674. throw new InvalidOperationException();
  675. }
  676. #endregion
  677. }
  678. }