@@ -625,32 +625,112 @@ class ClassHierarchyBuilder {
625
625
return classSet.hasSubtype (classHierarchyNode);
626
626
}
627
627
628
+ Map <ClassEntity , _InheritedCache > _inheritedCacheMap = {};
629
+
628
630
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
+ }
632
635
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];
636
649
}
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 >();
637
668
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);
643
672
}
644
673
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);
648
681
}
649
682
683
+ // A subclasses of [x] that implement [y].
684
+ subclassImplements (classSet.node, strict: true );
685
+
650
686
for (ClassHierarchyNode mixinApplication
651
687
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 ;
654
734
return true ;
655
735
}
656
736
}
0 commit comments