PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_2_0/Src/IronPython/Runtime/Types/Mro.cs

#
C# | 181 lines | 100 code | 24 blank | 57 comment | 27 complexity | 87228a9d288c3e8fc55c53a1463c7315 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  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.Generic;
  17. using System.Diagnostics;
  18. using IronPython.Runtime.Operations;
  19. namespace IronPython.Runtime.Types {
  20. /// <summary>
  21. /// Calculates the method resolution order for a Python class
  22. /// the rules are:
  23. /// If A is a subtype of B, then A has precedence (A >B)
  24. /// If C appears before D in the list of bases then C > D
  25. /// If E > F in one __mro__ then E > F in all __mro__'s for our subtype
  26. ///
  27. /// class A(object): pass
  28. /// class B(object): pass
  29. /// class C(B): pass
  30. /// class N(A,B,C): pass # illegal
  31. ///
  32. /// This is because:
  33. /// C.__mro__ == (C, B, object)
  34. /// N.__mro__ == (N, A, B, C, object)
  35. /// which would conflict, but:
  36. ///
  37. /// N(B,A) is ok (N, B, a, object)
  38. /// N(C, B, A) is ok (N, C, B, A, object)
  39. ///
  40. /// Calculates a C3 MRO as described in "The Python 2.3 Method Resolution Order"
  41. /// plus support for old-style classes.
  42. ///
  43. /// We build up a list of our base classes MRO's plus our base classes themselves.
  44. /// We go through the list in order. Look at the 1st class in the current list, and
  45. /// if it's not the non-first class in any other list then remove it from all the lists
  46. /// and append it to the mro. Otherwise continue to the next list. If all the classes at
  47. /// the start are no-good then the MRO is bad and we throw.
  48. ///
  49. /// For old-style classes if the old-style class is the only one in the list of bases add
  50. /// it as a depth-first old-style MRO, otherwise compute a new-style mro for all the classes
  51. /// and use that.
  52. /// </summary>
  53. class Mro {
  54. public Mro() {
  55. }
  56. public static List<PythonType> Calculate(PythonType startingType, IList<PythonType> bases) {
  57. return Calculate(startingType, new List<PythonType>(bases), false);
  58. }
  59. /// <summary>
  60. /// </summary>
  61. public static List<PythonType> Calculate(PythonType startingType, IList<PythonType> baseTypes, bool forceNewStyle) {
  62. List<PythonType> bases = new List<PythonType>();
  63. foreach (PythonType dt in baseTypes) bases.Add(dt);
  64. if (bases.Contains(startingType)) {
  65. throw PythonOps.TypeError("a __bases__ item causes an inheritance cycle ({0})", startingType.Name);
  66. }
  67. List<PythonType> mro = new List<PythonType>();
  68. mro.Add(startingType);
  69. if (bases.Count != 0) {
  70. List<IList<PythonType>> mroList = new List<IList<PythonType>>();
  71. // build up the list - it contains the MRO of all our
  72. // bases as well as the bases themselves in the order in
  73. // which they appear.
  74. int oldSytleCount = 0;
  75. foreach (PythonType type in bases) {
  76. if (type.IsOldClass) oldSytleCount++;
  77. }
  78. foreach (PythonType dt in bases) {
  79. if (!dt.IsOldClass) {
  80. mroList.Add(TupleToList(dt.ResolutionOrder));
  81. } else if (oldSytleCount == 1 && !forceNewStyle) {
  82. mroList.Add(GetOldStyleMro(dt));
  83. } else {
  84. mroList.Add(GetNewStyleMro(dt));
  85. }
  86. }
  87. mroList.Add(TupleToList(bases));
  88. for (; ; ) {
  89. bool removed = false, sawNonZero = false;
  90. // now that we have our list, look for good heads
  91. for (int i = 0; i < mroList.Count; i++) {
  92. if (mroList[i].Count == 0) continue; // we've removed everything from this list.
  93. sawNonZero = true;
  94. PythonType head = mroList[i][0];
  95. // see if we're in the tail of any other lists...
  96. bool inTail = false;
  97. for (int j = 0; j < mroList.Count; j++) {
  98. if (mroList[j].Count != 0 && !mroList[j][0].Equals(head) && mroList[j].Contains(head)) {
  99. inTail = true;
  100. break;
  101. }
  102. }
  103. if (!inTail) {
  104. if (mro.Contains(head)) {
  105. throw PythonOps.TypeError("a __bases__ item causes an inheritance cycle");
  106. }
  107. // add it to the linearization, and remove
  108. // it from our lists
  109. mro.Add(head);
  110. for (int j = 0; j < mroList.Count; j++) {
  111. mroList[j].Remove(head);
  112. }
  113. removed = true;
  114. break;
  115. }
  116. }
  117. if (!sawNonZero) break;
  118. if (!removed) {
  119. // we've iterated through the list once w/o removing anything
  120. throw PythonOps.TypeError("invalid order for base classes: {0}, {1}",
  121. mroList[0][0].Name,
  122. mroList[1][0].Name);
  123. }
  124. }
  125. }
  126. return mro;
  127. }
  128. private static IList<PythonType> TupleToList(IList<PythonType> t) {
  129. return new List<PythonType>(t);
  130. }
  131. private static IList<PythonType> GetOldStyleMro(PythonType oldStyleType) {
  132. List<PythonType> res = new List<PythonType>();
  133. GetOldStyleMroWorker(oldStyleType, res);
  134. return res;
  135. }
  136. private static void GetOldStyleMroWorker(PythonType curType, List<PythonType> res) {
  137. PythonType dt = curType as PythonType;
  138. Debug.Assert(dt != null);
  139. if (!res.Contains(curType)) {
  140. res.Add(curType);
  141. foreach (PythonType baseDt in dt.BaseTypes) {
  142. GetOldStyleMroWorker(baseDt, res);
  143. }
  144. }
  145. }
  146. private static IList<PythonType> GetNewStyleMro(PythonType oldStyleType) {
  147. PythonType dt = oldStyleType as PythonType;
  148. Debug.Assert(dt != null);
  149. List<PythonType> res = new List<PythonType>();
  150. res.Add(oldStyleType);
  151. foreach (PythonType baseDt in dt.BaseTypes) {
  152. res.AddRange(TupleToList(Calculate(baseDt, baseDt.BaseTypes, true)));
  153. }
  154. return res;
  155. }
  156. }
  157. }