Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Unified Diff: pkg/compiler/lib/src/universe/class_set.dart

Issue 1627333002: Optimize subclass/subtype queries (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Use strictSubtypeCount Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/types/union_type_mask.dart ('k') | pkg/compiler/lib/src/world.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/universe/class_set.dart
diff --git a/pkg/compiler/lib/src/universe/class_set.dart b/pkg/compiler/lib/src/universe/class_set.dart
index d895de29edc85d86e2cd0b6fc35277210e082a09..3790a03d097c89faf080a82f901e0d006da94eb8 100644
--- a/pkg/compiler/lib/src/universe/class_set.dart
+++ b/pkg/compiler/lib/src/universe/class_set.dart
@@ -6,6 +6,7 @@ library dart2js.world.class_set;
import 'dart:collection' show
IterableBase;
+import '../common.dart';
import '../elements/elements.dart' show
ClassElement;
import '../util/enumset.dart' show
@@ -92,12 +93,14 @@ class ClassHierarchyNode {
return mask;
}
+ final ClassHierarchyNode parentNode;
final ClassElement cls;
final EnumSet<Instantiation> _mask =
new EnumSet<Instantiation>.fromValues(
const <Instantiation>[Instantiation.UNINSTANTIATED]);
ClassElement _leastUpperInstantiatedSubclass;
+ int _instantiatedSubclassCount = 0;
/// `true` if [cls] has been directly instantiated.
///
@@ -111,14 +114,23 @@ class ClassHierarchyNode {
void set isDirectlyInstantiated(bool value) {
if (value != isDirectlyInstantiated) {
+ ClassHierarchyNode parent = parentNode;
if (value) {
_mask.remove(Instantiation.UNINSTANTIATED);
_mask.add(Instantiation.DIRECTLY_INSTANTIATED);
+ while (parent != null) {
+ parent._updateInstantiatedSubclassCount(1);
+ parent = parent.parentNode;
+ }
} else {
_mask.remove(Instantiation.DIRECTLY_INSTANTIATED);
if (_mask.isEmpty) {
_mask.add(Instantiation.UNINSTANTIATED);
}
+ while (parent != null) {
+ parent._updateInstantiatedSubclassCount(-1);
+ parent = parent.parentNode;
+ }
}
}
}
@@ -131,12 +143,18 @@ class ClassHierarchyNode {
/// class C extends B {}
/// main() => [new B(), new C()];
///
- bool get isIndirectlyInstantiated =>
- _mask.contains(Instantiation.INDIRECTLY_INSTANTIATED);
-
- void set isIndirectlyInstantiated(bool value) {
- if (value != isIndirectlyInstantiated) {
- if (value) {
+ bool get isIndirectlyInstantiated => _instantiatedSubclassCount > 0;
+
+ /// The number of strict subclasses that are directly or indirectly
+ /// instantiated.
+ int get instantiatedSubclassCount => _instantiatedSubclassCount;
+
+ void _updateInstantiatedSubclassCount(int change) {
+ bool before = isIndirectlyInstantiated;
+ _instantiatedSubclassCount += change;
+ bool after = isIndirectlyInstantiated;
+ if (before != after) {
+ if (after) {
_mask.remove(Instantiation.UNINSTANTIATED);
_mask.add(Instantiation.INDIRECTLY_INSTANTIATED);
} else {
@@ -151,7 +169,11 @@ class ClassHierarchyNode {
/// The nodes for the direct subclasses of [cls].
Link<ClassHierarchyNode> _directSubclasses = const Link<ClassHierarchyNode>();
- ClassHierarchyNode(this.cls);
+ ClassHierarchyNode(this.parentNode, this.cls) {
+ if (parentNode != null) {
+ parentNode.addDirectSubclass(this);
+ }
+ }
/// Adds [subclass] as a direct subclass of [cls].
void addDirectSubclass(ClassHierarchyNode subclass) {
@@ -177,24 +199,6 @@ class ClassHierarchyNode {
/// Returns an [Iterable] of the subclasses of [cls] possibly including [cls].
///
- /// The directly instantiated, indirectly instantiated and uninstantiated
- /// subclasses of [cls] are returned if [includeDirectlyInstantiated],
- /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`,
- /// respectively. If [strict] is `true`, [cls] itself is _not_ returned.
- Iterable<ClassElement> subclasses(
- {bool includeDirectlyInstantiated: true,
- bool includeIndirectlyInstantiated: true,
- bool includeUninstantiated: true,
- bool strict: false}) {
- EnumSet<Instantiation> mask = createMask(
- includeDirectlyInstantiated: includeDirectlyInstantiated,
- includeIndirectlyInstantiated:includeIndirectlyInstantiated,
- includeUninstantiated: includeUninstantiated);
- return subclassesByMask(mask, strict: strict);
- }
-
- /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls].
- ///
/// Subclasses are included if their instantiation properties intersect with
/// their corresponding [Instantiation] values in [mask]. If [strict] is
/// `true`, [cls] itself is _not_ returned.
@@ -205,6 +209,65 @@ class ClassHierarchyNode {
this, mask, includeRoot: !strict);
}
+ /// Applies [predicate] to each subclass of [cls] matching the criteria
+ /// specified by [mask] and [strict]. If [predicate] returns `true` on a
+ /// class, visitation is stopped immediately and the function returns `true`.
+ ///
+ /// [predicate] is applied to subclasses if their instantiation properties
+ /// intersect with their corresponding [Instantiation] values in [mask]. If
+ /// [strict] is `true`, [predicate] is _not_ called on [cls] itself.
+ bool anySubclass(
+ bool predicate(ClassElement cls),
+ EnumSet<Instantiation> mask,
+ {bool strict: false}) {
+
+ ForEach wrapper(ClassElement cls) {
+ return predicate(cls) ? ForEach.STOP : ForEach.CONTINUE;
+ }
+ return forEachSubclass(wrapper, mask, strict: strict) == ForEach.STOP;
+ }
+
+ /// Applies [f] to each subclass of [cls] matching the criteria specified by
+ /// [mask] and [strict].
+ ///
+ /// [f] is a applied to subclasses if their instantiation properties intersect
+ /// with their corresponding [Instantiation] values in [mask]. If [strict] is
+ /// `true`, [f] is _not_ called on [cls] itself.
+ ///
+ /// The visitation of subclasses can be cut short by the return value of [f].
+ /// If [ForEach.STOP] is returned, no further classes are visited and the
+ /// function stops immediately. If [ForEach.SKIP_SUBCLASSES] is returned, the
+ /// subclasses of the last visited class are skipped, but visitation
+ /// continues. The return value of the function is either [ForEach.STOP], if
+ /// visitation was stopped, or [ForEach.CONTINUE] if visitation continued to
+ /// the end.
+ ForEach forEachSubclass(
+ ForEachFunction f,
+ EnumSet<Instantiation> mask,
+ {bool strict: false}) {
+ ForEach forEach;
+ if (!strict && mask.intersects(_mask)) {
+ forEach = f(cls);
+ }
+ // Interpret `forEach == null` as `forEach == ForEach.CONTINUE`.
+ forEach ??= ForEach.CONTINUE;
+
+ if (forEach == ForEach.CONTINUE) {
+ if (mask.contains(Instantiation.UNINSTANTIATED) || isInstantiated) {
+ for (ClassHierarchyNode subclass in _directSubclasses) {
+ ForEach subForEach = subclass.forEachSubclass(f, mask);
+ if (subForEach == ForEach.STOP) {
+ return subForEach;
+ }
+ }
+ }
+ }
+ if (forEach == ForEach.STOP) {
+ return forEach;
+ }
+ return ForEach.CONTINUE;
+ }
+
/// Returns the most specific subclass of [cls] (including [cls]) that is
/// directly instantiated or a superclass of all directly instantiated
/// subclasses. If [cls] is not instantiated, `null` is returned.
@@ -275,7 +338,8 @@ class ClassHierarchyNode {
if (instantiatedOnly && !child.isInstantiated) {
continue;
}
- if (withRespectTo != null && !child.subclasses().any(isRelatedTo)) {
+ if (withRespectTo != null &&
+ !child.anySubclass(isRelatedTo, ClassHierarchyNode.ALL)) {
continue;
}
if (needsComma) {
@@ -367,22 +431,31 @@ class ClassSet {
ClassElement get cls => node.cls;
- /// Returns an [Iterable] of the subclasses of [cls] possibly including [cls].
- ///
- /// The directly instantiated, indirectly instantiated and uninstantiated
- /// subclasses of [cls] are returned if [includeDirectlyInstantiated],
- /// [includeIndirectlyInstantiated], and [includeUninstantiated] are `true`,
- /// respectively. If [strict] is `true`, [cls] itself is _not_ returned.
- Iterable<ClassElement> subclasses(
- {bool includeDirectlyInstantiated: true,
- bool includeIndirectlyInstantiated: true,
- bool includeUninstantiated: true,
- bool strict: false}) {
- EnumSet<Instantiation> mask = ClassHierarchyNode.createMask(
- includeDirectlyInstantiated: includeDirectlyInstantiated,
- includeIndirectlyInstantiated:includeIndirectlyInstantiated,
- includeUninstantiated: includeUninstantiated);
- return subclassesByMask(mask, strict: strict);
+ /// Returns the number of directly instantiated subtypes of [cls].
+ int get instantiatedSubtypeCount {
+ int count = node.instantiatedSubclassCount;
+ if (_directSubtypes != null) {
+ for (ClassHierarchyNode subtypeNode in _directSubtypes) {
+ if (subtypeNode.isDirectlyInstantiated) {
+ count++;
+ }
+ count += subtypeNode.instantiatedSubclassCount;
+ }
+ }
+ return count;
+ }
+
+ /// Returns `true` if all instantiated subtypes of [cls] are subclasses of
+ /// [cls].
+ bool get hasOnlyInstantiatedSubclasses {
Siggi Cherem (dart-lang) 2016/02/09 23:40:12 I was looking into something else and run into thi
Johnni Winther 2016/02/10 09:32:08 Yes. The cause is that implements is transitive. T
+ if (_directSubtypes != null) {
+ for (ClassHierarchyNode subtypeNode in _directSubtypes) {
+ if (subtypeNode.isInstantiated) {
+ return false;
+ }
+ }
+ }
+ return true;
}
/// Returns an [Iterable] of the subclasses of [cls] possibly including [cls].
@@ -414,7 +487,6 @@ class ClassSet {
return subtypesByMask(mask, strict: strict);
}
-
/// Returns an [Iterable] of the subtypes of [cls] possibly including [cls].
///
/// Subtypes are included if their instantiation properties intersect with
@@ -434,6 +506,91 @@ class ClassSet {
includeRoot: !strict);
}
+ /// Applies [predicate] to each subclass of [cls] matching the criteria
+ /// specified by [mask] and [strict]. If [predicate] returns `true` on a
+ /// class, visitation is stopped immediately and the function returns `true`.
+ ///
+ /// [predicate] is applied to subclasses if their instantiation properties
+ /// intersect with their corresponding [Instantiation] values in [mask]. If
+ /// [strict] is `true`, [predicate] is _not_ called on [cls] itself.
+ bool anySubclass(
+ bool predicate(ClassElement cls),
+ EnumSet<Instantiation> mask,
+ {bool strict: false}) {
+ return node.anySubclass(predicate, mask, strict: strict);
+ }
+
+ /// Applies [f] to each subclass of [cls] matching the criteria specified by
+ /// [mask] and [strict].
+ ///
+ /// [f] is a applied to subclasses if their instantiation properties intersect
+ /// with their corresponding [Instantiation] values in [mask]. If [strict] is
+ /// `true`, [f] is _not_ called on [cls] itself.
+ ///
+ /// The visitation of subclasses can be cut short by the return value of [f].
+ /// If [ForEach.STOP] is returned, no further classes are visited and the
+ /// function stops immediately. If [ForEach.SKIP_SUBCLASSES] is returned, the
+ /// subclasses of the last visited class are skipped, but visitation
+ /// continues. The return value of the function is either [ForEach.STOP], if
+ /// visitation was stopped, or [ForEach.CONTINUE] if visitation continued to
+ /// the end.
+ ForEach forEachSubclass(
+ ForEachFunction f,
+ EnumSet<Instantiation> mask,
+ {bool strict: false}) {
+ return node.forEachSubclass(f, mask, strict: strict);
+ }
+
+ /// Applies [predicate] to each subtype of [cls] matching the criteria
+ /// specified by [mask] and [strict]. If [predicate] returns `true` on a
+ /// class, visitation is stopped immediately and the function returns `true`.
+ ///
+ /// [predicate] is applied to subtypes if their instantiation properties
+ /// intersect with their corresponding [Instantiation] values in [mask]. If
+ /// [strict] is `true`, [predicate] is _not_ called on [cls] itself.
+ bool anySubtype(
+ bool predicate(ClassElement cls),
+ EnumSet<Instantiation> mask,
+ {bool strict: false}) {
+
+ ForEach wrapper(ClassElement cls) {
+ return predicate(cls) ? ForEach.STOP : ForEach.CONTINUE;
+ }
+ return forEachSubtype(wrapper, mask, strict: strict) == ForEach.STOP;
+ }
+
+ /// Applies [f] to each subtype of [cls] matching the criteria specified by
+ /// [mask] and [strict].
+ ///
+ /// [f] is a applied to subtypes if their instantiation properties intersect
+ /// with their corresponding [Instantiation] values in [mask]. If [strict] is
+ /// `true`, [f] is _not_ called on [cls] itself.
+ ///
+ /// The visitation of subtypes can be cut short by the return value of [f].
+ /// If [ForEach.STOP] is returned, no further classes are visited and the
+ /// function stops immediately. If [ForEach.SKIP_SUBCLASSES] is returned, the
+ /// subclasses of the last visited class are skipped, but visitation
+ /// continues. The return value of the function is either [ForEach.STOP], if
+ /// visitation was stopped, or [ForEach.CONTINUE] if visitation continued to
+ /// the end.
+ ForEach forEachSubtype(
+ ForEachFunction f,
+ EnumSet<Instantiation> mask,
+ {bool strict: false}) {
+ ForEach forEach = node.forEachSubclass(f, mask, strict: strict);
+ forEach ??= ForEach.CONTINUE;
Siggi Cherem (dart-lang) 2016/02/06 00:17:26 nit: here and elsewhere, I rather keep the variabl
Johnni Winther 2016/02/10 09:32:08 Will do. Code motion caused this ;-)
+ if (forEach == ForEach.CONTINUE && _directSubtypes != null) {
+ for (ClassHierarchyNode subclass in _directSubtypes) {
+ ForEach subForEach = subclass.forEachSubclass(f, mask);
+ if (subForEach == ForEach.STOP) {
+ return subForEach;
+ }
+ }
+ }
+ assert(forEach != ForEach.SKIP_SUBCLASSES);
+ return forEach;
+ }
+
/// Adds [subtype] as a subtype of [cls].
void addSubtype(ClassHierarchyNode subtype) {
if (node.contains(subtype.cls)) {
@@ -695,3 +852,20 @@ class SubtypesIterator extends Iterator<ClassElement> {
return false;
}
}
+
+/// Enum values returned from the [ForEachFunction] provided to the `forEachX`
+/// functions of [ClassHierarchyNode] and [ClassSet]. The value is used to
+/// control the continued iteration.
+enum ForEach {
Siggi Cherem (dart-lang) 2016/02/06 00:17:26 naming nit: could we rename this to something that
Johnni Winther 2016/02/10 09:32:08 If find the latter confusing but like enum class n
+ /// Iteration continues.
+ CONTINUE,
+ /// Iteration stops immediately.
+ STOP,
+ /// Iteration skips the subclasses of the current class.
+ SKIP_SUBCLASSES,
+}
+
+/// Visiting function used for the `forEachX` functions of [ClassHierarchyNode]
+/// and [ClassSet]. The return value controls the continued iteration. If `null`
+/// is returned, iteration continues to the end.
+typedef ForEach ForEachFunction(ClassElement cls);
« no previous file with comments | « pkg/compiler/lib/src/types/union_type_mask.dart ('k') | pkg/compiler/lib/src/world.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698