Chromium Code Reviews| Index: pkg/kernel/lib/class_hierarchy.dart |
| diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart |
| index 0945a8522c6d74442b2aadc0a42140023c4ec959..7734792299f84112204618ef8d2e51846e4d44da 100644 |
| --- a/pkg/kernel/lib/class_hierarchy.dart |
| +++ b/pkg/kernel/lib/class_hierarchy.dart |
| @@ -6,6 +6,7 @@ library kernel.class_hierarchy; |
| import 'ast.dart'; |
| import 'dart:math'; |
| import 'dart:typed_data'; |
| +import 'heap.dart'; |
| import 'type_algebra.dart'; |
| /// Data structure for answering various subclassing queries. |
| @@ -62,6 +63,155 @@ class ClassHierarchy { |
| return _infoFor[class_].directImplementers.isNotEmpty; |
| } |
| + /// Returns the number of steps in the longest inheritance path from [class_] |
| + /// to [rootClass]. |
| + int getClassDepth(Class class_) => _infoFor[class_].depth; |
| + |
| + /// Returns a list of classes appropriate for use in calculating a least upper |
| + /// bound. |
| + /// |
| + /// The returned list is a list of all classes that [class_] is a subtype of |
| + /// (including itself), sorted first by depth (deepest first) and then by |
| + /// class index. |
| + 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
|
| + return _getLeastUpperBoundInfos(_infoFor[class_]) |
| + .map((info) => info.classNode) |
| + .toList(); |
| + } |
| + |
| + List<_ClassInfo> _getLeastUpperBoundInfos(_ClassInfo info) { |
| + if (info.leastUpperBoundInfos != null) return info.leastUpperBoundInfos; |
| + var heap = new _LubHeap()..add(info); |
| + var chain = <_ClassInfo>[]; |
| + info.leastUpperBoundInfos = chain; |
| + _ClassInfo lastInfo = null; |
| + while (heap.isNotEmpty) { |
| + var nextInfo = heap.remove(); |
| + if (identical(nextInfo, lastInfo)) continue; |
| + chain.add(nextInfo); |
| + lastInfo = nextInfo; |
| + var classNode = nextInfo.classNode; |
| + void addToHeap(Supertype supertype) { |
| + heap.add(_infoFor[supertype.classNode]); |
| + } |
| + |
| + if (classNode.supertype != null) addToHeap(classNode.supertype); |
| + if (classNode.mixedInType != null) addToHeap(classNode.mixedInType); |
| + classNode.implementedTypes.forEach(addToHeap); |
| + } |
| + return chain; |
| + } |
| + |
| + /// Returns the least upper bound of two interface types, as defined by Dart |
| + /// 1.0. |
| + /// |
| + /// 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.
|
| + /// let S_J be the set of superinterfaces of J, and let |
| + /// S = (I union S_I) intersect (J union S_J). Furthermore, we define |
| + /// S_n = {T | T in S and depth(T) = n} for any finite n where depth(T) is |
| + /// the number of steps in the longest inheritance path from T to Object. Let |
| + /// q be the largest number such that S_q has cardinality one. The least |
| + /// upper bound of I and J is the sole element of S_q. |
| + /// |
| + /// This is called the "classic" least upper bound to distinguish it from the |
| + /// strong mode least upper bound, which has special behaviors in the case |
| + /// where one type is a subtype of the other, or where both types are based on |
| + /// the same class. |
| + InterfaceType getClassicLeastUpperBound( |
| + InterfaceType type1, InterfaceType type2) { |
| + // The algorithm is: first we compute a list of superclasses for both types, |
| + // ordered from greatest to least depth, and ordered by topological sort |
| + // index within each depth. Due to the sort order, we can find the |
| + // intersection of these lists by a simple walk. |
| + // |
| + // Then, for each class in the intersection, determine the exact type that |
| + // is implemented by type1 and type2. If the types match, that type is a |
| + // candidate (it's a member of S_n). As soon as we find a candidate which |
| + // is unique for its depth, we return it. |
| + // |
| + // As an optimization, if the class for I is a subtype of the class for J, |
| + // then we know that the list of superclasses of J is a subset of the list |
| + // of superclasses for I; therefore it is sufficient to compute just the |
| + // list of superclasses for J. To avoid complicating the code below (which |
| + // intersects the two lists), we set both lists equal to the list of |
| + // superclasses for J. And vice versa with the role of I and J swapped. |
| + |
| + // Compute the list of superclasses for both types, with the above |
| + // optimization. |
| + _ClassInfo info1 = _infoFor[type1.classNode]; |
| + _ClassInfo info2 = _infoFor[type2.classNode]; |
| + List<_ClassInfo> classes1; |
| + List<_ClassInfo> classes2; |
| + if (identical(info1, info2) || info1.isSubtypeOf(info2)) { |
| + classes1 = classes2 = _getLeastUpperBoundInfos(info2); |
| + } else if (info2.isSubtypeOf(info1)) { |
| + classes1 = classes2 = _getLeastUpperBoundInfos(info1); |
| + } else { |
| + classes1 = _getLeastUpperBoundInfos(info1); |
| + classes2 = _getLeastUpperBoundInfos(info2); |
| + } |
| + |
| + // Walk the lists finding their intersection, looking for a depth that has a |
| + // single candidate. |
| + int i1 = 0; |
| + int i2 = 0; |
| + InterfaceType candidate = null; |
| + int currentDepth = -1; |
| + int numCandidatesAtThisDepth = 0; |
| + while (true) { |
| + _ClassInfo next = classes1[i1]; |
| + _ClassInfo next2 = classes2[i2]; |
| + if (!identical(next, next2)) { |
| + if (_LubHeap.sortsBeforeStatic(next, next2)) { |
| + ++i1; |
| + } else { |
| + ++i2; |
| + } |
| + continue; |
| + } |
| + ++i2; |
| + ++i1; |
| + if (next.depth != currentDepth) { |
| + if (numCandidatesAtThisDepth == 1) return candidate; |
| + currentDepth = next.depth; |
| + numCandidatesAtThisDepth = 0; |
| + candidate = null; |
| + } else if (numCandidatesAtThisDepth > 1) { |
| + continue; |
| + } |
| + |
| + // For each class in the intersection, find the exact type that is |
| + // implemented by type1 and type2. If they match, it's a candidate. |
| + // |
| + // Two additional optimizations: |
| + // |
| + // - If this class lacks type parameters, we know there is a match without |
| + // needing to substitute. |
| + // |
| + // - If the depth is 0, we have reached Object, so we can return it |
| + // immediately. Since all interface types are subtypes of Object, this |
| + // ensures the loop terminates. |
| + if (next.classNode.typeParameters.isEmpty) { |
| + candidate = next.classNode.rawType; |
| + if (currentDepth == 0) return candidate; |
| + ++numCandidatesAtThisDepth; |
| + } else { |
| + var superType1 = identical(info1, next) |
| + ? type1 |
| + : Substitution.fromInterfaceType(type1).substituteType( |
| + info1.genericSuperTypes[next.classNode].asInterfaceType); |
| + var superType2 = identical(info2, next) |
| + ? type2 |
| + : Substitution.fromInterfaceType(type2).substituteType( |
| + info2.genericSuperTypes[next.classNode].asInterfaceType); |
| + if (superType1 == superType2) { |
| + candidate = superType1; |
| + ++numCandidatesAtThisDepth; |
| + } |
| + } |
| + } |
| + } |
| + |
| /// Returns the instantiation of [superclass] that is implemented by [class_], |
| /// or `null` if [class_] does not implement [superclass] at all. |
| Supertype getClassAsInstanceOf(Class class_, Class superclass) { |
| @@ -286,27 +436,33 @@ class ClassHierarchy { |
| /// Upwards traversal of the class hierarchy that orders classes so super |
| /// types before their subtypes. |
| + /// |
| + /// Returns the depth of the visited class (the number of steps in the longest |
| + /// inheritance path to the root class). |
| int _topSortIndex = 0; |
| - void _topologicalSortVisit(Class classNode) { |
| + int _topologicalSortVisit(Class classNode) { |
| var info = _infoFor[classNode]; |
| if (info != null) { |
| if (info.isBeingVisited) { |
| throw 'Cyclic inheritance involving ${info.classNode.name}'; |
| } |
| - return; // Already built. |
| + return info.depth; // Already built. |
| } |
| + int superDepth = -1; |
| _infoFor[classNode] = info = new _ClassInfo(classNode); |
| info.isBeingVisited = true; |
| if (classNode.supertype != null) { |
| - _topologicalSortVisit(classNode.supertype.classNode); |
| + superDepth = |
| + max(superDepth, _topologicalSortVisit(classNode.supertype.classNode)); |
| _recordSuperTypes(info, classNode.supertype); |
| } |
| if (classNode.mixedInType != null) { |
| - _topologicalSortVisit(classNode.mixedInType.classNode); |
| + superDepth = max( |
| + superDepth, _topologicalSortVisit(classNode.mixedInType.classNode)); |
| _recordSuperTypes(info, classNode.mixedInType); |
| } |
| for (var supertype in classNode.implementedTypes) { |
| - _topologicalSortVisit(supertype.classNode); |
| + superDepth = max(superDepth, _topologicalSortVisit(supertype.classNode)); |
| _recordSuperTypes(info, supertype); |
| } |
| _buildDeclaredMembers(classNode, info); |
| @@ -315,6 +471,7 @@ class ClassHierarchy { |
| info.topologicalIndex = id; |
| classes[id] = info.classNode; |
| info.isBeingVisited = false; |
| + return info.depth = superDepth + 1; |
| } |
| void _buildDeclaredMembers(Class classNode, _ClassInfo info) { |
| @@ -791,6 +948,7 @@ class _ClassInfo { |
| int topologicalIndex = 0; |
| int topDownIndex = -1; |
| bool isBeingVisited = false; |
| + int depth = 0; |
| // Super types must always occur before subtypes in these lists. |
| // For example: |
| @@ -810,6 +968,8 @@ class _ClassInfo { |
| Uint32List submixtureIntervalList; |
| Uint32List subtypeIntervalList; |
| + List<_ClassInfo> leastUpperBoundInfos; |
| + |
| bool isSubclassOf(_ClassInfo other) { |
| return _intervalListContains(other.subclassIntervalList, topDownIndex); |
| } |
| @@ -900,3 +1060,19 @@ class ClassSet { |
| return new ClassSet(_hierarchy, builder.buildIntervalList()); |
| } |
| } |
| + |
| +/// Heap for use in computing least upper bounds. |
| +/// |
| +/// The heap is sorted such that classes that are deepest in the hierarchy |
| +/// are removed first; in the case of ties, classes with lower topological sort |
| +/// index are removed first. |
| +class _LubHeap extends Heap<_ClassInfo> { |
| + @override |
| + bool sortsBefore(_ClassInfo a, _ClassInfo b) => sortsBeforeStatic(a, b); |
| + |
| + static bool sortsBeforeStatic(_ClassInfo a, _ClassInfo b) { |
| + if (a.depth > b.depth) return true; |
| + if (a.depth < b.depth) return false; |
| + return a.topologicalIndex < b.topologicalIndex; |
| + } |
| +} |