// 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 'ast.dart'; import 'dart:math'; import 'dart:typed_data'; import 'type_algebra.dart'; /// Data structure for answering various subclassing queries. class ClassHierarchy { /// All classes in the program. /// /// The list is ordered so that classes occur after their super classes. final List classes; final Map _infoFor = {}; ClassHierarchy(Program program) : this._internal(program, _countClasses(program)); Class get rootClass => classes[0]; /// Returns the index of [class_] in the [classes] list. int getClassIndex(Class class_) => _infoFor[class_].topologicalIndex; /// True if [subclass] inherits from [superclass] though zero or more /// `extends` relationships. bool isSubclassOf(Class subclass, Class superclass) { if (identical(subclass, superclass)) return true; return _infoFor[subclass].isSubclassOf(_infoFor[superclass]); } /// True if [submixture] inherits from [superclass] though zero or more /// `extends` and `with` relationships. bool isSubmixtureOf(Class submixture, Class superclass) { if (identical(submixture, superclass)) return true; return _infoFor[submixture].isSubmixtureOf(_infoFor[superclass]); } /// True if [subtype] inherits from [superclass] though zero or more /// `extends`, `with`, and `implements` relationships. bool isSubtypeOf(Class subtype, Class superclass) { if (identical(subtype, superclass)) return true; return _infoFor[subtype].isSubtypeOf(_infoFor[superclass]); } /// True if the given class is the direct super class of another class. bool isUsedAsSuperClass(Class class_) { return _infoFor[class_].directExtenders.isNotEmpty; } /// True if the given class is used as the right-hand operand to a /// mixin application (i.e. [Class.mixedInType]). bool isUsedAsMixin(Class class_) { return _infoFor[class_].directMixers.isNotEmpty; } /// True if the given class is used in an `implements` clause. bool isUsedAsSuperInterface(Class class_) { return _infoFor[class_].directImplementers.isNotEmpty; } /// 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) { if (identical(class_, superclass)) return class_.asThisSupertype; _ClassInfo info = _infoFor[class_]; _ClassInfo superInfo = _infoFor[superclass]; if (!info.isSubtypeOf(superInfo)) return null; if (superclass.typeParameters.isEmpty) return superclass.asRawSupertype; return info.genericSuperTypes[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) { Supertype castedType = getClassAsInstanceOf(type.classNode, superclass); if (castedType == null) return null; return Substitution .fromInterfaceType(type) .substituteType(castedType.asInterfaceType); } /// 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}) { _ClassInfo info = _infoFor[class_]; List list = setter ? info.implementedSetters : info.implementedGettersAndCalls; return _findMemberByName(list, name); } /// 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 getDispatchTargets(Class class_, {bool setters: false}) { _ClassInfo info = _infoFor[class_]; return setters ? info.implementedSetters : info.implementedGettersAndCalls; } /// Returns the possibly abstract interface member of [class_] with the given /// [name]. /// /// If [setters] is `false`, only fields, methods, and getters with that name /// will be found. If [setters] is `true`, only non-final fields and setters /// will be found. /// /// If multiple members with that name are inherited and not overidden, the /// member from the first declared supertype is returned. Member getInterfaceMember(Class class_, Name name, {bool setter: false}) { List list = getInterfaceMembers(class_, setters: setter); return _findMemberByName(list, name); } /// 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 getInterfaceMembers(Class class_, {bool setters: false}) { return _buildInterfaceMembers(class_, _infoFor[class_], setters: setters); } /// 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. /// /// 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)) { _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 declaredList, List 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 = _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; } } } /// True if the program contains another class that is a subtype of given one. bool hasProperSubtypes(Class class_) { // If there are no subtypes then the subtype set contains the class itself. return !getSubtypesOf(class_).isSingleton; } /// Returns the subtypes of [class_] as an interval list. ClassSet getSubtypesOf(Class class_) { return new ClassSet(this, _infoFor[class_].subtypeIntervalList); } /// Returns the subclasses of [class_] as an interval list. ClassSet getSubclassesOf(Class class_) { return new ClassSet(this, _infoFor[class_].subclassIntervalList); } ClassHierarchy._internal(Program program, int numberOfClasses) : classes = new List(numberOfClasses) { // Build the class ordering based on a topological sort. for (var library in program.libraries) { for (var classNode in library.classes) { _topologicalSortVisit(classNode); } } // 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_]; 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); } } // Run a downward traversal from the root, compute preorder numbers for // each class, and build their subtype sets as interval lists. _topDownSortVisit(_infoFor[rootClass]); for (int i = 0; i < classes.length; ++i) { var class_ = classes[i]; _buildInterfaceMembers(class_, _infoFor[class_], setters: true); _buildInterfaceMembers(class_, _infoFor[class_], setters: false); } } /// Upwards traversal of the class hierarchy that orders classes so super /// types before their subtypes. int _topSortIndex = 0; void _topologicalSortVisit(Class classNode) { var info = _infoFor[classNode]; if (info != null) { if (info.isBeingVisited) { throw 'Cyclic inheritance involving ${info.classNode.name}'; } return; // Already built. } _infoFor[classNode] = info = new _ClassInfo(classNode); info.isBeingVisited = true; if (classNode.supertype != null) { _topologicalSortVisit(classNode.supertype.classNode); _recordSuperTypes(info, classNode.supertype); } if (classNode.mixedInType != null) { _topologicalSortVisit(classNode.mixedInType.classNode); _recordSuperTypes(info, classNode.mixedInType); } for (var supertype in classNode.implementedTypes) { _topologicalSortVisit(supertype.classNode); _recordSuperTypes(info, supertype); } _buildDeclaredMembers(classNode, info); _buildImplementedMembers(classNode, info); int id = _topSortIndex++; info.topologicalIndex = id; classes[id] = info.classNode; info.isBeingVisited = false; } void _buildDeclaredMembers(Class classNode, _ClassInfo info) { if (classNode.mixedInType != null) { _ClassInfo mixedInfo = _infoFor[classNode.mixedInType.classNode]; info.declaredGettersAndCalls = mixedInfo.declaredGettersAndCalls; info.declaredSetters = mixedInfo.declaredSetters; } else { var members = info.declaredGettersAndCalls = []; var setters = info.declaredSetters = []; 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(_compareMembers); setters.sort(_compareMembers); } } void _buildImplementedMembers(Class classNode, _ClassInfo info) { List inheritedMembers; List inheritedSetters; if (classNode.supertype == null) { inheritedMembers = inheritedSetters = const []; } 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 _buildInterfaceMembers(Class classNode, _ClassInfo info, {bool setters}) { List members = setters ? info.interfaceSetters : info.interfaceGettersAndCalls; if (members != null) return members; List allInheritedMembers = []; List declared = setters ? info.declaredSetters : info.declaredGettersAndCalls; void inheritFrom(Supertype type) { if (type == null) return; List inherited = _buildInterfaceMembers( type.classNode, _infoFor[type.classNode], setters: setters); inherited = _getUnshadowedInheritedMembers(declared, inherited); allInheritedMembers = _merge(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 _inheritMembers( List declared, List inherited, {bool skipAbstractMembers: false}) { List result = [] ..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 = _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 _getUnshadowedInheritedMembers( List declared, List inherited) { List result = []..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 = _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; } /// 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. static List _merge(List first, List second) { if (first.isEmpty) return second; if (second.isEmpty) return first; List result = []..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 = _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; } void _recordSuperTypes(_ClassInfo subInfo, Supertype supertype) { _ClassInfo superInfo = _infoFor[supertype.classNode]; if (supertype.typeArguments.isEmpty) { if (superInfo.genericSuperTypes == null) return; // Since the immediate super type is not generic, all entries in its // super type map are also valid entries for this class. if (subInfo.genericSuperTypes == null && superInfo.ownsGenericSuperTypeMap) { // Instead of copying the map, take ownership of the map object. // This may result in more entries being added to the map later. Those // are not valid for the super type, but it works out because all // lookups in the map are guarded by a subtype check, so the super type // will not be bothered by the extra entries. subInfo.genericSuperTypes = superInfo.genericSuperTypes; superInfo.ownsGenericSuperTypeMap = false; } else { // Copy over the super type entries. subInfo.genericSuperTypes ??= {}; subInfo.genericSuperTypes.addAll(superInfo.genericSuperTypes); } } 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 ??= {}; superInfo.genericSuperTypes?.forEach((Class key, Supertype type) { subInfo.genericSuperTypes[key] = substitution.substituteSupertype(type); }); subInfo.genericSuperTypes[superclass] = supertype; } } /// 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; 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); } for (var subtype in info.directMixers) { _topDownSortVisit(subtype); submixtureSetBuilder.addIntervalList(subtype.submixtureIntervalList); subtypeSetBuilder.addIntervalList(subtype.subtypeIntervalList); } for (var subtype in info.directImplementers) { _topDownSortVisit(subtype); subtypeSetBuilder.addIntervalList(subtype.subtypeIntervalList); } info.subclassIntervalList = subclassSetBuilder.buildIntervalList(); info.submixtureIntervalList = isMixedIn ? submixtureSetBuilder.buildIntervalList() : info.subclassIntervalList; info.subtypeIntervalList = subtypeSetBuilder.buildIntervalList(); } static int _countClasses(Program program) { int count = 0; for (var library in program.libraries) { count += library.classes.length; } return count; } /// 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). /// /// The more numbers are condensed near the beginning, the more efficient the /// internal data structure is. List getExpenseHistogram() { var result = []; for (Class class_ in classes) { var info = _infoFor[class_]; int intervals = max(info.subclassIntervalList.length, info.subtypeIntervalList.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 subtype 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. double getCompressionRatio() { int intervals = 0; int sizes = 0; for (Class class_ in classes) { var info = _infoFor[class_]; intervals += (info.subclassIntervalList.length + info.subtypeIntervalList.length) ~/ 2; sizes += _intervalListSize(info.subclassIntervalList) + _intervalListSize(info.subtypeIntervalList); } 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) { _ClassInfo info = _infoFor[class_]; if (info.ownsGenericSuperTypeMap) { sum += _infoFor[class_].genericSuperTypes?.length ?? 0; } } return sum; } } class _IntervalListBuilder { final List events = []; 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]); } } List 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; } /// 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. Member _findMemberByName(List 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; } /// Compares members by name. int _compareMembers(Member first, Member second) { return _compareNames(first.name, second.name); } /// Compares names using an arbitrary as-fast-as-possible sorting criterion. 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; } class _ClassInfo { final Class classNode; int topologicalIndex = 0; int topDownIndex = -1; bool isBeingVisited = false; // 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 List<_ClassInfo> directExtenders = <_ClassInfo>[]; final List<_ClassInfo> directMixers = <_ClassInfo>[]; final List<_ClassInfo> directImplementers = <_ClassInfo>[]; /// Top-down indices of all subclasses of this class, represented as /// interleaved begin/end interval end points. Uint32List subclassIntervalList; Uint32List submixtureIntervalList; Uint32List subtypeIntervalList; 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. /// /// E.g. `List` maps to `List` for a class that directly of indirectly /// implements `List`. /// /// However, the map may contain additional entries for classes that are not /// supertypes of this class, so that a single map object can be shared /// between different classes. Lookups into the map should therefore be /// guarded by a subtype check. /// /// For example: /// /// class Q /// class A /// /// class B extends A /// class C extends B implements Q /// /// In this case, a single map object `{A: A, Q: Q}` may be /// shared by the classes `B` and `C`. Map genericSuperTypes; /// If true, this is the current "owner" of [genericSuperTypes], meaning /// we may add additional entries to the map or transfer ownership to another /// class. bool ownsGenericSuperTypeMap = true; /// Instance fields, getters, methods, and operators declared in this class /// or its mixed-in class, sorted according to [_compareMembers]. List declaredGettersAndCalls; /// Non-final instance fields and setters declared in this class or its /// mixed-in class, sorted according to [_compareMembers]. List declaredSetters; /// Instance fields, getters, methods, and operators implemented by this class /// (declared or inherited). List implementedGettersAndCalls; /// Non-final instance fields and setters implemented by this class /// (declared or inherited). List implementedSetters; List interfaceGettersAndCalls; List interfaceSetters; _ClassInfo(this.classNode); } /// An immutable set of classes, internally represented as an interval list. class ClassSet { final ClassHierarchy _hierarchy; final Uint32List _intervalList; 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]; } bool contains(Class class_) { return _intervalListContains( _intervalList, _hierarchy._infoFor[class_].topDownIndex); } 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()); } }