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