PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython/Runtime/Types/OldClass.cs

http://github.com/IronLanguages/main
C# | 652 lines | 489 code | 128 blank | 35 comment | 92 complexity | 06504cbe9bcf6e6c31f829683f467545 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  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. * dlr@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 FEATURE_CORE_DLR
  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.ComponentModel;
  23. using System.Diagnostics;
  24. using System.Dynamic;
  25. using System.Runtime.Serialization;
  26. using System.Threading;
  27. using Microsoft.Scripting;
  28. using Microsoft.Scripting.Runtime;
  29. using Microsoft.Scripting.Utils;
  30. using IronPython.Runtime.Operations;
  31. using SpecialNameAttribute = System.Runtime.CompilerServices.SpecialNameAttribute;
  32. namespace IronPython.Runtime.Types {
  33. // OldClass represents the type of old-style Python classes (which could not inherit from
  34. // built-in Python types).
  35. //
  36. // Object instances of old-style Python classes are represented by OldInstance.
  37. //
  38. // UserType is the equivalent of OldClass for new-style Python classes (which can inherit
  39. // from built-in types).
  40. [Flags]
  41. enum OldClassAttributes {
  42. None = 0x00,
  43. HasFinalizer = 0x01,
  44. HasSetAttr = 0x02,
  45. HasDelAttr = 0x04,
  46. }
  47. [PythonType("classobj"), DontMapGetMemberNamesToDir]
  48. [Serializable]
  49. [DebuggerTypeProxy(typeof(OldClass.OldClassDebugView)), DebuggerDisplay("old-style class {Name}")]
  50. public sealed class OldClass :
  51. #if FEATURE_CUSTOM_TYPE_DESCRIPTOR
  52. ICustomTypeDescriptor,
  53. #endif
  54. ICodeFormattable,
  55. IMembersList,
  56. IDynamicMetaObjectProvider,
  57. IPythonMembersList,
  58. IWeakReferenceable {
  59. [NonSerialized]
  60. private List<OldClass> _bases;
  61. private PythonType _type;
  62. internal PythonDictionary _dict;
  63. private int _attrs; // actually OldClassAttributes - losing type safety for thread safety
  64. internal object _name;
  65. private int _optimizedInstanceNamesVersion;
  66. private string[] _optimizedInstanceNames;
  67. private WeakRefTracker _tracker;
  68. public static string __doc__ = "classobj(name, bases, dict)";
  69. public static object __new__(CodeContext/*!*/ context, [NotNull]PythonType cls, string name, PythonTuple bases, PythonDictionary/*!*/ dict) {
  70. if (dict == null) {
  71. throw PythonOps.TypeError("dict must be a dictionary");
  72. } else if (cls != TypeCache.OldClass) {
  73. throw PythonOps.TypeError("{0} is not a subtype of classobj", cls.Name);
  74. }
  75. if (!dict.ContainsKey("__module__")) {
  76. object moduleValue;
  77. if (context.TryGetGlobalVariable("__name__", out moduleValue)) {
  78. dict["__module__"] = moduleValue;
  79. }
  80. }
  81. foreach (object o in bases) {
  82. if (o is PythonType) {
  83. return PythonOps.MakeClass(context, name, bases._data, String.Empty, dict);
  84. }
  85. }
  86. return new OldClass(name, bases, dict, String.Empty);
  87. }
  88. internal OldClass(string name, PythonTuple bases, PythonDictionary/*!*/ dict, string instanceNames) {
  89. _bases = ValidateBases(bases);
  90. Init(name, dict, instanceNames);
  91. }
  92. private void Init(string name, PythonDictionary/*!*/ dict, string instanceNames) {
  93. _name = name;
  94. InitializeInstanceNames(instanceNames);
  95. _dict = dict;
  96. if (!_dict._storage.Contains("__doc__")) {
  97. _dict._storage.Add(ref _dict._storage, "__doc__", null);
  98. }
  99. CheckSpecialMethods(_dict);
  100. }
  101. private void CheckSpecialMethods(PythonDictionary dict) {
  102. if (dict._storage.Contains("__del__")) {
  103. HasFinalizer = true;
  104. }
  105. if (dict._storage.Contains("__setattr__")) {
  106. HasSetAttr = true;
  107. }
  108. if (dict._storage.Contains("__delattr__")) {
  109. HasDelAttr = true;
  110. }
  111. foreach (OldClass oc in _bases) {
  112. if (oc.HasDelAttr) {
  113. HasDelAttr = true;
  114. }
  115. if (oc.HasSetAttr) {
  116. HasSetAttr = true;
  117. }
  118. if (oc.HasFinalizer) {
  119. HasFinalizer = true;
  120. }
  121. }
  122. }
  123. #if FEATURE_SERIALIZATION
  124. private OldClass(SerializationInfo info, StreamingContext context) {
  125. _bases = (List<OldClass>)info.GetValue("__class__", typeof(List<OldClass>));
  126. _name = info.GetValue("__name__", typeof(object));
  127. _dict = new PythonDictionary();
  128. InitializeInstanceNames(""); //TODO should we serialize the optimization data
  129. List<object> keys = (List<object>)info.GetValue("keys", typeof(List<object>));
  130. List<object> values = (List<object>)info.GetValue("values", typeof(List<object>));
  131. for (int i = 0; i < keys.Count; i++) {
  132. _dict[keys[i]] = values[i];
  133. }
  134. if (_dict.has_key("__del__")) HasFinalizer = true;
  135. }
  136. #pragma warning disable 169 // unused method - called via reflection from serialization
  137. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "context")]
  138. private void GetObjectData(SerializationInfo info, StreamingContext context) {
  139. ContractUtils.RequiresNotNull(info, "info");
  140. info.AddValue("__bases__", _bases);
  141. info.AddValue("__name__", _name);
  142. List<object> keys = new List<object>();
  143. List<object> values = new List<object>();
  144. foreach (KeyValuePair<object, object> kvp in _dict._storage.GetItems()) {
  145. keys.Add(kvp.Key);
  146. values.Add(kvp.Value);
  147. }
  148. info.AddValue("keys", keys);
  149. info.AddValue("values", values);
  150. }
  151. #pragma warning restore 169
  152. #endif
  153. private void InitializeInstanceNames(string instanceNames) {
  154. if (instanceNames.Length == 0) {
  155. _optimizedInstanceNames = ArrayUtils.EmptyStrings;
  156. _optimizedInstanceNamesVersion = 0;
  157. return;
  158. }
  159. string[] names = instanceNames.Split(',');
  160. _optimizedInstanceNames = new string[names.Length];
  161. for (int i = 0; i < names.Length; i++) {
  162. _optimizedInstanceNames[i] = names[i];
  163. }
  164. _optimizedInstanceNamesVersion = CustomInstanceDictionaryStorage.AllocateVersion();
  165. }
  166. internal string[] OptimizedInstanceNames {
  167. get { return _optimizedInstanceNames; }
  168. }
  169. internal int OptimizedInstanceNamesVersion {
  170. get { return _optimizedInstanceNamesVersion; }
  171. }
  172. internal string Name {
  173. get { return _name.ToString(); }
  174. }
  175. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
  176. internal bool TryLookupSlot(string name, out object ret) {
  177. if (_dict._storage.TryGetValue(name, out ret)) {
  178. return true;
  179. }
  180. // bases only ever contains OldClasses (tuples are immutable, and when assigned
  181. // to we verify the types in the Tuple)
  182. foreach (OldClass c in _bases) {
  183. if (c.TryLookupSlot(name, out ret)) return true;
  184. }
  185. ret = null;
  186. return false;
  187. }
  188. internal bool TryLookupOneSlot(PythonType lookingType, string name, out object ret) {
  189. if (_dict._storage.TryGetValue(name, out ret)) {
  190. ret = GetOldStyleDescriptor(TypeObject.Context.SharedContext, ret, null, lookingType);
  191. return true;
  192. }
  193. return false;
  194. }
  195. internal string FullName {
  196. get { return _dict["__module__"].ToString() + '.' + _name; }
  197. }
  198. internal List<OldClass> BaseClasses {
  199. get {
  200. return _bases;
  201. }
  202. }
  203. internal object GetOldStyleDescriptor(CodeContext context, object self, object instance, object type) {
  204. PythonTypeSlot dts = self as PythonTypeSlot;
  205. object callable;
  206. if (dts != null && dts.TryGetValue(context, instance, TypeObject, out callable)) {
  207. return callable;
  208. }
  209. return PythonOps.GetUserDescriptor(self, instance, type);
  210. }
  211. internal static object GetOldStyleDescriptor(CodeContext context, object self, object instance, PythonType type) {
  212. PythonTypeSlot dts = self as PythonTypeSlot;
  213. object callable;
  214. if (dts != null && dts.TryGetValue(context, instance, type, out callable)) {
  215. return callable;
  216. }
  217. return PythonOps.GetUserDescriptor(self, instance, type);
  218. }
  219. internal bool HasFinalizer {
  220. get {
  221. return (_attrs & (int)OldClassAttributes.HasFinalizer) != 0;
  222. }
  223. set {
  224. int oldAttrs, newAttrs;
  225. do {
  226. oldAttrs = _attrs;
  227. newAttrs = value ? oldAttrs | ((int)OldClassAttributes.HasFinalizer) : oldAttrs & ((int)~OldClassAttributes.HasFinalizer);
  228. } while (Interlocked.CompareExchange(ref _attrs, newAttrs, oldAttrs) != oldAttrs);
  229. }
  230. }
  231. internal bool HasSetAttr {
  232. get {
  233. return (_attrs & (int)OldClassAttributes.HasSetAttr) != 0;
  234. }
  235. set {
  236. int oldAttrs, newAttrs;
  237. do {
  238. oldAttrs = _attrs;
  239. newAttrs = value ? oldAttrs | ((int)OldClassAttributes.HasSetAttr) : oldAttrs & ((int)~OldClassAttributes.HasSetAttr);
  240. } while (Interlocked.CompareExchange(ref _attrs, newAttrs, oldAttrs) != oldAttrs);
  241. }
  242. }
  243. internal bool HasDelAttr {
  244. get {
  245. return (_attrs & (int)OldClassAttributes.HasDelAttr) != 0;
  246. }
  247. set {
  248. int oldAttrs, newAttrs;
  249. do {
  250. oldAttrs = _attrs;
  251. newAttrs = value ? oldAttrs | ((int)OldClassAttributes.HasDelAttr) : oldAttrs & ((int)~OldClassAttributes.HasDelAttr);
  252. } while (Interlocked.CompareExchange(ref _attrs, newAttrs, oldAttrs) != oldAttrs);
  253. }
  254. }
  255. public override string ToString() {
  256. return FullName;
  257. }
  258. #region Calls
  259. // Calling an OldClass instance means instantiating that class and invoking the __init__() member if
  260. // it's defined.
  261. // OldClass impls IDynamicMetaObjectProvider. But May wind up here still if IDynamicObj doesn't provide a rule (such as for list sigs).
  262. // If our IDynamicMetaObjectProvider implementation is complete, we can then remove these Call methods.
  263. [SpecialName]
  264. public object Call(CodeContext context, [NotNull]params object[] args\u00F8) {
  265. OldInstance inst = new OldInstance(context, this);
  266. object value;
  267. // lookup the slot directly - we don't go through __getattr__
  268. // until after the instance is created.
  269. if (TryLookupSlot("__init__", out value)) {
  270. PythonOps.CallWithContext(context, GetOldStyleDescriptor(context, value, inst, this), args\u00F8);
  271. } else if (args\u00F8.Length > 0) {
  272. MakeCallError();
  273. }
  274. return inst;
  275. }
  276. [SpecialName]
  277. public object Call(CodeContext context, [ParamDictionary]IDictionary<object, object> dict\u00F8, [NotNull]params object[] args\u00F8) {
  278. OldInstance inst = new OldInstance(context, this);
  279. object meth;
  280. if (PythonOps.TryGetBoundAttr(inst, "__init__", out meth)) {
  281. PythonCalls.CallWithKeywordArgs(context, meth, args\u00F8, dict\u00F8);
  282. } else if (dict\u00F8.Count > 0 || args\u00F8.Length > 0) {
  283. MakeCallError();
  284. }
  285. return inst;
  286. }
  287. #endregion // calls
  288. internal PythonType TypeObject {
  289. get {
  290. if (_type == null) {
  291. Interlocked.CompareExchange(ref _type, new PythonType(this), null);
  292. }
  293. return _type;
  294. }
  295. }
  296. private List<OldClass> ValidateBases(object value) {
  297. PythonTuple t = value as PythonTuple;
  298. if (t == null) throw PythonOps.TypeError("__bases__ must be a tuple object");
  299. List<OldClass> res = new List<OldClass>(t.__len__());
  300. foreach (object o in t) {
  301. OldClass oc = o as OldClass;
  302. if (oc == null) throw PythonOps.TypeError("__bases__ items must be classes (got {0})", PythonTypeOps.GetName(o));
  303. if (oc.IsSubclassOf(this)) {
  304. throw PythonOps.TypeError("a __bases__ item causes an inheritance cycle");
  305. }
  306. res.Add(oc);
  307. }
  308. return res;
  309. }
  310. internal object GetMember(CodeContext context, string name) {
  311. object value;
  312. if (!TryGetBoundCustomMember(context, name, out value)) {
  313. throw PythonOps.AttributeError("type object '{0}' has no attribute '{1}'", Name, name);
  314. }
  315. return value;
  316. }
  317. internal bool TryGetBoundCustomMember(CodeContext context, string name, out object value) {
  318. if (name == "__bases__") { value = PythonTuple.Make(_bases); return true; }
  319. if (name == "__name__") { value = _name; return true; }
  320. if (name == "__dict__") {
  321. //!!! user code can modify __del__ property of __dict__ behind our back
  322. HasDelAttr = HasSetAttr = true; // pessimisticlly assume the user is setting __setattr__ in the dict
  323. value = _dict; return true;
  324. }
  325. if (TryLookupSlot(name, out value)) {
  326. value = GetOldStyleDescriptor(context, value, null, this);
  327. return true;
  328. }
  329. return false;
  330. }
  331. internal bool DeleteCustomMember(CodeContext context, string name) {
  332. if (!_dict._storage.Remove(ref _dict._storage, name)) {
  333. throw PythonOps.AttributeError("{0} is not a valid attribute", name);
  334. }
  335. if (name == "__del__") {
  336. HasFinalizer = false;
  337. }
  338. if (name == "__setattr__") {
  339. HasSetAttr = false;
  340. }
  341. if (name == "__delattr__") {
  342. HasDelAttr = false;
  343. }
  344. return true;
  345. }
  346. internal static void RecurseAttrHierarchy(OldClass oc, IDictionary<object, object> attrs) {
  347. foreach (KeyValuePair<object, object> kvp in oc._dict._storage.GetItems()) {
  348. if (!attrs.ContainsKey(kvp.Key)) {
  349. attrs.Add(kvp.Key, kvp.Key);
  350. }
  351. }
  352. // recursively get attrs in parent hierarchy
  353. if (oc._bases.Count != 0) {
  354. foreach (OldClass parent in oc._bases) {
  355. RecurseAttrHierarchy(parent, attrs);
  356. }
  357. }
  358. }
  359. #region IMembersList Members
  360. IList<string> IMembersList.GetMemberNames() {
  361. return PythonOps.GetStringMemberList(this);
  362. }
  363. IList<object> IPythonMembersList.GetMemberNames(CodeContext/*!*/ context) {
  364. PythonDictionary attrs = new PythonDictionary(_dict);
  365. RecurseAttrHierarchy(this, attrs);
  366. return PythonOps.MakeListFromSequence(attrs);
  367. }
  368. #endregion
  369. internal bool IsSubclassOf(object other) {
  370. if (this == other) return true;
  371. OldClass dt = other as OldClass;
  372. if (dt == null) return false;
  373. List<OldClass> bases = _bases;
  374. foreach (OldClass bc in bases) {
  375. if (bc.IsSubclassOf(other)) {
  376. return true;
  377. }
  378. }
  379. return false;
  380. }
  381. #region ICustomTypeDescriptor Members
  382. #if FEATURE_CUSTOM_TYPE_DESCRIPTOR
  383. AttributeCollection ICustomTypeDescriptor.GetAttributes() {
  384. return CustomTypeDescHelpers.GetAttributes(this);
  385. }
  386. string ICustomTypeDescriptor.GetClassName() {
  387. return CustomTypeDescHelpers.GetClassName(this);
  388. }
  389. string ICustomTypeDescriptor.GetComponentName() {
  390. return CustomTypeDescHelpers.GetComponentName(this);
  391. }
  392. TypeConverter ICustomTypeDescriptor.GetConverter() {
  393. return CustomTypeDescHelpers.GetConverter(this);
  394. }
  395. EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
  396. return CustomTypeDescHelpers.GetDefaultEvent(this);
  397. }
  398. PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
  399. return CustomTypeDescHelpers.GetDefaultProperty(this);
  400. }
  401. object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
  402. return CustomTypeDescHelpers.GetEditor(this, editorBaseType);
  403. }
  404. EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
  405. return CustomTypeDescHelpers.GetEvents(attributes);
  406. }
  407. EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
  408. return CustomTypeDescHelpers.GetEvents(this);
  409. }
  410. PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
  411. return CustomTypeDescHelpers.GetProperties(attributes);
  412. }
  413. PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
  414. return CustomTypeDescHelpers.GetProperties(this);
  415. }
  416. object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
  417. return CustomTypeDescHelpers.GetPropertyOwner(this, pd);
  418. }
  419. #endif
  420. #endregion
  421. #region ICodeFormattable Members
  422. public string/*!*/ __repr__(CodeContext/*!*/ context) {
  423. return string.Format("<class {0} at {1}>", FullName, PythonOps.HexId(this));
  424. }
  425. #endregion
  426. #region Internal Member Accessors
  427. internal bool TryLookupInit(object inst, out object ret) {
  428. if (TryLookupSlot("__init__", out ret)) {
  429. ret = GetOldStyleDescriptor(DefaultContext.Default, ret, inst, this);
  430. return true;
  431. }
  432. return false;
  433. }
  434. internal static object MakeCallError() {
  435. // Normally, if we have an __init__ method, the method binder detects signature mismatches.
  436. // This can happen when a class does not define __init__ and therefore does not take any arguments.
  437. // Beware that calls like F(*(), **{}) have 2 arguments but they're empty and so it should still
  438. // match against def F().
  439. throw PythonOps.TypeError("this constructor takes no arguments");
  440. }
  441. internal void SetBases(object value) {
  442. _bases = ValidateBases(value);
  443. }
  444. internal void SetName(object value) {
  445. string n = value as string;
  446. if (n == null) throw PythonOps.TypeError("TypeError: __name__ must be a string object");
  447. _name = n;
  448. }
  449. internal void SetDictionary(object value) {
  450. PythonDictionary d = value as PythonDictionary;
  451. if (d == null) throw PythonOps.TypeError("__dict__ must be set to dictionary");
  452. _dict = d;
  453. }
  454. internal void SetNameHelper(string name, object value) {
  455. _dict._storage.Add(ref _dict._storage, name, value);
  456. if (name == "__del__") {
  457. HasFinalizer = true;
  458. } else if (name == "__setattr__") {
  459. HasSetAttr = true;
  460. } else if (name == "__delattr__") {
  461. HasDelAttr = true;
  462. }
  463. }
  464. internal object LookupValue(CodeContext context, string name) {
  465. object value;
  466. if (TryLookupValue(context, name, out value)) {
  467. return value;
  468. }
  469. throw PythonOps.AttributeErrorForMissingAttribute(this, name);
  470. }
  471. internal bool TryLookupValue(CodeContext context, string name, out object value) {
  472. if (TryLookupSlot(name, out value)) {
  473. value = GetOldStyleDescriptor(context, value, null, this);
  474. return true;
  475. }
  476. return false;
  477. }
  478. internal void DictionaryIsPublic() {
  479. HasDelAttr = true;
  480. HasSetAttr = true;
  481. }
  482. #endregion
  483. #region IDynamicMetaObjectProvider Members
  484. DynamicMetaObject/*!*/ IDynamicMetaObjectProvider.GetMetaObject(Expression/*!*/ parameter) {
  485. return new Binding.MetaOldClass(parameter, BindingRestrictions.Empty, this);
  486. }
  487. #endregion
  488. internal class OldClassDebugView {
  489. private readonly OldClass _class;
  490. public OldClassDebugView(OldClass klass) {
  491. _class = klass;
  492. }
  493. public List<OldClass> __bases__ {
  494. get {
  495. return _class.BaseClasses;
  496. }
  497. }
  498. [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
  499. internal List<ObjectDebugView> Members {
  500. get {
  501. var res = new List<ObjectDebugView>();
  502. if (_class._dict != null) {
  503. foreach (var v in _class._dict) {
  504. res.Add(new ObjectDebugView(v.Key, v.Value));
  505. }
  506. }
  507. return res;
  508. }
  509. }
  510. }
  511. #region IWeakReferenceable Members
  512. WeakRefTracker IWeakReferenceable.GetWeakRef() {
  513. return _tracker;
  514. }
  515. bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
  516. _tracker = value;
  517. return true;
  518. }
  519. void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
  520. _tracker = value;
  521. }
  522. #endregion
  523. }
  524. }