@@ -625,32 +625,112 @@ class ClassHierarchyBuilder {
625625 return classSet.hasSubtype (classHierarchyNode);
626626 }
627627
628+ Map <ClassEntity , _InheritedCache > _inheritedCacheMap = {};
629+
628630 bool isInheritedInSubtypeOf (ClassEntity x, ClassEntity y) {
629- ClassSet classSet = classSets[x];
630- assert (classSet != null ,
631- failedAt (x, "No ClassSet for $x (${x .runtimeType }): ${classSets }" ));
631+ _InheritedCache cache = _inheritedCacheMap[x] ?? = new _InheritedCache ();
632+ return cache.isInheritedInSubtypeOf (this , x, y);
633+ }
634+ }
632635
633- if (_isSubtypeOf (x, y)) {
634- // [x] implements [y] itself, possible through supertypes.
635- return true ;
636+ /// A cache object used for [ClassHierarchyBuilder.isInheritedInSubtypeOf] .
637+ class _InheritedCache {
638+ Map <ClassEntity , _InheritingSet > _map;
639+
640+ /// Returns whether a live class currently known to inherit from [x] and
641+ /// implement [y] .
642+ bool isInheritedInSubtypeOf (
643+ ClassHierarchyBuilder builder, ClassEntity x, ClassEntity y) {
644+ _InheritingSet set ;
645+ if (_map == null ) {
646+ _map = {};
647+ } else {
648+ set = _map[y];
636649 }
650+ if (set == null ) {
651+ set = _map[y] = _computeInheritingSet (builder, x, y);
652+ }
653+ return set .hasLiveClass (builder);
654+ }
655+
656+ /// Creates an [_InheritingSet] of classes that inherit members of a class [x]
657+ /// while implementing class [y] .
658+ _InheritingSet _computeInheritingSet (
659+ ClassHierarchyBuilder builder, ClassEntity x, ClassEntity y) {
660+ ClassSet classSet = builder.classSets[x];
661+
662+ assert (
663+ classSet != null ,
664+ failedAt (
665+ x, "No ClassSet for $x (${x .runtimeType }): ${builder .classSets }" ));
666+
667+ Set <ClassEntity > classes = new Set <ClassEntity >();
637668
638- /// Returns `true` if any live subclass of [node] implements [y] .
639- bool subclassImplements (ClassHierarchyNode node, {bool strict}) {
640- return node.anySubclass ((ClassEntity z) => _isSubtypeOf (z, y),
641- ClassHierarchyNode .INSTANTIATED ,
642- strict: strict);
669+ if (builder._isSubtypeOf (x, y)) {
670+ // [x] implements [y] itself, possible through supertypes.
671+ classes.add (x);
643672 }
644673
645- if (subclassImplements (classSet.node, strict: true )) {
646- // A subclass of [x] implements [y].
647- return true ;
674+ /// Add subclasses of [node] that implement [y] .
675+ void subclassImplements (ClassHierarchyNode node, {bool strict}) {
676+ node.forEachSubclass ((ClassEntity z) {
677+ if (builder._isSubtypeOf (z, y)) {
678+ classes.add (z);
679+ }
680+ }, ClassHierarchyNode .ALL , strict: strict);
648681 }
649682
683+ // A subclasses of [x] that implement [y].
684+ subclassImplements (classSet.node, strict: true );
685+
650686 for (ClassHierarchyNode mixinApplication
651687 in classSet.mixinApplicationNodes) {
652- if (subclassImplements (mixinApplication, strict: false )) {
653- // A subclass of [mixinApplication] implements [y].
688+ // A subclass of [mixinApplication] implements [y].
689+ subclassImplements (mixinApplication, strict: false );
690+ }
691+
692+ return new _InheritingSet (classes);
693+ }
694+ }
695+
696+ /// A set of classes that inherit members of a class 'x' while implementing
697+ /// class 'y'.
698+ ///
699+ /// The set is used [ClassHierarchyBuilder.isInheritedInSubtypeOf] to determine
700+ /// when members of a class is live.
701+ class _InheritingSet {
702+ /// If `true` the set of classes is known to contain a live class. In this
703+ /// case [_classes] is `null` . If `false` the set of classes is empty and
704+ /// therefore known never to contain live classes. In this case [_classes]
705+ /// is `null` . If `null` [_classes] is a non-empty set containing classes
706+ /// that are not yet known to be live.
707+ bool _result;
708+ Set <ClassEntity > _classes;
709+
710+ _InheritingSet (Set <ClassEntity > classes)
711+ : _result = classes.isEmpty ? false : null ,
712+ _classes = classes.isNotEmpty ? classes : null ;
713+
714+ /// Returns whether the set of classes is currently known to contain a live
715+ /// classes.
716+ ///
717+ /// The result of this method changes during the closed world computation.
718+ /// Initially, we haven't seen any live classes so we will return `false` even
719+ /// for a non-empty set of classes. As more classes are marked as
720+ /// instantiated, during tree-shaking, the result might change to `true` if
721+ /// one of the [_classes] has been marked as live.
722+ ///
723+ /// The result of this method _is_ monotone, though; when we have returned
724+ /// `true` (because at least one class is known to be live) we will continue
725+ /// to return `true` .
726+ bool hasLiveClass (ClassHierarchyBuilder builder) {
727+ if (_result != null ) return _result;
728+ for (ClassEntity cls in _classes) {
729+ if (builder.classHierarchyNodes[cls].isInstantiated) {
730+ // We now know this set contains a live class and done need to remember
731+ // that set of classes anymore.
732+ _result = true ;
733+ _classes = null ;
654734 return true ;
655735 }
656736 }
0 commit comments