PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Types/OldClass.cs

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