ClassHierarchy refactorings

This CL:
* Removes unused functions in the ClassHierarchy
* Turns the data structures used in the ClassHierarchy upside down
  (nodes have a lists of supers instead of lists of subs)
* Factors some things that needs the original list-of-subs into another
  class that the user can ask the ClassHierarchy to compute if needed.
  It appears that it isn't generally.

This is step #1 in turning the ClassHierarchy into an incremental
ClassHierarchy.

Change-Id: I48c5731c01c1b0e8bf1fcd4ddba7f2bf7ce3b9c9
Reviewed-on: https://dart-review.googlesource.com/53662
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
Jens Johansen 2018-05-14 11:33:20 +00:00 committed by commit-bot@chromium.org
parent 94a6a48edd
commit 7b3ba3f41f
7 changed files with 409 additions and 465 deletions

View file

@ -3,10 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
library kernel.class_hierarchy;
import 'ast.dart';
import 'dart:collection' show IterableBase;
import 'dart:collection';
import 'dart:math';
import 'dart:typed_data';
import 'ast.dart';
import 'src/heap.dart';
import 'type_algebra.dart';
@ -34,8 +35,8 @@ abstract class ClassHierarchy {
}
};
return new ClosedWorldClassHierarchy._internal(
component, numberOfClasses, onAmbiguousSupertypes)
.._initialize(mixinInferrer);
numberOfClasses, onAmbiguousSupertypes, mixinInferrer)
.._initialize(component.libraries);
}
/// Given the [unordered] classes, return them in such order that classes
@ -43,24 +44,9 @@ abstract class ClassHierarchy {
/// [unordered], they are not included.
Iterable<Class> getOrderedClasses(Iterable<Class> unordered);
/// Returns the unique index of the [class_].
int getClassIndex(Class class_);
/// True if the component contains another class that is a subtype of given one.
bool hasProperSubtypes(Class class_);
/// Returns the number of steps in the longest inheritance path from [class_]
/// to [Object].
int getClassDepth(Class class_);
/// 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> getRankedSuperclasses(Class class_);
/// Returns the least upper bound of two interface types, as defined by Dart
/// 1.0.
///
@ -119,12 +105,6 @@ abstract class ClassHierarchy {
/// The returned list should not be modified.
List<Member> getDispatchTargets(Class class_, {bool setters: false});
/// Returns the single concrete target for invocation of the given interface
/// target, or `null` if it could not be resolved or there are multiple
/// possible targets.
Member getSingleTargetForInterfaceInvocation(Member interfaceTarget,
{bool setter: false});
/// Returns the possibly abstract interface member of [class_] with the given
/// [name].
///
@ -153,20 +133,10 @@ abstract class ClassHierarchy {
/// classes.
List<Member> getDeclaredMembers(Class class_, {bool setters: false});
/// Returns the subclasses of [class_] as an interval list.
ClassSet getSubclassesOf(Class class_);
/// Returns the subtypes of [class_] as an interval list.
ClassSet getSubtypesOf(Class class_);
/// True if [subclass] inherits from [superclass] though zero or more
/// `extends` relationships.
bool isSubclassOf(Class subclass, Class superclass);
/// True if [submixture] inherits from [superclass] though zero or more
/// `extends` and `with` relationships.
bool isSubmixtureOf(Class submixture, Class superclass);
/// True if [subtype] inherits from [superclass] though zero or more
/// `extends`, `with`, and `implements` relationships.
bool isSubtypeOf(Class subtype, Class superclass);
@ -175,12 +145,6 @@ abstract class ClassHierarchy {
/// mixin application (i.e. [Class.mixedInType]).
bool isUsedAsMixin(Class class_);
/// True if the given class is the direct super class of another class.
bool isUsedAsSuperClass(Class class_);
/// True if the given class is used in an `implements` clause.
bool isUsedAsSuperInterface(Class class_);
/// Invokes [callback] for every member declared in or inherited by [class_]
/// that overrides or implements a member in a supertype of [class_]
/// (or in rare cases, overrides a member declared in [class_]).
@ -211,11 +175,16 @@ abstract class ClassHierarchy {
void forEachOverridePair(Class class_,
callback(Member declaredMember, Member interfaceMember, bool isSetter));
/// This method is invoked by the client after it changed the [classes], and
/// some of the information that this hierarchy might have cached, is not
/// valid anymore. The hierarchy may perform required updates and return the
/// same instance, or return a new instance.
ClassHierarchy applyChanges(Iterable<Class> classes);
/// This method is invoked by the client after a change: removal, adding, or
/// modified classes. For modified classes specify a class as both removed and
/// added: Some of the information that this hierarchy might have cached,
/// is not valid anymore.
/// Note, that if the changes includes changes to the relationship between
/// classes, it is the clients responsibility to mark all subclasses as
/// changed too.
///
ClassHierarchy applyChanges(
Iterable<Class> removedClasses, Iterable<Class> addedClasses);
/// Merges two sorted lists.
///
@ -328,35 +297,137 @@ abstract class ClassHierarchy {
}
}
abstract class ClassHierarchySubtypes {
/// Returns the subtypes of [class_] as an interval list.
ClassSet getSubtypesOf(Class class_);
/// Returns the single concrete target for invocation of the given interface
/// target, or `null` if it could not be resolved or there are multiple
/// possible targets.
Member getSingleTargetForInterfaceInvocation(Member interfaceTarget,
{bool setter: false});
}
class _ClassInfoSubtype {
final _ClassInfo classInfo;
int topDownIndex = -1;
/// Top-down indices of all subclasses of this class, represented as
/// interleaved begin/end interval end points.
Uint32List subtypeIntervalList;
_ClassInfoSubtype(this.classInfo);
}
class _ClosedWorldClassHierarchySubtypes implements ClassHierarchySubtypes {
final ClosedWorldClassHierarchy hierarchy;
final List<Class> _classesByTopDownIndex;
final Map<Class, _ClassInfoSubtype> _infoFor = <Class, _ClassInfoSubtype>{};
bool invalidated = false;
_ClosedWorldClassHierarchySubtypes(this.hierarchy)
: _classesByTopDownIndex = new List<Class>(hierarchy._infoFor.length) {
if (hierarchy._infoFor.isNotEmpty) {
for (Class class_ in hierarchy._infoFor.keys) {
_infoFor[class_] = new _ClassInfoSubtype(hierarchy._infoFor[class_]);
}
_topDownSortVisit(_infoFor[hierarchy._infoFor.keys.first]);
}
}
/// Downwards traversal of the class hierarchy that orders classes so local
/// hierarchies have contiguous indices.
int _topDownSortIndex = 0;
void _topDownSortVisit(_ClassInfoSubtype subInfo) {
if (subInfo.topDownIndex != -1) return;
int index = _topDownSortIndex++;
subInfo.topDownIndex = index;
_classesByTopDownIndex[index] = subInfo.classInfo.classNode;
var subtypeSetBuilder = new _IntervalListBuilder()..addSingleton(index);
for (_ClassInfo subtype in subInfo.classInfo.directExtenders) {
_ClassInfoSubtype subtypeInfo = _infoFor[subtype.classNode];
_topDownSortVisit(subtypeInfo);
subtypeSetBuilder.addIntervalList(subtypeInfo.subtypeIntervalList);
}
for (_ClassInfo subtype in subInfo.classInfo.directMixers) {
_ClassInfoSubtype subtypeInfo = _infoFor[subtype.classNode];
_topDownSortVisit(subtypeInfo);
subtypeSetBuilder.addIntervalList(subtypeInfo.subtypeIntervalList);
}
for (_ClassInfo subtype in subInfo.classInfo.directImplementers) {
_ClassInfoSubtype subtypeInfo = _infoFor[subtype.classNode];
_topDownSortVisit(subtypeInfo);
subtypeSetBuilder.addIntervalList(subtypeInfo.subtypeIntervalList);
}
subInfo.subtypeIntervalList = subtypeSetBuilder.buildIntervalList();
}
@override
Member getSingleTargetForInterfaceInvocation(Member interfaceTarget,
{bool setter: false}) {
if (invalidated) throw "This datastructure has been invalidated";
Name name = interfaceTarget.name;
Member target = null;
ClassSet subtypes = getSubtypesOf(interfaceTarget.enclosingClass);
for (Class c in subtypes) {
if (!c.isAbstract) {
Member candidate = hierarchy.getDispatchTarget(c, name, setter: setter);
if ((candidate != null) && !candidate.isAbstract) {
if (target == null) {
target = candidate;
} else if (target != candidate) {
return null;
}
}
}
}
return target;
}
@override
ClassSet getSubtypesOf(Class class_) {
if (invalidated) throw "This datastructure has been invalidated";
Set<Class> result = new Set<Class>();
Uint32List list = _infoFor[class_].subtypeIntervalList;
for (int i = 0; i < list.length; i += 2) {
int from = list[i];
int to = list[i + 1];
for (int j = from; j < to; j++) {
result.add(_classesByTopDownIndex[j]);
}
}
return new ClassSet(result);
}
}
/// Implementation of [ClassHierarchy] for closed world.
class ClosedWorldClassHierarchy implements ClassHierarchy {
final HandleAmbiguousSupertypes _onAmbiguousSupertypes;
MixinInferrer mixinInferrer;
/// The [Component] that this class hierarchy represents.
final Component _component;
/// The insert order is important.
final Map<Class, _ClassInfo> _infoFor =
new LinkedHashMap<Class, _ClassInfo>();
/// All classes in the component.
///
/// The list is ordered so that classes occur after their super classes.
final List<Class> classes;
Iterable<Class> get classes => _infoFor.keys;
int get numberOfClasses => _infoFor.length;
final Map<Class, _ClassInfo> _infoFor = <Class, _ClassInfo>{};
/// All classes ordered by [_ClassInfo.topDownIndex].
final List<Class> _classesByTopDownIndex;
_ClosedWorldClassHierarchySubtypes _cachedClassHierarchySubtypes;
ClosedWorldClassHierarchy._internal(
this._component, int numberOfClasses, this._onAmbiguousSupertypes)
: classes = new List<Class>(numberOfClasses),
_classesByTopDownIndex = new List<Class>(numberOfClasses);
int numberOfClasses, this._onAmbiguousSupertypes, this.mixinInferrer);
@override
int getClassIndex(Class class_) => _infoFor[class_].topologicalIndex;
ClassHierarchySubtypes computeSubtypesInformation() {
_cachedClassHierarchySubtypes ??=
new _ClosedWorldClassHierarchySubtypes(this);
return _cachedClassHierarchySubtypes;
}
@override
Iterable<Class> getOrderedClasses(Iterable<Class> unordered) {
var unorderedSet = unordered.toSet();
return classes.where(unorderedSet.contains);
return _infoFor.keys.where(unorderedSet.contains);
}
@override
@ -365,43 +436,17 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
return _infoFor[subclass].isSubclassOf(_infoFor[superclass]);
}
@override
bool isSubmixtureOf(Class submixture, Class superclass) {
if (identical(submixture, superclass)) return true;
return _infoFor[submixture].isSubmixtureOf(_infoFor[superclass]);
}
@override
bool isSubtypeOf(Class subtype, Class superclass) {
if (identical(subtype, superclass)) return true;
return _infoFor[subtype].isSubtypeOf(_infoFor[superclass]);
}
@override
bool isUsedAsSuperClass(Class class_) {
return _infoFor[class_].directExtenders.isNotEmpty;
}
@override
bool isUsedAsMixin(Class class_) {
return _infoFor[class_].directMixers.isNotEmpty;
}
@override
bool isUsedAsSuperInterface(Class class_) {
return _infoFor[class_].directImplementers.isNotEmpty;
}
@override
int getClassDepth(Class class_) => _infoFor[class_].depth;
@override
List<Class> getRankedSuperclasses(Class class_) {
return _getRankedSuperclassInfos(_infoFor[class_])
.map((info) => info.classNode)
.toList();
}
List<_ClassInfo> _getRankedSuperclassInfos(_ClassInfo info) {
if (info.leastUpperBoundInfos != null) return info.leastUpperBoundInfos;
var heap = new _LubHeap()..add(info);
@ -560,27 +605,6 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
return setters ? info.implementedSetters : info.implementedGettersAndCalls;
}
@override
Member getSingleTargetForInterfaceInvocation(Member interfaceTarget,
{bool setter: false}) {
Name name = interfaceTarget.name;
Member target = null;
ClassSet subtypes = getSubtypesOf(interfaceTarget.enclosingClass);
for (Class c in subtypes) {
if (!c.isAbstract) {
Member candidate = getDispatchTarget(c, name, setter: setter);
if ((candidate != null) && !candidate.isAbstract) {
if (target == null) {
target = candidate;
} else if (target != candidate) {
return null;
}
}
}
}
return target;
}
@override
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
List<Member> list = getInterfaceMembers(class_, setters: setter);
@ -663,25 +687,49 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
@override
bool hasProperSubtypes(Class class_) {
// If there are no subtypes then the subtype set contains the class itself.
return !getSubtypesOf(class_).isSingleton;
_ClassInfo info = _infoFor[class_];
return info.directExtenders.isNotEmpty ||
info.directImplementers.isNotEmpty ||
info.directMixers.isNotEmpty;
}
@override
ClassSet getSubtypesOf(Class class_) {
return new ClassSet(this, _infoFor[class_].subtypeIntervalList);
}
ClassHierarchy applyChanges(
Iterable<Class> removedClasses, Iterable<Class> addedClasses) {
if (removedClasses.isEmpty && addedClasses.isEmpty) return this;
@override
ClassSet getSubclassesOf(Class class_) {
return new ClassSet(this, _infoFor[class_].subclassIntervalList);
}
// Remove all references to the removed classes.
for (Class class_ in removedClasses) {
_ClassInfo info = _infoFor[class_];
if (class_.supertype != null) {
_infoFor[class_.supertype.classNode]?.directExtenders?.remove(info);
}
if (class_.mixedInType != null) {
_infoFor[class_.mixedInType.classNode]?.directMixers?.remove(info);
}
for (var supertype in class_.implementedTypes) {
_infoFor[supertype.classNode]?.directImplementers?.remove(info);
}
@override
ClassHierarchy applyChanges(Iterable<Class> classes) {
if (classes.isEmpty) return this;
return new ClassHierarchy(_component,
onAmbiguousSupertypes: _onAmbiguousSupertypes);
_infoFor.remove(class_);
}
// If we have a cached computation of subtypes, invalidate it and stop
// caching it.
if (_cachedClassHierarchySubtypes != null) {
_cachedClassHierarchySubtypes.invalidated = true;
}
// Add the new classes
List<Class> addedClassesSorted = new List<Class>();
int expectedStartIndex = _topSortIndex;
for (Class class_ in addedClasses) {
_topologicalSortVisit(class_, new Set<Class>(),
orderedList: addedClassesSorted);
}
_initialize2(addedClassesSorted, expectedStartIndex);
return this;
}
@override
@ -698,19 +746,29 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
return map == null ? null : map[superclass]?.first;
}
void _initialize(MixinInferrer mixinInferrer) {
void _initialize(List<Library> libraries) {
// Build the class ordering based on a topological sort.
for (var library in _component.libraries) {
for (var library in libraries) {
for (var classNode in library.classes) {
_topologicalSortVisit(classNode);
_topologicalSortVisit(classNode, new Set<Class>());
}
}
// Build index of direct children. Do this after the topological sort so
// that super types always occur before subtypes.
for (int i = 0; i < classes.length; ++i) {
var class_ = classes[i];
var info = _infoFor[class_];
_initialize2(_infoFor.keys, 0);
}
/// - Build index of direct children.
/// - Build list of super classes and super types.
/// - Infer and record supertypes for the classes.
/// - Record interface members.
/// - Perform some sanity checking.
/// Do this after the topological sort so that super types always occur
/// before subtypes.
void _initialize2(
Iterable<Class> classes, int expectedStartingTopologicalIndex) {
int i = expectedStartingTopologicalIndex;
for (Class class_ in classes) {
_ClassInfo info = _infoFor[class_];
if (class_.supertype != null) {
_infoFor[class_.supertype.classNode].directExtenders.add(info);
}
@ -720,53 +778,30 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
for (var supertype in class_.implementedTypes) {
_infoFor[supertype.classNode].directImplementers.add(info);
}
}
_topDownVisitClass(class_);
// Run a downward traversal from the root, compute preorder numbers for
// each class, and build their subtype sets as interval lists.
if (classes.isNotEmpty) {
_topDownSortVisit(_infoFor[classes[0]]);
}
// Now that the intervals for subclass, mixer, and implementer queries are
// built, we may infer and record supertypes for the classes.
for (int i = 0; i < classes.length; ++i) {
Class classNode = classes[i];
_ClassInfo info = _infoFor[classNode];
if (classNode.supertype != null) {
_recordSuperTypes(info, classNode.supertype);
if (class_.supertype != null) {
_recordSuperTypes(info, class_.supertype);
}
if (classNode.mixedInType != null) {
mixinInferrer?.infer(this, classNode);
_recordSuperTypes(info, classNode.mixedInType);
if (class_.mixedInType != null) {
mixinInferrer?.infer(this, class_);
_recordSuperTypes(info, class_.mixedInType);
}
for (Supertype supertype in classNode.implementedTypes) {
for (Supertype supertype in class_.implementedTypes) {
_recordSuperTypes(info, supertype);
}
}
for (int i = 0; i < classes.length; ++i) {
var class_ = classes[i];
_buildInterfaceMembers(class_, _infoFor[class_], setters: true);
_buildInterfaceMembers(class_, _infoFor[class_], setters: false);
}
_buildInterfaceMembers(class_, info, setters: true);
_buildInterfaceMembers(class_, info, setters: false);
for (int i = 0; i < classes.length; ++i) {
Class cls = classes[i];
if (cls == null) {
throw "No class at index $i.";
}
_ClassInfo info = _infoFor[cls];
if (info == null) {
throw "No info for ${cls.name} from ${cls.fileUri}.";
throw "No info for ${class_.name} from ${class_.fileUri}.";
}
if (info.topologicalIndex != i) {
throw "Unexpected topologicalIndex (${info.topologicalIndex} != $i) "
"for ${cls.name} from ${cls.fileUri}.";
}
if (info.subtypeIntervalList == null) {
throw "No subtypeIntervalList for ${cls.name} from ${cls.fileUri}.";
"for ${class_.name} from ${class_.fileUri}.";
}
i++;
}
}
@ -776,34 +811,45 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
/// Returns the depth of the visited class (the number of steps in the longest
/// inheritance path to the root class).
int _topSortIndex = 0;
int _topologicalSortVisit(Class classNode) {
int _topologicalSortVisit(Class classNode, Set<Class> beingVisited,
{List<Class> orderedList}) {
var info = _infoFor[classNode];
if (info != null) {
if (info.isBeingVisited) {
throw 'Cyclic inheritance involving ${info.classNode.name}';
}
return info.depth; // Already built.
return info.depth;
}
if (!beingVisited.add(classNode)) {
throw 'Cyclic inheritance involving ${classNode.name}';
}
info = new _ClassInfo(classNode);
int superDepth = -1;
_infoFor[classNode] = info = new _ClassInfo(classNode);
info.isBeingVisited = true;
if (classNode.supertype != null) {
superDepth =
max(superDepth, _topologicalSortVisit(classNode.supertype.classNode));
superDepth = max(
superDepth,
_topologicalSortVisit(classNode.supertype.classNode, beingVisited,
orderedList: orderedList));
}
if (classNode.mixedInType != null) {
superDepth = max(
superDepth, _topologicalSortVisit(classNode.mixedInType.classNode));
superDepth,
_topologicalSortVisit(classNode.mixedInType.classNode, beingVisited,
orderedList: orderedList));
}
for (var supertype in classNode.implementedTypes) {
superDepth = max(superDepth, _topologicalSortVisit(supertype.classNode));
superDepth = max(
superDepth,
_topologicalSortVisit(supertype.classNode, beingVisited,
orderedList: orderedList));
}
_buildDeclaredMembers(classNode, info);
_buildImplementedMembers(classNode, info);
int id = _topSortIndex++;
info.topologicalIndex = id;
classes[id] = info.classNode;
info.isBeingVisited = false;
info.topologicalIndex = _topSortIndex++;
_infoFor[classNode] = info;
orderedList?.add(classNode);
beingVisited.remove(classNode);
return info.depth = superDepth + 1;
}
@ -1004,54 +1050,48 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
}
}
/// Downwards traversal of the class hierarchy that orders classes so local
/// hierarchies have contiguous indices.
int _topDownSortIndex = 0;
void _topDownSortVisit(_ClassInfo info) {
if (info.topDownIndex != -1) return;
bool isMixedIn = info.directMixers.isNotEmpty;
int index = _topDownSortIndex++;
info.topDownIndex = index;
_classesByTopDownIndex[index] = info.classNode;
var subclassSetBuilder = new _IntervalListBuilder()..addSingleton(index);
var submixtureSetBuilder =
isMixedIn ? (new _IntervalListBuilder()..addSingleton(index)) : null;
var subtypeSetBuilder = new _IntervalListBuilder()..addSingleton(index);
for (var subtype in info.directExtenders) {
_topDownSortVisit(subtype);
subclassSetBuilder.addIntervalList(subtype.subclassIntervalList);
submixtureSetBuilder?.addIntervalList(subtype.submixtureIntervalList);
subtypeSetBuilder.addIntervalList(subtype.subtypeIntervalList);
/// Downwards traversal of the class hierarchy, building lists of super types
/// and super classes.
void _topDownVisitClass(Class class_) {
_ClassInfo info = _infoFor[class_];
var superclassSetBuilder = new _IntervalListBuilder()
..addSingleton(info.topologicalIndex);
var supertypeSetBuilder = new _IntervalListBuilder()
..addSingleton(info.topologicalIndex);
if (class_.supertype != null) {
_ClassInfo supertypeInfo = _infoFor[class_.supertype.classNode];
superclassSetBuilder
.addIntervalList(supertypeInfo.superclassIntervalList);
supertypeSetBuilder.addIntervalList(supertypeInfo.supertypeIntervalList);
}
for (var subtype in info.directMixers) {
_topDownSortVisit(subtype);
submixtureSetBuilder.addIntervalList(subtype.submixtureIntervalList);
subtypeSetBuilder.addIntervalList(subtype.subtypeIntervalList);
if (class_.mixedInType != null) {
_ClassInfo mixedInTypeInfo = _infoFor[class_.mixedInType.classNode];
supertypeSetBuilder
.addIntervalList(mixedInTypeInfo.supertypeIntervalList);
}
for (var subtype in info.directImplementers) {
_topDownSortVisit(subtype);
subtypeSetBuilder.addIntervalList(subtype.subtypeIntervalList);
for (Supertype supertype in class_.implementedTypes) {
_ClassInfo supertypeInfo = _infoFor[supertype.classNode];
supertypeSetBuilder.addIntervalList(supertypeInfo.supertypeIntervalList);
}
info.subclassIntervalList = subclassSetBuilder.buildIntervalList();
info.submixtureIntervalList = isMixedIn
? submixtureSetBuilder.buildIntervalList()
: info.subclassIntervalList;
info.subtypeIntervalList = subtypeSetBuilder.buildIntervalList();
info.superclassIntervalList = superclassSetBuilder.buildIntervalList();
info.supertypeIntervalList = supertypeSetBuilder.buildIntervalList();
}
/// Creates a histogram such that index `N` contains the number of classes
/// that have `N` intervals in its subclass or subtype set (whichever is
/// larger).
/// that have `N` intervals in itssupertype set.
///
/// The more numbers are condensed near the beginning, the more efficient the
/// internal data structure is.
List<int> getExpenseHistogram() {
var result = <int>[];
for (Class class_ in classes) {
for (Class class_ in _infoFor.keys) {
var info = _infoFor[class_];
int intervals = max(info.subclassIntervalList.length,
info.subtypeIntervalList.length) ~/
2;
int intervals = info.supertypeIntervalList.length ~/ 2;
if (intervals >= result.length) {
int oldLength = result.length;
result.length = intervals + 1;
@ -1062,29 +1102,30 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
return result;
}
/// Returns the average number of intervals per subtype relation (less
/// Returns the average number of intervals per supertype relation (less
/// is better, 1.0 is bad).
///
/// This is an estimate of the memory use compared to a data structure that
/// enumerates all subclass/subtype pairs.
/// enumerates all superclass/supertype pairs.
double getCompressionRatio() {
int intervals = 0;
int sizes = 0;
for (Class class_ in classes) {
for (Class class_ in _infoFor.keys) {
var info = _infoFor[class_];
intervals += (info.subclassIntervalList.length +
info.subtypeIntervalList.length) ~/
intervals += (info.superclassIntervalList.length +
info.supertypeIntervalList.length) ~/
2;
sizes += _intervalListSize(info.subclassIntervalList) +
_intervalListSize(info.subtypeIntervalList);
sizes += _intervalListSize(info.superclassIntervalList) +
_intervalListSize(info.supertypeIntervalList);
}
return sizes == 0 ? 1.0 : intervals / sizes;
}
/// Returns the number of entries in hash tables storing hierarchy data.
int getSuperTypeHashTableSize() {
int sum = 0;
for (Class class_ in classes) {
for (Class class_ in _infoFor.keys) {
sum += _infoFor[class_].genericSuperTypes?.length ?? 0;
}
return sum;
@ -1179,8 +1220,6 @@ int _intervalListSize(Uint32List intervalList) {
class _ClassInfo {
final Class classNode;
int topologicalIndex = 0;
int topDownIndex = -1;
bool isBeingVisited = false;
int depth = 0;
// Super types must always occur before subtypes in these lists.
@ -1191,30 +1230,15 @@ class _ClassInfo {
//
// Here `A` must occur before `B` in the list of direct extenders of Object,
// because `B` is a subtype of `A`.
final List<_ClassInfo> directExtenders = <_ClassInfo>[];
final List<_ClassInfo> directMixers = <_ClassInfo>[];
final List<_ClassInfo> directImplementers = <_ClassInfo>[];
final Set<_ClassInfo> directExtenders = new LinkedHashSet<_ClassInfo>();
final Set<_ClassInfo> directMixers = new LinkedHashSet<_ClassInfo>();
final Set<_ClassInfo> directImplementers = new LinkedHashSet<_ClassInfo>();
/// Top-down indices of all subclasses of this class, represented as
/// interleaved begin/end interval end points.
Uint32List subclassIntervalList;
Uint32List submixtureIntervalList;
Uint32List subtypeIntervalList;
Uint32List superclassIntervalList;
Uint32List supertypeIntervalList;
List<_ClassInfo> leastUpperBoundInfos;
bool isSubclassOf(_ClassInfo other) {
return _intervalListContains(other.subclassIntervalList, topDownIndex);
}
bool isSubmixtureOf(_ClassInfo other) {
return _intervalListContains(other.submixtureIntervalList, topDownIndex);
}
bool isSubtypeOf(_ClassInfo other) {
return _intervalListContains(other.subtypeIntervalList, topDownIndex);
}
/// Maps generic supertype classes to the instantiation implemented by this
/// class.
///
@ -1243,6 +1267,15 @@ class _ClassInfo {
_ClassInfo(this.classNode);
bool isSubclassOf(_ClassInfo other) {
return _intervalListContains(
superclassIntervalList, other.topologicalIndex);
}
bool isSubtypeOf(_ClassInfo other) {
return _intervalListContains(supertypeIntervalList, other.topologicalIndex);
}
void recordGenericSuperType(Class cls, Supertype type,
HandleAmbiguousSupertypes onAmbiguousSupertypes) {
List<Supertype> existing = genericSuperTypes[cls];
@ -1255,79 +1288,23 @@ class _ClassInfo {
}
}
/// An immutable set of classes, internally represented as an interval list.
/// An immutable set of classes.
class ClassSet extends IterableBase<Class> {
final ClosedWorldClassHierarchy _hierarchy;
final Uint32List _intervalList;
final Set<Class> _classes;
ClassSet(this._classes);
ClassSet(this._hierarchy, this._intervalList);
bool get isEmpty => _intervalList.isEmpty;
bool get isSingleton {
var list = _intervalList;
return list.length == 2 && list[0] + 1 == list[1];
}
@override
bool contains(Object class_) {
return _intervalListContains(
_intervalList, _hierarchy._infoFor[class_ as Class].topDownIndex);
return _classes.contains(_classes);
}
ClassSet union(ClassSet other) {
assert(_hierarchy == other._hierarchy);
if (identical(_intervalList, other._intervalList)) return this;
_IntervalListBuilder builder = new _IntervalListBuilder();
builder.addIntervalList(_intervalList);
builder.addIntervalList(other._intervalList);
return new ClassSet(_hierarchy, builder.buildIntervalList());
Set<Class> result = new Set<Class>.from(_classes);
result.addAll(other._classes);
return new ClassSet(result);
}
@override
Iterator<Class> get iterator =>
new _ClassSetIterator(_hierarchy, _intervalList);
}
/// Iterator for [ClassSet].
class _ClassSetIterator implements Iterator<Class> {
final ClosedWorldClassHierarchy _hierarchy;
final Uint32List _intervalList;
int _intervalIndex;
int _classIndex;
int _classIndexLimit;
// Interval list is a list of pairs (start, end).
static const int _intervalIndexStep = 2;
_ClassSetIterator(this._hierarchy, this._intervalList)
: _intervalIndex = -_intervalIndexStep,
_classIndex = -1,
_classIndexLimit = -1;
@override
bool moveNext() {
if (_classIndex + 1 < _classIndexLimit) {
_classIndex++;
return true;
}
if (_intervalIndex + _intervalIndexStep < _intervalList.length) {
_intervalIndex += _intervalIndexStep;
_classIndex = _intervalList[_intervalIndex];
_classIndexLimit = _intervalList[_intervalIndex + 1];
assert(_classIndex < _classIndexLimit);
return true;
}
_classIndex = _classIndexLimit = -1;
return false;
}
@override
Class get current => (_classIndex >= 0)
? _hierarchy._classesByTopDownIndex[_classIndex]
: null;
Iterator<Class> get iterator => _classes.iterator;
}
/// Heap for use in computing least upper bounds.

View file

@ -63,7 +63,7 @@ class MixinFullResolution {
}
// We might need to update the class hierarchy.
hierarchy = hierarchy.applyChanges(transformedClasses);
hierarchy = hierarchy.applyChanges(transformedClasses, transformedClasses);
if (!doSuperResolution) {
return;

View file

@ -94,6 +94,9 @@ class ProgramRoot {
class TreeShaker {
final CoreTypes coreTypes;
final ClosedWorldClassHierarchy hierarchy;
final ClassHierarchySubtypes hierarchySubtypes;
final Map<Class, int> numberedClasses;
final List<Class> classes;
final Component component;
final bool strongMode;
final List<ProgramRoot> programRoots;
@ -213,7 +216,7 @@ class TreeShaker {
}
ClassRetention getClassRetention(Class classNode) {
int index = hierarchy.getClassIndex(classNode);
int index = numberedClasses[classNode];
return _classRetention[index];
}
@ -227,11 +230,14 @@ class TreeShaker {
TreeShaker._internal(this.coreTypes, this.hierarchy, this.component,
this.strongMode, this.programRoots)
: this._dispatchedNames = new List<Set<Name>>(hierarchy.classes.length),
: this._dispatchedNames = new List<Set<Name>>(hierarchy.numberOfClasses),
this._usedMembersWithHost =
new List<Set<Member>>(hierarchy.classes.length),
new List<Set<Member>>(hierarchy.numberOfClasses),
this._classRetention = new List<ClassRetention>.filled(
hierarchy.classes.length, ClassRetention.None) {
hierarchy.numberOfClasses, ClassRetention.None),
this.hierarchySubtypes = hierarchy.computeSubtypesInformation(),
this.numberedClasses = createMapNumberIndex(hierarchy.classes),
this.classes = new List<Class>.from(hierarchy.classes) {
_visitor = new _TreeShakerVisitor(this);
_covariantVisitor = new _ExternalTypeVisitor(this, isCovariant: true);
_contravariantVisitor =
@ -246,6 +252,14 @@ class TreeShaker {
}
}
static Map<Class, int> createMapNumberIndex(Iterable<Class> classes) {
Map<Class, int> result = new Map<Class, int>();
for (Class class_ in classes) {
result[class_] = result.length;
}
return result;
}
void _build() {
if (component.mainMethod == null) {
throw 'Cannot perform tree shaking on a component without a main method';
@ -271,8 +285,8 @@ class TreeShaker {
// Mark overridden members in order to preserve abstract members as
// necessary.
if (strongMode) {
for (int i = hierarchy.classes.length - 1; i >= 0; --i) {
Class class_ = hierarchy.classes[i];
for (int i = classes.length - 1; i >= 0; --i) {
Class class_ = classes[i];
if (isHierarchyUsed(class_)) {
hierarchy.forEachOverridePair(class_,
(Member ownMember, Member superMember, bool isSetter) {
@ -305,7 +319,7 @@ class TreeShaker {
/// Registers the given name as seen in a dynamic dispatch, and discovers used
/// instance members accordingly.
void _addDispatchedName(Class receiver, Name name) {
int index = hierarchy.getClassIndex(receiver);
int index = numberedClasses[receiver];
Set<Name> receiverNames = _dispatchedNames[index] ??= new Set<Name>();
// TODO(asgerf): make use of selector arity and getter/setter kind
if (receiverNames.add(name)) {
@ -332,7 +346,7 @@ class TreeShaker {
}
}
}
var subtypes = hierarchy.getSubtypesOf(receiver);
var subtypes = hierarchySubtypes.getSubtypesOf(receiver);
var receiverSet = _receiversOfName[name];
_receiversOfName[name] = receiverSet == null
? subtypes
@ -358,7 +372,7 @@ class TreeShaker {
/// Registers the given class as instantiated and discovers new dispatch
/// target candidates accordingly.
void _addInstantiatedClass(Class classNode) {
int index = hierarchy.getClassIndex(classNode);
int index = numberedClasses[classNode];
ClassRetention retention = _classRetention[index];
if (retention.index < ClassRetention.Instance.index) {
_classRetention[index] = ClassRetention.Instance;
@ -368,7 +382,7 @@ class TreeShaker {
/// Register that an external subclass of the given class may be instantiated.
void _addInstantiatedExternalSubclass(Class classNode) {
int index = hierarchy.getClassIndex(classNode);
int index = numberedClasses[classNode];
ClassRetention retention = _classRetention[index];
if (retention.index < ClassRetention.ExternalInstance.index) {
_classRetention[index] = ClassRetention.ExternalInstance;
@ -491,7 +505,7 @@ class TreeShaker {
/// Registers the given class as being used in a type annotation.
void _addClassUsedInType(Class classNode) {
int index = hierarchy.getClassIndex(classNode);
int index = numberedClasses[classNode];
ClassRetention retention = _classRetention[index];
if (retention.index < ClassRetention.Hierarchy.index) {
_classRetention[index] = ClassRetention.Hierarchy;
@ -516,7 +530,7 @@ class TreeShaker {
void _addStaticNamespace(TreeNode container) {
assert(container is Class || container is Library);
if (container is Class) {
int index = hierarchy.getClassIndex(container);
int index = numberedClasses[container];
var oldRetention = _classRetention[index];
if (oldRetention == ClassRetention.None) {
_classRetention[index] = ClassRetention.Namespace;
@ -537,7 +551,7 @@ class TreeShaker {
}
if (host != null) {
// Check if the member has been seen with this host before.
int index = hierarchy.getClassIndex(host);
int index = numberedClasses[host];
Set<Member> members = _usedMembersWithHost[index] ??= new Set<Member>();
if (!members.add(member)) return;
_usedMembers.putIfAbsent(member, _makeIncompleteSummary);

View file

@ -60,12 +60,23 @@ main(List<String> args) {
hierarchies.add(buildHierarchy());
}
List<Class> classes = hierarchies.first.classes.toList();
int currentHierarchy = 0;
ClosedWorldClassHierarchy getClassHierarchy() {
currentHierarchy = (currentHierarchy + 1) % hierarchies.length;
return hierarchies[currentHierarchy];
}
{
var classHierarchy = getClassHierarchy();
if (classHierarchy is ClosedWorldClassHierarchy) {
for (Class class_ in classes) {
classHierarchy.hasProperSubtypes(class_);
}
}
}
Random rnd = new Random(12345);
const int numQueryTrials = 100000;
@ -76,12 +87,11 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
int first = rnd.nextInt(classes.length);
int second = rnd.nextInt(classes.length);
Class firstClass = classes[first];
Class secondClass = classes[second];
classHierarchy.isSubclassOf(firstClass, secondClass);
classHierarchy.isSubmixtureOf(firstClass, secondClass);
classHierarchy.isSubtypeOf(firstClass, secondClass);
classHierarchy.getClassAsInstanceOf(firstClass, secondClass);
}
@ -89,10 +99,10 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
int first = rnd.nextInt(classes.length);
int second = rnd.nextInt(classes.length);
Class firstClass = classes[first];
Class secondClass = classes[second];
classHierarchy.isSubclassOf(firstClass, secondClass);
}
int subclassQueryTime = watch.elapsedMicroseconds;
@ -100,21 +110,10 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
classHierarchy.isSubmixtureOf(firstClass, secondClass);
}
int submixtureQueryTime = watch.elapsedMicroseconds;
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
int first = rnd.nextInt(classes.length);
int second = rnd.nextInt(classes.length);
Class firstClass = classes[first];
Class secondClass = classes[second];
classHierarchy.isSubtypeOf(firstClass, secondClass);
}
int subtypeQueryTime = watch.elapsedMicroseconds;
@ -122,10 +121,10 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
int first = rnd.nextInt(classes.length);
int second = rnd.nextInt(classes.length);
Class firstClass = classes[first];
Class secondClass = classes[second];
classHierarchy.getClassAsInstanceOf(firstClass, secondClass);
}
int asInstanceOfQueryTime = watch.elapsedMicroseconds;
@ -133,21 +132,18 @@ main(List<String> args) {
// Estimate the overhead from test case generation.
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
classHierarchy.classes[first];
classHierarchy.classes[second];
int first = rnd.nextInt(classes.length);
int second = rnd.nextInt(classes.length);
classes[first];
classes[second];
}
int queryNoise = watch.elapsedMicroseconds;
subclassQueryTime -= queryNoise;
submixtureQueryTime -= queryNoise;
subtypeQueryTime -= queryNoise;
asInstanceOfQueryTime -= queryNoise;
String subclassPerSecond = perSecond(subclassQueryTime, numQueryTrials);
String submixturePerSecond = perSecond(submixtureQueryTime, numQueryTrials);
String subtypePerSecond = perSecond(subtypeQueryTime, numQueryTrials);
String asInstanceOfPerSecond =
perSecond(asInstanceOfQueryTime, numQueryTrials);
@ -156,8 +152,8 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
Class classNode = classes[classId];
classHierarchy.getDispatchTarget(classNode, new Name('toString'));
}
int dispatchToStringTime = watch.elapsedMicroseconds;
@ -165,8 +161,8 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
Class classNode = classes[classId];
classHierarchy.getDispatchTarget(classNode, new Name('getFloo'));
}
int dispatchGenericGetTime = watch.elapsedMicroseconds;
@ -174,8 +170,8 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
Class classNode = classes[classId];
for (var _ in classHierarchy.getDispatchTargets(classNode)) {}
}
int dispatchAllTargetsTime = watch.elapsedMicroseconds;
@ -184,8 +180,8 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
Class classNode = classes[classId];
classHierarchy.getInterfaceMember(classNode, new Name('toString'));
}
int interfaceToStringTime = watch.elapsedMicroseconds;
@ -193,8 +189,8 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
Class classNode = classes[classId];
classHierarchy.getInterfaceMember(classNode, new Name('getFloo'));
}
int interfaceGenericGetTime = watch.elapsedMicroseconds;
@ -202,8 +198,8 @@ main(List<String> args) {
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
Class classNode = classes[classId];
for (var _ in classHierarchy.getInterfaceMembers(classNode)) {}
}
int interfaceAllTargetsTime = watch.elapsedMicroseconds;
@ -211,9 +207,8 @@ main(List<String> args) {
// Estimate overhead from test case generation.
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
classHierarchy.classes[classId];
int classId = rnd.nextInt(classes.length);
classes[classId];
}
int dispatchTargetNoise = watch.elapsedMicroseconds;
@ -241,7 +236,7 @@ main(List<String> args) {
watch.reset();
var classHierarchy = getClassHierarchy();
int numberOfOverridePairs = 0;
for (var class_ in classHierarchy.classes) {
for (var class_ in classes) {
classHierarchy.forEachOverridePair(class_, (member, supermember, isSetter) {
++numberOfOverridePairs;
});
@ -251,13 +246,18 @@ main(List<String> args) {
String overridePairsPerSecond =
perSecond(overrideTime, numberOfOverridePairs);
List<int> depth = new List(classHierarchy.classes.length);
Map<Class, int> classIds = new Map<Class, int>();
for (Class class_ in classes) {
classIds[class_] = classIds.length;
}
List<int> depth = new List(classes.length);
for (int i = 0; i < depth.length; ++i) {
int parentDepth = 0;
var classNode = classHierarchy.classes[i];
var classNode = classes[i];
for (var supertype in classNode.supers) {
var superclass = supertype.classNode;
int index = classHierarchy.getClassIndex(superclass);
int index = classIds[superclass];
if (!(index < i)) {
throw '${classNode.name}($i) extends ${superclass.name}($index)';
}
@ -271,7 +271,7 @@ main(List<String> args) {
double medianDepth = median(depth);
int totalDepth = sum(depth);
int numberOfClasses = classHierarchy.classes.length;
int numberOfClasses = classes.length;
String expenseHistogram =
classHierarchy.getExpenseHistogram().skip(1).join(' ');
@ -280,7 +280,6 @@ classes: $numberOfClasses
build.cold: $coldBuildTime ms
build.hot: $hotBuildTime ms
query.isSubclassOf: $subclassPerSecond
query.isSubmixtureOf: $submixturePerSecond
query.isSubtypeOf: $subtypePerSecond
query.getClassAsInstanceOf: $asInstanceOfPerSecond
query.getDispatchTarget(toString): $dispatchToStringPerSecond

View file

@ -19,22 +19,17 @@ main(List<String> args) {
void testClassHierarchyOnComponent(Component component, {bool verbose: false}) {
BasicClassHierarchy basic = new BasicClassHierarchy(component);
ClosedWorldClassHierarchy classHierarchy = new ClassHierarchy(component);
int total = classHierarchy.classes.length;
int total = classHierarchy.numberOfClasses;
int progress = 0;
for (var class1 in classHierarchy.classes) {
for (var class2 in classHierarchy.classes) {
bool isSubclass = classHierarchy.isSubclassOf(class1, class2);
bool isSubmixture = classHierarchy.isSubmixtureOf(class1, class2);
bool isSubtype = classHierarchy.isSubtypeOf(class1, class2);
var asInstance = classHierarchy.getClassAsInstanceOf(class1, class2);
if (isSubclass != basic.isSubclassOf(class1, class2)) {
fail('isSubclassOf(${class1.name}, ${class2.name}) returned '
'$isSubclass but should be ${!isSubclass}');
}
if (isSubmixture != basic.isSubmixtureOf(class1, class2)) {
fail('isSubmixtureOf(${class1.name}, ${class2.name}) returned '
'$isSubclass but should be ${!isSubclass}');
}
if (isSubtype != basic.isSubtypeOf(class1, class2)) {
fail('isSubtypeOf(${class1.name}, ${class2.name}) returned '
'$isSubtype but should be ${!isSubtype}');

View file

@ -25,7 +25,7 @@ class ClosedWorldClassHierarchyTest extends _ClassHierarchyTest {
void test_applyChanges() {
var a = addClass(new Class(name: 'A', supertype: objectSuper));
addClass(new Class(name: 'B', supertype: a.asThisSupertype));
var b = addClass(new Class(name: 'B', supertype: a.asThisSupertype));
_assertTestLibraryText('''
class A {}
@ -33,12 +33,20 @@ class B extends self::A {}
''');
// No updated classes, the same hierarchy.
expect(hierarchy.applyChanges([]), same(hierarchy));
expect(hierarchy.applyChanges([], []), same(hierarchy));
expect(hierarchy.hasProperSubtypes(a), true);
// Has updated classes, a new hierarchy.
var newHierarchy = hierarchy.applyChanges([a]);
expect(newHierarchy, isNot(same(hierarchy)));
expect(newHierarchy, new isInstanceOf<ClosedWorldClassHierarchy>());
// Has updated classes, still the same hierarchy (instance). Can answer
// queries about the new classes.
var c = new Class(name: 'C', supertype: a.asThisSupertype);
expect(hierarchy.applyChanges([b], [c]), same(hierarchy));
expect(hierarchy.isSubclassOf(a, c), false);
expect(hierarchy.isSubclassOf(c, a), true);
expect(hierarchy.hasProperSubtypes(a), true);
// Remove so A should no longer be a super of anything.
expect(hierarchy.applyChanges([c], []), same(hierarchy));
expect(hierarchy.hasProperSubtypes(a), false);
}
void test_getSingleTargetForInterfaceInvocation() {
@ -84,12 +92,13 @@ abstract class E implements self::C {
''');
ClosedWorldClassHierarchy cwch = hierarchy as ClosedWorldClassHierarchy;
ClassHierarchySubtypes cwchst = cwch.computeSubtypesInformation();
expect(cwch.getSingleTargetForInterfaceInvocation(methodInA), methodInB);
expect(cwch.getSingleTargetForInterfaceInvocation(methodInB),
expect(cwchst.getSingleTargetForInterfaceInvocation(methodInA), methodInB);
expect(cwchst.getSingleTargetForInterfaceInvocation(methodInB),
null); // B::foo and D::foo
expect(cwch.getSingleTargetForInterfaceInvocation(methodInD), methodInD);
expect(cwch.getSingleTargetForInterfaceInvocation(methodInE),
expect(cwchst.getSingleTargetForInterfaceInvocation(methodInD), methodInD);
expect(cwchst.getSingleTargetForInterfaceInvocation(methodInE),
null); // no concrete subtypes
}
@ -129,15 +138,16 @@ class H extends self::G implements self::C, self::A {}
''');
ClosedWorldClassHierarchy cwch = hierarchy as ClosedWorldClassHierarchy;
ClassHierarchySubtypes cwchst = cwch.computeSubtypesInformation();
expect(cwch.getSubtypesOf(a), unorderedEquals([a, d, f, h]));
expect(cwch.getSubtypesOf(b), unorderedEquals([b, e, f]));
expect(cwch.getSubtypesOf(c), unorderedEquals([c, e, f, h]));
expect(cwch.getSubtypesOf(d), unorderedEquals([d]));
expect(cwch.getSubtypesOf(e), unorderedEquals([e, f]));
expect(cwch.getSubtypesOf(f), unorderedEquals([f]));
expect(cwch.getSubtypesOf(g), unorderedEquals([g, h]));
expect(cwch.getSubtypesOf(h), unorderedEquals([h]));
expect(cwchst.getSubtypesOf(a), unorderedEquals([a, d, f, h]));
expect(cwchst.getSubtypesOf(b), unorderedEquals([b, e, f]));
expect(cwchst.getSubtypesOf(c), unorderedEquals([c, e, f, h]));
expect(cwchst.getSubtypesOf(d), unorderedEquals([d]));
expect(cwchst.getSubtypesOf(e), unorderedEquals([e, f]));
expect(cwchst.getSubtypesOf(f), unorderedEquals([f]));
expect(cwchst.getSubtypesOf(g), unorderedEquals([g, h]));
expect(cwchst.getSubtypesOf(h), unorderedEquals([h]));
}
}
@ -583,33 +593,6 @@ class Z {}
expect(hierarchy.getClassAsInstanceOf(z, a), null);
}
void test_getClassDepth() {
var base = addClass(new Class(name: 'base', supertype: objectSuper));
var extends_ =
addClass(new Class(name: 'extends_', supertype: base.asThisSupertype));
var with_ = addClass(new Class(
name: 'with_',
supertype: objectSuper,
mixedInType: base.asThisSupertype));
var implements_ = addClass(new Class(
name: 'implements_',
supertype: objectSuper,
implementedTypes: [base.asThisSupertype]));
_assertTestLibraryText('''
class base {}
class extends_ extends self::base {}
class with_ = core::Object with self::base {}
class implements_ implements self::base {}
''');
expect(hierarchy.getClassDepth(objectClass), 0);
expect(hierarchy.getClassDepth(base), 1);
expect(hierarchy.getClassDepth(extends_), 2);
expect(hierarchy.getClassDepth(with_), 2);
expect(hierarchy.getClassDepth(implements_), 2);
}
/// Copy of the tests/language/least_upper_bound_expansive_test.dart test.
void test_getClassicLeastUpperBound_expansive() {
var int = coreTypes.intClass.rawType;
@ -1291,32 +1274,6 @@ class B extends self::A {
assertOrderOfClasses([c, b], [b, c]);
}
void test_getRankedSuperclasses() {
var a = addImplementsClass('A', []);
var b = addImplementsClass('B', [a]);
var c = addImplementsClass('C', [a]);
var d = addImplementsClass('D', [c]);
var e = addImplementsClass('E', [b, d]);
_assertTestLibraryText('''
class A {}
class B implements self::A {}
class C implements self::A {}
class D implements self::C {}
class E implements self::B, self::D {}
''');
expect(hierarchy.getRankedSuperclasses(a), [a, objectClass]);
expect(hierarchy.getRankedSuperclasses(b), [b, a, objectClass]);
expect(hierarchy.getRankedSuperclasses(c), [c, a, objectClass]);
expect(hierarchy.getRankedSuperclasses(d), [d, c, a, objectClass]);
if (hierarchy.getClassIndex(b) < hierarchy.getClassIndex(c)) {
expect(hierarchy.getRankedSuperclasses(e), [e, d, b, c, a, objectClass]);
} else {
expect(hierarchy.getRankedSuperclasses(e), [e, d, c, b, a, objectClass]);
}
}
void test_getTypeAsInstanceOf_generic_extends() {
var int = coreTypes.intClass.rawType;
var bool = coreTypes.boolClass.rawType;

View file

@ -7,7 +7,7 @@ library vm.transformations.cha_devirtualization;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClosedWorldClassHierarchy;
show ClassHierarchy, ClassHierarchySubtypes, ClosedWorldClassHierarchy;
import '../metadata/direct_call.dart';
@ -15,9 +15,10 @@ import '../metadata/direct_call.dart';
/// analysis. Assumes strong mode and closed world.
Component transformComponent(CoreTypes coreTypes, Component component) {
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
final hierarchy = new ClassHierarchy(component,
ClosedWorldClassHierarchy hierarchy = new ClassHierarchy(component,
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
new CHADevirtualization(coreTypes, component, hierarchy)
final hierarchySubtypes = hierarchy.computeSubtypesInformation();
new CHADevirtualization(coreTypes, component, hierarchy, hierarchySubtypes)
.visitComponent(component);
return component;
}
@ -140,15 +141,16 @@ abstract class Devirtualization extends RecursiveVisitor<Null> {
/// Devirtualization based on the closed-world class hierarchy analysis.
class CHADevirtualization extends Devirtualization {
final ClosedWorldClassHierarchy _hierarchy;
final ClassHierarchySubtypes _hierarchySubtype;
CHADevirtualization(CoreTypes coreTypes, Component component, this._hierarchy)
: super(coreTypes, component, _hierarchy);
CHADevirtualization(CoreTypes coreTypes, Component component,
ClosedWorldClassHierarchy hierarchy, this._hierarchySubtype)
: super(coreTypes, component, hierarchy);
@override
DirectCallMetadata getDirectCall(TreeNode node, Member target,
{bool setter = false}) {
Member singleTarget = _hierarchy
Member singleTarget = _hierarchySubtype
.getSingleTargetForInterfaceInvocation(target, setter: setter);
if (singleTarget == null) {
return null;