PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Operations/ObjectOps.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 459 lines | 287 code | 70 blank | 102 comment | 90 complexity | 6f14f5d7a7a7b492db7a439b0a90781a 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. using System;
  16. using System.Collections.Generic;
  17. using System.Reflection;
  18. using System.Threading;
  19. using Microsoft.Scripting;
  20. using Microsoft.Scripting.Math;
  21. using Microsoft.Scripting.Runtime;
  22. using Microsoft.Scripting.Utils;
  23. using IronPython.Runtime.Types;
  24. #if CLR2
  25. using Complex = Microsoft.Scripting.Math.Complex64;
  26. #else
  27. using System.Numerics;
  28. #endif
  29. namespace IronPython.Runtime.Operations {
  30. /// <summary>
  31. /// Contains Python extension methods that are added to object
  32. /// </summary>
  33. public static class ObjectOps {
  34. /// <summary> Types for which the pickle module has built-in support (from PEP 307 case 2) </summary>
  35. [MultiRuntimeAware]
  36. private static Dictionary<PythonType, object> _nativelyPickleableTypes;
  37. /// <summary>
  38. /// __class__, a custom slot so that it works for both objects and types.
  39. /// </summary>
  40. [SlotField]
  41. public static PythonTypeSlot __class__ = new PythonTypeTypeSlot();
  42. /// <summary>
  43. /// Removes an attribute from the provided member
  44. /// </summary>
  45. public static void __delattr__(CodeContext/*!*/ context, object self, string name) {
  46. if (self is PythonType) {
  47. throw PythonOps.TypeError("can't apply this __delattr__ to type object");
  48. }
  49. PythonOps.ObjectDeleteAttribute(context, self, name);
  50. }
  51. /// <summary>
  52. /// Returns the hash code of the given object
  53. /// </summary>
  54. public static int __hash__(object self) {
  55. if (self == null) return NoneTypeOps.NoneHashCode;
  56. return self.GetHashCode();
  57. }
  58. /// <summary>
  59. /// Gets the specified attribute from the object without running any custom lookup behavior
  60. /// (__getattr__ and __getattribute__)
  61. /// </summary>
  62. public static object __getattribute__(CodeContext/*!*/ context, object self, string name) {
  63. return PythonOps.ObjectGetAttribute(context, self, name);
  64. }
  65. /// <summary>
  66. /// Initializes the object. The base class does nothing.
  67. /// </summary>
  68. public static void __init__(CodeContext/*!*/ context, object self) {
  69. }
  70. /// <summary>
  71. /// Initializes the object. The base class does nothing.
  72. /// </summary>
  73. public static void __init__(CodeContext/*!*/ context, object self, [NotNull]params object[] args\u00F8) {
  74. InstanceOps.CheckInitArgs(context, null, args\u00F8, self);
  75. }
  76. /// <summary>
  77. /// Initializes the object. The base class does nothing.
  78. /// </summary>
  79. public static void __init__(CodeContext/*!*/ context, object self, [ParamDictionary]IDictionary<object, object> kwargs, params object[] args\u00F8) {
  80. InstanceOps.CheckInitArgs(context, kwargs, args\u00F8, self);
  81. }
  82. /// <summary>
  83. /// Creates a new instance of the type
  84. /// </summary>
  85. [StaticExtensionMethod]
  86. public static object __new__(CodeContext/*!*/ context, PythonType cls) {
  87. if (cls == null) {
  88. throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(cls)));
  89. }
  90. return cls.CreateInstance(context);
  91. }
  92. /// <summary>
  93. /// Creates a new instance of the type
  94. /// </summary>
  95. [StaticExtensionMethod]
  96. public static object __new__(CodeContext/*!*/ context, PythonType cls, [NotNull]params object[] args\u00F8) {
  97. if (cls == null) {
  98. throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(cls)));
  99. }
  100. InstanceOps.CheckNewArgs(context, null, args\u00F8, cls);
  101. return cls.CreateInstance(context);
  102. }
  103. /// <summary>
  104. /// Creates a new instance of the type
  105. /// </summary>
  106. [StaticExtensionMethod]
  107. public static object __new__(CodeContext/*!*/ context, PythonType cls, [ParamDictionary]IDictionary<object, object> kwargs\u00F8, [NotNull]params object[] args\u00F8) {
  108. if (cls == null) {
  109. throw PythonOps.TypeError("__new__ expected type object, got {0}", PythonOps.Repr(context, DynamicHelpers.GetPythonType(cls)));
  110. }
  111. InstanceOps.CheckNewArgs(context, kwargs\u00F8, args\u00F8, cls);
  112. return cls.CreateInstance(context);
  113. }
  114. /// <summary>
  115. /// Runs the pickle protocol
  116. /// </summary>
  117. public static object __reduce__(CodeContext/*!*/ context, object self) {
  118. return __reduce_ex__(context, self, 0);
  119. }
  120. /// <summary>
  121. /// Runs the pickle protocol
  122. /// </summary>
  123. public static object __reduce_ex__(CodeContext/*!*/ context, object self) {
  124. return __reduce_ex__(context, self, 0);
  125. }
  126. /// <summary>
  127. /// Runs the pickle protocol
  128. /// </summary>
  129. public static object __reduce_ex__(CodeContext/*!*/ context, object self, object protocol) {
  130. object objectReduce = PythonOps.GetBoundAttr(context, DynamicHelpers.GetPythonTypeFromType(typeof(object)), "__reduce__");
  131. object myReduce;
  132. if (PythonOps.TryGetBoundAttr(context, DynamicHelpers.GetPythonType(self), "__reduce__", out myReduce)) {
  133. if (!PythonOps.IsRetBool(myReduce, objectReduce)) {
  134. // A derived class overrode __reduce__ but not __reduce_ex__, so call
  135. // specialized __reduce__ instead of generic __reduce_ex__.
  136. // (see the "The __reduce_ex__ API" section of PEP 307)
  137. return PythonOps.CallWithContext(context, myReduce, self);
  138. }
  139. }
  140. if (PythonContext.GetContext(context).ConvertToInt32(protocol) < 2) {
  141. return ReduceProtocol0(context, self);
  142. } else {
  143. return ReduceProtocol2(context, self);
  144. }
  145. }
  146. /// <summary>
  147. /// Returns the code representation of the object. The default implementation returns
  148. /// a string which consists of the type and a unique numerical identifier.
  149. /// </summary>
  150. public static string __repr__(object self) {
  151. return String.Format("<{0} object at {1}>",
  152. DynamicHelpers.GetPythonType(self).Name,
  153. PythonOps.HexId(self));
  154. }
  155. /// <summary>
  156. /// Sets an attribute on the object without running any custom object defined behavior.
  157. /// </summary>
  158. public static void __setattr__(CodeContext/*!*/ context, object self, string name, object value) {
  159. if (self is PythonType) {
  160. throw PythonOps.TypeError("can't apply this __setattr__ to type object");
  161. }
  162. PythonOps.ObjectSetAttribute(context, self, name, value);
  163. }
  164. private static int AdjustPointerSize(int size) {
  165. if (IntPtr.Size == 4) {
  166. return size;
  167. }
  168. return size * 2;
  169. }
  170. /// <summary>
  171. /// Returns the number of bytes of memory required to allocate the object.
  172. /// </summary>
  173. public static int __sizeof__(object self) {
  174. IPythonObject ipo = self as IPythonObject;
  175. int res = AdjustPointerSize(8); // vtable, sync blk
  176. if (ipo != null) {
  177. res += AdjustPointerSize(12); // class, dict, slots
  178. }
  179. Type t = PythonTypeOps.GetFinalSystemType(DynamicHelpers.GetPythonType(self));
  180. res += GetTypeSize(t);
  181. return res;
  182. }
  183. private static int GetTypeSize(Type t) {
  184. FieldInfo[] fields = t.GetFields(System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
  185. int res = 0;
  186. foreach (FieldInfo fi in fields) {
  187. if (fi.FieldType.IsClass || fi.FieldType.IsInterface) {
  188. res += AdjustPointerSize(4);
  189. } else if (fi.FieldType.IsPrimitive) {
  190. return System.Runtime.InteropServices.Marshal.SizeOf(fi.FieldType);
  191. } else {
  192. res += GetTypeSize(fi.FieldType);
  193. }
  194. }
  195. return res;
  196. }
  197. /// <summary>
  198. /// Returns a friendly string representation of the object.
  199. /// </summary>
  200. public static string __str__(CodeContext/*!*/ context, object o) {
  201. return PythonOps.Repr(context, o);
  202. }
  203. public static NotImplementedType __subclasshook__(params object[] args) {
  204. return NotImplementedType.Value;
  205. }
  206. public static string __format__(CodeContext/*!*/ context, object self, [NotNull]string/*!*/ formatSpec) {
  207. string text = PythonOps.ToString(context, self);
  208. StringFormatSpec spec = StringFormatSpec.FromString(formatSpec);
  209. if (spec.Type != null && spec.Type != 's') {
  210. throw PythonOps.ValueError("Unknown format code '{0}' for object of type 'str'", spec.Type.Value.ToString());
  211. } else if (spec.Sign != null) {
  212. throw PythonOps.ValueError("Sign not allowed in string format specifier");
  213. } else if (spec.Alignment == '=') {
  214. throw PythonOps.ValueError("'=' alignment not allowed in string format specifier");
  215. }
  216. // apply precision to shorten the string first
  217. if (spec.Precision != null) {
  218. int precision = spec.Precision.Value;
  219. if (text.Length > precision) {
  220. text = text.Substring(0, precision);
  221. }
  222. }
  223. // then apply the minimum width & padding
  224. text = spec.AlignText(text);
  225. // finally return the text
  226. return text;
  227. }
  228. #region Pickle helpers
  229. // This is a dynamically-initialized property rather than a statically-initialized field
  230. // to avoid a bootstrapping dependency loop
  231. private static Dictionary<PythonType, object> NativelyPickleableTypes {
  232. get {
  233. if (_nativelyPickleableTypes == null) {
  234. Dictionary<PythonType, object> typeDict = new Dictionary<PythonType, object>();
  235. typeDict.Add(TypeCache.Null, null);
  236. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(bool)), null);
  237. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(int)), null);
  238. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(double)), null);
  239. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(Complex)), null);
  240. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(string)), null);
  241. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(PythonTuple)), null);
  242. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(List)), null);
  243. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(PythonDictionary)), null);
  244. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(OldInstance)), null);
  245. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(OldClass)), null);
  246. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(PythonFunction)), null);
  247. typeDict.Add(DynamicHelpers.GetPythonTypeFromType(typeof(BuiltinFunction)), null);
  248. // type dict needs to be ensured to be fully initialized before assigning back
  249. Thread.MemoryBarrier();
  250. _nativelyPickleableTypes = typeDict;
  251. }
  252. return _nativelyPickleableTypes;
  253. }
  254. }
  255. /// <summary>
  256. /// Return a dict that maps slot names to slot values, but only include slots that have been assigned to.
  257. /// Looks up slots in base types as well as the current type.
  258. ///
  259. /// Sort-of Python equivalent (doesn't look up base slots, while the real code does):
  260. /// return dict([(slot, getattr(self, slot)) for slot in type(self).__slots__ if hasattr(self, slot)])
  261. ///
  262. /// Return null if the object has no __slots__, or empty dict if it has __slots__ but none are initialized.
  263. /// </summary>
  264. private static PythonDictionary GetInitializedSlotValues(object obj) {
  265. PythonDictionary initializedSlotValues = new PythonDictionary();
  266. IList<PythonType> mro = DynamicHelpers.GetPythonType(obj).ResolutionOrder;
  267. object slots;
  268. object slotValue;
  269. foreach (object type in mro) {
  270. if (PythonOps.TryGetBoundAttr(type, "__slots__", out slots)) {
  271. List<string> slotNames = PythonType.SlotsToList(slots);
  272. foreach (string slotName in slotNames) {
  273. if (slotName == "__dict__") continue;
  274. // don't reassign same-named slots from types earlier in the MRO
  275. if (initializedSlotValues.__contains__(slotName)) continue;
  276. if (PythonOps.TryGetBoundAttr(obj, slotName, out slotValue)) {
  277. initializedSlotValues[slotName] = slotValue;
  278. }
  279. }
  280. }
  281. }
  282. if (initializedSlotValues.Count == 0) return null;
  283. return initializedSlotValues;
  284. }
  285. /// <summary>
  286. /// Implements the default __reduce_ex__ method as specified by PEP 307 case 2 (new-style instance, protocol 0 or 1)
  287. /// </summary>
  288. internal static PythonTuple ReduceProtocol0(CodeContext/*!*/ context, object self) {
  289. // CPython implements this in copy_reg._reduce_ex
  290. PythonType myType = DynamicHelpers.GetPythonType(self); // PEP 307 calls this "D"
  291. ThrowIfNativelyPickable(myType);
  292. object getState;
  293. bool hasGetState = PythonOps.TryGetBoundAttr(context, self, "__getstate__", out getState);
  294. object slots;
  295. if (PythonOps.TryGetBoundAttr(context, myType, "__slots__", out slots) && PythonOps.Length(slots) > 0 && !hasGetState) {
  296. // ??? does this work with superclass slots?
  297. throw PythonOps.TypeError("a class that defines __slots__ without defining __getstate__ cannot be pickled with protocols 0 or 1");
  298. }
  299. PythonType closestNonPythonBase = FindClosestNonPythonBase(myType); // PEP 307 calls this "B"
  300. object func = PythonContext.GetContext(context).PythonReconstructor;
  301. object funcArgs = PythonTuple.MakeTuple(
  302. myType,
  303. closestNonPythonBase,
  304. TypeCache.Object == closestNonPythonBase ? null : PythonCalls.Call(context, closestNonPythonBase, self)
  305. );
  306. object state;
  307. if (hasGetState) {
  308. state = PythonOps.CallWithContext(context, getState);
  309. } else {
  310. IPythonObject ipo = self as IPythonObject;
  311. if (ipo != null) {
  312. state = ipo.Dict;
  313. } else if (!PythonOps.TryGetBoundAttr(context, self, "__dict__", out state)) {
  314. state = null;
  315. }
  316. }
  317. if (!PythonOps.IsTrue(state)) state = null;
  318. return PythonTuple.MakeTuple(func, funcArgs, state);
  319. }
  320. private static void ThrowIfNativelyPickable(PythonType type) {
  321. if (NativelyPickleableTypes.ContainsKey(type)) {
  322. throw PythonOps.TypeError("can't pickle {0} objects", type.Name);
  323. }
  324. }
  325. /// <summary>
  326. /// Returns the closest base class (in terms of MRO) that isn't defined in Python code
  327. /// </summary>
  328. private static PythonType FindClosestNonPythonBase(PythonType type) {
  329. foreach (PythonType pythonBase in type.ResolutionOrder) {
  330. if (pythonBase.IsSystemType) {
  331. return pythonBase;
  332. }
  333. }
  334. throw PythonOps.TypeError("can't pickle {0} instance: no non-Python bases found", type.Name);
  335. }
  336. /// <summary>
  337. /// Implements the default __reduce_ex__ method as specified by PEP 307 case 3 (new-style instance, protocol 2)
  338. /// </summary>
  339. private static PythonTuple ReduceProtocol2(CodeContext/*!*/ context, object self) {
  340. PythonType myType = DynamicHelpers.GetPythonType(self);
  341. object func, state, listIterator, dictIterator;
  342. object[] funcArgs;
  343. func = PythonContext.GetContext(context).NewObject;
  344. object getNewArgsCallable;
  345. if (PythonOps.TryGetBoundAttr(context, myType, "__getnewargs__", out getNewArgsCallable)) {
  346. // TypeError will bubble up if __getnewargs__ isn't callable
  347. PythonTuple newArgs = PythonOps.CallWithContext(context, getNewArgsCallable, self) as PythonTuple;
  348. if (newArgs == null) {
  349. throw PythonOps.TypeError("__getnewargs__ should return a tuple");
  350. }
  351. funcArgs = new object[1 + newArgs.Count];
  352. funcArgs[0] = myType;
  353. for (int i = 0; i < newArgs.Count; i++) funcArgs[i + 1] = newArgs[i];
  354. } else {
  355. funcArgs = new object[] { myType };
  356. }
  357. if (!PythonTypeOps.TryInvokeUnaryOperator(context,
  358. self,
  359. "__getstate__",
  360. out state)) {
  361. object dict;
  362. IPythonObject ipo = self as IPythonObject;
  363. if (ipo != null) {
  364. dict = ipo.Dict;
  365. } else if (!PythonOps.TryGetBoundAttr(context, self, "__dict__", out dict)) {
  366. dict = null;
  367. }
  368. PythonDictionary initializedSlotValues = GetInitializedSlotValues(self);
  369. if (initializedSlotValues != null && initializedSlotValues.Count == 0) {
  370. initializedSlotValues = null;
  371. }
  372. if (dict == null && initializedSlotValues == null) state = null;
  373. else if (dict != null && initializedSlotValues == null) state = dict;
  374. else if (dict != null && initializedSlotValues != null) state = PythonTuple.MakeTuple(dict, initializedSlotValues);
  375. else /*dict == null && initializedSlotValues != null*/ state = PythonTuple.MakeTuple(null, initializedSlotValues);
  376. }
  377. listIterator = null;
  378. if (self is List) {
  379. listIterator = PythonOps.GetEnumerator(self);
  380. }
  381. dictIterator = null;
  382. if (self is PythonDictionary || self is PythonDictionary) {
  383. dictIterator = PythonOps.Invoke(context, self, "iteritems", ArrayUtils.EmptyObjects);
  384. }
  385. return PythonTuple.MakeTuple(func, PythonTuple.MakeTuple(funcArgs), state, listIterator, dictIterator);
  386. }
  387. #endregion
  388. }
  389. }