OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |