OLD | NEW |
---|---|
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 library kernel.class_hierarchy; | 4 library kernel.class_hierarchy; |
5 | 5 |
6 import 'ast.dart'; | 6 import 'ast.dart'; |
7 import 'dart:math'; | 7 import 'dart:math'; |
8 import 'dart:typed_data'; | 8 import 'dart:typed_data'; |
9 import 'heap.dart'; | |
9 import 'type_algebra.dart'; | 10 import 'type_algebra.dart'; |
10 | 11 |
11 /// Data structure for answering various subclassing queries. | 12 /// Data structure for answering various subclassing queries. |
12 class ClassHierarchy { | 13 class ClassHierarchy { |
13 /// All classes in the program. | 14 /// All classes in the program. |
14 /// | 15 /// |
15 /// The list is ordered so that classes occur after their super classes. | 16 /// The list is ordered so that classes occur after their super classes. |
16 final List<Class> classes; | 17 final List<Class> classes; |
17 | 18 |
18 final Map<Class, _ClassInfo> _infoFor = <Class, _ClassInfo>{}; | 19 final Map<Class, _ClassInfo> _infoFor = <Class, _ClassInfo>{}; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
55 /// mixin application (i.e. [Class.mixedInType]). | 56 /// mixin application (i.e. [Class.mixedInType]). |
56 bool isUsedAsMixin(Class class_) { | 57 bool isUsedAsMixin(Class class_) { |
57 return _infoFor[class_].directMixers.isNotEmpty; | 58 return _infoFor[class_].directMixers.isNotEmpty; |
58 } | 59 } |
59 | 60 |
60 /// True if the given class is used in an `implements` clause. | 61 /// True if the given class is used in an `implements` clause. |
61 bool isUsedAsSuperInterface(Class class_) { | 62 bool isUsedAsSuperInterface(Class class_) { |
62 return _infoFor[class_].directImplementers.isNotEmpty; | 63 return _infoFor[class_].directImplementers.isNotEmpty; |
63 } | 64 } |
64 | 65 |
66 /// Returns the number of steps in the longest inheritance path from [class_] | |
67 /// to [rootClass]. | |
68 int getClassDepth(Class class_) => _infoFor[class_].depth; | |
69 | |
70 /// Returns a list of classes appropriate for use in calculating a least upper | |
71 /// bound. | |
72 /// | |
73 /// The returned list is a list of all classes that [class_] is a subtype of | |
74 /// (including itself), sorted first by depth (deepest first) and then by | |
75 /// class index. | |
76 List<Class> getLeastUpperBoundClasses(Class class_) { | |
ahe
2017/05/01 16:50:34
Consider renaming to something like getRankedSuper
Paul Berry
2017/05/01 18:12:13
Done. I went with "getRankedSuperclasses" to emph
| |
77 return _getLeastUpperBoundInfos(_infoFor[class_]) | |
78 .map((info) => info.classNode) | |
79 .toList(); | |
80 } | |
81 | |
82 List<_ClassInfo> _getLeastUpperBoundInfos(_ClassInfo info) { | |
83 if (info.leastUpperBoundInfos != null) return info.leastUpperBoundInfos; | |
84 var heap = new _LubHeap()..add(info); | |
85 var chain = <_ClassInfo>[]; | |
86 info.leastUpperBoundInfos = chain; | |
87 _ClassInfo lastInfo = null; | |
88 while (heap.isNotEmpty) { | |
89 var nextInfo = heap.remove(); | |
90 if (identical(nextInfo, lastInfo)) continue; | |
91 chain.add(nextInfo); | |
92 lastInfo = nextInfo; | |
93 var classNode = nextInfo.classNode; | |
94 void addToHeap(Supertype supertype) { | |
95 heap.add(_infoFor[supertype.classNode]); | |
96 } | |
97 | |
98 if (classNode.supertype != null) addToHeap(classNode.supertype); | |
99 if (classNode.mixedInType != null) addToHeap(classNode.mixedInType); | |
100 classNode.implementedTypes.forEach(addToHeap); | |
101 } | |
102 return chain; | |
103 } | |
104 | |
105 /// Returns the least upper bound of two interface types, as defined by Dart | |
106 /// 1.0. | |
107 /// | |
108 /// Given two interfaces I and J, lest S_I be the set of superinterfaces of I, | |
ahe
2017/05/01 16:50:34
lest -> let.
Paul Berry
2017/05/01 18:12:13
Done.
| |
109 /// let S_J be the set of superinterfaces of J, and let | |
110 /// S = (I union S_I) intersect (J union S_J). Furthermore, we define | |
111 /// S_n = {T | T in S and depth(T) = n} for any finite n where depth(T) is | |
112 /// the number of steps in the longest inheritance path from T to Object. Let | |
113 /// q be the largest number such that S_q has cardinality one. The least | |
114 /// upper bound of I and J is the sole element of S_q. | |
115 /// | |
116 /// This is called the "classic" least upper bound to distinguish it from the | |
117 /// strong mode least upper bound, which has special behaviors in the case | |
118 /// where one type is a subtype of the other, or where both types are based on | |
119 /// the same class. | |
120 InterfaceType getClassicLeastUpperBound( | |
121 InterfaceType type1, InterfaceType type2) { | |
122 // The algorithm is: first we compute a list of superclasses for both types, | |
123 // ordered from greatest to least depth, and ordered by topological sort | |
124 // index within each depth. Due to the sort order, we can find the | |
125 // intersection of these lists by a simple walk. | |
126 // | |
127 // Then, for each class in the intersection, determine the exact type that | |
128 // is implemented by type1 and type2. If the types match, that type is a | |
129 // candidate (it's a member of S_n). As soon as we find a candidate which | |
130 // is unique for its depth, we return it. | |
131 // | |
132 // As an optimization, if the class for I is a subtype of the class for J, | |
133 // then we know that the list of superclasses of J is a subset of the list | |
134 // of superclasses for I; therefore it is sufficient to compute just the | |
135 // list of superclasses for J. To avoid complicating the code below (which | |
136 // intersects the two lists), we set both lists equal to the list of | |
137 // superclasses for J. And vice versa with the role of I and J swapped. | |
138 | |
139 // Compute the list of superclasses for both types, with the above | |
140 // optimization. | |
141 _ClassInfo info1 = _infoFor[type1.classNode]; | |
142 _ClassInfo info2 = _infoFor[type2.classNode]; | |
143 List<_ClassInfo> classes1; | |
144 List<_ClassInfo> classes2; | |
145 if (identical(info1, info2) || info1.isSubtypeOf(info2)) { | |
146 classes1 = classes2 = _getLeastUpperBoundInfos(info2); | |
147 } else if (info2.isSubtypeOf(info1)) { | |
148 classes1 = classes2 = _getLeastUpperBoundInfos(info1); | |
149 } else { | |
150 classes1 = _getLeastUpperBoundInfos(info1); | |
151 classes2 = _getLeastUpperBoundInfos(info2); | |
152 } | |
153 | |
154 // Walk the lists finding their intersection, looking for a depth that has a | |
155 // single candidate. | |
156 int i1 = 0; | |
157 int i2 = 0; | |
158 InterfaceType candidate = null; | |
159 int currentDepth = -1; | |
160 int numCandidatesAtThisDepth = 0; | |
161 while (true) { | |
162 _ClassInfo next = classes1[i1]; | |
163 _ClassInfo next2 = classes2[i2]; | |
164 if (!identical(next, next2)) { | |
165 if (_LubHeap.sortsBeforeStatic(next, next2)) { | |
166 ++i1; | |
167 } else { | |
168 ++i2; | |
169 } | |
170 continue; | |
171 } | |
172 ++i2; | |
173 ++i1; | |
174 if (next.depth != currentDepth) { | |
175 if (numCandidatesAtThisDepth == 1) return candidate; | |
176 currentDepth = next.depth; | |
177 numCandidatesAtThisDepth = 0; | |
178 candidate = null; | |
179 } else if (numCandidatesAtThisDepth > 1) { | |
180 continue; | |
181 } | |
182 | |
183 // For each class in the intersection, find the exact type that is | |
184 // implemented by type1 and type2. If they match, it's a candidate. | |
185 // | |
186 // Two additional optimizations: | |
187 // | |
188 // - If this class lacks type parameters, we know there is a match without | |
189 // needing to substitute. | |
190 // | |
191 // - If the depth is 0, we have reached Object, so we can return it | |
192 // immediately. Since all interface types are subtypes of Object, this | |
193 // ensures the loop terminates. | |
194 if (next.classNode.typeParameters.isEmpty) { | |
195 candidate = next.classNode.rawType; | |
196 if (currentDepth == 0) return candidate; | |
197 ++numCandidatesAtThisDepth; | |
198 } else { | |
199 var superType1 = identical(info1, next) | |
200 ? type1 | |
201 : Substitution.fromInterfaceType(type1).substituteType( | |
202 info1.genericSuperTypes[next.classNode].asInterfaceType); | |
203 var superType2 = identical(info2, next) | |
204 ? type2 | |
205 : Substitution.fromInterfaceType(type2).substituteType( | |
206 info2.genericSuperTypes[next.classNode].asInterfaceType); | |
207 if (superType1 == superType2) { | |
208 candidate = superType1; | |
209 ++numCandidatesAtThisDepth; | |
210 } | |
211 } | |
212 } | |
213 } | |
214 | |
65 /// Returns the instantiation of [superclass] that is implemented by [class_], | 215 /// Returns the instantiation of [superclass] that is implemented by [class_], |
66 /// or `null` if [class_] does not implement [superclass] at all. | 216 /// or `null` if [class_] does not implement [superclass] at all. |
67 Supertype getClassAsInstanceOf(Class class_, Class superclass) { | 217 Supertype getClassAsInstanceOf(Class class_, Class superclass) { |
68 if (identical(class_, superclass)) return class_.asThisSupertype; | 218 if (identical(class_, superclass)) return class_.asThisSupertype; |
69 _ClassInfo info = _infoFor[class_]; | 219 _ClassInfo info = _infoFor[class_]; |
70 _ClassInfo superInfo = _infoFor[superclass]; | 220 _ClassInfo superInfo = _infoFor[superclass]; |
71 if (!info.isSubtypeOf(superInfo)) return null; | 221 if (!info.isSubtypeOf(superInfo)) return null; |
72 if (superclass.typeParameters.isEmpty) return superclass.asRawSupertype; | 222 if (superclass.typeParameters.isEmpty) return superclass.asRawSupertype; |
73 return info.genericSuperTypes[superclass]; | 223 return info.genericSuperTypes[superclass]; |
74 } | 224 } |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
279 | 429 |
280 for (int i = 0; i < classes.length; ++i) { | 430 for (int i = 0; i < classes.length; ++i) { |
281 var class_ = classes[i]; | 431 var class_ = classes[i]; |
282 _buildInterfaceMembers(class_, _infoFor[class_], setters: true); | 432 _buildInterfaceMembers(class_, _infoFor[class_], setters: true); |
283 _buildInterfaceMembers(class_, _infoFor[class_], setters: false); | 433 _buildInterfaceMembers(class_, _infoFor[class_], setters: false); |
284 } | 434 } |
285 } | 435 } |
286 | 436 |
287 /// Upwards traversal of the class hierarchy that orders classes so super | 437 /// Upwards traversal of the class hierarchy that orders classes so super |
288 /// types before their subtypes. | 438 /// types before their subtypes. |
439 /// | |
440 /// Returns the depth of the visited class (the number of steps in the longest | |
441 /// inheritance path to the root class). | |
289 int _topSortIndex = 0; | 442 int _topSortIndex = 0; |
290 void _topologicalSortVisit(Class classNode) { | 443 int _topologicalSortVisit(Class classNode) { |
291 var info = _infoFor[classNode]; | 444 var info = _infoFor[classNode]; |
292 if (info != null) { | 445 if (info != null) { |
293 if (info.isBeingVisited) { | 446 if (info.isBeingVisited) { |
294 throw 'Cyclic inheritance involving ${info.classNode.name}'; | 447 throw 'Cyclic inheritance involving ${info.classNode.name}'; |
295 } | 448 } |
296 return; // Already built. | 449 return info.depth; // Already built. |
297 } | 450 } |
451 int superDepth = -1; | |
298 _infoFor[classNode] = info = new _ClassInfo(classNode); | 452 _infoFor[classNode] = info = new _ClassInfo(classNode); |
299 info.isBeingVisited = true; | 453 info.isBeingVisited = true; |
300 if (classNode.supertype != null) { | 454 if (classNode.supertype != null) { |
301 _topologicalSortVisit(classNode.supertype.classNode); | 455 superDepth = |
456 max(superDepth, _topologicalSortVisit(classNode.supertype.classNode)); | |
302 _recordSuperTypes(info, classNode.supertype); | 457 _recordSuperTypes(info, classNode.supertype); |
303 } | 458 } |
304 if (classNode.mixedInType != null) { | 459 if (classNode.mixedInType != null) { |
305 _topologicalSortVisit(classNode.mixedInType.classNode); | 460 superDepth = max( |
461 superDepth, _topologicalSortVisit(classNode.mixedInType.classNode)); | |
306 _recordSuperTypes(info, classNode.mixedInType); | 462 _recordSuperTypes(info, classNode.mixedInType); |
307 } | 463 } |
308 for (var supertype in classNode.implementedTypes) { | 464 for (var supertype in classNode.implementedTypes) { |
309 _topologicalSortVisit(supertype.classNode); | 465 superDepth = max(superDepth, _topologicalSortVisit(supertype.classNode)); |
310 _recordSuperTypes(info, supertype); | 466 _recordSuperTypes(info, supertype); |
311 } | 467 } |
312 _buildDeclaredMembers(classNode, info); | 468 _buildDeclaredMembers(classNode, info); |
313 _buildImplementedMembers(classNode, info); | 469 _buildImplementedMembers(classNode, info); |
314 int id = _topSortIndex++; | 470 int id = _topSortIndex++; |
315 info.topologicalIndex = id; | 471 info.topologicalIndex = id; |
316 classes[id] = info.classNode; | 472 classes[id] = info.classNode; |
317 info.isBeingVisited = false; | 473 info.isBeingVisited = false; |
474 return info.depth = superDepth + 1; | |
318 } | 475 } |
319 | 476 |
320 void _buildDeclaredMembers(Class classNode, _ClassInfo info) { | 477 void _buildDeclaredMembers(Class classNode, _ClassInfo info) { |
321 if (classNode.mixedInType != null) { | 478 if (classNode.mixedInType != null) { |
322 _ClassInfo mixedInfo = _infoFor[classNode.mixedInType.classNode]; | 479 _ClassInfo mixedInfo = _infoFor[classNode.mixedInType.classNode]; |
323 info.declaredGettersAndCalls = mixedInfo.declaredGettersAndCalls; | 480 info.declaredGettersAndCalls = mixedInfo.declaredGettersAndCalls; |
324 info.declaredSetters = mixedInfo.declaredSetters; | 481 info.declaredSetters = mixedInfo.declaredSetters; |
325 } else { | 482 } else { |
326 var members = info.declaredGettersAndCalls = <Member>[]; | 483 var members = info.declaredGettersAndCalls = <Member>[]; |
327 var setters = info.declaredSetters = <Member>[]; | 484 var setters = info.declaredSetters = <Member>[]; |
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
784 if (delta != 0) return delta; | 941 if (delta != 0) return delta; |
785 } | 942 } |
786 return 0; | 943 return 0; |
787 } | 944 } |
788 | 945 |
789 class _ClassInfo { | 946 class _ClassInfo { |
790 final Class classNode; | 947 final Class classNode; |
791 int topologicalIndex = 0; | 948 int topologicalIndex = 0; |
792 int topDownIndex = -1; | 949 int topDownIndex = -1; |
793 bool isBeingVisited = false; | 950 bool isBeingVisited = false; |
951 int depth = 0; | |
794 | 952 |
795 // Super types must always occur before subtypes in these lists. | 953 // Super types must always occur before subtypes in these lists. |
796 // For example: | 954 // For example: |
797 // | 955 // |
798 // class A extends Object | 956 // class A extends Object |
799 // class B extends Object implements A | 957 // class B extends Object implements A |
800 // | 958 // |
801 // Here `A` must occur before `B` in the list of direct extenders of Object, | 959 // Here `A` must occur before `B` in the list of direct extenders of Object, |
802 // because `B` is a subtype of `A`. | 960 // because `B` is a subtype of `A`. |
803 final List<_ClassInfo> directExtenders = <_ClassInfo>[]; | 961 final List<_ClassInfo> directExtenders = <_ClassInfo>[]; |
804 final List<_ClassInfo> directMixers = <_ClassInfo>[]; | 962 final List<_ClassInfo> directMixers = <_ClassInfo>[]; |
805 final List<_ClassInfo> directImplementers = <_ClassInfo>[]; | 963 final List<_ClassInfo> directImplementers = <_ClassInfo>[]; |
806 | 964 |
807 /// Top-down indices of all subclasses of this class, represented as | 965 /// Top-down indices of all subclasses of this class, represented as |
808 /// interleaved begin/end interval end points. | 966 /// interleaved begin/end interval end points. |
809 Uint32List subclassIntervalList; | 967 Uint32List subclassIntervalList; |
810 Uint32List submixtureIntervalList; | 968 Uint32List submixtureIntervalList; |
811 Uint32List subtypeIntervalList; | 969 Uint32List subtypeIntervalList; |
812 | 970 |
971 List<_ClassInfo> leastUpperBoundInfos; | |
972 | |
813 bool isSubclassOf(_ClassInfo other) { | 973 bool isSubclassOf(_ClassInfo other) { |
814 return _intervalListContains(other.subclassIntervalList, topDownIndex); | 974 return _intervalListContains(other.subclassIntervalList, topDownIndex); |
815 } | 975 } |
816 | 976 |
817 bool isSubmixtureOf(_ClassInfo other) { | 977 bool isSubmixtureOf(_ClassInfo other) { |
818 return _intervalListContains(other.submixtureIntervalList, topDownIndex); | 978 return _intervalListContains(other.submixtureIntervalList, topDownIndex); |
819 } | 979 } |
820 | 980 |
821 bool isSubtypeOf(_ClassInfo other) { | 981 bool isSubtypeOf(_ClassInfo other) { |
822 return _intervalListContains(other.subtypeIntervalList, topDownIndex); | 982 return _intervalListContains(other.subtypeIntervalList, topDownIndex); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
893 | 1053 |
894 ClassSet union(ClassSet other) { | 1054 ClassSet union(ClassSet other) { |
895 assert(_hierarchy == other._hierarchy); | 1055 assert(_hierarchy == other._hierarchy); |
896 if (identical(_intervalList, other._intervalList)) return this; | 1056 if (identical(_intervalList, other._intervalList)) return this; |
897 _IntervalListBuilder builder = new _IntervalListBuilder(); | 1057 _IntervalListBuilder builder = new _IntervalListBuilder(); |
898 builder.addIntervalList(_intervalList); | 1058 builder.addIntervalList(_intervalList); |
899 builder.addIntervalList(other._intervalList); | 1059 builder.addIntervalList(other._intervalList); |
900 return new ClassSet(_hierarchy, builder.buildIntervalList()); | 1060 return new ClassSet(_hierarchy, builder.buildIntervalList()); |
901 } | 1061 } |
902 } | 1062 } |
1063 | |
1064 /// Heap for use in computing least upper bounds. | |
1065 /// | |
1066 /// The heap is sorted such that classes that are deepest in the hierarchy | |
1067 /// are removed first; in the case of ties, classes with lower topological sort | |
1068 /// index are removed first. | |
1069 class _LubHeap extends Heap<_ClassInfo> { | |
1070 @override | |
1071 bool sortsBefore(_ClassInfo a, _ClassInfo b) => sortsBeforeStatic(a, b); | |
1072 | |
1073 static bool sortsBeforeStatic(_ClassInfo a, _ClassInfo b) { | |
1074 if (a.depth > b.depth) return true; | |
1075 if (a.depth < b.depth) return false; | |
1076 return a.topologicalIndex < b.topologicalIndex; | |
1077 } | |
1078 } | |
OLD | NEW |