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