Chromium Code Reviews| 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 |