PageRenderTime 66ms CodeModel.GetById 12ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_2_0/Src/IronPython/Runtime/Operations/InstanceOps.cs

#
C# | 530 lines | 346 code | 89 blank | 95 comment | 96 complexity | 1f13af2303a559ca67205d958f27abf4 MD5 | raw file
  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 * dlr@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
 16using System; using Microsoft;
 17using System.Collections;
 18using System.Collections.Generic;
 19using System.Diagnostics;
 20using System.Reflection;
 21using System.Runtime.InteropServices;
 22using Microsoft.Scripting.Actions;
 23
 24using Microsoft.Scripting;
 25using Microsoft.Scripting.Generation;
 26using Microsoft.Scripting.Runtime;
 27using Microsoft.Scripting.Utils;
 28
 29using IronPython.Runtime.Types;
 30
 31namespace IronPython.Runtime.Operations {
 32    /// <summary>
 33    /// InstanceOps contains methods that get added to CLS types depending on what
 34    /// methods and constructors they define. These have not been added directly to
 35    /// PythonType since they need to be added conditionally.
 36    /// 
 37    /// Possibilities include:
 38    /// 
 39    ///     __new__, one of 3 __new__ sets can be added:
 40    ///         DefaultNew - This is the __new__ used for a PythonType (list, dict, object, etc...) that
 41    ///             has only 1 default public constructor that takes no parameters.  These types are 
 42    ///             mutable types, and __new__ returns a new instance of the type, and __init__ can be used
 43    ///             to re-initialize the types.  This __new__ allows an unlimited number of arguments to
 44    ///             be passed if a non-default __init__ is also defined.
 45    ///
 46    ///         NonDefaultNew - This is used when a type has more than one constructor, or only has one
 47    ///             that takes more than zero parameters.  This __new__ does not allow an arbitrary # of
 48    ///             extra arguments.
 49    /// 
 50    ///         DefaultNewCls - This is the default new used for CLS types that have only a single ctor
 51    ///             w/ an arbitray number of arguments.  This constructor allows setting of properties
 52    ///             based upon an extra set of kw-args, e.g.: System.Windows.Forms.Button(Text='abc').  It
 53    ///             is only used on non-Python types.
 54    /// 
 55    ///     __init__:
 56    ///         For types that do not define __init__ we have an __init__ function that takes an
 57    ///         unlimited number of arguments and does nothing.  All types share the same reference
 58    ///         to 1 instance of this.
 59    /// 
 60    ///     next: Defined when a type is an enumerator to expose the Python iter protocol.
 61    /// 
 62    /// 
 63    ///     repr: Added for types that override ToString
 64    /// 
 65    ///     get: added for types that implement IDescriptor
 66    /// </summary>
 67    public static class InstanceOps {
 68        [MultiRuntimeAware]
 69        private static BuiltinFunction _New;
 70        internal static readonly BuiltinFunction NewCls = CreateFunction("__new__", "DefaultNew", "DefaultNewClsKW");
 71        internal static readonly BuiltinFunction OverloadedNew = CreateFunction("__new__", "OverloadedNewBasic", "OverloadedNewKW", "OverloadedNewClsKW");
 72        internal static readonly BuiltinFunction NonDefaultNewInst = CreateNonDefaultNew();
 73        [MultiRuntimeAware]
 74        internal static BuiltinMethodDescriptor _Init;
 75
 76        internal static BuiltinMethodDescriptor Init {
 77            get {
 78                if (_Init == null) {
 79                    _Init = GetInitMethod();
 80                }
 81                return _Init;
 82            }
 83        }
 84        
 85
 86        internal static BuiltinFunction New {
 87            get {
 88                if (_New == null) {
 89                    _New = (BuiltinFunction)PythonTypeOps.GetSlot(
 90                        TypeInfo.GetExtensionMemberGroup(typeof(object), typeof(ObjectOps).GetMember("__new__")),
 91                        "__new__",
 92                        false // privateBinding
 93                    );
 94                }
 95                return _New;
 96            }
 97        }
 98
 99        internal static BuiltinFunction CreateNonDefaultNew() {
100            return CreateFunction("__new__", "NonDefaultNew", "NonDefaultNewKW", "NonDefaultNewKWNoParams");
101        }
102
103        public static object DefaultNew(CodeContext context, PythonType type\u00F8, params object[] args\u00F8) {
104            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
105
106            CheckInitArgs(context, null, args\u00F8, type\u00F8);
107
108            return type\u00F8.CreateInstance(context);
109        }
110
111        public static object DefaultNewClsKW(CodeContext context, PythonType type\u00F8, [ParamDictionary] IAttributesCollection kwargs\u00F8, params object[] args\u00F8) {
112            object res = DefaultNew(context, type\u00F8, args\u00F8);
113
114            if (kwargs\u00F8.Count > 0) {
115                foreach (KeyValuePair<object, object> kvp in (IDictionary<object, object>)kwargs\u00F8) {
116                    PythonOps.SetAttr(context,
117                        res,
118                        SymbolTable.StringToId(kvp.Key.ToString()),
119                        kvp.Value);
120                }
121            }
122            return res;
123        }
124
125        public static object OverloadedNewBasic(CodeContext context, SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, object[], object>>> storage, BuiltinFunction overloads\u00F8, PythonType type\u00F8, params object[] args\u00F8) {
126            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
127            if (args\u00F8 == null) args\u00F8 = new object[1];
128            return overloads\u00F8.Call(context, storage, null, args\u00F8);
129        }
130
131        public static object OverloadedNewKW(CodeContext context, BuiltinFunction overloads\u00F8, PythonType type\u00F8, [ParamDictionary] IAttributesCollection kwargs\u00F8) {
132            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
133            
134            return overloads\u00F8.Call(context, null, null, ArrayUtils.EmptyObjects, kwargs\u00F8);
135        }
136
137        public static object OverloadedNewClsKW(CodeContext context, BuiltinFunction overloads\u00F8, PythonType type\u00F8, [ParamDictionary] IAttributesCollection kwargs\u00F8, params object[] args\u00F8) {
138            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
139            if (args\u00F8 == null) args\u00F8 = new object[1];
140
141            return overloads\u00F8.Call(context, null, null, args\u00F8, kwargs\u00F8);
142        }
143
144        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "context"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "args\u00F8")]
145        public static void DefaultInit(CodeContext context, object self, params object[] args\u00F8) {
146        }
147
148        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "self"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kwargs\u00F8"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "context"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "args\u00F8")]
149        public static void DefaultInitKW(CodeContext context, object self, [ParamDictionary] IAttributesCollection kwargs\u00F8, params object[] args\u00F8) {
150        }
151
152        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "context")]
153        [StaticExtensionMethod]
154        public static object NonDefaultNew(CodeContext context, PythonType type\u00F8, params object[] args\u00F8) {
155            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
156            if (args\u00F8 == null) args\u00F8 = new object[1];
157            return type\u00F8.CreateInstance(context, args\u00F8);
158        }
159
160        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "context")]
161        [StaticExtensionMethod]
162        public static object NonDefaultNewKW(CodeContext context, PythonType type\u00F8, [ParamDictionary] IAttributesCollection kwargs\u00F8, params object[] args\u00F8) {
163            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
164            if (args\u00F8 == null) args\u00F8 = new object[1];
165
166            string []names;
167            GetKeywordArgs(kwargs\u00F8, args\u00F8, out args\u00F8, out names);
168            return type\u00F8.CreateInstance(context, args\u00F8, names);
169        }
170
171        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "context")]
172        [StaticExtensionMethod]
173        public static object NonDefaultNewKWNoParams(CodeContext context, PythonType type\u00F8, [ParamDictionary] IAttributesCollection kwargs\u00F8) {
174            if (type\u00F8 == null) throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(type\u00F8)));
175
176            string[] names;
177            object[] args;
178            GetKeywordArgs(kwargs\u00F8, ArrayUtils.EmptyObjects, out args, out names);
179            return type\u00F8.CreateInstance(context, args, names);
180        }
181
182        public static object IterMethod(CodeContext/*!*/ context, object self) {
183            return self;
184        }
185
186        public static object NextMethod(object self) {
187            IEnumerator i = (IEnumerator)self;
188            if (i.MoveNext()) return i.Current;
189            throw PythonOps.StopIteration();
190        }
191
192        public static int LengthMethod(ICollection self) {
193            return self.Count;
194        }
195
196        public static int GenericLengthMethod<T>(ICollection<T> self) {
197            return self.Count;
198        }
199
200        public static string SimpleRepr(object self) {
201            return String.Format("<{0} object at {1}>",
202                PythonTypeOps.GetName(self),
203                PythonOps.HexId(self));
204        }
205
206        public static string FancyRepr(object self) {
207            PythonType pt = (PythonType)DynamicHelpers.GetPythonType(self);
208            // we can't call ToString on a UserType because we'll stack overflow, so
209            // only do FancyRepr for reflected types.
210            if (pt.IsSystemType) {
211                string toStr = self.ToString();
212                if (toStr == null) toStr = String.Empty;
213
214                // get the type name to display (CLI name or Python name)
215                Type type = pt.UnderlyingSystemType;
216                string typeName = type.FullName;
217
218                // Get the underlying .ToString() representation.  Truncate multiple
219                // lines, and don't display it if it's object's default representation (type name)
220
221                // skip initial empty lines:
222                int i = 0;
223                while (i < toStr.Length && (toStr[i] == '\r' || toStr[i] == '\n')) i++;
224
225                // read the first non-empty line:
226                int j = i;
227                while (j < toStr.Length && toStr[j] != '\r' && toStr[j] != '\n') j++;
228
229                // skip following empty lines:
230                int k = j;
231                while (k < toStr.Length && (toStr[k] == '\r' || toStr[k] == '\n')) k++;
232
233                if (j > i) {
234                    string first_non_empty_line = toStr.Substring(i, j - i);
235                    bool has_multiple_non_empty_lines = k < toStr.Length;
236
237                    return String.Format("<{0} object at {1} [{2}{3}]>",
238                        typeName,
239                        PythonOps.HexId(self),
240                        first_non_empty_line,
241                        has_multiple_non_empty_lines ? "..." : String.Empty);
242
243                } else {
244                    return String.Format("<{0} object at {1}>",
245                             typeName,
246                             PythonOps.HexId(self));
247                }
248            }
249            return SimpleRepr(self);
250        }
251
252        public static object ReprHelper(CodeContext context, object self) {
253            return ((ICodeFormattable)self).__repr__(context);
254        }
255
256        public static string ToStringMethod(object self) {
257            string res = self.ToString();
258            if (res == null) return String.Empty;
259            return res;
260        }
261
262        // Value equality helpers:  These are the default implementation for classes that implement
263        // IValueEquality.  We promote the ReflectedType to having these helper methods which will
264        // automatically test the type and return NotImplemented for mixed comparisons.  For non-mixed
265        // comparisons we have a fully optimized version which returns bool.
266
267        public static bool ValueEqualsMethod<T>(T x, [NotNull]T y) 
268            where T : IValueEquality {
269            return x.ValueEquals(y);
270        }
271
272        public static bool ValueNotEqualsMethod<T>(T x, [NotNull]T y)
273            where T : IValueEquality {
274            return !x.ValueEquals(y);
275        }
276
277        [return: MaybeNotImplemented]
278        public static object ValueEqualsMethod<T>([NotNull]T x, object y) 
279            where T : IValueEquality {
280            if (!(y is T)) return NotImplementedType.Value;
281
282            return RuntimeHelpers.BooleanToObject(x.ValueEquals(y));
283        }
284
285        [return: MaybeNotImplemented]
286        public static object ValueNotEqualsMethod<T>([NotNull]T x, object y) 
287            where T : IValueEquality {
288            if (!(y is T)) return NotImplementedType.Value;
289
290            return RuntimeHelpers.BooleanToObject(!x.ValueEquals(y));
291        }
292
293        [return: MaybeNotImplemented]
294        public static object ValueEqualsMethod<T>(object y, [NotNull]T x)
295            where T : IValueEquality {
296            if (!(y is T)) return NotImplementedType.Value;
297
298            return RuntimeHelpers.BooleanToObject(x.ValueEquals(y));
299        }
300
301        [return: MaybeNotImplemented]
302        public static object ValueNotEqualsMethod<T>(object y, [NotNull]T x)
303            where T : IValueEquality {
304            if (!(y is T)) return NotImplementedType.Value;
305
306            return RuntimeHelpers.BooleanToObject(x.ValueEquals(y));
307        }
308
309        // TODO: Remove me
310        public static object ValueHashMethod(object x) {
311            return ((IValueEquality)x).GetValueHashCode();
312        }
313
314        public static int GetHashCodeMethod(object x) {
315            return x.GetHashCode();
316        }
317
318        public static bool EqualsMethod(object x, object y) {
319            return x.Equals(y);
320        }
321
322        public static bool NotEqualsMethod(object x, object y) {
323            return !x.Equals(y);
324        }
325
326        /// <summary>
327        /// Provides the implementation of __enter__ for objects which implement IDisposable.
328        /// </summary>
329        public static object EnterMethod(IDisposable/*!*/ self) {
330            return self;
331        }
332
333        /// <summary>
334        /// Provides the implementation of __exit__ for objects which implement IDisposable.
335        /// </summary>
336        public static void ExitMethod(IDisposable/*!*/ self, object exc_type, object exc_value, object exc_back) {
337            self.Dispose();
338        }
339
340        [PropertyMethod, StaticExtensionMethod]
341        public static List/*!*/ Get__all__<T>(CodeContext/*!*/ context) {
342            Debug.Assert(typeof(T).IsSealed && typeof(T).IsAbstract, "__all__ should only be produced for static members"); 
343
344            PythonType pt = DynamicHelpers.GetPythonTypeFromType(typeof(T));
345
346            List names = new List();
347            foreach (string name in pt.GetMemberNames(context)) {
348                object res;
349                if (IsStaticTypeMemberInAll(context, pt, SymbolTable.StringToId(name), out res)) {
350                    names.AddNoLock(name);
351                }
352            }
353
354            return names;
355        }
356
357        /// <summary>
358        /// Determines if a type member can be imported.  This is used to treat static types like modules.
359        /// </summary>
360        private static bool IsStaticTypeMemberInAll(CodeContext/*!*/ context, PythonType/*!*/ pt, SymbolId name, out object res) {
361            PythonTypeSlot pts;
362            res = null;
363            if (pt.TryResolveSlot(context, name, out pts)) {
364                if (name == Symbols.Doc || name == Symbols.Class) {
365                    // these exist but we don't want to clobber __doc__ on import * or bring in __class__
366                    return false;
367                } else if (pts is ReflectedGetterSetter) {
368                    // property or indexer, these fetch the value at runtime, the user needs to explicitly
369                    // import them using from type import property
370                    return false;
371                }
372
373                ReflectedField rf = pts as ReflectedField;
374                if (rf != null && !rf.info.IsInitOnly && !rf.info.IsLiteral) {
375                    // only bring in read-only fields, if the value can change the user needs to explicitly
376                    // import by name
377                    return false;
378                }
379
380                BuiltinMethodDescriptor method = pts as BuiltinMethodDescriptor;
381                if (method != null && (!method.DeclaringType.IsSealed || !method.DeclaringType.IsAbstract)) {
382                    // inherited object member on a static class (GetHashCode, Equals, etc...)
383                    return false;
384                }
385
386                BuiltinFunction bf = pts as BuiltinFunction;
387                if (bf != null && (!bf.DeclaringType.IsSealed || !bf.DeclaringType.IsAbstract)) {
388                    // __new__/ReferenceEquals inherited from object
389                    return false;
390                }
391
392                if (pts.TryGetValue(context, null, pt, out res)) {
393                    return true;
394                }
395            }
396
397            res = null;
398            return false;
399        }
400
401        /// <summary>
402        /// Implements __contains__ for types implementing IEnumerable of T.
403        /// </summary>
404        public static bool ContainsGenericMethod<T>(CodeContext/*!*/ context, IEnumerable<T> enumerable, T value) {
405            foreach(T item in enumerable) {
406                if (PythonOps.EqualRetBool(context, item, value)) {
407                    return true;
408                }
409            }
410
411            return false;
412        }
413
414        /// <summary>
415        /// Implements __contains__ for types implementing IEnumerable
416        /// </summary>
417        public static bool ContainsMethod(CodeContext/*!*/ context, IEnumerable enumerable, object value) {
418            IEnumerator ie = enumerable.GetEnumerator();
419            while (ie.MoveNext()) {
420                if (PythonOps.EqualRetBool(context, ie.Current, value)) {
421                    return true;
422                }
423            }
424
425            return false;
426        }
427
428        /// <summary>
429        /// Implements __contains__ for types implementing IEnumerable of T.
430        /// </summary>
431        public static bool ContainsGenericMethodIEnumerator<T>(CodeContext/*!*/ context, IEnumerator<T> enumerator, T value) {
432            while (enumerator.MoveNext()) {
433                if (PythonOps.EqualRetBool(context, enumerator.Current, value)) {
434                    return true;
435                }
436            }
437
438            return false;
439        }
440
441        /// <summary>
442        /// Implements __contains__ for types implementing IEnumerable
443        /// </summary>
444        public static bool ContainsMethodIEnumerator(CodeContext/*!*/ context, IEnumerator enumerator, object value) {
445            while (enumerator.MoveNext()) {
446                if (PythonOps.EqualRetBool(context, enumerator.Current, value)) {
447                    return true;
448                }
449            }
450
451            return false;
452        }
453
454        public static object GetMethod(CodeContext context, object self, object instance, [Optional]object typeContext) {
455            PythonTypeSlot dts = self as PythonTypeSlot;
456            PythonType dt = typeContext as PythonType;
457
458            Debug.Assert(dts != null);
459
460            object res;
461            if (dts.TryGetValue(context, instance, dt, out res))
462                return res;
463
464            // context is hiding __get__
465            throw PythonOps.AttributeErrorForMissingAttribute(dt == null ? "?" : dt.Name, Symbols.GetDescriptor);
466        }
467
468#if !SILVERLIGHT
469        /// <summary>
470        /// Implements __reduce_ex__ for .NET types which are serializable.  This uses the .NET
471        /// serializer to get a string of raw data which can be serialized.
472        /// </summary>
473        public static PythonTuple SerializeReduce(CodeContext/*!*/ context, object/*!*/ self, int protocol) {
474            PythonTuple data = ClrModule.Serialize(self);
475
476            object deserializeNew;
477            bool res = PythonContext.GetContext(context).ClrModule.TryGetName(
478                SymbolTable.StringToId("Deserialize"),
479                out deserializeNew
480            );
481            Debug.Assert(res);
482
483            return PythonTuple.MakeTuple(
484                deserializeNew,                 // function to call, clr.DeserializeNew 
485                data,                           // data to pass to it - our type & the raw data from the .NET serializer
486                null                            // state, unused
487            );
488        }
489#endif
490
491        internal static void CheckInitArgs(CodeContext context, IAttributesCollection dict, object[] args, PythonType pt) {
492            PythonTypeSlot dts;
493            object initObj;
494            if (((args != null && args.Length > 0) || (dict != null && dict.Count > 0)) &&
495                (pt.TryResolveSlot(context, Symbols.Init, out dts)) && dts.TryGetValue(context, null, pt, out initObj) &&
496                initObj == Init) {
497
498                throw PythonOps.TypeError("default __new__ does not take parameters");
499            }
500        }
501
502        private static BuiltinMethodDescriptor GetInitMethod() {
503            PythonTypeSlot pts;
504            TypeCache.Object.TryResolveSlot(DefaultContext.Default, Symbols.Init, out pts);
505
506            Debug.Assert(pts != null);
507            return (BuiltinMethodDescriptor)pts;
508        }
509
510        private static BuiltinFunction CreateFunction(string name, params string[] methodNames) {
511            MethodBase[] methods = new MethodBase[methodNames.Length];
512            for (int i = 0; i < methods.Length; i++) {
513                methods[i] = typeof(InstanceOps).GetMethod(methodNames[i]);
514            }
515            return BuiltinFunction.MakeMethod(name, methods, typeof(object), FunctionType.Function | FunctionType.AlwaysVisible);
516        }
517
518        private static void GetKeywordArgs(IAttributesCollection dict, object[] args, out object[] finalArgs, out string[] names) {
519            finalArgs = new object[args.Length + dict.Count];
520            Array.Copy(args, finalArgs, args.Length);
521            names = new string[dict.Count];
522            int i = 0;
523            foreach (KeyValuePair<object, object> kvp in (IDictionary<object, object>)dict) {
524                names[i] = (string)kvp.Key;
525                finalArgs[i + args.Length] = kvp.Value;
526                i++;
527            }
528        }
529    }
530}