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