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

/IronPython_Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/StructOps.cs

#
C# | 304 lines | 224 code | 52 blank | 28 comment | 31 complexity | 332a4f64d95f16727c57c08074dbcd18 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.Generic;
  22. using System.Diagnostics;
  23. using Microsoft.Scripting;
  24. using Microsoft.Scripting.Runtime;
  25. using Microsoft.Scripting.Utils;
  26. using IronRuby.Runtime;
  27. using System.Runtime.CompilerServices;
  28. namespace IronRuby.Builtins {
  29. using EachSite = Func<CallSite, object, Proc, object>;
  30. [RubyClass("Struct", Extends = typeof(RubyStruct), Inherits = typeof(object)), Includes(typeof(Enumerable))]
  31. public static partial class RubyStructOps {
  32. [RubyConstructor]
  33. public static void AllocatorUndefined(RubyClass/*!*/ self, params object[] args) {
  34. throw RubyExceptions.CreateAllocatorUndefinedError(self);
  35. }
  36. [RubyMethod("new", RubyMethodAttributes.PublicSingleton)]
  37. public static object NewAnonymousStruct(BlockParam block, RubyClass/*!*/ self, [NotNull]RubySymbol/*!*/ firstAttibuteName,
  38. [DefaultProtocol, NotNullItems]params string/*!*/[]/*!*/ attributeNames) {
  39. return CreateAnonymousWithFirstAttribute(block, self, RubyOps.ConvertSymbolToClrString(firstAttibuteName), attributeNames);
  40. }
  41. [RubyMethod("new", RubyMethodAttributes.PublicSingleton)]
  42. public static object NewAnonymousStruct(BlockParam block, RubyClass/*!*/ self, [NotNull]string/*!*/ firstAttibuteName,
  43. [DefaultProtocol, NotNullItems]params string/*!*/[]/*!*/ attributeNames) {
  44. return CreateAnonymousWithFirstAttribute(block, self, firstAttibuteName, attributeNames);
  45. }
  46. [RubyMethod("new", RubyMethodAttributes.PublicSingleton)]
  47. public static object NewStruct(BlockParam block, RubyClass/*!*/ self, [DefaultProtocol]MutableString className,
  48. [DefaultProtocol, NotNullItems]params string/*!*/[]/*!*/ attributeNames) {
  49. if (className == null) {
  50. return Create(block, self, null, attributeNames);
  51. }
  52. string strName = className.ConvertToString();
  53. RubyUtils.CheckConstantName(strName);
  54. return Create(block, self, strName, attributeNames);
  55. }
  56. public static object CreateAnonymousWithFirstAttribute(BlockParam block, RubyClass/*!*/ self,
  57. string/*!*/ firstAttribute, string/*!*/[]/*!*/ attributeNames) {
  58. return Create(block, self, null, ArrayUtils.Insert(firstAttribute, attributeNames));
  59. }
  60. /// <summary>
  61. /// Struct#new
  62. /// Creates Struct classes with the specified name and members
  63. /// </summary>
  64. private static object Create(BlockParam block, RubyClass/*!*/ self, string className, string/*!*/[]/*!*/ attributeNames) {
  65. var result = RubyStruct.DefineStruct(self, className, attributeNames);
  66. if (block != null) {
  67. return RubyUtils.EvaluateInModule(result, block, null, result);
  68. }
  69. return result;
  70. }
  71. // Reinitialization. Called only from derived struct's initializer.
  72. [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)]
  73. public static void Reinitialize(RubyStruct/*!*/ self, params object[]/*!*/ items) {
  74. self.SetValues(items);
  75. }
  76. // Copies data from one Struct instance into another:
  77. [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)]
  78. public static RubyStruct/*!*/ InitializeCopy(RubyStruct/*!*/ self, [NotNull]RubyStruct/*!*/ source) {
  79. // TODO: compare non-singleton classes?
  80. if (self.ImmediateClass.GetNonSingletonClass() != source.ImmediateClass.GetNonSingletonClass()) {
  81. throw RubyExceptions.CreateTypeError("wrong argument class");
  82. }
  83. self.SetValues(source.Values);
  84. return self;
  85. }
  86. [RubyMethod("members")]
  87. public static RubyArray/*!*/ GetMembers(RubyStruct/*!*/ self) {
  88. return RubyStruct.GetMembers(self);
  89. }
  90. [RubyMethod("length")]
  91. [RubyMethod("size")]
  92. public static int GetSize(RubyStruct/*!*/ self) {
  93. return self.ItemCount;
  94. }
  95. [RubyMethod("[]")]
  96. public static object GetValue(RubyStruct/*!*/ self, int index) {
  97. return self[NormalizeIndex(self.ItemCount, index)];
  98. }
  99. [RubyMethod("[]")]
  100. public static object GetValue(RubyStruct/*!*/ self, [NotNull]RubySymbol/*!*/ name) {
  101. return self[name.ToString()];
  102. }
  103. [RubyMethod("[]")]
  104. public static object GetValue(RubyStruct/*!*/ self, [NotNull]MutableString/*!*/ name) {
  105. return self[name.ConvertToString()];
  106. }
  107. [RubyMethod("[]")]
  108. public static object GetValue(ConversionStorage<int>/*!*/ conversionStorage, RubyStruct/*!*/ self, object index) {
  109. return self[NormalizeIndex(self.ItemCount, Protocols.CastToFixnum(conversionStorage, index))];
  110. }
  111. [RubyMethod("[]=")]
  112. public static object SetValue(RubyStruct/*!*/ self, int index, object value) {
  113. return self[NormalizeIndex(self.ItemCount, index)] = value;
  114. }
  115. [RubyMethod("[]=")]
  116. public static object SetValue(RubyStruct/*!*/ self, [NotNull]RubySymbol/*!*/ name, object value) {
  117. return self[name.ToString()] = value;
  118. }
  119. [RubyMethod("[]=")]
  120. public static object SetValue(RubyStruct/*!*/ self, [NotNull]MutableString/*!*/ name, object value) {
  121. return self[name.ConvertToString()] = value;
  122. }
  123. [RubyMethod("[]=")]
  124. public static object SetValue(ConversionStorage<int>/*!*/ conversionStorage, RubyStruct/*!*/ self, object index, object value) {
  125. return self[NormalizeIndex(self.ItemCount, Protocols.CastToFixnum(conversionStorage, index))] = value;
  126. }
  127. [RubyMethod("each")]
  128. public static object Each(BlockParam block, RubyStruct/*!*/ self) {
  129. if (block == null && self.ItemCount > 0) {
  130. throw RubyExceptions.NoBlockGiven();
  131. }
  132. foreach (var value in self.Values) {
  133. object result;
  134. if (block.Yield(value, out result)) {
  135. return result;
  136. }
  137. }
  138. return self;
  139. }
  140. [RubyMethod("each_pair")]
  141. public static object EachPair(BlockParam block, RubyStruct/*!*/ self) {
  142. if (block == null && self.ItemCount > 0) {
  143. throw RubyExceptions.NoBlockGiven();
  144. }
  145. var context = self.ImmediateClass.Context;
  146. foreach (KeyValuePair<string, object> entry in self.GetItems()) {
  147. object result;
  148. if (block.Yield(context.EncodeIdentifier(entry.Key), entry.Value, out result)) {
  149. return result;
  150. }
  151. }
  152. return self;
  153. }
  154. [RubyMethod("to_a")]
  155. [RubyMethod("values")]
  156. public static RubyArray/*!*/ Values(RubyStruct/*!*/ self) {
  157. return new RubyArray(self.Values);
  158. }
  159. [RubyMethod("hash")]
  160. public static int Hash(UnaryOpStorage/*!*/ hashStorage, ConversionStorage<int>/*!*/ fixnumCast, RubyStruct/*!*/ self) {
  161. return self.GetHashCode(hashStorage, fixnumCast);
  162. }
  163. [RubyMethod("eql?")]
  164. public static bool Equal(BinaryOpStorage/*!*/ eqlStorage, RubyStruct/*!*/ self, object other) {
  165. return self.Equals(eqlStorage, other);
  166. }
  167. // same pattern as RubyStruct.Equals, but we need to call == instead of eql?
  168. [RubyMethod("==")]
  169. public static bool Equals(BinaryOpStorage/*!*/ equals, RubyStruct/*!*/ self, object obj) {
  170. var other = obj as RubyStruct;
  171. if (!self.StructReferenceEquals(other)) {
  172. return false;
  173. }
  174. Debug.Assert(self.ItemCount == other.ItemCount);
  175. return IListOps.Equals(equals, self.Values, other.Values);
  176. }
  177. [RubyMethod("to_s")]
  178. [RubyMethod("inspect")]
  179. public static MutableString/*!*/ Inspect(RubyStruct/*!*/ self) {
  180. RubyContext context = self.ImmediateClass.Context;
  181. using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
  182. // #<struct Struct::Foo name=nil, val=nil>
  183. var result = MutableString.CreateMutable(RubyEncoding.Binary);
  184. result.Append("#<struct ");
  185. result.Append(context.Inspect(context.GetClassOf(self)));
  186. if (handle == null) {
  187. return result.Append(":...>");
  188. }
  189. result.Append(' ');
  190. object[] data = self.Values;
  191. var members = self.GetNames();
  192. for (int i = 0; i < data.Length; i++) {
  193. if (i != 0) {
  194. result.Append(", ");
  195. }
  196. // TODO (encoding):
  197. result.Append(members[i]);
  198. result.Append('=');
  199. result.Append(context.Inspect(data[i]));
  200. }
  201. result.Append('>');
  202. return result;
  203. }
  204. }
  205. // For some unknown reason Struct defines the method even though it is mixed in from Enumerable
  206. // Until we discover the difference, delegate to Enumerable#select
  207. [RubyMethod("select")]
  208. public static object Select(CallSiteStorage<EachSite>/*!*/ each, BlockParam predicate, RubyStruct/*!*/ self) {
  209. return Enumerable.Select(each, predicate, self);
  210. }
  211. // equivalent to Array#values_at over the data array
  212. [RubyMethod("values_at")]
  213. public static RubyArray/*!*/ ValuesAt(ConversionStorage<int>/*!*/ fixnumCast, RubyStruct/*!*/ self, params object[]/*!*/ values) {
  214. RubyArray result = new RubyArray();
  215. object[] data = self.Values;
  216. for (int i = 0; i < values.Length; ++i) {
  217. Range range = values[i] as Range;
  218. if (range != null) {
  219. int begin = Protocols.CastToFixnum(fixnumCast, range.Begin);
  220. int end = Protocols.CastToFixnum(fixnumCast, range.End);
  221. if (range.ExcludeEnd) {
  222. end -= 1;
  223. }
  224. begin = NormalizeIndex(data.Length, begin);
  225. end = NormalizeIndex(data.Length, end);
  226. Debug.Assert(end - begin <= data.Length); // because we normalized the indicies
  227. if (end - begin > 0) {
  228. result.AddCapacity(end - begin);
  229. for (int j = begin; j <= end; j++) {
  230. result.Add(data[j]);
  231. }
  232. }
  233. } else {
  234. int index = NormalizeIndex(data.Length, Protocols.CastToFixnum(fixnumCast, values[i]));
  235. result.Add(data[index]);
  236. }
  237. }
  238. return result;
  239. }
  240. private static int NormalizeIndex(int itemCount, int index) {
  241. int normalized = index;
  242. if (normalized < 0) {
  243. normalized += itemCount;
  244. }
  245. if (normalized >= 0 && normalized < itemCount) {
  246. return normalized;
  247. }
  248. // MRI reports the normalized index, but we'll report the original one
  249. throw RubyExceptions.CreateIndexError("offset {0} too small for struct (size:{1})", index, itemCount);
  250. }
  251. }
  252. }