blob: 604208a990a242f68d82e60a00ad74382e253761 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library kernel.class_hierarchy;
import 'dart:collection';
import 'dart:math';
import 'dart:typed_data';
import 'ast.dart';
import 'src/heap.dart';
import 'type_algebra.dart';
typedef HandleAmbiguousSupertypes = void Function(Class, Supertype, Supertype);
abstract class MixinInferrer {
void infer(ClassHierarchy hierarchy, Class classNode);
}
/// Interface for answering various subclassing queries.
abstract class ClassHierarchy {
factory ClassHierarchy(Component component,
{HandleAmbiguousSupertypes onAmbiguousSupertypes,
MixinInferrer mixinInferrer}) {
onAmbiguousSupertypes ??= (Class cls, Supertype a, Supertype b) {
if (!cls.isAnonymousMixin) {
// See https://github.com/dart-lang/sdk/issues/32091
throw "$cls can't implement both $a and $b";
}
};
return new ClosedWorldClassHierarchy._internal(
onAmbiguousSupertypes, mixinInferrer)
.._initialize(component.libraries);
}
void set onAmbiguousSupertypes(
HandleAmbiguousSupertypes onAmbiguousSupertypes);
/// Given the [unordered] classes, return them in such order that classes
/// occur after their superclasses. If some superclasses are not in
/// [unordered], they are not included.
Iterable<Class> getOrderedClasses(Iterable<Class> unordered);
/// True if the component contains another class that is a subtype of given one.
bool hasProperSubtypes(Class class_);
/// Returns the least upper bound of two interface types, as defined by Dart
/// 1.0.
///
/// Given two interfaces I and J, let S_I be the set of superinterfaces of I,
/// 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);
/// 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);
/// Returns the instantiation of [superclass] that is implemented by [type],
/// or `null` if [type] does not implement [superclass] at all.
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass);
/// Returns the instantiation of [superclass] that is implemented by [type],
/// or `null` if [type] does not implement [superclass]. [superclass] must
/// be a generic class.
Supertype asInstantiationOf(Supertype type, Class superclass);
/// Returns the instance member that would respond to a dynamic dispatch of
/// [name] to an instance of [class_], or `null` if no such member exists.
///
/// If [setter] is `false`, the name is dispatched as a getter or call,
/// and will return a field, getter, method, or operator (or null).
///
/// If [setter] is `true`, the name is dispatched as a setter, roughly
/// corresponding to `name=` in the Dart specification, but note that the
/// returned member will not have a name ending with `=`. In this case,
/// a non-final field or setter (or null) will be returned.
///
/// If the class is abstract, abstract members are ignored and the dispatch
/// is resolved if the class was not abstract.
Member getDispatchTarget(Class class_, Name name, {bool setter: false});
/// Returns the list of potential targets of dynamic dispatch to an instance
/// of [class_].
///
/// If [setters] is `false`, only potential targets of a getter or call
/// dispatch are returned. If [setters] is `true`, only potential targets
/// of a setter dispatch are returned.
///
/// See [getDispatchTarget] for more details.
///
/// The returned list should not be modified.
List<Member> getDispatchTargets(Class class_, {bool setters: false});
/// Returns the possibly abstract interface member of [class_] with the given
/// [name].
///
/// If [setter] is `false`, only fields, methods, and getters with that name
/// will be found. If [setter] is `true`, only non-final fields and setters
/// will be found.
///
/// If multiple members with that name are inherited and not overridden, the
/// member from the first declared supertype is returned.
Member getInterfaceMember(Class class_, Name name, {bool setter: false});
/// Returns the list of members denoting the interface for [class_], which
/// may include abstract members.
///
/// The list may contain multiple members with a given name. This happens
/// when members are inherited through different supertypes and not overridden
/// in the class.
///
/// Also see [getInterfaceMember].
List<Member> getInterfaceMembers(Class class_, {bool setters: false});
/// Returns the list of members declared in [class_], including abstract
/// members.
///
/// Members are sorted by name so that they may be efficiently compared across
/// classes.
List<Member> getDeclaredMembers(Class class_, {bool setters: false});
/// True if [subclass] inherits from [superclass] though zero or more
/// `extends` relationships.
bool isSubclassOf(Class subclass, Class superclass);
/// True if [subtype] inherits from [superclass] though zero or more
/// `extends`, `with`, and `implements` relationships.
bool isSubtypeOf(Class subtype, Class superclass);
/// True if the given class is used as the right-hand operand to a
/// mixin application (i.e. [Class.mixedInType]).
bool isUsedAsMixin(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_]).
///
/// We use the term "inheritable" for members that are candidates for
/// inheritance but may have been overridden. The "declared" members of a
/// mixin application are those declared in the mixed-in type. The callback is
/// invoked in the following cases:
///
/// 1. A member declared in the class overrides a member inheritable through
/// one of the supertypes of the class.
///
/// 2. A non-abstract member is inherited from a superclass, and in the
/// context of this class, it overrides an abstract member inheritable through
/// one of its superinterfaces.
///
/// 3. A non-abstract member is inherited from a superclass, and it overrides
/// an abstract member declared in this class.
///
/// This method will not report that a member overrides itself. A given pair
/// may be reported multiple times when there are multiple inheritance paths
/// to the overridden member.
///
/// It is possible for two methods to override one another in both directions.
///
/// By default getters and setters are overridden separately. The [isSetter]
/// callback parameter determines which type of access is being overridden.
void forEachOverridePair(Class class_,
callback(Member declaredMember, Member interfaceMember, bool isSetter));
/// This method is invoked by the client after a change: removal, addition,
/// or modification of 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 it is the clients responsibility to mark all subclasses as
/// changed too.
ClassHierarchy applyTreeChanges(
Iterable<Class> removedClasses, Iterable<Class> addedClasses,
{Component reissueAmbiguousSupertypesFor});
/// This method is invoked by the client after a member change on classes:
/// Some of the information that this hierarchy might have cached,
/// is not valid anymore.
/// Note, that it is the clients responsibility to mark all subclasses as
/// changed too, or - if [findDescendants] is true, the ClassHierarchy will
/// spend the time to find them for the caller.
ClassHierarchy applyMemberChanges(Iterable<Class> classes,
{bool findDescendants: false});
/// Merges two sorted lists.
///
/// If a given member occurs in both lists, the merge will attempt to exclude
/// the duplicate member, but is not strictly guaranteed to do so.
///
/// The sort has the following stability properties:
///
/// - If both x and y came from the same input list, and x preceded y in the
/// input list, x will precede y in the output list. This holds even if x
/// and y have matching names.
///
/// - If m is a contiguous subsequence of the output list containing at least
/// one element from each input list, and all elements of m have matching
/// names, then the elements of m from [first] will precede the elements of
/// m from [second].
static List<Member> mergeSortedLists(
List<Member> first, List<Member> second) {
if (first.isEmpty) return second;
if (second.isEmpty) return first;
List<Member> result = <Member>[]..length = first.length + second.length;
int storeIndex = 0;
int i = 0, j = 0;
while (i < first.length && j < second.length) {
Member firstMember = first[i];
Member secondMember = second[j];
int compare = ClassHierarchy.compareMembers(firstMember, secondMember);
if (compare <= 0) {
result[storeIndex++] = firstMember;
++i;
// If the same member occurs in both lists, skip the duplicate.
if (identical(firstMember, secondMember)) {
++j;
}
} else {
result[storeIndex++] = secondMember;
++j;
}
}
while (i < first.length) {
result[storeIndex++] = first[i++];
}
while (j < second.length) {
result[storeIndex++] = second[j++];
}
result.length = storeIndex;
return result;
}
/// Compares members by name, using the same sort order as
/// [getDeclaredMembers] and [getInterfaceMembers].
static int compareMembers(Member first, Member second) {
return _compareNames(first.name, second.name);
}
/// Compares names, using the same sort order as [getDeclaredMembers] and
/// [getInterfaceMembers].
///
/// This is an arbitrary as-fast-as-possible sorting criterion.
static int _compareNames(Name firstName, Name secondName) {
int firstHash = firstName.hashCode;
int secondHash = secondName.hashCode;
if (firstHash != secondHash) return firstHash - secondHash;
String firstString = firstName.name;
String secondString = secondName.name;
int firstLength = firstString.length;
int secondLength = secondString.length;
if (firstLength != secondLength) {
return firstLength - secondLength;
}
Library firstLibrary = firstName.library;
Library secondLibrary = secondName.library;
if (firstLibrary != secondLibrary) {
if (firstLibrary == null) return -1;
if (secondLibrary == null) return 1;
return firstLibrary.compareTo(secondLibrary);
}
for (int i = 0; i < firstLength; ++i) {
int firstUnit = firstString.codeUnitAt(i);
int secondUnit = secondString.codeUnitAt(i);
int delta = firstUnit - secondUnit;
if (delta != 0) return delta;
}
return 0;
}
/// Returns the member with the given name, or `null` if no member has the
/// name. In case the list contains multiple members with the given name,
/// the one that occurs first in the list is returned.
///
/// The list is assumed to be sorted according to [compareMembers].
static Member findMemberByName(List<Member> members, Name name) {
int low = 0, high = members.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
Member pivot = members[mid];
int comparison = _compareNames(name, pivot.name);
if (comparison < 0) {
high = mid - 1;
} else if (comparison > 0) {
low = mid + 1;
} else if (high != mid) {
// Ensure we find the first element of the given name.
high = mid;
} else {
return pivot;
}
}
return null;
}
}
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 {
HandleAmbiguousSupertypes _onAmbiguousSupertypes;
HandleAmbiguousSupertypes _onAmbiguousSupertypesNotWrapped;
MixinInferrer mixinInferrer;
void set onAmbiguousSupertypes(
HandleAmbiguousSupertypes onAmbiguousSupertypes) {
_onAmbiguousSupertypesNotWrapped = onAmbiguousSupertypes;
_onAmbiguousSupertypes = (Class class_, Supertype a, Supertype b) {
onAmbiguousSupertypes(class_, a, b);
List<Supertype> recorded = _recordedAmbiguousSupertypes[class_];
if (recorded == null) {
recorded = new List<Supertype>();
_recordedAmbiguousSupertypes[class_] = recorded;
}
recorded.add(a);
recorded.add(b);
};
}
/// The insert order is important.
final Map<Class, _ClassInfo> _infoFor =
new LinkedHashMap<Class, _ClassInfo>();
/// Recorded errors for classes we have already calculated the class hierarchy
/// for, but will have to be reissued when re-using the calculation.
final Map<Class, List<Supertype>> _recordedAmbiguousSupertypes =
new LinkedHashMap<Class, List<Supertype>>();
Iterable<Class> get classes => _infoFor.keys;
int get numberOfClasses => _infoFor.length;
_ClosedWorldClassHierarchySubtypes _cachedClassHierarchySubtypes;
ClosedWorldClassHierarchy._internal(
HandleAmbiguousSupertypes onAmbiguousSupertypes, this.mixinInferrer) {
this.onAmbiguousSupertypes = onAmbiguousSupertypes;
}
ClassHierarchySubtypes computeSubtypesInformation() {
_cachedClassHierarchySubtypes ??=
new _ClosedWorldClassHierarchySubtypes(this);
return _cachedClassHierarchySubtypes;
}
@override
Iterable<Class> getOrderedClasses(Iterable<Class> unordered) {
var unorderedSet = unordered.toSet();
return _infoFor.keys.where(unorderedSet.contains);
}
@override
bool isSubclassOf(Class subclass, Class superclass) {
if (identical(subclass, superclass)) return true;
return _infoFor[subclass].isSubclassOf(_infoFor[superclass]);
}
@override
bool isSubtypeOf(Class subtype, Class superclass) {
if (identical(subtype, superclass)) return true;
return _infoFor[subtype].isSubtypeOf(_infoFor[superclass]);
}
@override
bool isUsedAsMixin(Class class_) {
return _infoFor[class_].directMixers.isNotEmpty;
}
List<_ClassInfo> _getRankedSuperclassInfos(_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;
}
@override
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 = _getRankedSuperclassInfos(info2);
} else if (info2.isSubtypeOf(info1)) {
classes1 = classes2 = _getRankedSuperclassInfos(info1);
} else {
classes1 = _getRankedSuperclassInfos(info1);
classes2 = _getRankedSuperclassInfos(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].first.asInterfaceType);
var superType2 = identical(info2, next)
? type2
: Substitution.fromInterfaceType(type2).substituteType(
info2.genericSuperTypes[next.classNode].first.asInterfaceType);
if (superType1 == superType2) {
candidate = superType1;
++numCandidatesAtThisDepth;
}
}
}
}
@override
Supertype getClassAsInstanceOf(Class class_, Class superclass) {
if (identical(class_, superclass)) return class_.asThisSupertype;
_ClassInfo info = _infoFor[class_];
if (info == null) {
throw "${class_.fileUri}: No class info for ${class_.name}";
}
_ClassInfo superInfo = _infoFor[superclass];
if (info == null) {
throw "${superclass.fileUri}: No class info for ${superclass.name}";
}
if (!info.isSubtypeOf(superInfo)) return null;
if (superclass.typeParameters.isEmpty) return superclass.asRawSupertype;
return info.genericSuperTypes[superclass]?.first;
}
@override
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass) {
Supertype castedType = getClassAsInstanceOf(type.classNode, superclass);
if (castedType == null) return null;
return Substitution
.fromInterfaceType(type)
.substituteType(castedType.asInterfaceType);
}
@override
Member getDispatchTarget(Class class_, Name name, {bool setter: false}) {
_ClassInfo info = _infoFor[class_];
List<Member> list =
setter ? info.implementedSetters : info.implementedGettersAndCalls;
return ClassHierarchy.findMemberByName(list, name);
}
@override
List<Member> getDispatchTargets(Class class_, {bool setters: false}) {
_ClassInfo info = _infoFor[class_];
return setters ? info.implementedSetters : info.implementedGettersAndCalls;
}
@override
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
List<Member> list = getInterfaceMembers(class_, setters: setter);
return ClassHierarchy.findMemberByName(list, name);
}
@override
List<Member> getInterfaceMembers(Class class_, {bool setters: false}) {
return _buildInterfaceMembers(class_, _infoFor[class_], setters: setters);
}
@override
List<Member> getDeclaredMembers(Class class_, {bool setters: false}) {
var info = _infoFor[class_];
return setters ? info.declaredSetters : info.declaredGettersAndCalls;
}
@override
void forEachOverridePair(Class class_,
callback(Member declaredMember, Member interfaceMember, bool isSetter),
{bool crossGettersSetters: false}) {
_ClassInfo info = _infoFor[class_];
for (var supertype in class_.supers) {
var superclass = supertype.classNode;
var superGetters = getInterfaceMembers(superclass);
var superSetters = getInterfaceMembers(superclass, setters: true);
_reportOverrides(info.implementedGettersAndCalls, superGetters, callback);
_reportOverrides(info.declaredGettersAndCalls, superGetters, callback,
onlyAbstract: true);
_reportOverrides(info.implementedSetters, superSetters, callback,
isSetter: true);
_reportOverrides(info.declaredSetters, superSetters, callback,
isSetter: true, onlyAbstract: true);
}
if (!class_.isAbstract) {
// If a non-abstract class declares an abstract method M whose
// implementation M' is inherited from the superclass, then the inherited
// method M' overrides the declared method M.
// This flies in the face of conventional override logic, but is necessary
// because an instance of the class will contain the method M' which can
// be invoked through the interface of M.
// Note that [_reportOverrides] does not report self-overrides, so in
// most cases these calls will just scan both lists and report nothing.
_reportOverrides(info.implementedGettersAndCalls,
info.declaredGettersAndCalls, callback);
_reportOverrides(info.implementedSetters, info.declaredSetters, callback,
isSetter: true);
}
}
static void _reportOverrides(
List<Member> declaredList,
List<Member> inheritedList,
callback(Member declaredMember, Member interfaceMember, bool isSetter),
{bool isSetter: false,
bool onlyAbstract: false}) {
int i = 0, j = 0;
while (i < declaredList.length && j < inheritedList.length) {
Member declared = declaredList[i];
if (onlyAbstract && !declared.isAbstract) {
++i;
continue;
}
Member inherited = inheritedList[j];
int comparison = ClassHierarchy.compareMembers(declared, inherited);
if (comparison < 0) {
++i;
} else if (comparison > 0) {
++j;
} else {
if (!identical(declared, inherited)) {
callback(declared, inherited, isSetter);
}
// A given declared member may override multiple interface members,
// so only move past the interface member.
++j;
}
}
}
@override
bool hasProperSubtypes(Class class_) {
_ClassInfo info = _infoFor[class_];
return info.directExtenders.isNotEmpty ||
info.directImplementers.isNotEmpty ||
info.directMixers.isNotEmpty;
}
@override
ClassHierarchy applyTreeChanges(
Iterable<Class> removedClasses, Iterable<Class> addedClasses,
{Component reissueAmbiguousSupertypesFor}) {
// 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);
}
_infoFor.remove(class_);
_recordedAmbiguousSupertypes.remove(class_);
}
// If we have a cached computation of subtypes, invalidate it and stop
// caching it.
if (_cachedClassHierarchySubtypes != null) {
_cachedClassHierarchySubtypes.invalidated = true;
}
if (_recordedAmbiguousSupertypes.isNotEmpty &&
reissueAmbiguousSupertypesFor != null) {
Set<Library> libs =
new Set<Library>.from(reissueAmbiguousSupertypesFor.libraries);
for (Class class_ in _recordedAmbiguousSupertypes.keys) {
if (!libs.contains(class_.enclosingLibrary)) continue;
List<Supertype> recorded = _recordedAmbiguousSupertypes[class_];
for (int i = 0; i < recorded.length; i += 2) {
_onAmbiguousSupertypesNotWrapped(
class_, recorded[i], recorded[i + 1]);
}
}
}
// 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);
}
_initializeTopologicallySortedClasses(
addedClassesSorted, expectedStartIndex);
return this;
}
@override
ClassHierarchy applyMemberChanges(Iterable<Class> classes,
{bool findDescendants: false}) {
if (classes.isEmpty) return this;
List<_ClassInfo> infos = new List<_ClassInfo>();
if (findDescendants) {
Set<_ClassInfo> processedClasses = new Set<_ClassInfo>();
List<_ClassInfo> worklist = <_ClassInfo>[];
for (Class class_ in classes) {
_ClassInfo info = _infoFor[class_];
worklist.add(info);
}
while (worklist.isNotEmpty) {
_ClassInfo info = worklist.removeLast();
if (processedClasses.add(info)) {
worklist.addAll(info.directExtenders);
worklist.addAll(info.directImplementers);
worklist.addAll(info.directMixers);
}
}
infos.addAll(processedClasses);
} else {
for (Class class_ in classes) {
_ClassInfo info = _infoFor[class_];
infos.add(info);
}
}
infos.sort((_ClassInfo a, _ClassInfo b) {
return a.topologicalIndex - b.topologicalIndex;
});
for (_ClassInfo info in infos) {
Class class_ = info.classNode;
_buildDeclaredMembers(class_, info);
_buildImplementedMembers(class_, info);
info.interfaceSetters = null;
info.interfaceGettersAndCalls = null;
_buildInterfaceMembers(class_, info, setters: true);
_buildInterfaceMembers(class_, info, setters: false);
}
return this;
}
@override
Supertype asInstantiationOf(Supertype type, Class superclass) {
// This is similar to getTypeAsInstanceOf, except that it assumes that
// superclass is a generic class. It thus does not rely on being able
// to answer isSubtypeOf queries and so can be used before we have built
// the intervals needed for those queries.
assert(superclass.typeParameters.isNotEmpty);
if (type.classNode == superclass) {
return superclass.asThisSupertype;
}
var map = _infoFor[type.classNode]?.genericSuperTypes;
return map == null ? null : map[superclass]?.first;
}
void _initialize(List<Library> libraries) {
// Build the class ordering based on a topological sort.
for (var library in libraries) {
for (var classNode in library.classes) {
_topologicalSortVisit(classNode, new Set<Class>());
}
}
_initializeTopologicallySortedClasses(_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 _initializeTopologicallySortedClasses(
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);
}
if (class_.mixedInType != null) {
_infoFor[class_.mixedInType.classNode].directMixers.add(info);
}
for (var supertype in class_.implementedTypes) {
_infoFor[supertype.classNode].directImplementers.add(info);
}
_collectSupersForClass(class_);
if (class_.supertype != null) {
_recordSuperTypes(info, class_.supertype);
}
if (class_.mixedInType != null) {
mixinInferrer?.infer(this, class_);
_recordSuperTypes(info, class_.mixedInType);
}
for (Supertype supertype in class_.implementedTypes) {
_recordSuperTypes(info, supertype);
}
_buildInterfaceMembers(class_, info, setters: true);
_buildInterfaceMembers(class_, info, setters: false);
if (info == null) {
throw "No info for ${class_.name} from ${class_.fileUri}.";
}
if (info.topologicalIndex != i) {
throw "Unexpected topologicalIndex (${info.topologicalIndex} != $i) "
"for ${class_.name} from ${class_.fileUri}.";
}
i++;
}
}
/// 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;
int _topologicalSortVisit(Class classNode, Set<Class> beingVisited,
{List<Class> orderedList}) {
var info = _infoFor[classNode];
if (info != null) {
return info.depth;
}
if (!beingVisited.add(classNode)) {
throw 'Cyclic inheritance involving ${classNode.name}';
}
info = new _ClassInfo(classNode);
int superDepth = -1;
if (classNode.supertype != null) {
superDepth = max(
superDepth,
_topologicalSortVisit(classNode.supertype.classNode, beingVisited,
orderedList: orderedList));
}
if (classNode.mixedInType != null) {
superDepth = max(
superDepth,
_topologicalSortVisit(classNode.mixedInType.classNode, beingVisited,
orderedList: orderedList));
}
for (var supertype in classNode.implementedTypes) {
superDepth = max(
superDepth,
_topologicalSortVisit(supertype.classNode, beingVisited,
orderedList: orderedList));
}
_buildDeclaredMembers(classNode, info);
_buildImplementedMembers(classNode, info);
info.topologicalIndex = _topSortIndex++;
_infoFor[classNode] = info;
orderedList?.add(classNode);
beingVisited.remove(classNode);
return info.depth = superDepth + 1;
}
void _buildDeclaredMembers(Class classNode, _ClassInfo info) {
if (classNode.mixedInType != null) {
_ClassInfo mixedInfo = _infoFor[classNode.mixedInType.classNode];
List<Member> declaredGettersAndCalls = <Member>[];
for (Member mixinMember in mixedInfo.declaredGettersAndCalls) {
if (mixinMember is! Procedure ||
(mixinMember is Procedure &&
!mixinMember.isNoSuchMethodForwarder)) {
declaredGettersAndCalls.add(mixinMember);
}
}
List<Member> declaredSetters = <Member>[];
for (Member mixinMember in mixedInfo.declaredSetters) {
if (mixinMember is! Procedure ||
(mixinMember is Procedure &&
!mixinMember.isNoSuchMethodForwarder)) {
declaredSetters.add(mixinMember);
}
}
info.declaredGettersAndCalls = declaredGettersAndCalls;
info.declaredSetters = declaredSetters;
} else {
var members = info.declaredGettersAndCalls = <Member>[];
var setters = info.declaredSetters = <Member>[];
for (Procedure procedure in classNode.procedures) {
if (procedure.isStatic) continue;
if (procedure.kind == ProcedureKind.Setter) {
setters.add(procedure);
} else {
members.add(procedure);
}
}
for (Field field in classNode.fields) {
if (field.isStatic) continue;
if (field.hasImplicitGetter) {
members.add(field);
}
if (field.hasImplicitSetter) {
setters.add(field);
}
}
members.sort(ClassHierarchy.compareMembers);
setters.sort(ClassHierarchy.compareMembers);
}
}
void _buildImplementedMembers(Class classNode, _ClassInfo info) {
List<Member> inheritedMembers;
List<Member> inheritedSetters;
if (classNode.supertype == null) {
inheritedMembers = inheritedSetters = const <Member>[];
} else {
_ClassInfo superInfo = _infoFor[classNode.supertype.classNode];
inheritedMembers = superInfo.implementedGettersAndCalls;
inheritedSetters = superInfo.implementedSetters;
}
info.implementedGettersAndCalls = _inheritMembers(
info.declaredGettersAndCalls, inheritedMembers,
skipAbstractMembers: true);
info.implementedSetters = _inheritMembers(
info.declaredSetters, inheritedSetters,
skipAbstractMembers: true);
}
List<Member> _buildInterfaceMembers(Class classNode, _ClassInfo info,
{bool setters}) {
if (info == null) {
throw "${classNode.fileUri}: No class info for ${classNode.name}";
}
List<Member> members =
setters ? info.interfaceSetters : info.interfaceGettersAndCalls;
if (members != null) return members;
List<Member> allInheritedMembers = <Member>[];
List<Member> declared =
setters ? info.declaredSetters : info.declaredGettersAndCalls;
void inheritFrom(Supertype type) {
if (type == null) return;
List<Member> inherited = _buildInterfaceMembers(
type.classNode, _infoFor[type.classNode],
setters: setters);
inherited = _getUnshadowedInheritedMembers(declared, inherited);
allInheritedMembers =
ClassHierarchy.mergeSortedLists(allInheritedMembers, inherited);
}
inheritFrom(classNode.supertype);
inheritFrom(classNode.mixedInType);
classNode.implementedTypes.forEach(inheritFrom);
members = _inheritMembers(declared, allInheritedMembers);
if (setters) {
info.interfaceSetters = members;
} else {
info.interfaceGettersAndCalls = members;
}
return members;
}
/// Computes the list of implemented members, based on the declared instance
/// members and inherited instance members.
///
/// Both lists must be sorted by name beforehand.
static List<Member> _inheritMembers(
List<Member> declared, List<Member> inherited,
{bool skipAbstractMembers: false}) {
List<Member> result = <Member>[]..length =
declared.length + inherited.length;
// Since both lists are sorted, we can fuse them like in merge sort.
int storeIndex = 0;
int i = 0, j = 0;
while (i < declared.length && j < inherited.length) {
Member declaredMember = declared[i];
Member inheritedMember = inherited[j];
if (skipAbstractMembers && declaredMember.isAbstract) {
++i;
continue;
}
if (skipAbstractMembers && inheritedMember.isAbstract) {
++j;
continue;
}
int comparison =
ClassHierarchy.compareMembers(declaredMember, inheritedMember);
if (comparison < 0) {
result[storeIndex++] = declaredMember;
++i;
} else if (comparison > 0) {
result[storeIndex++] = inheritedMember;
++j;
} else {
result[storeIndex++] = declaredMember;
++i;
++j; // Move past overridden member.
}
}
// One of the two lists is now exhausted, copy over the remains.
while (i < declared.length) {
Member declaredMember = declared[i++];
if (skipAbstractMembers && declaredMember.isAbstract) continue;
result[storeIndex++] = declaredMember;
}
while (j < inherited.length) {
Member inheritedMember = inherited[j++];
if (skipAbstractMembers && inheritedMember.isAbstract) continue;
result[storeIndex++] = inheritedMember;
}
result.length = storeIndex;
return result;
}
/// Returns the subset of members in [inherited] for which a member with the
/// same name does not occur in [declared].
///
/// The input lists must be sorted, and the returned list is sorted.
static List<Member> _getUnshadowedInheritedMembers(
List<Member> declared, List<Member> inherited) {
List<Member> result = <Member>[]..length = inherited.length;
int storeIndex = 0;
int i = 0, j = 0;
while (i < declared.length && j < inherited.length) {
Member declaredMember = declared[i];
Member inheritedMember = inherited[j];
int comparison =
ClassHierarchy.compareMembers(declaredMember, inheritedMember);
if (comparison < 0) {
++i;
} else if (comparison > 0) {
result[storeIndex++] = inheritedMember;
++j;
} else {
// Move past the shadowed member, but retain the declared member, as
// it may shadow multiple members.
++j;
}
}
// If the list of declared members is exhausted, copy over the remains of
// the inherited members.
while (j < inherited.length) {
result[storeIndex++] = inherited[j++];
}
result.length = storeIndex;
return result;
}
void _recordSuperTypes(_ClassInfo subInfo, Supertype supertype) {
_ClassInfo superInfo = _infoFor[supertype.classNode];
if (supertype.typeArguments.isEmpty) {
if (superInfo.genericSuperTypes == null) return;
// Copy over the super type entries.
subInfo.genericSuperTypes ??= <Class, List<Supertype>>{};
superInfo.genericSuperTypes?.forEach((Class key, List<Supertype> types) {
for (Supertype type in types) {
subInfo.recordGenericSuperType(key, type, _onAmbiguousSupertypes);
}
});
} else {
// Copy over all transitive generic super types, and substitute the
// free variables with those provided in [supertype].
Class superclass = supertype.classNode;
var substitution = Substitution.fromPairs(
superclass.typeParameters, supertype.typeArguments);
subInfo.genericSuperTypes ??= <Class, List<Supertype>>{};
superInfo.genericSuperTypes?.forEach((Class key, List<Supertype> types) {
for (Supertype type in types) {
subInfo.recordGenericSuperType(key,
substitution.substituteSupertype(type), _onAmbiguousSupertypes);
}
});
subInfo.recordGenericSuperType(
superclass, supertype, _onAmbiguousSupertypes);
}
}
/// Build lists of super types and super classes.
/// Note that the super class and super types of the class must already have
/// had their supers collected.
void _collectSupersForClass(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);
}
if (class_.mixedInType != null) {
_ClassInfo mixedInTypeInfo = _infoFor[class_.mixedInType.classNode];
supertypeSetBuilder
.addIntervalList(mixedInTypeInfo.supertypeIntervalList);
}
for (Supertype supertype in class_.implementedTypes) {
_ClassInfo supertypeInfo = _infoFor[supertype.classNode];
supertypeSetBuilder.addIntervalList(supertypeInfo.supertypeIntervalList);
}
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 supertype 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 _infoFor.keys) {
var info = _infoFor[class_];
int intervals = info.supertypeIntervalList.length ~/ 2;
if (intervals >= result.length) {
int oldLength = result.length;
result.length = intervals + 1;
result.fillRange(oldLength, result.length, 0);
}
result[intervals] += 1;
}
return result;
}
/// 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 superclass/supertype pairs.
double getCompressionRatio() {
int intervals = 0;
int sizes = 0;
for (Class class_ in _infoFor.keys) {
var info = _infoFor[class_];
intervals += (info.superclassIntervalList.length +
info.supertypeIntervalList.length) ~/
2;
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 _infoFor.keys) {
sum += _infoFor[class_].genericSuperTypes?.length ?? 0;
}
return sum;
}
}
class _IntervalListBuilder {
final List<int> events = <int>[];
void addInterval(int start, int end) {
// Add an event point for each interval end point, using the low bit to
// distinguish opening from closing end points. Closing end points should
// have the high bit to ensure they occur after an opening end point.
events.add(start << 1);
events.add((end << 1) + 1);
}
void addSingleton(int x) {
addInterval(x, x + 1);
}
void addIntervalList(Uint32List intervals) {
for (int i = 0; i < intervals.length; i += 2) {
addInterval(intervals[i], intervals[i + 1]);
}
}
Uint32List buildIntervalList() {
// Sort the event points and sweep left to right while tracking how many
// intervals we are currently inside. Record an interval end point when the
// number of intervals drop to zero or increase from zero to one.
// Event points are encoded so that an opening end point occur before a
// closing end point at the same value.
events.sort();
int insideCount = 0; // The number of intervals we are currently inside.
int storeIndex = 0;
for (int i = 0; i < events.length; ++i) {
int event = events[i];
if (event & 1 == 0) {
// Start point
++insideCount;
if (insideCount == 1) {
// Store the results temporarily back in the event array.
events[storeIndex++] = event >> 1;
}
} else {
// End point
--insideCount;
if (insideCount == 0) {
events[storeIndex++] = event >> 1;
}
}
}
// Copy the results over to a typed array of the correct length.
var result = new Uint32List(storeIndex);
for (int i = 0; i < storeIndex; ++i) {
result[i] = events[i];
}
return result;
}
}
bool _intervalListContains(Uint32List intervalList, int x) {
int low = 0, high = intervalList.length - 1;
if (high == -1 || x < intervalList[0] || intervalList[high] <= x) {
return false;
}
// Find the lower bound of x in the list.
// If the lower bound is at an even index, the lower bound is an opening point
// of an interval that contains x, otherwise it is a closing point of an
// interval below x and there is no interval containing x.
while (low < high) {
int mid = high - ((high - low) >> 1); // Get middle, rounding up.
int pivot = intervalList[mid];
if (pivot <= x) {
low = mid;
} else {
high = mid - 1;
}
}
return low == high && (low & 1) == 0;
}
int _intervalListSize(Uint32List intervalList) {
int size = 0;
for (int i = 0; i < intervalList.length; i += 2) {
size += intervalList[i + 1] - intervalList[i];
}
return size;
}
class _ClassInfo {
final Class classNode;
int topologicalIndex = 0;
int depth = 0;
// Super types must always occur before subtypes in these lists.
// For example:
//
// class A extends Object
// class B extends Object implements A
//
// Here `A` must occur before `B` in the list of direct extenders of Object,
// because `B` is a subtype of `A`.
final Set<_ClassInfo> directExtenders = new LinkedHashSet<_ClassInfo>();
final Set<_ClassInfo> directMixers = new LinkedHashSet<_ClassInfo>();
final Set<_ClassInfo> directImplementers = new LinkedHashSet<_ClassInfo>();
Uint32List superclassIntervalList;
Uint32List supertypeIntervalList;
List<_ClassInfo> leastUpperBoundInfos;
/// Maps generic supertype classes to the instantiation implemented by this
/// class.
///
/// E.g. `List` maps to `List<String>` for a class that directly of indirectly
/// implements `List<String>`.
Map<Class, List<Supertype>> genericSuperTypes;
/// Instance fields, getters, methods, and operators declared in this class
/// or its mixed-in class, sorted according to [_compareMembers].
List<Member> declaredGettersAndCalls;
/// Non-final instance fields and setters declared in this class or its
/// mixed-in class, sorted according to [_compareMembers].
List<Member> declaredSetters;
/// Instance fields, getters, methods, and operators implemented by this class
/// (declared or inherited).
List<Member> implementedGettersAndCalls;
/// Non-final instance fields and setters implemented by this class
/// (declared or inherited).
List<Member> implementedSetters;
List<Member> interfaceGettersAndCalls;
List<Member> interfaceSetters;
_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];
if (existing == null) {
genericSuperTypes[cls] = <Supertype>[type];
} else if (type != existing.first) {
existing.add(type);
onAmbiguousSupertypes(classNode, existing.first, type);
}
}
}
/// An immutable set of classes.
class ClassSet extends IterableBase<Class> {
final Set<Class> _classes;
ClassSet(this._classes);
bool contains(Object class_) {
return _classes.contains(_classes);
}
ClassSet union(ClassSet other) {
Set<Class> result = new Set<Class>.from(_classes);
result.addAll(other._classes);
return new ClassSet(result);
}
@override
Iterator<Class> get iterator => _classes.iterator;
}
/// 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;
}
}