PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/DLR_Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs

https://bitbucket.org/mdavid/dlr
C# | 1326 lines | 1035 code | 249 blank | 42 comment | 266 complexity | 56592a782086f663aa61195ce245d83d MD5 | raw file
  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. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Reflection;
  20. using System.Runtime.CompilerServices;
  21. using System.Runtime.InteropServices;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Generation;
  24. using Microsoft.Scripting.Runtime;
  25. using Microsoft.Scripting.Utils;
  26. using IronRuby.Runtime;
  27. using IronRuby.Runtime.Calls;
  28. using IronRuby.Runtime.Conversions;
  29. namespace IronRuby.Builtins {
  30. using EachSite = Func<CallSite, object, Proc, object>;
  31. [RubyModule(Extends = typeof(IList), Restrictions = ModuleRestrictions.None)]
  32. [Includes(typeof(Enumerable))]
  33. public static class IListOps {
  34. #region Helpers
  35. // MRI: Some operations check frozen flag even if they don't change the array content.
  36. private static void RequireNotFrozen(IList/*!*/ self) {
  37. RubyArray array = self as RubyArray;
  38. if (array != null && array.IsFrozen) {
  39. throw RubyExceptions.CreateObjectFrozenError();
  40. }
  41. }
  42. internal static int NormalizeIndex(IList/*!*/ list, int index) {
  43. return NormalizeIndex(list.Count, index);
  44. }
  45. internal static int NormalizeIndexThrowIfNegative(IList/*!*/ list, int index) {
  46. index = NormalizeIndex(list.Count, index);
  47. if (index < 0) {
  48. throw RubyExceptions.CreateIndexError("index {0} out of array", index);
  49. }
  50. return index;
  51. }
  52. internal static int NormalizeIndex(int count, int index) {
  53. return index < 0 ? index + count : index;
  54. }
  55. internal static bool NormalizeRange(int listCount, ref int start, ref int count) {
  56. start = NormalizeIndex(listCount, start);
  57. if (start < 0 || start > listCount || count < 0) {
  58. return false;
  59. }
  60. if (count != 0) {
  61. count = start + count > listCount ? listCount - start : count;
  62. }
  63. return true;
  64. }
  65. internal static bool NormalizeRange(ConversionStorage<int>/*!*/ fixnumCast, int listCount, Range/*!*/ range, out int begin, out int count) {
  66. begin = Protocols.CastToFixnum(fixnumCast, range.Begin);
  67. int end = Protocols.CastToFixnum(fixnumCast, range.End);
  68. begin = NormalizeIndex(listCount, begin);
  69. if (begin < 0 || begin > listCount) {
  70. count = 0;
  71. return false;
  72. }
  73. end = NormalizeIndex(listCount, end);
  74. count = range.ExcludeEnd ? end - begin : end - begin + 1;
  75. return true;
  76. }
  77. private static bool InRangeNormalized(IList/*!*/ list, ref int index) {
  78. if (index < 0) {
  79. index = index + list.Count;
  80. }
  81. return index >= 0 && index < list.Count;
  82. }
  83. private static IList/*!*/ GetResultRange(CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  84. IList/*!*/ list, int index, int count) {
  85. IList result = CreateResultArray(allocateStorage, list);
  86. int stop = index + count;
  87. for (int i = index; i < stop; i++) {
  88. result.Add(list[i]);
  89. }
  90. return result;
  91. }
  92. private static void InsertRange(IList/*!*/ list, int index, IList/*!*/ items, int start, int count) {
  93. RubyArray array;
  94. List<object> listOfObject;
  95. ICollection<object> collection;
  96. if ((array = list as RubyArray) != null) {
  97. array.InsertRange(index, items, start, count);
  98. } else if ((listOfObject = list as List<object>) != null && ((collection = items as ICollection<object>) != null)) {
  99. listOfObject.InsertRange(index, collection);
  100. } else {
  101. for (int i = 0; i < count; i++) {
  102. list.Insert(index + i, items[start + i]);
  103. }
  104. }
  105. }
  106. internal static void RemoveRange(IList/*!*/ collection, int index, int count) {
  107. if (count <= 1) {
  108. if (count > 0) {
  109. collection.RemoveAt(index);
  110. }
  111. return;
  112. }
  113. List<object> list;
  114. RubyArray array;
  115. if ((array = collection as RubyArray) != null) {
  116. array.RemoveRange(index, count);
  117. } else if ((list = collection as List<object>) != null) {
  118. list.RemoveRange(index, count);
  119. } else {
  120. for (int i = index + count - 1; i >= index; i--) {
  121. collection.RemoveAt(i);
  122. }
  123. }
  124. }
  125. internal static void AddRange(IList/*!*/ collection, IList/*!*/ items) {
  126. int count = items.Count;
  127. if (count <= 1) {
  128. if (count > 0) {
  129. collection.Add(items[0]);
  130. }
  131. return;
  132. }
  133. RubyArray array = collection as RubyArray;
  134. if (array != null) {
  135. array.AddRange(items);
  136. } else {
  137. for (int i = 0; i < count; i++) {
  138. collection.Add(items[i]);
  139. }
  140. }
  141. }
  142. private static IList/*!*/ CreateResultArray(CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage, IList/*!*/ list) {
  143. // RubyArray:
  144. var array = list as RubyArray;
  145. if (array != null) {
  146. return array.CreateInstance();
  147. }
  148. // interop - call a default ctor to get an instance:
  149. var allocate = allocateStorage.GetCallSite("allocate", 0);
  150. var cls = allocateStorage.Context.GetClassOf(list);
  151. var result = allocate.Target(allocate, cls) as IList;
  152. if (result != null) {
  153. return result;
  154. }
  155. throw RubyExceptions.CreateTypeError("{0}#allocate should return IList", cls.Name);
  156. }
  157. internal static IEnumerable<Int32>/*!*/ ReverseEnumerateIndexes(IList/*!*/ collection) {
  158. for (int originalSize = collection.Count, i = originalSize - 1; i >= 0; i--) {
  159. yield return i;
  160. if (collection.Count < originalSize) {
  161. i = originalSize - (originalSize - collection.Count);
  162. originalSize = collection.Count;
  163. }
  164. }
  165. }
  166. #endregion
  167. #region initialize_copy, replace, clear, to_a, to_ary
  168. [RubyMethod("replace")]
  169. [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)]
  170. public static IList/*!*/ Replace(IList/*!*/ self, [NotNull, DefaultProtocol]IList/*!*/ other) {
  171. self.Clear();
  172. AddRange(self, other);
  173. return self;
  174. }
  175. [RubyMethod("clear")]
  176. public static IList Clear(IList/*!*/ self) {
  177. self.Clear();
  178. return self;
  179. }
  180. [RubyMethod("to_a")]
  181. [RubyMethod("to_ary")]
  182. public static RubyArray/*!*/ ToArray(IList/*!*/ self) {
  183. RubyArray list = new RubyArray(self.Count);
  184. foreach (object item in self) {
  185. list.Add(item);
  186. }
  187. return list;
  188. }
  189. #endregion
  190. #region *, +, concat
  191. [RubyMethod("*")]
  192. public static IList/*!*/ Repetition(CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  193. IList/*!*/ self, int repeat) {
  194. if (repeat < 0) {
  195. throw RubyExceptions.CreateArgumentError("negative argument");
  196. }
  197. IList result = CreateResultArray(allocateStorage, self);
  198. RubyArray array = result as RubyArray;
  199. if (array != null) {
  200. array.AddCapacity(self.Count * repeat);
  201. }
  202. for (int i = 0; i < repeat; ++i) {
  203. AddRange(result, self);
  204. }
  205. allocateStorage.Context.TaintObjectBy<IList>(result, self);
  206. return result;
  207. }
  208. [RubyMethod("*")]
  209. public static MutableString Repetition(ConversionStorage<MutableString>/*!*/ tosConversion,
  210. IList/*!*/ self, [NotNull]MutableString/*!*/ separator) {
  211. return Join(tosConversion, self, separator);
  212. }
  213. [RubyMethod("*")]
  214. public static object Repetition(
  215. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  216. ConversionStorage<MutableString>/*!*/ tosConversion,
  217. IList/*!*/ self, [DefaultProtocol, NotNull]Union<MutableString, int> repeat) {
  218. if (repeat.IsFixnum()) {
  219. return Repetition(allocateStorage, self, repeat.Fixnum());
  220. } else {
  221. return Repetition(tosConversion, self, repeat.String());
  222. }
  223. }
  224. [RubyMethod("+")]
  225. public static RubyArray/*!*/ Concatenate(IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  226. RubyArray result = new RubyArray(self.Count + other.Count);
  227. AddRange(result, self);
  228. AddRange(result, other);
  229. return result;
  230. }
  231. [RubyMethod("concat")]
  232. public static IList/*!*/ Concat(IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  233. AddRange(self, other);
  234. return self;
  235. }
  236. [RubyMethod("-")]
  237. public static RubyArray/*!*/ Difference(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage,
  238. IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  239. RubyArray result = new RubyArray();
  240. // cost: (|self| + |other|) * (hash + eql) + dict
  241. var remove = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  242. bool removeNull = false;
  243. foreach (var item in other) {
  244. if (item != null) {
  245. remove[item] = true;
  246. } else {
  247. removeNull = true;
  248. }
  249. }
  250. foreach (var item in self) {
  251. if (!(item != null ? remove.ContainsKey(item) : removeNull)) {
  252. result.Add(item);
  253. }
  254. }
  255. return result;
  256. }
  257. internal static int IndexOf(CallSite<Func<CallSite, object, object, object>>/*!*/ equalitySite, IList/*!*/ self, object item) {
  258. for (int i = 0; i < self.Count; i++) {
  259. if (Protocols.IsTrue(equalitySite.Target(equalitySite, item, self[i]))) {
  260. return i;
  261. }
  262. }
  263. return -1;
  264. }
  265. #endregion
  266. #region ==, <=>, eql?, hash
  267. [RubyMethod("==")]
  268. public static bool Equals(RespondToStorage/*!*/ respondTo, BinaryOpStorage/*!*/ equals, IList/*!*/ self, object other) {
  269. return Protocols.RespondTo(respondTo, other, "to_ary") && Protocols.IsEqual(equals, other, self);
  270. }
  271. [MultiRuntimeAware]
  272. private static RubyUtils.RecursionTracker _EqualsTracker = new RubyUtils.RecursionTracker();
  273. [RubyMethod("==")]
  274. public static bool Equals(BinaryOpStorage/*!*/ equals, IList/*!*/ self, [NotNull]IList/*!*/ other) {
  275. Assert.NotNull(self, other);
  276. if (ReferenceEquals(self, other)) {
  277. return true;
  278. }
  279. if (self.Count != other.Count) {
  280. return false;
  281. }
  282. using (IDisposable handleSelf = _EqualsTracker.TrackObject(self), handleOther = _EqualsTracker.TrackObject(other)) {
  283. if (handleSelf == null && handleOther == null) {
  284. // both arrays went recursive:
  285. return true;
  286. }
  287. var site = equals.GetCallSite("==");
  288. for (int i = 0; i < self.Count; ++i) {
  289. if (!Protocols.IsEqual(site, self[i], other[i])) {
  290. return false;
  291. }
  292. }
  293. }
  294. return true;
  295. }
  296. [MultiRuntimeAware]
  297. private static RubyUtils.RecursionTracker _ComparisonTracker = new RubyUtils.RecursionTracker();
  298. [RubyMethod("<=>")]
  299. public static object Compare(BinaryOpStorage/*!*/ comparisonStorage, IList/*!*/ self, [DefaultProtocol, NotNull]IList/*!*/ other) {
  300. using (IDisposable handleSelf = _ComparisonTracker.TrackObject(self), handleOther = _ComparisonTracker.TrackObject(other)) {
  301. if (handleSelf == null && handleOther == null) {
  302. // both arrays went recursive:
  303. return ScriptingRuntimeHelpers.Int32ToObject(0);
  304. }
  305. int limit = Math.Min(self.Count, other.Count);
  306. var compare = comparisonStorage.GetCallSite("<=>");
  307. for (int i = 0; i < limit; i++) {
  308. object result = compare.Target(compare, self[i], other[i]);
  309. if (!(result is int) || (int)result != 0) {
  310. return result;
  311. }
  312. }
  313. return ScriptingRuntimeHelpers.Int32ToObject(Math.Sign(self.Count - other.Count));
  314. }
  315. }
  316. [RubyMethod("eql?")]
  317. public static bool HashEquals(BinaryOpStorage/*!*/ eqlStorage, IList/*!*/ self, object other) {
  318. return RubyArray.Equals(eqlStorage, self, other);
  319. }
  320. [RubyMethod("hash")]
  321. public static int GetHashCode(UnaryOpStorage/*!*/ hashStorage, ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self) {
  322. return RubyArray.GetHashCode(hashStorage, fixnumCast, self);
  323. }
  324. #endregion
  325. #region slice, [], at
  326. [RubyMethod("[]")]
  327. [RubyMethod("slice")]
  328. public static object GetElement(IList list, [DefaultProtocol]int index) {
  329. return InRangeNormalized(list, ref index) ? list[index] : null;
  330. }
  331. [RubyMethod("[]")]
  332. [RubyMethod("slice")]
  333. public static IList GetElements(CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  334. IList/*!*/ list, [DefaultProtocol]int index, [DefaultProtocol]int count) {
  335. if (!NormalizeRange(list.Count, ref index, ref count)) {
  336. return null;
  337. }
  338. return GetResultRange(allocateStorage, list, index, count);
  339. }
  340. [RubyMethod("[]")]
  341. [RubyMethod("slice")]
  342. public static IList GetElement(ConversionStorage<int>/*!*/ fixnumCast,
  343. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  344. IList array, [NotNull]Range/*!*/ range) {
  345. int start, count;
  346. if (!NormalizeRange(fixnumCast, array.Count, range, out start, out count)) {
  347. return null;
  348. }
  349. return count < 0 ? CreateResultArray(allocateStorage, array) : GetElements(allocateStorage, array, start, count);
  350. }
  351. [RubyMethod("at")]
  352. public static object At(IList/*!*/ self, [DefaultProtocol]int index) {
  353. return GetElement(self, index);
  354. }
  355. #endregion
  356. #region []=
  357. public static void ExpandList(IList list, int index) {
  358. int diff = index - list.Count;
  359. for (int i = 0; i < diff; i++) {
  360. list.Add(null);
  361. }
  362. }
  363. public static void OverwriteOrAdd(IList list, int index, object value) {
  364. if (index < list.Count) {
  365. list[index] = value;
  366. } else {
  367. list.Add(value);
  368. }
  369. }
  370. public static void DeleteItems(IList list, int index, int length) {
  371. if (index >= list.Count) {
  372. ExpandList(list, index);
  373. } else {
  374. // normalize for max length
  375. if (index + length > list.Count) {
  376. length = list.Count - index;
  377. }
  378. if (length == 0) {
  379. RequireNotFrozen(list);
  380. } else {
  381. RemoveRange(list, index, length);
  382. }
  383. }
  384. }
  385. [RubyMethod("[]=")]
  386. public static object SetElement(RubyArray/*!*/ self, [DefaultProtocol]int index, object value) {
  387. index = NormalizeIndexThrowIfNegative(self, index);
  388. if (index >= self.Count) {
  389. self.AddMultiple(index + 1 - self.Count, null);
  390. }
  391. return self[index] = value;
  392. }
  393. [RubyMethod("[]=")]
  394. public static object SetElement(IList/*!*/ self, [DefaultProtocol]int index, object value) {
  395. index = NormalizeIndexThrowIfNegative(self, index);
  396. if (index < self.Count) {
  397. self[index] = value;
  398. } else {
  399. ExpandList(self, index);
  400. self.Add(value);
  401. }
  402. return value;
  403. }
  404. [RubyMethod("[]=")]
  405. public static object SetElement(ConversionStorage<IList>/*!*/ arrayTryCast, IList/*!*/ self,
  406. [DefaultProtocol]int index, [DefaultProtocol]int length, object value) {
  407. if (length < 0) {
  408. throw RubyExceptions.CreateIndexError("negative length ({0})", length);
  409. }
  410. index = NormalizeIndexThrowIfNegative(self, index);
  411. if (value == null) {
  412. DeleteItems(self, index, length);
  413. return null;
  414. }
  415. IList valueAsList = value as IList;
  416. if (valueAsList == null) {
  417. valueAsList = Protocols.TryCastToArray(arrayTryCast, value);
  418. }
  419. if (valueAsList != null && valueAsList.Count == 0) {
  420. DeleteItems(self, index, length);
  421. } else {
  422. if (valueAsList == null) {
  423. Insert(self, index, value);
  424. if (length > 0) {
  425. RemoveRange(self, index + 1, Math.Min(length, self.Count - index - 1));
  426. }
  427. } else {
  428. if (value == self) {
  429. var newList = new object[self.Count];
  430. self.CopyTo(newList, 0);
  431. valueAsList = newList;
  432. }
  433. ExpandList(self, index);
  434. int limit = length > valueAsList.Count ? valueAsList.Count : length;
  435. for (int i = 0; i < limit; i++) {
  436. OverwriteOrAdd(self, index + i, valueAsList[i]);
  437. }
  438. if (length < valueAsList.Count) {
  439. InsertRange(self, index + limit, valueAsList, limit, valueAsList.Count - limit);
  440. } else {
  441. RemoveRange(self, index + limit, Math.Min(length - valueAsList.Count, self.Count - (index + limit)));
  442. }
  443. }
  444. }
  445. return value;
  446. }
  447. [RubyMethod("[]=")]
  448. public static object SetElement(ConversionStorage<IList>/*!*/ arrayTryCast, ConversionStorage<int>/*!*/ fixnumCast,
  449. IList/*!*/ self, [NotNull]Range/*!*/ range, object value) {
  450. int begin = Protocols.CastToFixnum(fixnumCast, range.Begin);
  451. int end = Protocols.CastToFixnum(fixnumCast, range.End);
  452. begin = begin < 0 ? begin + self.Count : begin;
  453. if (begin < 0) {
  454. throw RubyExceptions.CreateRangeError("{0}..{1} out of range", begin, end);
  455. }
  456. end = end < 0 ? end + self.Count : end;
  457. int count = range.ExcludeEnd ? end - begin : end - begin + 1;
  458. return SetElement(arrayTryCast, self, begin, Math.Max(count, 0), value);
  459. }
  460. #endregion
  461. #region &, |
  462. [RubyMethod("&")]
  463. public static RubyArray/*!*/ Intersection(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage,
  464. IList/*!*/ self, [DefaultProtocol]IList/*!*/ other) {
  465. Dictionary<object, bool> items = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  466. RubyArray result = new RubyArray();
  467. // first get the items in the RHS
  468. foreach (object item in other) {
  469. items[item] = true;
  470. }
  471. // now, go through the items in the LHS, adding ones that were also in the RHS
  472. // this ensures that we return the items in the correct order
  473. foreach (object item in self) {
  474. if (items.Remove(item)) {
  475. result.Add(item);
  476. if (items.Count == 0) {
  477. break; // all done
  478. }
  479. }
  480. }
  481. return result;
  482. }
  483. private static void AddUniqueItems(IList/*!*/ list, IList/*!*/ result, Dictionary<object, bool> seen, ref bool nilSeen) {
  484. foreach (object item in list) {
  485. if (item == null) {
  486. if (!nilSeen) {
  487. nilSeen = true;
  488. result.Add(null);
  489. }
  490. continue;
  491. }
  492. if (!seen.ContainsKey(item)) {
  493. seen.Add(item, true);
  494. result.Add(item);
  495. }
  496. }
  497. }
  498. [RubyMethod("|")]
  499. public static RubyArray/*!*/ Union(UnaryOpStorage/*!*/ hashStorage, BinaryOpStorage/*!*/ eqlStorage,
  500. IList/*!*/ self, [DefaultProtocol]IList other) {
  501. var seen = new Dictionary<object, bool>(new EqualityComparer(hashStorage, eqlStorage));
  502. bool nilSeen = false;
  503. var result = new RubyArray();
  504. // Union merges the two arrays, removing duplicates
  505. AddUniqueItems(self, result, seen, ref nilSeen);
  506. AddUniqueItems(other, result, seen, ref nilSeen);
  507. return result;
  508. }
  509. #endregion
  510. #region assoc, rassoc
  511. public static IList GetContainerOf(BinaryOpStorage/*!*/ equals, IList list, int index, object item) {
  512. foreach (object current in list) {
  513. IList subArray = current as IList;
  514. if (subArray != null && subArray.Count > index) {
  515. if (Protocols.IsEqual(equals, subArray[index], item)) {
  516. return subArray;
  517. }
  518. }
  519. }
  520. return null;
  521. }
  522. [RubyMethod("assoc")]
  523. public static IList GetContainerOfFirstItem(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  524. return GetContainerOf(equals, self, 0, item);
  525. }
  526. [RubyMethod("rassoc")]
  527. public static IList/*!*/ GetContainerOfSecondItem(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  528. return GetContainerOf(equals, self, 1, item);
  529. }
  530. #endregion
  531. #region collect!, map!, compact, compact!
  532. [RubyMethod("collect!")]
  533. [RubyMethod("map!")]
  534. public static object CollectInPlace(BlockParam block, IList/*!*/ self) {
  535. Assert.NotNull(self);
  536. if (self.Count > 0 && block == null) {
  537. throw RubyExceptions.NoBlockGiven();
  538. }
  539. int i = 0;
  540. while (i < self.Count) {
  541. object result;
  542. if (block.Yield(self[i], out result)) {
  543. return result;
  544. }
  545. self[i] = result;
  546. i++;
  547. }
  548. return self;
  549. }
  550. [RubyMethod("compact")]
  551. public static IList/*!*/ Compact(CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage, IList/*!*/ self) {
  552. IList result = CreateResultArray(allocateStorage, self);
  553. foreach (object item in self) {
  554. if (item != null) {
  555. result.Add(item);
  556. }
  557. }
  558. allocateStorage.Context.TaintObjectBy<IList>(result, self);
  559. return result;
  560. }
  561. [RubyMethod("compact!")]
  562. public static IList CompactInPlace(IList/*!*/ self) {
  563. RequireNotFrozen(self);
  564. bool changed = false;
  565. int i = 0;
  566. while (i < self.Count) {
  567. if (self[i] == null) {
  568. changed = true;
  569. self.RemoveAt(i);
  570. } else {
  571. i++;
  572. }
  573. }
  574. return changed ? self : null;
  575. }
  576. #endregion
  577. #region delete, delete_at, reject, reject!
  578. public static bool Remove(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  579. int i = 0;
  580. bool removed = false;
  581. while (i < self.Count) {
  582. if (Protocols.IsEqual(equals, self[i], item)) {
  583. self.RemoveAt(i);
  584. removed = true;
  585. } else {
  586. i++;
  587. }
  588. }
  589. return removed;
  590. }
  591. [RubyMethod("delete")]
  592. public static object Delete(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  593. return Remove(equals, self, item) ? item : null;
  594. }
  595. [RubyMethod("delete")]
  596. public static object Delete(BinaryOpStorage/*!*/ equals, BlockParam block, IList/*!*/ self, object item) {
  597. bool removed = Remove(equals, self, item);
  598. if (!removed && block != null) {
  599. object result;
  600. block.Yield(out result);
  601. return result;
  602. }
  603. return removed ? item : null;
  604. }
  605. [RubyMethod("delete_at")]
  606. public static object DeleteAt(IList/*!*/ self, [DefaultProtocol]int index) {
  607. index = index < 0 ? index + self.Count : index;
  608. if (index < 0 || index >= self.Count) {
  609. return null;
  610. }
  611. object result = GetElement(self, index);
  612. self.RemoveAt(index);
  613. return result;
  614. }
  615. [RubyMethod("delete_if")]
  616. public static object DeleteIf(BlockParam block, IList/*!*/ self) {
  617. bool changed, jumped;
  618. DeleteIf(block, self, out changed, out jumped);
  619. return self;
  620. }
  621. [RubyMethod("reject!")]
  622. public static object RejectInPlace(BlockParam block, IList/*!*/ self) {
  623. bool changed, jumped;
  624. object result = DeleteIf(block, self, out changed, out jumped);
  625. return jumped ? result : changed ? self : null;
  626. }
  627. [RubyMethod("reject")]
  628. public static object Reject(CallSiteStorage<EachSite>/*!*/ each, CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  629. BlockParam predicate, IList/*!*/ self) {
  630. IList result = CreateResultArray(allocateStorage, self);
  631. if (predicate == null && self.Count > 0) {
  632. throw RubyExceptions.NoBlockGiven();
  633. }
  634. for (int i = 0; i < self.Count; i++) {
  635. object item = self[i];
  636. object blockResult;
  637. if (predicate.Yield(item, out blockResult)) {
  638. return blockResult;
  639. }
  640. if (RubyOps.IsFalse(blockResult)) {
  641. result.Add(item);
  642. }
  643. }
  644. return result;
  645. }
  646. private static object DeleteIf(BlockParam block, IList/*!*/ self, out bool changed, out bool jumped) {
  647. changed = false;
  648. jumped = false;
  649. if (block == null && self.Count > 0) {
  650. throw RubyExceptions.NoBlockGiven();
  651. }
  652. RequireNotFrozen(self);
  653. // TODO: if block jumps the array is not modified:
  654. int i = 0;
  655. while (i < self.Count) {
  656. object result;
  657. if (block.Yield(self[i], out result)) {
  658. jumped = true;
  659. return result;
  660. }
  661. if (RubyOps.IsTrue(result)) {
  662. changed = true;
  663. self.RemoveAt(i);
  664. } else {
  665. i++;
  666. }
  667. }
  668. return null;
  669. }
  670. #endregion
  671. #region each, each_index
  672. [RubyMethod("each")]
  673. public static object Each(BlockParam block, IList/*!*/ self) {
  674. if (self.Count > 0 && block == null) {
  675. throw RubyExceptions.NoBlockGiven();
  676. }
  677. for (int i = 0; i < self.Count; i++) {
  678. object result;
  679. if (block.Yield(self[i], out result)) {
  680. return result;
  681. }
  682. }
  683. return self;
  684. }
  685. [RubyMethod("each_index")]
  686. public static object EachIndex(BlockParam block, IList/*!*/ self) {
  687. if (self.Count > 0 && block == null) {
  688. throw RubyExceptions.NoBlockGiven();
  689. }
  690. int i = 0;
  691. while (i < self.Count) {
  692. object result;
  693. if (block.Yield(i, out result)) {
  694. return result;
  695. }
  696. i++;
  697. }
  698. return self;
  699. }
  700. #endregion
  701. #region fetch
  702. [RubyMethod("fetch")]
  703. public static object Fetch(
  704. ConversionStorage<int>/*!*/ fixnumCast,
  705. BlockParam outOfRangeValueProvider,
  706. IList/*!*/ list,
  707. object/*!*/ index,
  708. [Optional]object defaultValue) {
  709. int convertedIndex = Protocols.CastToFixnum(fixnumCast, index);
  710. if (InRangeNormalized(list, ref convertedIndex)) {
  711. return list[convertedIndex];
  712. }
  713. if (outOfRangeValueProvider != null) {
  714. if (defaultValue != Missing.Value) {
  715. fixnumCast.Context.ReportWarning("block supersedes default value argument");
  716. }
  717. object result;
  718. outOfRangeValueProvider.Yield(index, out result);
  719. return result;
  720. }
  721. if (defaultValue == Missing.Value) {
  722. throw RubyExceptions.CreateIndexError("index {0} out of array", convertedIndex);
  723. }
  724. return defaultValue;
  725. }
  726. #endregion
  727. #region fill
  728. [RubyMethod("fill")]
  729. public static IList/*!*/ Fill(IList/*!*/ self, object obj, [DefaultParameterValue(0)]int start) {
  730. // Note: Array#fill(obj, start) is not equivalent to Array#fill(obj, start, 0)
  731. // (as per MRI behavior, the latter can expand the array if start > length, but the former doesn't)
  732. start = Math.Max(0, NormalizeIndex(self, start));
  733. for (int i = start; i < self.Count; i++) {
  734. self[i] = obj;
  735. }
  736. return self;
  737. }
  738. [RubyMethod("fill")]
  739. public static IList/*!*/ Fill(IList/*!*/ self, object obj, int start, int length) {
  740. // Note: Array#fill(obj, start) is not equivalent to Array#fill(obj, start, 0)
  741. // (as per MRI behavior, the latter can expand the array if start > length, but the former doesn't)
  742. start = Math.Max(0, NormalizeIndex(self, start));
  743. ExpandList(self, Math.Min(start, start + length));
  744. for (int i = 0; i < length; i++) {
  745. OverwriteOrAdd(self, start + i, obj);
  746. }
  747. return self;
  748. }
  749. [RubyMethod("fill")]
  750. public static IList/*!*/ Fill(ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self, object obj, object start, [DefaultParameterValue(null)]object length) {
  751. int startFixnum = (start == null) ? 0 : Protocols.CastToFixnum(fixnumCast, start);
  752. if (length == null) {
  753. return Fill(self, obj, startFixnum);
  754. } else {
  755. return Fill(self, obj, startFixnum, Protocols.CastToFixnum(fixnumCast, length));
  756. }
  757. }
  758. [RubyMethod("fill")]
  759. public static IList/*!*/ Fill(ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self, object obj, [NotNull]Range/*!*/ range) {
  760. int begin = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.Begin));
  761. int end = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.End));
  762. int length = Math.Max(0, end - begin + (range.ExcludeEnd ? 0 : 1));
  763. return Fill(self, obj, begin, length);
  764. }
  765. [RubyMethod("fill")]
  766. public static object Fill([NotNull]BlockParam/*!*/ block, IList/*!*/ self, [DefaultParameterValue(0)]int start) {
  767. start = Math.Max(0, NormalizeIndex(self, start));
  768. for (int i = start; i < self.Count; i++) {
  769. object result;
  770. if (block.Yield(i, out result)) {
  771. return result;
  772. }
  773. self[i] = result;
  774. }
  775. return self;
  776. }
  777. [RubyMethod("fill")]
  778. public static object Fill([NotNull]BlockParam/*!*/ block, IList/*!*/ self, int start, int length) {
  779. start = Math.Max(0, NormalizeIndex(self, start));
  780. ExpandList(self, Math.Min(start, start + length));
  781. for (int i = start; i < start + length; i++) {
  782. object result;
  783. if (block.Yield(i, out result)) {
  784. return result;
  785. }
  786. OverwriteOrAdd(self, i, result);
  787. }
  788. return self;
  789. }
  790. [RubyMethod("fill")]
  791. public static object Fill(ConversionStorage<int>/*!*/ fixnumCast, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, object start, [DefaultParameterValue(null)]object length) {
  792. int startFixnum = (start == null) ? 0 : Protocols.CastToFixnum(fixnumCast, start);
  793. if (length == null) {
  794. return Fill(block, self, startFixnum);
  795. } else {
  796. return Fill(block, self, startFixnum, Protocols.CastToFixnum(fixnumCast, length));
  797. }
  798. }
  799. [RubyMethod("fill")]
  800. public static object Fill(ConversionStorage<int>/*!*/ fixnumCast, [NotNull]BlockParam/*!*/ block, IList/*!*/ self, [NotNull]Range/*!*/ range) {
  801. int begin = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.Begin));
  802. int end = NormalizeIndex(self, Protocols.CastToFixnum(fixnumCast, range.End));
  803. int length = Math.Max(0, end - begin + (range.ExcludeEnd ? 0 : 1));
  804. return Fill(block, self, begin, length);
  805. }
  806. #endregion
  807. #region first, last
  808. [RubyMethod("first")]
  809. public static object First(IList/*!*/ self) {
  810. return self.Count == 0 ? null : self[0];
  811. }
  812. [RubyMethod("first")]
  813. public static IList/*!*/ First(IList/*!*/ self, [DefaultProtocol]int count) {
  814. if (count < 0) {
  815. throw RubyExceptions.CreateArgumentError("negative array size (or size too big)");
  816. }
  817. if (count > self.Count) {
  818. count = self.Count;
  819. }
  820. return new RubyArray(self, 0, count);
  821. }
  822. [RubyMethod("last")]
  823. public static object Last(IList/*!*/ self) {
  824. return self.Count == 0 ? null : self[self.Count - 1];
  825. }
  826. [RubyMethod("last")]
  827. public static IList/*!*/ Last(IList/*!*/ self, [DefaultProtocol]int count) {
  828. if (count < 0) {
  829. throw RubyExceptions.CreateArgumentError("negative array size (or size too big)");
  830. }
  831. if (count > self.Count) {
  832. count = self.Count;
  833. }
  834. return new RubyArray(self, self.Count - count, count);
  835. }
  836. #endregion
  837. #region flatten, flatten!
  838. private static int IndexOfList(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ list, int start, out IList listItem) {
  839. for (int i = start; i < list.Count; i++) {
  840. listItem = Protocols.TryCastToArray(tryToAry, list[i]);
  841. if (listItem != null) {
  842. return i;
  843. }
  844. }
  845. listItem = null;
  846. return -1;
  847. }
  848. /// <summary>
  849. /// Enumerates all items of the list recursively - if there are any items convertible to IList the items of that lists are enumerated as well.
  850. /// Returns null if there are no nested lists and so the list can be enumerated using a standard enumerator.
  851. /// </summary>
  852. public static IEnumerable<object> EnumerateRecursively(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ list, Func<IList, object>/*!*/ loopDetected) {
  853. IList nested;
  854. int nestedIndex = IndexOfList(tryToAry, list, 0, out nested);
  855. if (nestedIndex == -1) {
  856. return null;
  857. }
  858. return EnumerateRecursively(tryToAry, list, list, nested, nestedIndex, loopDetected);
  859. }
  860. private static IEnumerable<object>/*!*/ EnumerateRecursively(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ root,
  861. IList/*!*/ list, IList nested, int nestedIndex, Func<IList, object>/*!*/ loopDetected) {
  862. var worklist = new Stack<KeyValuePair<IList, int>>();
  863. var recursionPath = new Dictionary<object, bool>(ReferenceEqualityComparer.Instance);
  864. int start = 0;
  865. while (true) {
  866. if (nestedIndex >= 0) {
  867. // push a workitem for the items following the nested list:
  868. if (nestedIndex < list.Count - 1) {
  869. worklist.Push(new KeyValuePair<IList, int>(list, nestedIndex + 1));
  870. }
  871. // yield items preceding the nested list:
  872. for (int i = start; i < nestedIndex; i++) {
  873. yield return list[i];
  874. }
  875. // push a workitem for the nested list:
  876. if (nestedIndex != -1) {
  877. worklist.Push(new KeyValuePair<IList, int>(nested, 0));
  878. }
  879. } else {
  880. // there is no nested list => yield all remaining items:
  881. for (int i = start; i < list.Count; i++) {
  882. yield return list[i];
  883. }
  884. }
  885. // finished nested list workitem:
  886. if (start == 0) {
  887. recursionPath.Remove(list);
  888. }
  889. next:
  890. if (worklist.Count == 0) {
  891. break;
  892. }
  893. var workitem = worklist.Pop();
  894. list = workitem.Key;
  895. start = workitem.Value;
  896. // starting nested workitem:
  897. if (start == 0) {
  898. if (ReferenceEquals(nested, root) || recursionPath.ContainsKey(nested)) {
  899. yield return loopDetected(nested);
  900. goto next;
  901. } else {
  902. recursionPath.Add(nested, true);
  903. }
  904. }
  905. nestedIndex = IndexOfList(tryToAry, list, start, out nested);
  906. }
  907. }
  908. [RubyMethod("flatten")]
  909. public static IList/*!*/ Flatten(CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage, ConversionStorage<IList>/*!*/ tryToAry,
  910. IList/*!*/ self) {
  911. IList result = CreateResultArray(allocateStorage, self);
  912. var recEnum = EnumerateRecursively(tryToAry, self, (_) => { throw RubyExceptions.CreateArgumentError("tried to flatten recursive array"); });
  913. if (recEnum != null) {
  914. foreach (var item in recEnum) {
  915. result.Add(item);
  916. }
  917. } else {
  918. AddRange(result, self);
  919. }
  920. return result;
  921. }
  922. [RubyMethod("flatten!")]
  923. public static IList FlattenInPlace(ConversionStorage<IList>/*!*/ tryToAry, IList/*!*/ self) {
  924. IList nested;
  925. int nestedIndex = IndexOfList(tryToAry, self, 0, out nested);
  926. if (nestedIndex == -1) {
  927. return null;
  928. }
  929. var remaining = new object[self.Count - nestedIndex];
  930. for (int i = 0, j = nestedIndex; i < remaining.Length; i++) {
  931. remaining[i] = self[j++];
  932. }
  933. bool isRecursive = false;
  934. var recEnum = EnumerateRecursively(tryToAry, self, remaining, nested, 0, (rec) => {
  935. isRecursive = true;
  936. return rec;
  937. });
  938. // rewrite items following the first nested list (including the list):
  939. int itemCount = nestedIndex;
  940. foreach (var item in recEnum) {
  941. if (itemCount < self.Count) {
  942. self[itemCount] = item;
  943. } else {
  944. self.Add(item);
  945. }
  946. itemCount++;
  947. }
  948. // empty arrays can make the list shrink:
  949. while (self.Count > itemCount) {
  950. self.RemoveAt(self.Count - 1);
  951. }
  952. if (isRecursive) {
  953. throw RubyExceptions.CreateArgumentError("tried to flatten recursive array");
  954. }
  955. return self;
  956. }
  957. #endregion
  958. #region include?, index, rindex
  959. [RubyMethod("include?")]
  960. public static bool Include(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  961. return Index(equals, self, item) != null;
  962. }
  963. [RubyMethod("index")]
  964. public static object Index(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  965. for (int i = 0; i < self.Count; ++i) {
  966. if (Protocols.IsEqual(equals, self[i], item)) {
  967. return i;
  968. }
  969. }
  970. return null;
  971. }
  972. [RubyMethod("rindex")]
  973. public static object ReverseIndex(BinaryOpStorage/*!*/ equals, IList/*!*/ self, object item) {
  974. foreach (int index in IListOps.ReverseEnumerateIndexes(self)) {
  975. if (Protocols.IsEqual(equals, self[index], item)) {
  976. return index;
  977. }
  978. }
  979. return null;
  980. }
  981. #endregion
  982. #region indexes, indices, values_at
  983. [RubyMethod("indexes")]
  984. [RubyMethod("indices")]
  985. public static object Indexes(ConversionStorage<int>/*!*/ fixnumCast,
  986. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  987. IList/*!*/ self, params object[]/*!*/ values) {
  988. fixnumCast.Context.ReportWarning("Array#indexes and Array#indices are deprecated; use Array#values_at");
  989. RubyArray result = new RubyArray();
  990. for (int i = 0; i < values.Length; ++i) {
  991. Range range = values[i] as Range;
  992. if (range != null) {
  993. IList fragment = GetElement(fixnumCast, allocateStorage, self, range);
  994. if (fragment != null) {
  995. result.Add(fragment);
  996. }
  997. } else {
  998. result.Add(GetElement(self, Protocols.CastToFixnum(fixnumCast, values[i])));
  999. }
  1000. }
  1001. return result;
  1002. }
  1003. [RubyMethod("values_at")]
  1004. public static RubyArray/*!*/ ValuesAt(ConversionStorage<int>/*!*/ fixnumCast,
  1005. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  1006. IList/*!*/ self, params object[]/*!*/ values) {
  1007. RubyArray result = new RubyArray();
  1008. for (int i = 0; i < values.Length; i++) {
  1009. Range range = values[i] as Range;
  1010. if (range != null) {
  1011. int start, count;
  1012. if (!NormalizeRange(fixnumCast, self.Count, range, out start, out count)) {
  1013. continue;
  1014. }
  1015. if (count > 0) {
  1016. result.AddRange(GetElements(allocateStorage, self, start, count));
  1017. if (start + count >= self.Count) {
  1018. result.Add(null);
  1019. }
  1020. }
  1021. } else {
  1022. result.Add(GetElement(self, Protocols.CastToFixnum(fixnumCast, values[i])));
  1023. }
  1024. }
  1025. return result;
  1026. }
  1027. #endregion
  1028. #region join, to_s, inspect
  1029. private static void JoinRecursive(ConversionStorage<MutableString>/*!*/ tosConversion,
  1030. IList/*!*/ list, List<MutableString/*!*/>/*!*/ parts,
  1031. ref bool? isBinary, ref bool taint, ref Dictionary<object, bool> seen) {
  1032. foreach (object item in list) {
  1033. IList listItem;
  1034. if ((listItem = item as IList) != null) {
  1035. bool _;
  1036. if (ReferenceEquals(listItem, list) || seen != null && seen.TryGetValue(listItem, out _)) {
  1037. parts.Add(RubyUtils.InfiniteRecursionMarker);
  1038. } else {
  1039. if (seen == null) {
  1040. seen = new Dictionary<object, bool>(ReferenceEqualityComparer.Instance);
  1041. }
  1042. seen.Add(listItem, true);
  1043. JoinRecursive(tosConversion, listItem, parts, ref isBinary, ref taint, ref seen);
  1044. seen.Remove(listItem);
  1045. taint |= tosConversion.Context.IsObjectTainted(listItem);
  1046. }
  1047. } else if (item != null) {
  1048. var tosSite = tosConversion.Site;
  1049. if (tosSite == null) {
  1050. tosSite = tosConversion.GetSite(ConvertToSAction.Make(tosConversion.Context));
  1051. }
  1052. var strItem = tosSite.Target(tosSite, item);
  1053. parts.Add(strItem);
  1054. taint |= strItem.IsTainted;
  1055. isBinary = isBinary.HasValue ? (isBinary | strItem.IsBinary) : strItem.IsBinary;
  1056. } else {
  1057. parts.Add(null);
  1058. }
  1059. }
  1060. }
  1061. public static MutableString/*!*/ Join(ConversionStorage<MutableString>/*!*/ tosConversion, IList/*!*/ self, MutableString/*!*/ separator) {
  1062. var parts = new List<MutableString>(self.Count);
  1063. bool partTainted = false;
  1064. bool? isBinary = (separator != null) ? separator.IsBinary : (bool?)null;
  1065. Dictionary<object, bool> seen = null;
  1066. JoinRecursive(tosConversion, self, parts, ref isBinary, ref partTainted, ref seen);
  1067. if (parts.Count == 0) {
  1068. return MutableString.CreateEmpty();
  1069. }
  1070. if (separator != null && separator.IsBinary != isBinary && !separator.IsAscii()) {
  1071. isBinary = true;
  1072. }
  1073. MutableString any = separator;
  1074. int length = (separator != null) ? (isBinary.HasValue && isBinary.Value ? separator.GetByteCount() : separator.GetCharCount()) * (parts.Count - 1) : 0;
  1075. foreach (MutableString part in parts) {
  1076. if (part != null) {
  1077. length += (isBinary.HasValue && isBinary.Value) ? part.GetByteCount() : part.GetCharCount();