PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/Ruby/Ruby/Runtime/Calls/BlockDispatcher.cs

http://github.com/IronLanguages/main
C# | 445 lines | 370 code | 48 blank | 27 comment | 27 complexity | d5cc14e85bb36a52d57c492fde8d2670 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. * 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 FEATURE_CORE_DLR
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections.Generic;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Actions;
  24. using Microsoft.Scripting.Runtime;
  25. using Microsoft.Scripting.Utils;
  26. using System.Reflection;
  27. using IronRuby.Builtins;
  28. using System.Collections.ObjectModel;
  29. using System.Collections;
  30. using System.Diagnostics;
  31. namespace IronRuby.Runtime.Calls {
  32. using Ast = Expression;
  33. using AstFactory = IronRuby.Compiler.Ast.AstFactory;
  34. using BlockCallTarget0 = Func<BlockParam, object, object>;
  35. using BlockCallTarget1 = Func<BlockParam, object, object, object>;
  36. using BlockCallTarget2 = Func<BlockParam, object, object, object, object>;
  37. using BlockCallTarget3 = Func<BlockParam, object, object, object, object, object>;
  38. using BlockCallTarget4 = Func<BlockParam, object, object, object, object, object, object>;
  39. using BlockCallTargetN = Func<BlockParam, object, object[], object>;
  40. using BlockCallTargetProcN = Func<BlockParam, object, object[], Proc, object>;
  41. using BlockCallTargetUnsplatN = Func<BlockParam, object, object[], RubyArray, object>;
  42. using BlockCallTargetUnsplatProcN = Func<BlockParam, object, object[], RubyArray, Proc, object>;
  43. [Flags]
  44. public enum BlockSignatureAttributes {
  45. None = 0,
  46. // {|..., &b|}
  47. HasProcParameter = 1,
  48. // {|...,*,...|}
  49. HasUnsplatParameter = 2, // TODO: 1.9: arity < 0 iff the block has unsplat => we can remove signature attributes enum
  50. // bits 31..3 store arity (might be different from formal parameter count)
  51. }
  52. internal abstract class BlockDispatcher<T> : BlockDispatcher where T : class {
  53. protected T _block;
  54. public BlockDispatcher(BlockSignatureAttributes attributesAndArity, string sourcePath, int sourceLine)
  55. : base(attributesAndArity, sourcePath, sourceLine) {
  56. }
  57. public override Delegate/*!*/ Method {
  58. get { return (Delegate)(object)_block; }
  59. }
  60. internal override BlockDispatcher/*!*/ SetMethod(object/*!*/ method) {
  61. // Note: this might potentially be executed by multiple threads. So we can assert that _block == null here.
  62. // It's ok if the delegate is overwritten multiple times since all the target methods are equivalent.
  63. _block = (T)method;
  64. return this;
  65. }
  66. }
  67. internal abstract class BlockDispatcherN<T> : BlockDispatcher<T> where T : class {
  68. protected readonly int _parameterCount;
  69. public override int ParameterCount { get { return _parameterCount; } }
  70. public BlockDispatcherN(int parameterCount, BlockSignatureAttributes attributesAndArity, string sourcePath, int sourceLine)
  71. : base(attributesAndArity, sourcePath, sourceLine) {
  72. _parameterCount = parameterCount;
  73. }
  74. protected object[]/*!*/ MakeArray(object arg1) {
  75. var array = new object[_parameterCount];
  76. array[0] = arg1;
  77. return array;
  78. }
  79. protected object[]/*!*/ MakeArray(object arg1, object arg2) {
  80. var array = new object[_parameterCount];
  81. array[0] = arg1;
  82. array[1] = arg2;
  83. return array;
  84. }
  85. protected object[]/*!*/ MakeArray(object arg1, object arg2, object arg3) {
  86. var array = new object[_parameterCount];
  87. array[0] = arg1;
  88. array[1] = arg2;
  89. array[2] = arg3;
  90. return array;
  91. }
  92. protected object[]/*!*/ MakeArray(object arg1, object arg2, object arg3, object arg4) {
  93. var array = new object[_parameterCount];
  94. array[0] = arg1;
  95. array[1] = arg2;
  96. array[2] = arg3;
  97. array[3] = arg4;
  98. return array;
  99. }
  100. }
  101. public abstract class BlockDispatcher {
  102. // position of the block definition (opening brace):
  103. private readonly string _sourcePath;
  104. private readonly int _sourceLine;
  105. private readonly BlockSignatureAttributes _attributesAndArity;
  106. public bool HasUnsplatParameter {
  107. get { return (_attributesAndArity & BlockSignatureAttributes.HasUnsplatParameter) != 0; }
  108. }
  109. public bool HasProcParameter {
  110. get { return (_attributesAndArity & BlockSignatureAttributes.HasProcParameter) != 0; }
  111. }
  112. public int Arity {
  113. get { return ((int)_attributesAndArity >> 2); }
  114. }
  115. public static BlockSignatureAttributes MakeAttributes(BlockSignatureAttributes attributes, int arity) {
  116. return attributes | (BlockSignatureAttributes)(arity << 2);
  117. }
  118. // Doesn't include unsplat parameter.
  119. // Includes anonymous parameter.
  120. public abstract int ParameterCount { get; }
  121. public abstract Delegate/*!*/ Method { get; }
  122. internal abstract BlockDispatcher/*!*/ SetMethod(object/*!*/ method);
  123. public string SourcePath { get { return _sourcePath; } }
  124. public int SourceLine { get { return _sourceLine; } }
  125. public abstract object Invoke(BlockParam/*!*/ param, object self, Proc procArg);
  126. public abstract object InvokeNoAutoSplat(BlockParam/*!*/ param, object self, Proc procArg, object arg1);
  127. public abstract object Invoke(BlockParam/*!*/ param, object self, Proc procArg, object arg1);
  128. public abstract object Invoke(BlockParam/*!*/ param, object self, Proc procArg, object arg1, object arg2);
  129. public abstract object Invoke(BlockParam/*!*/ param, object self, Proc procArg, object arg1, object arg2, object arg3);
  130. public abstract object Invoke(BlockParam/*!*/ param, object self, Proc procArg, object arg1, object arg2, object arg3, object arg4);
  131. public abstract object Invoke(BlockParam/*!*/ param, object self, Proc procArg, object[]/*!*/ args);
  132. public abstract object InvokeSplat(BlockParam/*!*/ param, object self, Proc procArg, IList/*!*/ splattee);
  133. public abstract object InvokeSplat(BlockParam/*!*/ param, object self, Proc procArg, object arg1, IList/*!*/ splattee);
  134. public abstract object InvokeSplat(BlockParam/*!*/ param, object self, Proc procArg, object arg1, object arg2, IList/*!*/ splattee);
  135. public abstract object InvokeSplat(BlockParam/*!*/ param, object self, Proc procArg, object arg1, object arg2, object arg3, IList/*!*/ splattee);
  136. public abstract object InvokeSplat(BlockParam/*!*/ param, object self, Proc procArg, object arg1, object arg2, object arg3, object arg4, IList/*!*/ splattee);
  137. public abstract object InvokeSplat(BlockParam/*!*/ param, object self, Proc procArg, object[]/*!*/ args, IList/*!*/ splattee);
  138. public abstract object InvokeSplatRhs(BlockParam/*!*/ param, object self, Proc procArg, object[]/*!*/ args, IList/*!*/ splattee, object rhs);
  139. internal const int MaxBlockArity = 4;
  140. internal const int HiddenParameterCount = 2;
  141. internal BlockDispatcher(BlockSignatureAttributes attributesAndArity, string sourcePath, int sourceLine) {
  142. _attributesAndArity = attributesAndArity;
  143. _sourcePath = sourcePath;
  144. _sourceLine = sourceLine;
  145. }
  146. internal static BlockDispatcher/*!*/ Create(int parameterCount, BlockSignatureAttributes attributesAndArity, string sourcePath, int sourceLine) {
  147. if ((attributesAndArity & BlockSignatureAttributes.HasUnsplatParameter) == 0) {
  148. if ((attributesAndArity & BlockSignatureAttributes.HasProcParameter) == 0) {
  149. switch (parameterCount) {
  150. case 0: return new BlockDispatcher0(attributesAndArity, sourcePath, sourceLine);
  151. case 1: return new BlockDispatcher1(attributesAndArity, sourcePath, sourceLine);
  152. case 2: return new BlockDispatcher2(attributesAndArity, sourcePath, sourceLine);
  153. case 3: return new BlockDispatcher3(attributesAndArity, sourcePath, sourceLine);
  154. case 4: return new BlockDispatcher4(attributesAndArity, sourcePath, sourceLine);
  155. default: return new BlockDispatcherN(parameterCount, attributesAndArity, sourcePath, sourceLine);
  156. }
  157. } else {
  158. return new BlockDispatcherProcN(parameterCount, attributesAndArity, sourcePath, sourceLine);
  159. }
  160. } else {
  161. if ((attributesAndArity & BlockSignatureAttributes.HasProcParameter) == 0) {
  162. return new BlockDispatcherUnsplatN(parameterCount, attributesAndArity, sourcePath, sourceLine);
  163. } else {
  164. return new BlockDispatcherUnsplatProcN(parameterCount, attributesAndArity, sourcePath, sourceLine);
  165. }
  166. }
  167. }
  168. internal static LambdaExpression/*!*/ CreateLambda(Expression body, string name, ICollection<ParameterExpression> parameters,
  169. int parameterCount, BlockSignatureAttributes attributes) {
  170. if ((attributes & BlockSignatureAttributes.HasUnsplatParameter) == 0) {
  171. if ((attributes & BlockSignatureAttributes.HasProcParameter) == 0) {
  172. switch (parameterCount) {
  173. case 0: return Ast.Lambda<BlockCallTarget0>(body, name, parameters);
  174. case 1: return Ast.Lambda<BlockCallTarget1>(body, name, parameters);
  175. case 2: return Ast.Lambda<BlockCallTarget2>(body, name, parameters);
  176. case 3: return Ast.Lambda<BlockCallTarget3>(body, name, parameters);
  177. case 4: return Ast.Lambda<BlockCallTarget4>(body, name, parameters);
  178. default: return Ast.Lambda<BlockCallTargetN>(body, name, parameters);
  179. }
  180. } else {
  181. return Ast.Lambda<BlockCallTargetProcN>(body, name, parameters);
  182. }
  183. } else {
  184. if ((attributes & BlockSignatureAttributes.HasProcParameter) == 0) {
  185. return Ast.Lambda<BlockCallTargetUnsplatN>(body, name, parameters);
  186. } else {
  187. return Ast.Lambda<BlockCallTargetUnsplatProcN>(body, name, parameters);
  188. }
  189. }
  190. }
  191. private static void CopyArgumentsFromSplattee(object[]/*!*/ args, int initializedArgCount, int parameterCount,
  192. out int nextArg, out int nextItem, IList/*!*/ splattee) {
  193. int i = Math.Min(initializedArgCount, parameterCount);
  194. int j = 0;
  195. while (i < parameterCount && j < splattee.Count) {
  196. args[i++] = splattee[j++];
  197. }
  198. nextArg = i;
  199. nextItem = j;
  200. }
  201. // Expects first "initializeArgCount" slots of "args" array initialized with actual argument values
  202. // and fills the rest by splatting "splattee". The size of the array "args" is the number of formal parameters the block takes.
  203. internal static object[]/*!*/ CopyArgumentsFromSplattee(object[]/*!*/ args, int initializedArgCount, IList/*!*/ splattee) {
  204. int nextArg, nextItem;
  205. CopyArgumentsFromSplattee(args, initializedArgCount, args.Length, out nextArg, out nextItem, splattee);
  206. return args;
  207. }
  208. internal static void CreateArgumentsFromSplattee(int parameterCount, out int nextArg, out int nextItem, ref object[]/*!*/ args, IList/*!*/ splattee) {
  209. // the args array is passed to the block, we need at least space for all explicit parameters:
  210. int originalLength = args.Length;
  211. if (args.Length < parameterCount) {
  212. Array.Resize(ref args, parameterCount);
  213. }
  214. CopyArgumentsFromSplattee(args, originalLength, parameterCount, out nextArg, out nextItem, splattee);
  215. }
  216. internal static object[]/*!*/ CreateArgumentsFromSplatteeAndRhs(int parameterCount, object[]/*!*/ args, IList/*!*/ splattee, object rhs) {
  217. int nextArg, nextItem;
  218. // the args array is passed to the block, we need at least space for all explicit parameters:
  219. CreateArgumentsFromSplattee(parameterCount, out nextArg, out nextItem, ref args, splattee);
  220. if (nextArg < args.Length) {
  221. args[nextArg++] = rhs;
  222. }
  223. return args;
  224. }
  225. #if OBSOLETE
  226. private Expression/*!*/ AddWarning(Expression/*!*/ codeContextExpression, Expression/*!*/ expression) {
  227. Assert.NotNull(codeContextExpression, expression);
  228. // do not report warning if the only parameter is a nested left value:
  229. if (FirstArgumentIsNestedLValue) {
  230. return expression;
  231. }
  232. return Methods.MultipleValuesForBlockParameterWarning", codeContextExpression, expression);
  233. }
  234. private void SetCallRuleArguments(
  235. Expression/*!*/ blockParameterExpression, // special arg #0
  236. Expression/*!*/ selfParameterExpression, // special arg #1
  237. CallArguments/*!*/ args, // user args
  238. Expression/*!*/ codeContextExpression,
  239. MetaObjectBuilder/*!*/ rule,
  240. ArgsBuilder/*!*/ actualArgs) {
  241. // mandatory args:
  242. actualArgs.Add(blockParameterExpression);
  243. actualArgs.Add(selfParameterExpression);
  244. int parameterIndex = 0;
  245. // mimics CompoundLeftValue.TransformWrite //
  246. // L(1,-)?
  247. bool leftOneNone = OptionalParamCount == 1 && !HasParamsArray;
  248. // L(0,*)?
  249. bool leftNoneSplat = OptionalParamCount == 0 && HasParamsArray;
  250. // R(0,*)?
  251. bool rightNoneSplat = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.List;
  252. // R(1,-)?
  253. bool rightOneNone = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.Simple
  254. || args.Signature.IsSimple && args.Length == 1;
  255. // R(1,*)?
  256. bool rightOneSplat = !args.Signature.IsSimple && args.Length == 2 &&
  257. args.GetArgumentKind(0) == ArgumentKind.Simple &&
  258. args.GetArgumentKind(1) == ArgumentKind.List;
  259. // R(0,-)?
  260. bool rightNoneNone = args.Length == 0;
  261. if (leftOneNone) {
  262. Expression rvalue;
  263. if (rightOneNone) {
  264. // simple assignment
  265. rvalue = args.Expressions[parameterIndex];
  266. } else if (rightOneSplat && TestEmptyList(rule, args.Values[parameterIndex + 1], args.Expressions[parameterIndex + 1])) {
  267. // simple assignment if the splatted value is an empty array:
  268. rvalue = args.Expressions[parameterIndex];
  269. } else if (rightNoneNone) {
  270. // nil assignment
  271. rvalue = AddWarning(codeContextExpression, AstUtils.Constant(null));
  272. } else if (rightNoneSplat) {
  273. // Splat(RHS[*]):
  274. rvalue = MakeArgumentSplatWithWarning(rule, args.Values[parameterIndex], args.Expressions[parameterIndex], codeContextExpression);
  275. } else {
  276. // more than one argument -> pack to an array + warning
  277. // MakeArray(RHS) + SplatAppend(RHS*):
  278. List<Expression> arguments = new List<Expression>();
  279. AddBlockArguments(rule, arguments, args, parameterIndex);
  280. rvalue = AddWarning(codeContextExpression, ArgsBuilder.MakeArgsArray(arguments));
  281. }
  282. actualArgs.Add(rvalue);
  283. } else {
  284. // R(0,*) || R(1,-) && !L(0,*) ==> CompoundLeftValue.TransformWrite does Unsplat, MakeArray otherwise.
  285. //
  286. // However, we are not constructing a materalized resulting array (contrary to CompoundLeftValue.TransformWrite).
  287. // The resulting array is comprised of slots on the stack (loaded to the formal parameters of the block #1, ..., #n).
  288. // Therefore, we effectively need to take items of imaginary Unsplat's result and put them into the actualArgs as arguments.
  289. //
  290. // Unsplat of x makes an array containing x if x is not an array, otherwise it returns x.
  291. // So, we just need to take elements of x and push them onto the stack.
  292. //
  293. List<Expression> arguments = new List<Expression>();
  294. if (rightNoneSplat) {
  295. ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], false);
  296. } else if (rightOneNone && !leftNoneSplat) {
  297. ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], true);
  298. } else {
  299. AddBlockArguments(rule, arguments, args, parameterIndex);
  300. }
  301. actualArgs.AddRange(arguments);
  302. }
  303. actualArgs.AddForEachMissingArgument(delegate() { return AstUtils.Constant(null); });
  304. if (HasParamsArray) {
  305. actualArgs.AddParamsArray();
  306. }
  307. }
  308. private bool TestEmptyList(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter) {
  309. int listLength;
  310. ParameterExpression listVariable;
  311. return ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable) && listLength == 0;
  312. }
  313. private Expression/*!*/ MakeArgumentSplatWithWarning(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter,
  314. Expression/*!*/ codeContextExpression) {
  315. int listLength;
  316. ParameterExpression listVariable;
  317. if (ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable)) {
  318. if (listLength == 0) {
  319. // return nil argument + Warning
  320. return AddWarning(codeContextExpression, AstUtils.Constant(null));
  321. } else if (listLength == 1) {
  322. // return the only item of the array:
  323. return Ast.Call(
  324. listVariable,
  325. typeof(IList).GetMethod("get_Item"),
  326. AstUtils.Constant(0)
  327. );
  328. } else {
  329. // return the array itself + Warning:
  330. return AddWarning(codeContextExpression, parameter);
  331. }
  332. } else {
  333. // not an array, return the value:
  334. return parameter;
  335. }
  336. }
  337. private Expression/*!*/ MakeArgumentUnsplat(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter) {
  338. int listLength;
  339. ParameterExpression listVariable;
  340. if (ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable)) {
  341. // an array, return:
  342. return parameter;
  343. } else {
  344. // not an array, wrap:
  345. return AstFactory.OptimizedOpCall("MakeArray", parameter);
  346. }
  347. }
  348. private void AddBlockArguments(MetaObjectBuilder/*!*/ rule, List<Expression>/*!*/ actualArgs, CallArguments/*!*/ args, int parameterIndex) {
  349. while (parameterIndex < args.Length) {
  350. switch (args.GetArgumentKind(parameterIndex)) {
  351. case ArgumentKind.Simple:
  352. actualArgs.Add(args.Expressions[parameterIndex]);
  353. break;
  354. case ArgumentKind.List:
  355. ArgsBuilder.SplatListToArguments(rule, actualArgs, args.Values[parameterIndex], args.Expressions[parameterIndex], false);
  356. break;
  357. case ArgumentKind.Instance:
  358. case ArgumentKind.Block:
  359. default:
  360. throw new NotImplementedException();
  361. }
  362. parameterIndex++;
  363. }
  364. }
  365. #endif
  366. }
  367. }