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

Side by Side Diff: pkg/compiler/lib/src/world.dart

Issue 2813503005: Extract ClosedWorldBase from ClosedWorldImpl (Closed)
Patch Set: Updated invariant comment. Created 3 years, 8 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dart2js.world; 5 library dart2js.world;
6 6
7 import 'closure.dart' show ClosureClassElement, SynthesizedCallMethodElementX; 7 import 'closure.dart' show ClosureClassElement, SynthesizedCallMethodElementX;
8 import 'common/backend_api.dart' show BackendClasses; 8 import 'common/backend_api.dart' show BackendClasses;
9 import 'common.dart'; 9 import 'common.dart';
10 import 'constants/constant_system.dart'; 10 import 'constants/constant_system.dart';
11 import 'common_elements.dart' show CommonElements; 11 import 'common_elements.dart' show CommonElements;
12 import 'elements/entities.dart'; 12 import 'elements/entities.dart';
13 import 'elements/elements.dart' 13 import 'elements/elements.dart'
14 show 14 show
15 ClassElement, 15 ClassElement,
16 Element, 16 Element,
17 FunctionElement, 17 FunctionElement,
18 MemberElement, 18 MemberElement,
19 MixinApplicationElement, 19 MixinApplicationElement,
20 TypedefElement, 20 TypedefElement;
21 FieldElement;
22 import 'elements/resolution_types.dart'; 21 import 'elements/resolution_types.dart';
23 import 'js_backend/backend.dart' show JavaScriptBackend; 22 import 'js_backend/backend.dart' show JavaScriptBackend;
24 import 'js_backend/interceptor_data.dart' show InterceptorData; 23 import 'js_backend/interceptor_data.dart' show InterceptorData;
25 import 'js_backend/native_data.dart' show NativeData; 24 import 'js_backend/native_data.dart' show NativeData;
26 import 'ordered_typeset.dart'; 25 import 'ordered_typeset.dart';
27 import 'types/masks.dart' show CommonMasks, FlatTypeMask, TypeMask; 26 import 'types/masks.dart' show CommonMasks, FlatTypeMask, TypeMask;
28 import 'universe/class_set.dart'; 27 import 'universe/class_set.dart';
29 import 'universe/function_set.dart' show FunctionSet, FunctionSetBuilder; 28 import 'universe/function_set.dart' show FunctionSet, FunctionSetBuilder;
30 import 'universe/selector.dart' show Selector; 29 import 'universe/selector.dart' show Selector;
31 import 'universe/side_effects.dart' show SideEffects; 30 import 'universe/side_effects.dart' show SideEffects;
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 /// itself. 113 /// itself.
115 void forEachStrictSubclassOf( 114 void forEachStrictSubclassOf(
116 ClassEntity cls, IterationStep f(ClassEntity cls)); 115 ClassEntity cls, IterationStep f(ClassEntity cls));
117 116
118 /// Returns `true` if [predicate] applies to any live class that extend [cls] 117 /// Returns `true` if [predicate] applies to any live class that extend [cls]
119 /// _not_ including [cls] itself. 118 /// _not_ including [cls] itself.
120 bool anyStrictSubclassOf(ClassEntity cls, bool predicate(ClassEntity cls)); 119 bool anyStrictSubclassOf(ClassEntity cls, bool predicate(ClassEntity cls));
121 120
122 /// Returns an iterable over the directly instantiated that implement [cls] 121 /// Returns an iterable over the directly instantiated that implement [cls]
123 /// possibly including [cls] itself, if it is live. 122 /// possibly including [cls] itself, if it is live.
124 Iterable<ClassElement> subtypesOf(ClassEntity cls); 123 Iterable<ClassEntity> subtypesOf(ClassEntity cls);
125 124
126 /// Returns an iterable over the live classes that implement [cls] _not_ 125 /// Returns an iterable over the live classes that implement [cls] _not_
127 /// including [cls] if it is live. 126 /// including [cls] if it is live.
128 Iterable<ClassElement> strictSubtypesOf(ClassEntity cls); 127 Iterable<ClassEntity> strictSubtypesOf(ClassEntity cls);
129 128
130 /// Returns the number of live classes that implement [cls] _not_ 129 /// Returns the number of live classes that implement [cls] _not_
131 /// including [cls] itself. 130 /// including [cls] itself.
132 int strictSubtypeCount(ClassEntity cls); 131 int strictSubtypeCount(ClassEntity cls);
133 132
134 /// Applies [f] to each live class that implements [cls] _not_ including [cls] 133 /// Applies [f] to each live class that implements [cls] _not_ including [cls]
135 /// itself. 134 /// itself.
136 void forEachStrictSubtypeOf( 135 void forEachStrictSubtypeOf(
137 ClassEntity cls, IterationStep f(ClassEntity cls)); 136 ClassEntity cls, IterationStep f(ClassEntity cls));
138 137
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 /// class A {} 175 /// class A {}
177 /// class B {} 176 /// class B {}
178 /// class C implements A, B {} 177 /// class C implements A, B {}
179 /// class D extends C {} 178 /// class D extends C {}
180 /// 179 ///
181 /// the query 180 /// the query
182 /// 181 ///
183 /// commonSubclasses(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE) 182 /// commonSubclasses(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE)
184 /// 183 ///
185 /// return the set {C} because [D] is implied by [C]. 184 /// return the set {C} because [D] is implied by [C].
186 Iterable<ClassEntity> commonSubclasses(ClassElement cls1, ClassQuery query1, 185 Iterable<ClassEntity> commonSubclasses(
187 ClassElement cls2, ClassQuery query2); 186 ClassEntity cls1, ClassQuery query1, ClassEntity cls2, ClassQuery query2);
188 187
189 /// Returns an iterable over the live mixin applications that mixin [cls]. 188 /// Returns an iterable over the live mixin applications that mixin [cls].
190 Iterable<ClassEntity> mixinUsesOf(ClassEntity cls); 189 Iterable<ClassEntity> mixinUsesOf(ClassEntity cls);
191 190
192 /// Returns `true` if [cls] is mixed into a live class. 191 /// Returns `true` if [cls] is mixed into a live class.
193 bool isUsedAsMixin(ClassEntity cls); 192 bool isUsedAsMixin(ClassEntity cls);
194 193
195 /// Returns `true` if any live class that mixes in [cls] implements [type]. 194 /// Returns `true` if any live class that mixes in [cls] implements [type].
196 bool hasAnySubclassOfMixinUseThatImplements( 195 bool hasAnySubclassOfMixinUseThatImplements(
197 ClassEntity cls, ClassEntity type); 196 ClassEntity cls, ClassEntity type);
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 EXACT, 380 EXACT,
382 381
383 /// The class and all subclasses (transitively) are included. 382 /// The class and all subclasses (transitively) are included.
384 SUBCLASS, 383 SUBCLASS,
385 384
386 /// The class and all classes that implement or subclass it (transitively) 385 /// The class and all classes that implement or subclass it (transitively)
387 /// are included. 386 /// are included.
388 SUBTYPE, 387 SUBTYPE,
389 } 388 }
390 389
391 class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner { 390 abstract class ClosedWorldBase implements ClosedWorld, ClosedWorldRefiner {
392 final JavaScriptBackend _backend; 391 final JavaScriptBackend _backend;
393 BackendClasses get backendClasses => _backend.backendClasses; 392 BackendClasses get backendClasses => _backend.backendClasses;
394 InterceptorData get interceptorData => _backend.interceptorData; 393 InterceptorData get interceptorData => _backend.interceptorData;
395 FunctionSet _allFunctions; 394 FunctionSet _allFunctions;
396 395
397 final Iterable<TypedefElement> _allTypedefs; 396 final Iterable<TypedefElement> _allTypedefs;
398 397
399 final Map<ClassEntity, Set<ClassEntity>> _mixinUses; 398 final Map<ClassEntity, Set<ClassEntity>> _mixinUses;
400 Map<ClassEntity, List<ClassEntity>> _liveMixinUses; 399 Map<ClassEntity, List<ClassEntity>> _liveMixinUses;
401 400
(...skipping 16 matching lines...) Expand all
418 417
419 final Set<Element> functionsThatMightBePassedToApply = 418 final Set<Element> functionsThatMightBePassedToApply =
420 new Set<FunctionElement>(); 419 new Set<FunctionElement>();
421 420
422 CommonMasks _commonMasks; 421 CommonMasks _commonMasks;
423 422
424 final CommonElements commonElements; 423 final CommonElements commonElements;
425 424
426 final ResolutionWorldBuilder _resolverWorld; 425 final ResolutionWorldBuilder _resolverWorld;
427 426
428 ClosedWorldImpl( 427 ClosedWorldBase(
429 {JavaScriptBackend backend, 428 {JavaScriptBackend backend,
430 this.commonElements, 429 this.commonElements,
431 ResolutionWorldBuilder resolutionWorldBuilder, 430 ResolutionWorldBuilder resolutionWorldBuilder,
432 FunctionSetBuilder functionSetBuilder, 431 FunctionSetBuilder functionSetBuilder,
433 Iterable<TypedefElement> allTypedefs, 432 Iterable<TypedefElement> allTypedefs,
434 Map<ClassEntity, Set<ClassEntity>> mixinUses, 433 Map<ClassEntity, Set<ClassEntity>> mixinUses,
435 Map<ClassEntity, Set<ClassEntity>> typesImplementedBySubclasses, 434 Map<ClassEntity, Set<ClassEntity>> typesImplementedBySubclasses,
436 Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes, 435 Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes,
437 Map<ClassEntity, ClassSet> classSets}) 436 Map<ClassEntity, ClassSet> classSets})
438 : this._backend = backend, 437 : this._backend = backend,
(...skipping 24 matching lines...) Expand all
463 } 462 }
464 463
465 ConstantSystem get constantSystem => _backend.constantSystem; 464 ConstantSystem get constantSystem => _backend.constantSystem;
466 465
467 TypeMask getCachedMask(ClassEntity base, int flags, TypeMask createMask()) { 466 TypeMask getCachedMask(ClassEntity base, int flags, TypeMask createMask()) {
468 Map<ClassEntity, TypeMask> cachedMasks = 467 Map<ClassEntity, TypeMask> cachedMasks =
469 _canonicalizedTypeMasks[flags] ??= <ClassEntity, TypeMask>{}; 468 _canonicalizedTypeMasks[flags] ??= <ClassEntity, TypeMask>{};
470 return cachedMasks.putIfAbsent(base, createMask); 469 return cachedMasks.putIfAbsent(base, createMask);
471 } 470 }
472 471
473 bool _checkClass(ClassElement cls) => cls.isDeclaration; 472 bool _checkClass(ClassEntity cls);
474
475 bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
476 return invariant(cls, cls.isDeclaration,
477 message: '$cls must be the declaration.') &&
478 invariant(cls, cls.isResolved,
479 message:
480 '$cls must be resolved.') /* &&
481 // TODO(johnniwinther): Reinsert this or similar invariant.
482 (!mustBeInstantiated ||
483 invariant(cls, isInstantiated(cls),
484 message: '$cls is not instantiated.'))*/
485 ;
486 }
487
488 /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
489 /// instance of [y].
490 bool isSubtypeOf(ClassElement x, ClassElement y) {
491 assert(checkInvariants(x));
492 assert(checkInvariants(y, mustBeInstantiated: false));
493
494 if (y == commonElements.objectClass) return true;
495 if (x == commonElements.objectClass) return false;
496 if (x.asInstanceOf(y) != null) return true;
497 if (y != commonElements.functionClass) return false;
498 return x.callType != null;
499 }
500
501 /// Return `true` if [x] is a (non-strict) subclass of [y].
502 bool isSubclassOf(ClassElement x, ClassElement y) {
503 assert(checkInvariants(x));
504 assert(checkInvariants(y));
505
506 if (y == commonElements.objectClass) return true;
507 if (x == commonElements.objectClass) return false;
508 while (x != null && x.hierarchyDepth >= y.hierarchyDepth) {
509 if (x == y) return true;
510 x = x.superclass;
511 }
512 return false;
513 }
514 473
515 @override 474 @override
516 bool isInstantiated(ClassEntity cls) { 475 bool isInstantiated(ClassEntity cls) {
517 assert(_checkClass(cls)); 476 assert(_checkClass(cls));
518 ClassHierarchyNode node = _classHierarchyNodes[cls]; 477 ClassHierarchyNode node = _classHierarchyNodes[cls];
519 return node != null && node.isInstantiated; 478 return node != null && node.isInstantiated;
520 } 479 }
521 480
522 @override 481 @override
523 bool isDirectlyInstantiated(ClassEntity cls) { 482 bool isDirectlyInstantiated(ClassEntity cls) {
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 @override 684 @override
726 ClassEntity getLubOfInstantiatedSubtypes(ClassEntity cls) { 685 ClassEntity getLubOfInstantiatedSubtypes(ClassEntity cls) {
727 assert(_checkClass(cls)); 686 assert(_checkClass(cls));
728 if (nativeData.isJsInteropClass(cls)) { 687 if (nativeData.isJsInteropClass(cls)) {
729 return _backend.helpers.jsJavaScriptObjectClass; 688 return _backend.helpers.jsJavaScriptObjectClass;
730 } 689 }
731 ClassSet classSet = _classSets[cls]; 690 ClassSet classSet = _classSets[cls];
732 return classSet != null ? classSet.getLubOfInstantiatedSubtypes() : null; 691 return classSet != null ? classSet.getLubOfInstantiatedSubtypes() : null;
733 } 692 }
734 693
694 Set<ClassEntity> _commonContainedClasses(ClassEntity cls1, ClassQuery query1,
695 ClassEntity cls2, ClassQuery query2) {
696 Iterable<ClassEntity> xSubset = _containedSubset(cls1, query1);
697 if (xSubset == null) return null;
698 Iterable<ClassEntity> ySubset = _containedSubset(cls2, query2);
699 if (ySubset == null) return null;
700 return xSubset.toSet().intersection(ySubset.toSet());
701 }
702
703 Iterable<ClassEntity> _containedSubset(ClassEntity cls, ClassQuery query) {
704 switch (query) {
705 case ClassQuery.EXACT:
706 return null;
707 case ClassQuery.SUBCLASS:
708 return strictSubclassesOf(cls);
709 case ClassQuery.SUBTYPE:
710 return strictSubtypesOf(cls);
711 }
712 throw new ArgumentError('Unexpected query: $query.');
713 }
714
715 /// Returns `true` if [cls] is mixed into a live class.
716 bool isUsedAsMixin(ClassEntity cls) {
717 return !mixinUsesOf(cls).isEmpty;
718 }
719
720 /// Returns `true` if any live class that mixes in [cls] implements [type].
721 bool hasAnySubclassOfMixinUseThatImplements(
722 ClassEntity cls, ClassEntity type) {
723 return mixinUsesOf(cls)
724 .any((use) => hasAnySubclassThatImplements(use, type));
725 }
726
727 /// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass
728 /// of a mixin application of [y].
729 bool everySubtypeIsSubclassOfOrMixinUseOf(ClassEntity x, ClassEntity y) {
730 assert(_checkClass(x));
731 assert(_checkClass(y));
732 Map<ClassEntity, bool> secondMap =
733 _subtypeCoveredByCache[x] ??= <ClassEntity, bool>{};
734 return secondMap[y] ??= subtypesOf(x).every((ClassEntity cls) =>
735 isSubclassOf(cls, y) || isSubclassOfMixinUseOf(cls, y));
736 }
737
738 /// Returns `true` if any subclass of [superclass] implements [type].
739 bool hasAnySubclassThatImplements(ClassEntity superclass, ClassEntity type) {
740 assert(_checkClass(superclass));
741 Set<ClassEntity> subclasses = _typesImplementedBySubclasses[superclass];
742 if (subclasses == null) return false;
743 return subclasses.contains(type);
744 }
745
746 /// Returns whether a [selector] call on an instance of [cls]
747 /// will hit a method at runtime, and not go through [noSuchMethod].
748 bool hasConcreteMatch(ClassEntity cls, Selector selector,
749 {ClassEntity stopAtSuperclass});
750
751 @override
752 bool needsNoSuchMethod(
753 ClassEntity base, Selector selector, ClassQuery query) {
754 /// Returns `true` if subclasses in the [rootNode] tree needs noSuchMethod
755 /// handling.
756 bool subclassesNeedNoSuchMethod(ClassHierarchyNode rootNode) {
757 if (!rootNode.isInstantiated) {
758 // No subclass needs noSuchMethod handling since they are all
759 // uninstantiated.
760 return false;
761 }
762 ClassEntity rootClass = rootNode.cls;
763 if (hasConcreteMatch(rootClass, selector)) {
764 // The root subclass has a concrete implementation so no subclass needs
765 // noSuchMethod handling.
766 return false;
767 } else if (rootNode.isExplicitlyInstantiated) {
768 // The root class need noSuchMethod handling.
769 return true;
770 }
771 IterationStep result = rootNode.forEachSubclass((ClassEntity subclass) {
772 if (hasConcreteMatch(subclass, selector, stopAtSuperclass: rootClass)) {
773 // Found a match - skip all subclasses.
774 return IterationStep.SKIP_SUBCLASSES;
775 } else {
776 // Stop fast - we found a need for noSuchMethod handling.
777 return IterationStep.STOP;
778 }
779 }, ClassHierarchyNode.EXPLICITLY_INSTANTIATED, strict: true);
780 // We stopped fast so we need noSuchMethod handling.
781 return result == IterationStep.STOP;
782 }
783
784 ClassSet classSet = getClassSet(base);
785 ClassHierarchyNode node = classSet.node;
786 if (query == ClassQuery.EXACT) {
787 return node.isExplicitlyInstantiated && !hasConcreteMatch(base, selector);
788 } else if (query == ClassQuery.SUBCLASS) {
789 return subclassesNeedNoSuchMethod(node);
790 } else {
791 if (subclassesNeedNoSuchMethod(node)) return true;
792 for (ClassHierarchyNode subtypeNode in classSet.subtypeNodes) {
793 if (subclassesNeedNoSuchMethod(subtypeNode)) return true;
794 }
795 return false;
796 }
797 }
798
799 /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
800 /// of known classes.
801 ///
802 /// This method is only provided for testing. For queries on classes, use the
803 /// methods defined in [ClosedWorld].
804 ClassHierarchyNode getClassHierarchyNode(ClassEntity cls) {
805 assert(_checkClass(cls));
806 return _classHierarchyNodes[cls];
807 }
808
809 /// Returns [ClassSet] for [cls] used to model the extends and implements
810 /// relations of known classes.
811 ///
812 /// This method is only provided for testing. For queries on classes, use the
813 /// methods defined in [ClosedWorld].
814 ClassSet getClassSet(ClassEntity cls) {
815 assert(_checkClass(cls));
816 return _classSets[cls];
817 }
818
819 Iterable<TypedefElement> get allTypedefs => _allTypedefs;
820
821 bool hasAnyUserDefinedGetter(Selector selector, TypeMask mask) {
822 return allFunctions.filter(selector, mask).any((each) => each.isGetter);
823 }
824
825 FieldEntity locateSingleField(Selector selector, TypeMask mask) {
826 MemberEntity result = locateSingleElement(selector, mask);
827 return (result != null && result.isField) ? result : null;
828 }
829
830 MemberEntity locateSingleElement(Selector selector, TypeMask mask) {
831 mask ??= commonMasks.dynamicType;
832 return mask.locateSingleElement(selector, this);
833 }
834
835 TypeMask extendMaskIfReachesAll(Selector selector, TypeMask mask) {
836 bool canReachAll = true;
837 if (mask != null) {
838 canReachAll = _backend.backendUsage.isInvokeOnUsed &&
839 mask.needsNoSuchMethodHandling(selector, this);
840 }
841 return canReachAll ? commonMasks.dynamicType : mask;
842 }
843
844 bool fieldNeverChanges(MemberEntity element) {
845 if (!element.isField) return false;
846 if (nativeData.isNativeMember(element)) {
847 // Some native fields are views of data that may be changed by operations.
848 // E.g. node.firstChild depends on parentNode.removeBefore(n1, n2).
849 // TODO(sra): Refine the effect classification so that native effects are
850 // distinct from ordinary Dart effects.
851 return false;
852 }
853
854 if (!element.isAssignable) {
855 return true;
856 }
857 if (element.isInstanceMember) {
858 return !_resolverWorld.hasInvokedSetter(element) &&
859 !_resolverWorld.fieldSetters.contains(element);
860 }
861 return false;
862 }
863
864 SideEffects getSideEffectsOfSelector(Selector selector, TypeMask mask) {
865 // We're not tracking side effects of closures.
866 if (selector.isClosureCall) return new SideEffects();
867 SideEffects sideEffects = new SideEffects.empty();
868 for (MemberElement e in allFunctions.filter(selector, mask)) {
869 if (e.isField) {
870 if (selector.isGetter) {
871 if (!fieldNeverChanges(e)) {
872 sideEffects.setDependsOnInstancePropertyStore();
873 }
874 } else if (selector.isSetter) {
875 sideEffects.setChangesInstanceProperty();
876 } else {
877 assert(selector.isCall);
878 sideEffects.setAllSideEffects();
879 sideEffects.setDependsOnSomething();
880 }
881 } else {
882 sideEffects.add(getSideEffectsOfElement(e));
883 }
884 }
885 return sideEffects;
886 }
887 }
888
889 class ClosedWorldImpl extends ClosedWorldBase {
890 ClosedWorldImpl(
891 {JavaScriptBackend backend,
892 CommonElements commonElements,
893 ResolutionWorldBuilder resolutionWorldBuilder,
894 FunctionSetBuilder functionSetBuilder,
895 Iterable<TypedefElement> allTypedefs,
896 Map<ClassEntity, Set<ClassEntity>> mixinUses,
897 Map<ClassEntity, Set<ClassEntity>> typesImplementedBySubclasses,
898 Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes,
899 Map<ClassEntity, ClassSet> classSets})
900 : super(
901 backend: backend,
902 commonElements: commonElements,
903 resolutionWorldBuilder: resolutionWorldBuilder,
904 functionSetBuilder: functionSetBuilder,
905 allTypedefs: allTypedefs,
906 mixinUses: mixinUses,
907 typesImplementedBySubclasses: typesImplementedBySubclasses,
908 classHierarchyNodes: classHierarchyNodes,
909 classSets: classSets);
910
911 bool _checkClass(ClassElement cls) => cls.isDeclaration;
912
913 bool _checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
914 return invariant(cls, cls.isDeclaration,
915 message: '$cls must be the declaration.') &&
916 invariant(cls, cls.isResolved,
917 message:
918 '$cls must be resolved.') /* &&
919 // TODO(johnniwinther): Reinsert this or similar invariant. Currently
920 // various call sites use uninstantiated classes for isSubtypeOf or
921 // isSubclassOf. Some are valid, some are not. Work out better invariants
922 // to catch the latter.
923 (!mustBeInstantiated ||
924 invariant(cls, isInstantiated(cls),
925 message: '$cls is not instantiated.'))*/
926 ;
927 }
928
929 /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
930 /// instance of [y].
931 bool isSubtypeOf(ClassElement x, ClassElement y) {
932 assert(_checkInvariants(x));
933 assert(_checkInvariants(y, mustBeInstantiated: false));
934
935 if (y == commonElements.objectClass) return true;
936 if (x == commonElements.objectClass) return false;
937 if (x.asInstanceOf(y) != null) return true;
938 if (y != commonElements.functionClass) return false;
939 return x.callType != null;
940 }
941
942 /// Return `true` if [x] is a (non-strict) subclass of [y].
943 bool isSubclassOf(ClassElement x, ClassElement y) {
944 assert(_checkInvariants(x));
945 assert(_checkInvariants(y));
946
947 if (y == commonElements.objectClass) return true;
948 if (x == commonElements.objectClass) return false;
949 while (x != null && x.hierarchyDepth >= y.hierarchyDepth) {
950 if (x == y) return true;
951 x = x.superclass;
952 }
953 return false;
954 }
955
735 /// Returns an iterable over the common supertypes of the [classes]. 956 /// Returns an iterable over the common supertypes of the [classes].
736 Iterable<ClassElement> commonSupertypesOf(Iterable<ClassElement> classes) { 957 Iterable<ClassElement> commonSupertypesOf(Iterable<ClassElement> classes) {
737 Iterator<ClassElement> iterator = classes.iterator; 958 Iterator<ClassElement> iterator = classes.iterator;
738 if (!iterator.moveNext()) return const <ClassElement>[]; 959 if (!iterator.moveNext()) return const <ClassElement>[];
739 960
740 ClassElement cls = iterator.current; 961 ClassElement cls = iterator.current;
741 assert(checkInvariants(cls)); 962 assert(_checkInvariants(cls));
742 OrderedTypeSet typeSet = cls.allSupertypesAndSelf; 963 OrderedTypeSet typeSet = cls.allSupertypesAndSelf;
743 if (!iterator.moveNext()) return typeSet.types.map((type) => type.element); 964 if (!iterator.moveNext()) return typeSet.types.map((type) => type.element);
744 965
745 int depth = typeSet.maxDepth; 966 int depth = typeSet.maxDepth;
746 Link<OrderedTypeSet> otherTypeSets = const Link<OrderedTypeSet>(); 967 Link<OrderedTypeSet> otherTypeSets = const Link<OrderedTypeSet>();
747 do { 968 do {
748 ClassElement otherClass = iterator.current; 969 ClassElement otherClass = iterator.current;
749 assert(checkInvariants(otherClass)); 970 assert(_checkInvariants(otherClass));
750 OrderedTypeSet otherTypeSet = otherClass.allSupertypesAndSelf; 971 OrderedTypeSet otherTypeSet = otherClass.allSupertypesAndSelf;
751 otherTypeSets = otherTypeSets.prepend(otherTypeSet); 972 otherTypeSets = otherTypeSets.prepend(otherTypeSet);
752 if (otherTypeSet.maxDepth < depth) { 973 if (otherTypeSet.maxDepth < depth) {
753 depth = otherTypeSet.maxDepth; 974 depth = otherTypeSet.maxDepth;
754 } 975 }
755 } while (iterator.moveNext()); 976 } while (iterator.moveNext());
756 977
757 List<ClassElement> commonSupertypes = <ClassElement>[]; 978 List<ClassElement> commonSupertypes = <ClassElement>[];
758 OUTER: 979 OUTER:
759 for (Link<ResolutionDartType> link = typeSet[depth]; 980 for (Link<ResolutionDartType> link = typeSet[depth];
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
797 // Run through the direct supertypes of the class. If the common 1018 // Run through the direct supertypes of the class. If the common
798 // set contains the direct supertype of the class, we ignore the 1019 // set contains the direct supertype of the class, we ignore the
799 // the class because the supertype is a better candidate. 1020 // the class because the supertype is a better candidate.
800 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { 1021 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) {
801 if (common.contains(link.head.element)) return false; 1022 if (common.contains(link.head.element)) return false;
802 } 1023 }
803 return true; 1024 return true;
804 }); 1025 });
805 } 1026 }
806 1027
807 Set<ClassEntity> _commonContainedClasses(ClassEntity cls1, ClassQuery query1,
808 ClassEntity cls2, ClassQuery query2) {
809 Iterable<ClassEntity> xSubset = _containedSubset(cls1, query1);
810 if (xSubset == null) return null;
811 Iterable<ClassEntity> ySubset = _containedSubset(cls2, query2);
812 if (ySubset == null) return null;
813 return xSubset.toSet().intersection(ySubset.toSet());
814 }
815
816 Iterable<ClassEntity> _containedSubset(ClassEntity cls, ClassQuery query) {
817 switch (query) {
818 case ClassQuery.EXACT:
819 return null;
820 case ClassQuery.SUBCLASS:
821 return strictSubclassesOf(cls);
822 case ClassQuery.SUBTYPE:
823 return strictSubtypesOf(cls);
824 }
825 throw new ArgumentError('Unexpected query: $query.');
826 }
827
828 /// Returns an iterable over the live mixin applications that mixin [cls]. 1028 /// Returns an iterable over the live mixin applications that mixin [cls].
829 Iterable<ClassEntity> mixinUsesOf(ClassEntity cls) { 1029 Iterable<ClassEntity> mixinUsesOf(ClassEntity cls) {
830 if (_liveMixinUses == null) { 1030 if (_liveMixinUses == null) {
831 _liveMixinUses = new Map<ClassEntity, List<ClassEntity>>(); 1031 _liveMixinUses = new Map<ClassEntity, List<ClassEntity>>();
832 for (ClassElement mixin in _mixinUses.keys) { 1032 for (ClassElement mixin in _mixinUses.keys) {
833 List<ClassEntity> uses = <ClassEntity>[]; 1033 List<ClassEntity> uses = <ClassEntity>[];
834 1034
835 void addLiveUse(MixinApplicationElement mixinApplication) { 1035 void addLiveUse(MixinApplicationElement mixinApplication) {
836 if (isInstantiated(mixinApplication)) { 1036 if (isInstantiated(mixinApplication)) {
837 uses.add(mixinApplication); 1037 uses.add(mixinApplication);
838 } else if (mixinApplication.isNamedMixinApplication) { 1038 } else if (mixinApplication.isNamedMixinApplication) {
839 Set<ClassEntity> next = _mixinUses[mixinApplication]; 1039 Set<ClassEntity> next = _mixinUses[mixinApplication];
840 if (next != null) { 1040 if (next != null) {
841 next.forEach(addLiveUse); 1041 next.forEach(addLiveUse);
842 } 1042 }
843 } 1043 }
844 } 1044 }
845 1045
846 _mixinUses[mixin].forEach(addLiveUse); 1046 _mixinUses[mixin].forEach(addLiveUse);
847 if (uses.isNotEmpty) { 1047 if (uses.isNotEmpty) {
848 _liveMixinUses[mixin] = uses; 1048 _liveMixinUses[mixin] = uses;
849 } 1049 }
850 } 1050 }
851 } 1051 }
852 Iterable<ClassEntity> uses = _liveMixinUses[cls]; 1052 Iterable<ClassEntity> uses = _liveMixinUses[cls];
853 return uses != null ? uses : const <ClassEntity>[]; 1053 return uses != null ? uses : const <ClassEntity>[];
854 } 1054 }
855 1055
856 /// Returns `true` if [cls] is mixed into a live class.
857 bool isUsedAsMixin(ClassEntity cls) {
858 return !mixinUsesOf(cls).isEmpty;
859 }
860
861 /// Returns `true` if any live class that mixes in [cls] implements [type].
862 bool hasAnySubclassOfMixinUseThatImplements(
863 ClassEntity cls, ClassEntity type) {
864 return mixinUsesOf(cls)
865 .any((use) => hasAnySubclassThatImplements(use, type));
866 }
867
868 /// Returns `true` if any live class that mixes in [mixin] is also a subclass 1056 /// Returns `true` if any live class that mixes in [mixin] is also a subclass
869 /// of [superclass]. 1057 /// of [superclass].
870 bool hasAnySubclassThatMixes(ClassEntity superclass, ClassEntity mixin) { 1058 bool hasAnySubclassThatMixes(ClassEntity superclass, ClassEntity mixin) {
871 return mixinUsesOf(mixin).any((ClassElement each) { 1059 return mixinUsesOf(mixin).any((ClassElement each) {
872 return each.isSubclassOf(superclass); 1060 return each.isSubclassOf(superclass);
873 }); 1061 });
874 } 1062 }
875 1063
876 /// Returns `true` if [cls] or any superclass mixes in [mixin]. 1064 /// Returns `true` if [cls] or any superclass mixes in [mixin].
877 bool isSubclassOfMixinUseOf(ClassEntity cls, ClassEntity mixin) { 1065 bool isSubclassOfMixinUseOf(ClassEntity cls, ClassEntity mixin) {
878 assert(_checkClass(cls)); 1066 assert(_checkClass(cls));
879 assert(_checkClass(mixin)); 1067 assert(_checkClass(mixin));
880 if (isUsedAsMixin(mixin)) { 1068 if (isUsedAsMixin(mixin)) {
881 ClassElement current = cls; 1069 ClassElement current = cls;
882 while (current != null) { 1070 while (current != null) {
883 if (current.isMixinApplication) { 1071 if (current.isMixinApplication) {
884 MixinApplicationElement application = current; 1072 MixinApplicationElement application = current;
885 if (application.mixin == mixin) return true; 1073 if (application.mixin == mixin) return true;
886 } 1074 }
887 current = current.superclass; 1075 current = current.superclass;
888 } 1076 }
889 } 1077 }
890 return false; 1078 return false;
891 } 1079 }
892 1080
893 /// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass
894 /// of a mixin application of [y].
895 bool everySubtypeIsSubclassOfOrMixinUseOf(ClassEntity x, ClassEntity y) {
896 assert(_checkClass(x));
897 assert(_checkClass(y));
898 Map<ClassEntity, bool> secondMap =
899 _subtypeCoveredByCache[x] ??= <ClassEntity, bool>{};
900 return secondMap[y] ??= subtypesOf(x).every((ClassEntity cls) =>
901 isSubclassOf(cls, y) || isSubclassOfMixinUseOf(cls, y));
902 }
903
904 /// Returns `true` if any subclass of [superclass] implements [type].
905 bool hasAnySubclassThatImplements(ClassEntity superclass, ClassEntity type) {
906 assert(_checkClass(superclass));
907 Set<ClassEntity> subclasses = _typesImplementedBySubclasses[superclass];
908 if (subclasses == null) return false;
909 return subclasses.contains(type);
910 }
911
912 @override 1081 @override
913 bool hasElementIn(ClassEntity cls, Selector selector, Element element) { 1082 bool hasElementIn(ClassEntity cls, Selector selector, Element element) {
914 // Use [:implementation:] of [element] 1083 // Use [:implementation:] of [element]
915 // because our function set only stores declarations. 1084 // because our function set only stores declarations.
916 Element result = findMatchIn(cls, selector); 1085 Element result = findMatchIn(cls, selector);
917 return result == null 1086 return result == null
918 ? false 1087 ? false
919 : result.implementation == element.implementation; 1088 : result.implementation == element.implementation;
920 } 1089 }
921 1090
(...skipping 14 matching lines...) Expand all
936 MemberElement element = findMatchIn(cls, selector); 1105 MemberElement element = findMatchIn(cls, selector);
937 if (element == null) return false; 1106 if (element == null) return false;
938 1107
939 if (element.isAbstract) { 1108 if (element.isAbstract) {
940 ClassElement enclosingClass = element.enclosingClass; 1109 ClassElement enclosingClass = element.enclosingClass;
941 return hasConcreteMatch(enclosingClass.superclass, selector); 1110 return hasConcreteMatch(enclosingClass.superclass, selector);
942 } 1111 }
943 return selector.appliesUntyped(element); 1112 return selector.appliesUntyped(element);
944 } 1113 }
945 1114
946 @override
947 bool needsNoSuchMethod(
948 ClassElement base, Selector selector, ClassQuery query) {
949 /// Returns `true` if subclasses in the [rootNode] tree needs noSuchMethod
950 /// handling.
951 bool subclassesNeedNoSuchMethod(ClassHierarchyNode rootNode) {
952 if (!rootNode.isInstantiated) {
953 // No subclass needs noSuchMethod handling since they are all
954 // uninstantiated.
955 return false;
956 }
957 ClassElement rootClass = rootNode.cls;
958 if (hasConcreteMatch(rootClass, selector)) {
959 // The root subclass has a concrete implementation so no subclass needs
960 // noSuchMethod handling.
961 return false;
962 } else if (rootNode.isExplicitlyInstantiated) {
963 // The root class need noSuchMethod handling.
964 return true;
965 }
966 IterationStep result = rootNode.forEachSubclass((ClassElement subclass) {
967 if (hasConcreteMatch(subclass, selector, stopAtSuperclass: rootClass)) {
968 // Found a match - skip all subclasses.
969 return IterationStep.SKIP_SUBCLASSES;
970 } else {
971 // Stop fast - we found a need for noSuchMethod handling.
972 return IterationStep.STOP;
973 }
974 }, ClassHierarchyNode.EXPLICITLY_INSTANTIATED, strict: true);
975 // We stopped fast so we need noSuchMethod handling.
976 return result == IterationStep.STOP;
977 }
978
979 ClassSet classSet = getClassSet(base);
980 ClassHierarchyNode node = classSet.node;
981 if (query == ClassQuery.EXACT) {
982 return node.isExplicitlyInstantiated && !hasConcreteMatch(base, selector);
983 } else if (query == ClassQuery.SUBCLASS) {
984 return subclassesNeedNoSuchMethod(node);
985 } else {
986 if (subclassesNeedNoSuchMethod(node)) return true;
987 for (ClassHierarchyNode subtypeNode in classSet.subtypeNodes) {
988 if (subclassesNeedNoSuchMethod(subtypeNode)) return true;
989 }
990 return false;
991 }
992 }
993
994 /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
995 /// of known classes.
996 ///
997 /// This method is only provided for testing. For queries on classes, use the
998 /// methods defined in [ClosedWorld].
999 ClassHierarchyNode getClassHierarchyNode(ClassEntity cls) {
1000 assert(_checkClass(cls));
1001 return _classHierarchyNodes[cls];
1002 }
1003
1004 /// Returns [ClassSet] for [cls] used to model the extends and implements
1005 /// relations of known classes.
1006 ///
1007 /// This method is only provided for testing. For queries on classes, use the
1008 /// methods defined in [ClosedWorld].
1009 ClassSet getClassSet(ClassEntity cls) {
1010 assert(_checkClass(cls));
1011 return _classSets[cls];
1012 }
1013
1014 void registerClosureClass(ClosureClassElement cls) { 1115 void registerClosureClass(ClosureClassElement cls) {
1015 ClassHierarchyNode parentNode = getClassHierarchyNode(cls.superclass); 1116 ClassHierarchyNode parentNode = getClassHierarchyNode(cls.superclass);
1016 ClassHierarchyNode node = _classHierarchyNodes[cls] = 1117 ClassHierarchyNode node = _classHierarchyNodes[cls] =
1017 new ClassHierarchyNode(parentNode, cls, cls.hierarchyDepth); 1118 new ClassHierarchyNode(parentNode, cls, cls.hierarchyDepth);
1018 for (ResolutionInterfaceType type in cls.allSupertypes) { 1119 for (ResolutionInterfaceType type in cls.allSupertypes) {
1019 ClassSet subtypeSet = getClassSet(type.element); 1120 ClassSet subtypeSet = getClassSet(type.element);
1020 subtypeSet.addSubtype(node); 1121 subtypeSet.addSubtype(node);
1021 } 1122 }
1022 _classSets[cls] = new ClassSet(node); 1123 _classSets[cls] = new ClassSet(node);
1023 _updateSuperClassHierarchyNodeForClass(node); 1124 _updateSuperClassHierarchyNodeForClass(node);
1024 node.isDirectlyInstantiated = true; 1125 node.isDirectlyInstantiated = true;
1025 } 1126 }
1026 1127
1027 void _updateSuperClassHierarchyNodeForClass(ClassHierarchyNode node) { 1128 void _updateSuperClassHierarchyNodeForClass(ClassHierarchyNode node) {
1028 // Ensure that classes implicitly implementing `Function` are in its 1129 // Ensure that classes implicitly implementing `Function` are in its
1029 // subtype set. 1130 // subtype set.
1030 ClassElement cls = node.cls; 1131 ClassElement cls = node.cls;
1031 if (cls != commonElements.functionClass && 1132 if (cls != commonElements.functionClass &&
1032 cls.implementsFunction(commonElements)) { 1133 cls.implementsFunction(commonElements)) {
1033 ClassSet subtypeSet = getClassSet(commonElements.functionClass); 1134 ClassSet subtypeSet = getClassSet(commonElements.functionClass);
1034 subtypeSet.addSubtype(node); 1135 subtypeSet.addSubtype(node);
1035 } 1136 }
1036 if (!node.isInstantiated && node.parentNode != null) { 1137 if (!node.isInstantiated && node.parentNode != null) {
1037 _updateSuperClassHierarchyNodeForClass(node.parentNode); 1138 _updateSuperClassHierarchyNodeForClass(node.parentNode);
1038 } 1139 }
1039 } 1140 }
1040 1141
1041 Iterable<TypedefElement> get allTypedefs => _allTypedefs;
1042
1043 @override 1142 @override
1044 String dump([ClassElement cls]) { 1143 String dump([ClassElement cls]) {
1045 StringBuffer sb = new StringBuffer(); 1144 StringBuffer sb = new StringBuffer();
1046 if (cls != null) { 1145 if (cls != null) {
1047 sb.write("Classes in the closed world related to $cls:\n"); 1146 sb.write("Classes in the closed world related to $cls:\n");
1048 } else { 1147 } else {
1049 sb.write("Instantiated classes in the closed world:\n"); 1148 sb.write("Instantiated classes in the closed world:\n");
1050 } 1149 }
1051 getClassHierarchyNode(commonElements.objectClass) 1150 getClassHierarchyNode(commonElements.objectClass)
1052 .printOn(sb, ' ', instantiatedOnly: cls == null, withRespectTo: cls); 1151 .printOn(sb, ' ', instantiatedOnly: cls == null, withRespectTo: cls);
1053 return sb.toString(); 1152 return sb.toString();
1054 } 1153 }
1055 1154
1056 bool hasAnyUserDefinedGetter(Selector selector, TypeMask mask) {
1057 return allFunctions.filter(selector, mask).any((each) => each.isGetter);
1058 }
1059
1060 FieldElement locateSingleField(Selector selector, TypeMask mask) {
1061 Element result = locateSingleElement(selector, mask);
1062 return (result != null && result.isField) ? result : null;
1063 }
1064
1065 MemberElement locateSingleElement(Selector selector, TypeMask mask) {
1066 mask ??= commonMasks.dynamicType;
1067 return mask.locateSingleElement(selector, this);
1068 }
1069
1070 TypeMask extendMaskIfReachesAll(Selector selector, TypeMask mask) {
1071 bool canReachAll = true;
1072 if (mask != null) {
1073 canReachAll = _backend.backendUsage.isInvokeOnUsed &&
1074 mask.needsNoSuchMethodHandling(selector, this);
1075 }
1076 return canReachAll ? commonMasks.dynamicType : mask;
1077 }
1078
1079 void addFunctionCalledInLoop(Element element) {
1080 functionsCalledInLoop.add(element.declaration);
1081 }
1082
1083 bool isCalledInLoop(Element element) {
1084 return functionsCalledInLoop.contains(element.declaration);
1085 }
1086
1087 bool fieldNeverChanges(MemberElement element) {
1088 if (!element.isField) return false;
1089 if (nativeData.isNativeMember(element)) {
1090 // Some native fields are views of data that may be changed by operations.
1091 // E.g. node.firstChild depends on parentNode.removeBefore(n1, n2).
1092 // TODO(sra): Refine the effect classification so that native effects are
1093 // distinct from ordinary Dart effects.
1094 return false;
1095 }
1096
1097 if (element.isFinal || element.isConst) {
1098 return true;
1099 }
1100 if (element.isInstanceMember) {
1101 return !_resolverWorld.hasInvokedSetter(element) &&
1102 !_resolverWorld.fieldSetters.contains(element);
1103 }
1104 return false;
1105 }
1106
1107 SideEffects getSideEffectsOfElement(Element element) { 1155 SideEffects getSideEffectsOfElement(Element element) {
1108 // The type inferrer (where the side effects are being computed), 1156 // The type inferrer (where the side effects are being computed),
1109 // does not see generative constructor bodies because they are 1157 // does not see generative constructor bodies because they are
1110 // created by the backend. Also, it does not make any distinction 1158 // created by the backend. Also, it does not make any distinction
1111 // between a constructor and its body for side effects. This 1159 // between a constructor and its body for side effects. This
1112 // implies that currently, the side effects of a constructor body 1160 // implies that currently, the side effects of a constructor body
1113 // contain the side effects of the initializers. 1161 // contain the side effects of the initializers.
1114 assert(!element.isGenerativeConstructorBody); 1162 assert(!element.isGenerativeConstructorBody);
1115 assert(!element.isField); 1163 assert(!element.isField);
1116 return sideEffects.putIfAbsent(element.declaration, () { 1164 return sideEffects.putIfAbsent(element.declaration, () {
1117 return new SideEffects(); 1165 return new SideEffects();
1118 }); 1166 });
1119 } 1167 }
1120 1168
1121 @override 1169 @override
1122 SideEffects getCurrentlyKnownSideEffects(Element element) { 1170 SideEffects getCurrentlyKnownSideEffects(Element element) {
1123 return getSideEffectsOfElement(element); 1171 return getSideEffectsOfElement(element);
1124 } 1172 }
1125 1173
1126 void registerSideEffects(Element element, SideEffects effects) { 1174 void registerSideEffects(Element element, SideEffects effects) {
1127 if (sideEffectsFreeElements.contains(element)) return; 1175 if (sideEffectsFreeElements.contains(element)) return;
1128 sideEffects[element.declaration] = effects; 1176 sideEffects[element.declaration] = effects;
1129 } 1177 }
1130 1178
1131 void registerSideEffectsFree(Element element) { 1179 void registerSideEffectsFree(Element element) {
1132 sideEffects[element.declaration] = new SideEffects.empty(); 1180 sideEffects[element.declaration] = new SideEffects.empty();
1133 sideEffectsFreeElements.add(element); 1181 sideEffectsFreeElements.add(element);
1134 } 1182 }
1135 1183
1136 SideEffects getSideEffectsOfSelector(Selector selector, TypeMask mask) { 1184 void addFunctionCalledInLoop(Element element) {
1137 // We're not tracking side effects of closures. 1185 functionsCalledInLoop.add(element.declaration);
1138 if (selector.isClosureCall) return new SideEffects(); 1186 }
1139 SideEffects sideEffects = new SideEffects.empty(); 1187
1140 for (MemberElement e in allFunctions.filter(selector, mask)) { 1188 bool isCalledInLoop(Element element) {
1141 if (e.isField) { 1189 return functionsCalledInLoop.contains(element.declaration);
1142 if (selector.isGetter) {
1143 if (!fieldNeverChanges(e)) {
1144 sideEffects.setDependsOnInstancePropertyStore();
1145 }
1146 } else if (selector.isSetter) {
1147 sideEffects.setChangesInstanceProperty();
1148 } else {
1149 assert(selector.isCall);
1150 sideEffects.setAllSideEffects();
1151 sideEffects.setDependsOnSomething();
1152 }
1153 } else {
1154 sideEffects.add(getSideEffectsOfElement(e));
1155 }
1156 }
1157 return sideEffects;
1158 } 1190 }
1159 1191
1160 void registerCannotThrow(Element element) { 1192 void registerCannotThrow(Element element) {
1161 elementsThatCannotThrow.add(element); 1193 elementsThatCannotThrow.add(element);
1162 } 1194 }
1163 1195
1164 bool getCannotThrow(Element element) { 1196 bool getCannotThrow(Element element) {
1165 return elementsThatCannotThrow.contains(element); 1197 return elementsThatCannotThrow.contains(element);
1166 } 1198 }
1167 1199
(...skipping 12 matching lines...) Expand all
1180 return getMightBePassedToApply(element.expression); 1212 return getMightBePassedToApply(element.expression);
1181 } 1213 }
1182 return functionsThatMightBePassedToApply.contains(element); 1214 return functionsThatMightBePassedToApply.contains(element);
1183 } 1215 }
1184 1216
1185 @override 1217 @override
1186 bool getCurrentlyKnownMightBePassedToApply(Element element) { 1218 bool getCurrentlyKnownMightBePassedToApply(Element element) {
1187 return getMightBePassedToApply(element); 1219 return getMightBePassedToApply(element);
1188 } 1220 }
1189 } 1221 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698