PageRenderTime 1512ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyArray.cs

https://github.com/Catweazle/ironruby
C# | 415 lines | 291 code | 89 blank | 35 comment | 17 complexity | 3afb51b3e1651c87fbab6a58f1acf692 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using IronRuby.Runtime;
  20. using Microsoft.Scripting;
  21. using Microsoft.Scripting.Utils;
  22. using IronRuby.Runtime.Calls;
  23. namespace IronRuby.Builtins {
  24. /// <summary>
  25. /// Implements Ruby array.
  26. /// Not thread safe (even when frozen).
  27. /// </summary>
  28. [DebuggerDisplay("{GetDebugView()}")]
  29. public partial class RubyArray : IList<object>, IList, IRubyObjectState, IDuplicable {
  30. private readonly List<object>/*!*/ _content;
  31. // The lowest bit is tainted flag.
  32. // The version is set to FrozenVersion when the string is frozen. FrozenVersion is the maximum version, so any update to the version
  33. // triggers an OverflowException, which we convert to InvalidOperationException.
  34. private uint _versionAndFlags;
  35. private const uint IsTaintedFlag = 1;
  36. private const int FlagCount = 1;
  37. private const uint FlagsMask = (1U << FlagCount) - 1;
  38. private const uint VersionMask = ~FlagsMask;
  39. private const uint FrozenVersion = VersionMask;
  40. #region Construction
  41. public RubyArray() {
  42. _content = new List<object>();
  43. }
  44. public RubyArray(int capacity) {
  45. _content = new List<object>(capacity);
  46. }
  47. public RubyArray(IEnumerable<object>/*!*/ collection) {
  48. _content = new List<object>(collection);
  49. }
  50. public RubyArray(IEnumerable/*!*/ collection)
  51. : this(CollectionUtils.ToEnumerable<object>(collection)) {
  52. }
  53. public RubyArray(IList/*!*/ list, int start, int count) {
  54. ContractUtils.RequiresNotNull(list, "list");
  55. ContractUtils.RequiresArrayRange(list.Count, start, count, "start", "count");
  56. var data = new List<object>();
  57. for (int i = 0; i < count; i++) {
  58. data.Add(list[start + i]);
  59. }
  60. _content = data;
  61. }
  62. public static RubyArray/*!*/ Create(object item) {
  63. var result = new RubyArray();
  64. result._content.Add(item);
  65. return result;
  66. }
  67. /// <summary>
  68. /// Creates a blank instance of a RubyArray or its subclass given the Ruby class object.
  69. /// </summary>
  70. public static RubyArray/*!*/ CreateInstance(RubyClass/*!*/ rubyClass) {
  71. return (rubyClass.GetUnderlyingSystemType() == typeof(RubyArray)) ? new RubyArray() : new RubyArray.Subclass(rubyClass);
  72. }
  73. /// <summary>
  74. /// Creates an empty instance.
  75. /// Doesn't copy instance data.
  76. /// Preserves the class of the Array.
  77. /// </summary>
  78. public virtual RubyArray/*!*/ CreateInstance() {
  79. return new RubyArray();
  80. }
  81. object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) {
  82. var result = CreateInstance();
  83. context.CopyInstanceData(this, result, copySingletonMembers);
  84. return result;
  85. }
  86. #endregion
  87. #region Versioning, Flags
  88. private void Mutate() {
  89. try {
  90. checked { _versionAndFlags += (1 << FlagCount); }
  91. } catch (OverflowException) {
  92. throw RubyExceptions.CreateTypeError("can't modify frozen object");
  93. }
  94. }
  95. public bool IsTainted {
  96. get {
  97. return (_versionAndFlags & IsTaintedFlag) != 0;
  98. }
  99. set {
  100. Mutate();
  101. _versionAndFlags = (_versionAndFlags & ~IsTaintedFlag) | (value ? IsTaintedFlag : 0);
  102. }
  103. }
  104. public bool IsFrozen {
  105. get {
  106. return (_versionAndFlags & VersionMask) == FrozenVersion;
  107. }
  108. }
  109. void IRubyObjectState.Freeze() {
  110. Freeze();
  111. }
  112. public RubyArray/*!*/ Freeze() {
  113. _versionAndFlags |= FrozenVersion;
  114. return this;
  115. }
  116. #endregion
  117. #region HashCode, Equality
  118. [MultiRuntimeAware]
  119. private static RubyUtils.RecursionTracker _HashTracker = new RubyUtils.RecursionTracker();
  120. public static int GetHashCode(UnaryOpStorage/*!*/ hashStorage, ConversionStorage<int>/*!*/ fixnumCast, IList/*!*/ self) {
  121. int hash = self.Count;
  122. using (IDisposable handle = _HashTracker.TrackObject(self)) {
  123. if (handle == null) {
  124. // hashing of recursive array
  125. return 0;
  126. }
  127. var hashSite = hashStorage.GetCallSite("hash");
  128. var toIntSite = fixnumCast.GetSite(ConvertToFixnumAction.Make(fixnumCast.Context));
  129. foreach (object item in self) {
  130. hash = (hash << 1) ^ toIntSite.Target(toIntSite, hashSite.Target(hashSite, item));
  131. }
  132. }
  133. return hash;
  134. }
  135. [MultiRuntimeAware]
  136. private static RubyUtils.RecursionTracker _EqualsTracker = new RubyUtils.RecursionTracker();
  137. public static bool Equals(BinaryOpStorage/*!*/ eqlStorage, IList/*!*/ self, object obj) {
  138. if (ReferenceEquals(self, obj)) {
  139. return true;
  140. }
  141. IList other = obj as IList;
  142. if (other == null || self.Count != other.Count) {
  143. return false;
  144. }
  145. using (IDisposable handle = _EqualsTracker.TrackObject(self)) {
  146. if (handle == null) {
  147. // hashing of recursive array
  148. return false;
  149. }
  150. var site = eqlStorage.GetCallSite("eql?");
  151. for (int i = 0; i < self.Count; i++) {
  152. if (!Protocols.IsTrue(site.Target(site, self[i], other[i]))) {
  153. return false;
  154. }
  155. }
  156. }
  157. return true;
  158. }
  159. #endregion
  160. #region IList<object> Members
  161. public int IndexOf(object item) {
  162. return _content.IndexOf(item);
  163. }
  164. public object this[int index] {
  165. get {
  166. return _content[index];
  167. }
  168. set {
  169. Mutate();
  170. _content[index] = value;
  171. }
  172. }
  173. public void Insert(int index, object item) {
  174. Mutate();
  175. _content.Insert(index, item);
  176. }
  177. public void RemoveAt(int index) {
  178. Mutate();
  179. _content.RemoveAt(index);
  180. }
  181. #endregion
  182. #region ICollection<object> Members
  183. public int Count {
  184. get { return _content.Count; }
  185. }
  186. public bool IsReadOnly {
  187. get { return IsFrozen; }
  188. }
  189. public bool Contains(object item) {
  190. return _content.Contains(item);
  191. }
  192. public void CopyTo(object[]/*!*/ array, int arrayIndex) {
  193. _content.CopyTo(array, arrayIndex);
  194. }
  195. public void Add(object item) {
  196. Mutate();
  197. _content.Add(item);
  198. }
  199. public void Clear() {
  200. Mutate();
  201. _content.Clear();
  202. }
  203. public bool Remove(object item) {
  204. Mutate();
  205. return _content.Remove(item);
  206. }
  207. #endregion
  208. #region IEnumerable<object> Members
  209. public IEnumerator<object>/*!*/ GetEnumerator() {
  210. return _content.GetEnumerator();
  211. }
  212. #endregion
  213. #region IEnumerable Members
  214. IEnumerator/*!*/ IEnumerable.GetEnumerator() {
  215. return ((IEnumerable)_content).GetEnumerator();
  216. }
  217. #endregion
  218. #region IList Members
  219. bool IList.IsFixedSize {
  220. get { return IsReadOnly; }
  221. }
  222. void IList.Remove(object value) {
  223. Remove(value);
  224. }
  225. int IList.Add(object value) {
  226. Mutate();
  227. int result = _content.Count;
  228. _content.Add(value);
  229. return result;
  230. }
  231. #endregion
  232. #region ICollection Members (read-only)
  233. void ICollection.CopyTo(Array array, int index) {
  234. ((ICollection)_content).CopyTo(array, index);
  235. }
  236. bool ICollection.IsSynchronized {
  237. get { return ((ICollection)_content).IsSynchronized; }
  238. }
  239. object ICollection.SyncRoot {
  240. get { return ((ICollection)_content).SyncRoot; }
  241. }
  242. #endregion
  243. #region List specific
  244. // read-only //
  245. public int Capacity {
  246. get { return _content.Capacity; }
  247. // cannot remove items:
  248. set { _content.Capacity = value; }
  249. }
  250. public void CopyTo(object[]/*!*/ result) {
  251. _content.CopyTo(result);
  252. }
  253. public int FindIndex(Predicate<object> match) {
  254. return _content.FindIndex(match);
  255. }
  256. #if !SILVERLIGHT
  257. public int FindIndex(int startIndex, Predicate<object> match) {
  258. return _content.FindIndex(startIndex, match);
  259. }
  260. public int FindIndex(int startIndex, int count, Predicate<object> match) {
  261. return _content.FindIndex(startIndex, count, match);
  262. }
  263. #endif
  264. public int BinarySearch(object item) {
  265. return _content.BinarySearch(item);
  266. }
  267. public int BinarySearch(object item, IComparer<object> comparer) {
  268. return _content.BinarySearch(item, comparer);
  269. }
  270. public int BinarySearch(int index, int count, object item, IComparer<object> comparer) {
  271. return _content.BinarySearch(index, count, item, comparer);
  272. }
  273. // mutating //
  274. public void Reverse() {
  275. Mutate();
  276. _content.Reverse();
  277. }
  278. public RubyArray/*!*/ AddMultiple(int count, object value) {
  279. Mutate();
  280. _content.Capacity += count;
  281. for (int i = 0; i < count; i++) {
  282. _content.Add(value);
  283. }
  284. return this;
  285. }
  286. public RubyArray/*!*/ AddRange(IList/*!*/ items) {
  287. Mutate();
  288. // items could be equal to this => we need to capture the count before we iterate:
  289. int count = items.Count;
  290. _content.Capacity += count;
  291. for (int i = 0; i < count; i++) {
  292. _content.Add(items[i]);
  293. }
  294. return this;
  295. }
  296. public RubyArray/*!*/ AddRange(IEnumerable/*!*/ items) {
  297. Mutate();
  298. foreach (var item in items) {
  299. _content.Add(item);
  300. }
  301. return this;
  302. }
  303. public void InsertRange(int index, IEnumerable<object>/*!*/ collection) {
  304. Mutate();
  305. _content.InsertRange(index, collection);
  306. }
  307. public void RemoveRange(int index, int count) {
  308. Mutate();
  309. _content.RemoveRange(index, count);
  310. }
  311. public void Sort() {
  312. Mutate();
  313. _content.Sort();
  314. }
  315. public void Sort(Comparison<object>/*!*/ comparison) {
  316. Mutate();
  317. _content.Sort(comparison);
  318. }
  319. #endregion
  320. #region DebugView
  321. internal string/*!*/ GetDebugView() {
  322. return RubyContext._Default.Inspect(this).ToString();
  323. }
  324. #endregion
  325. }
  326. }