blob: e45c3ac2bdb01784f493b1e8a647fc72870f7bc1 [file] [log] [blame]
// Copyright (c) 2017, 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.
import '../common_elements.dart';
import '../elements/elements.dart' show ClassElement, MixinApplicationElement;
import '../elements/entities.dart';
import '../elements/types.dart' show InterfaceType;
import 'class_set.dart';
class ClassHierarchyBuilder {
// We keep track of subtype and subclass relationships in four
// distinct sets to make class hierarchy analysis faster.
final Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes =
<ClassEntity, ClassHierarchyNode>{};
final Map<ClassEntity, ClassSet> classSets = <ClassEntity, ClassSet>{};
final Map<ClassEntity, Set<ClassEntity>> mixinUses =
new Map<ClassEntity, Set<ClassEntity>>();
final CommonElements _commonElements;
final ClassQueries _classQueries;
ClassHierarchyBuilder(this._commonElements, this._classQueries);
void registerClass(ClassEntity cls) {
_ensureClassSet(_classQueries.getDeclaration(cls));
}
ClassHierarchyNode _ensureClassHierarchyNode(ClassEntity cls) {
assert(_classQueries.checkClass(cls));
return classHierarchyNodes.putIfAbsent(cls, () {
ClassHierarchyNode parentNode;
ClassEntity superclass = _classQueries.getSuperClass(cls);
if (superclass != null) {
parentNode = _ensureClassHierarchyNode(superclass);
}
return new ClassHierarchyNode(
parentNode, cls, _classQueries.getHierarchyDepth(cls));
});
}
ClassSet _ensureClassSet(ClassEntity cls) {
assert(_classQueries.checkClass(cls));
return classSets.putIfAbsent(cls, () {
ClassHierarchyNode node = _ensureClassHierarchyNode(cls);
ClassSet classSet = new ClassSet(node);
for (InterfaceType type in _classQueries.getSupertypes(cls)) {
// TODO(johnniwinther): Optimization: Avoid adding [cls] to
// superclasses.
ClassSet subtypeSet = _ensureClassSet(type.element);
subtypeSet.addSubtype(node);
}
ClassEntity appliedMixin = _classQueries.getAppliedMixin(cls);
if (appliedMixin != null) {
// TODO(johnniwinther): Store this in the [ClassSet].
registerMixinUse(cls, appliedMixin);
}
return classSet;
});
}
void _updateSuperClassHierarchyNodeForClass(ClassHierarchyNode node) {
// Ensure that classes implicitly implementing `Function` are in its
// subtype set.
ClassEntity cls = node.cls;
if (cls != _commonElements.functionClass &&
_classQueries.implementsFunction(cls)) {
ClassSet subtypeSet = _ensureClassSet(_commonElements.functionClass);
subtypeSet.addSubtype(node);
}
if (!node.isInstantiated && node.parentNode != null) {
_updateSuperClassHierarchyNodeForClass(node.parentNode);
}
}
void updateClassHierarchyNodeForClass(ClassEntity cls,
{bool directlyInstantiated: false, bool abstractlyInstantiated: false}) {
ClassHierarchyNode node = _ensureClassHierarchyNode(cls);
_updateSuperClassHierarchyNodeForClass(node);
if (directlyInstantiated) {
node.isDirectlyInstantiated = true;
}
if (abstractlyInstantiated) {
node.isAbstractlyInstantiated = true;
}
}
void registerMixinUse(ClassEntity mixinApplication, ClassEntity mixin) {
// TODO(johnniwinther): Add map restricted to live classes.
// We don't support patch classes as mixin.
Set<ClassEntity> users =
mixinUses.putIfAbsent(mixin, () => new Set<ClassEntity>());
users.add(mixinApplication);
}
}
abstract class ClassQueries {
bool checkClass(covariant ClassEntity cls);
bool validateClass(covariant ClassEntity cls);
/// Returns the declaration of [cls].
ClassEntity getDeclaration(covariant ClassEntity cls);
/// Returns the class mixed into [cls] if any.
ClassEntity getAppliedMixin(covariant ClassEntity cls);
/// Returns the hierarchy depth of [cls].
int getHierarchyDepth(covariant ClassEntity cls);
/// Returns `true` if [cls] implements `Function` either explicitly or through
/// a `call` method.
bool implementsFunction(covariant ClassEntity cls);
/// Returns the superclass of [cls] if any.
ClassEntity getSuperClass(covariant ClassEntity cls);
/// Returns all supertypes of [cls].
Iterable<InterfaceType> getSupertypes(covariant ClassEntity cls);
}
class ElementClassQueries extends ClassQueries {
final CommonElements commonElements;
ElementClassQueries(this.commonElements);
@override
ClassEntity getDeclaration(ClassElement cls) {
return cls.declaration;
}
@override
ClassEntity getAppliedMixin(ClassElement cls) {
if (cls.isMixinApplication) {
MixinApplicationElement mixinApplication = cls;
// Note: If [mixinApplication] is malformed [mixin] is `null`.
return mixinApplication.mixin;
}
return null;
}
@override
int getHierarchyDepth(ClassElement cls) => cls.hierarchyDepth;
@override
bool checkClass(ClassElement cls) => cls.isDeclaration;
@override
bool validateClass(ClassElement cls) => cls.isResolved;
@override
bool implementsFunction(ClassElement cls) =>
cls.implementsFunction(commonElements);
@override
ClassEntity getSuperClass(ClassElement cls) => cls.superclass;
@override
Iterable<InterfaceType> getSupertypes(ClassElement cls) => cls.allSupertypes;
}