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 SynthesizedCallMethodElementX; | 7 import 'closure.dart' show 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 'compiler.dart' show Compiler; | 10 import 'compiler.dart' show Compiler; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 /// Returns `true` if [cls] is either directly or indirectly instantiated. | 48 /// Returns `true` if [cls] is either directly or indirectly instantiated. |
49 bool isInstantiated(ClassElement cls); | 49 bool isInstantiated(ClassElement cls); |
50 | 50 |
51 /// Returns `true` if [cls] is directly instantiated. | 51 /// Returns `true` if [cls] is directly instantiated. |
52 bool isDirectlyInstantiated(ClassElement cls); | 52 bool isDirectlyInstantiated(ClassElement cls); |
53 | 53 |
54 /// Returns `true` if [cls] is indirectly instantiated, that is through a | 54 /// Returns `true` if [cls] is indirectly instantiated, that is through a |
55 /// subclass. | 55 /// subclass. |
56 bool isIndirectlyInstantiated(ClassElement cls); | 56 bool isIndirectlyInstantiated(ClassElement cls); |
57 | 57 |
| 58 /// Returns `true` if [cls] is abstract and thus can only be instantiated |
| 59 /// through subclasses. |
| 60 bool isAbstract(ClassElement cls); |
| 61 |
58 /// Returns `true` if [cls] is implemented by an instantiated class. | 62 /// Returns `true` if [cls] is implemented by an instantiated class. |
59 bool isImplemented(ClassElement cls); | 63 bool isImplemented(ClassElement cls); |
60 | 64 |
61 /// Return `true` if [x] is a subclass of [y]. | 65 /// Return `true` if [x] is a subclass of [y]. |
62 bool isSubclassOf(ClassElement x, ClassElement y); | 66 bool isSubclassOf(ClassElement x, ClassElement y); |
63 | 67 |
64 /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an | 68 /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an |
65 /// instance of [y]. | 69 /// instance of [y]. |
66 bool isSubtypeOf(ClassElement x, ClassElement y); | 70 bool isSubtypeOf(ClassElement x, ClassElement y); |
67 | 71 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 /// Returns `true` if [cls] or any superclass mixes in [mixin]. | 153 /// Returns `true` if [cls] or any superclass mixes in [mixin]. |
150 bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin); | 154 bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin); |
151 | 155 |
152 /// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass | 156 /// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass |
153 /// of a mixin application of [y]. | 157 /// of a mixin application of [y]. |
154 bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y); | 158 bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y); |
155 | 159 |
156 /// Returns `true` if any subclass of [superclass] implements [type]. | 160 /// Returns `true` if any subclass of [superclass] implements [type]. |
157 bool hasAnySubclassThatImplements(ClassElement superclass, ClassElement type); | 161 bool hasAnySubclassThatImplements(ClassElement superclass, ClassElement type); |
158 | 162 |
| 163 /// Returns `true` if a call of [selector] on [cls] and/or subclasses/subtypes |
| 164 /// need noSuchMethod handling. |
| 165 /// |
| 166 /// If the receiver is guaranteed to have a member that matches what we're |
| 167 /// looking for, there's no need to introduce a noSuchMethod handler. It will |
| 168 /// never be called. |
| 169 /// |
| 170 /// As an example, consider this class hierarchy: |
| 171 /// |
| 172 /// A <-- noSuchMethod |
| 173 /// / \ |
| 174 /// C B <-- foo |
| 175 /// |
| 176 /// If we know we're calling foo on an object of type B we don't have to worry |
| 177 /// about the noSuchMethod method in A because objects of type B implement |
| 178 /// foo. On the other hand, if we end up calling foo on something of type C we |
| 179 /// have to add a handler for it. |
| 180 /// |
| 181 /// If the holders of all user-defined noSuchMethod implementations that might |
| 182 /// be applicable to the receiver type have a matching member for the current |
| 183 /// name and selector, we avoid introducing a noSuchMethod handler. |
| 184 /// |
| 185 /// As an example, consider this class hierarchy: |
| 186 /// |
| 187 /// A <-- foo |
| 188 /// / \ |
| 189 /// noSuchMethod --> B C <-- bar |
| 190 /// | | |
| 191 /// C D <-- noSuchMethod |
| 192 /// |
| 193 /// When calling foo on an object of type A, we know that the implementations |
| 194 /// of noSuchMethod are in the classes B and D that also (indirectly) |
| 195 /// implement foo, so we do not need a handler for it. |
| 196 /// |
| 197 /// If we're calling bar on an object of type D, we don't need the handler |
| 198 /// either because all objects of type D implement bar through inheritance. |
| 199 /// |
| 200 /// If we're calling bar on an object of type A we do need the handler because |
| 201 /// we may have to call B.noSuchMethod since B does not implement bar. |
| 202 bool needsNoSuchMethod(ClassElement cls, Selector selector, ClassQuery query); |
| 203 |
| 204 /// Returns whether [element] will be the one used at runtime when being |
| 205 /// invoked on an instance of [cls]. [selector] is used to ensure library |
| 206 /// privacy is taken into account. |
| 207 bool hasElementIn(ClassElement cls, Selector selector, Element element); |
| 208 |
159 /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies | 209 /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies |
160 /// of known classes. | 210 /// of known classes. |
161 /// | 211 /// |
162 /// This method is only provided for testing. For queries on classes, use the | 212 /// This method is only provided for testing. For queries on classes, use the |
163 /// methods defined in [ClosedWorld]. | 213 /// methods defined in [ClosedWorld]. |
164 ClassHierarchyNode getClassHierarchyNode(ClassElement cls); | 214 ClassHierarchyNode getClassHierarchyNode(ClassElement cls); |
165 | 215 |
166 /// Returns [ClassSet] for [cls] used to model the extends and implements | 216 /// Returns [ClassSet] for [cls] used to model the extends and implements |
167 /// relations of known classes. | 217 /// relations of known classes. |
168 /// | 218 /// |
169 /// This method is only provided for testing. For queries on classes, use the | 219 /// This method is only provided for testing. For queries on classes, use the |
170 /// methods defined in [ClosedWorld]. | 220 /// methods defined in [ClosedWorld]. |
171 ClassSet getClassSet(ClassElement cls); | 221 ClassSet getClassSet(ClassElement cls); |
172 | 222 |
173 // TODO(johnniwinther): Find a better strategy for caching these. | 223 /// Return the cached mask for [base] with the given flags, or |
174 @deprecated | 224 /// calls [createMask] to create the mask and cache it. |
175 List<Map<ClassElement, TypeMask>> get canonicalizedTypeMasks; | 225 // TODO(johnniwinther): Find a better strategy for caching these? |
| 226 TypeMask getCachedMask(ClassElement base, int flags, TypeMask createMask()); |
176 | 227 |
177 /// Returns the [FunctionSet] containing all live functions in the closed | 228 /// Returns the [FunctionSet] containing all live functions in the closed |
178 /// world. | 229 /// world. |
179 FunctionSet get allFunctions; | 230 FunctionSet get allFunctions; |
180 | 231 |
181 /// Returns `true` if the field [element] is known to be effectively final. | 232 /// Returns `true` if the field [element] is known to be effectively final. |
182 bool fieldNeverChanges(Element element); | 233 bool fieldNeverChanges(Element element); |
183 | 234 |
184 /// Extends the receiver type [mask] for calling [selector] to take live | 235 /// Extends the receiver type [mask] for calling [selector] to take live |
185 /// `noSuchMethod` handlers into account. | 236 /// `noSuchMethod` handlers into account. |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 | 318 |
268 ClosedWorld closeWorld(); | 319 ClosedWorld closeWorld(); |
269 | 320 |
270 /// Returns an iterable over all mixin applications that mixin [cls]. | 321 /// Returns an iterable over all mixin applications that mixin [cls]. |
271 Iterable<MixinApplicationElement> allMixinUsesOf(ClassElement cls); | 322 Iterable<MixinApplicationElement> allMixinUsesOf(ClassElement cls); |
272 } | 323 } |
273 | 324 |
274 class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld { | 325 class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld { |
275 bool _closed = false; | 326 bool _closed = false; |
276 | 327 |
| 328 TypeMask getCachedMask(ClassElement base, int flags, TypeMask createMask()) { |
| 329 Map<ClassElement, TypeMask> cachedMasks = |
| 330 _canonicalizedTypeMasks[flags] ??= <ClassElement, TypeMask>{}; |
| 331 return cachedMasks.putIfAbsent(base, createMask); |
| 332 } |
| 333 |
277 /// Cache of [FlatTypeMask]s grouped by the 8 possible values of the | 334 /// Cache of [FlatTypeMask]s grouped by the 8 possible values of the |
278 /// `FlatTypeMask.flags` property. | 335 /// `FlatTypeMask.flags` property. |
279 List<Map<ClassElement, TypeMask>> canonicalizedTypeMasks = | 336 final List<Map<ClassElement, TypeMask>> _canonicalizedTypeMasks = |
280 new List<Map<ClassElement, TypeMask>>.filled(8, null); | 337 new List<Map<ClassElement, TypeMask>>.filled(8, null); |
281 | 338 |
282 bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) { | 339 bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) { |
283 return invariant(cls, cls.isDeclaration, | 340 return invariant(cls, cls.isDeclaration, |
284 message: '$cls must be the declaration.') && | 341 message: '$cls must be the declaration.') && |
285 invariant(cls, cls.isResolved, | 342 invariant(cls, cls.isResolved, |
286 message: | 343 message: |
287 '$cls must be resolved.') /* && | 344 '$cls must be resolved.') /* && |
288 // TODO(johnniwinther): Reinsert this or similar invariant. | 345 // TODO(johnniwinther): Reinsert this or similar invariant. |
289 (!mustBeInstantiated || | 346 (!mustBeInstantiated || |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 return node != null && node.isDirectlyInstantiated; | 392 return node != null && node.isDirectlyInstantiated; |
336 } | 393 } |
337 | 394 |
338 @override | 395 @override |
339 bool isIndirectlyInstantiated(ClassElement cls) { | 396 bool isIndirectlyInstantiated(ClassElement cls) { |
340 assert(isClosed); | 397 assert(isClosed); |
341 ClassHierarchyNode node = _classHierarchyNodes[cls.declaration]; | 398 ClassHierarchyNode node = _classHierarchyNodes[cls.declaration]; |
342 return node != null && node.isIndirectlyInstantiated; | 399 return node != null && node.isIndirectlyInstantiated; |
343 } | 400 } |
344 | 401 |
| 402 @override |
| 403 bool isAbstract(ClassElement cls) => cls.isAbstract; |
| 404 |
345 /// Returns `true` if [cls] is implemented by an instantiated class. | 405 /// Returns `true` if [cls] is implemented by an instantiated class. |
346 bool isImplemented(ClassElement cls) { | 406 bool isImplemented(ClassElement cls) { |
347 assert(isClosed); | 407 assert(isClosed); |
348 return _compiler.resolverWorld.isImplemented(cls); | 408 return _compiler.resolverWorld.isImplemented(cls); |
349 } | 409 } |
350 | 410 |
351 /// Returns an iterable over the directly instantiated classes that extend | 411 /// Returns an iterable over the directly instantiated classes that extend |
352 /// [cls] possibly including [cls] itself, if it is live. | 412 /// [cls] possibly including [cls] itself, if it is live. |
353 Iterable<ClassElement> subclassesOf(ClassElement cls) { | 413 Iterable<ClassElement> subclassesOf(ClassElement cls) { |
354 assert(isClosed); | 414 assert(isClosed); |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
615 /// Returns `true` if any live class that mixes in [mixin] is also a subclass | 675 /// Returns `true` if any live class that mixes in [mixin] is also a subclass |
616 /// of [superclass]. | 676 /// of [superclass]. |
617 bool hasAnySubclassThatMixes(ClassElement superclass, ClassElement mixin) { | 677 bool hasAnySubclassThatMixes(ClassElement superclass, ClassElement mixin) { |
618 assert(isClosed); | 678 assert(isClosed); |
619 return mixinUsesOf(mixin).any((each) => each.isSubclassOf(superclass)); | 679 return mixinUsesOf(mixin).any((each) => each.isSubclassOf(superclass)); |
620 } | 680 } |
621 | 681 |
622 /// Returns `true` if [cls] or any superclass mixes in [mixin]. | 682 /// Returns `true` if [cls] or any superclass mixes in [mixin]. |
623 bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin) { | 683 bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin) { |
624 assert(isClosed); | 684 assert(isClosed); |
| 685 assert(cls.isDeclaration); |
| 686 assert(mixin.isDeclaration); |
625 if (isUsedAsMixin(mixin)) { | 687 if (isUsedAsMixin(mixin)) { |
626 ClassElement current = cls.declaration; | 688 ClassElement current = cls; |
627 mixin = mixin.declaration; | |
628 while (current != null) { | 689 while (current != null) { |
629 current = current.declaration; | |
630 if (current.isMixinApplication) { | 690 if (current.isMixinApplication) { |
631 MixinApplicationElement application = current; | 691 MixinApplicationElement application = current; |
632 if (application.mixin.declaration == mixin) return true; | 692 if (application.mixin == mixin) return true; |
633 } | 693 } |
634 current = current.superclass; | 694 current = current.superclass; |
635 } | 695 } |
636 } | 696 } |
637 return false; | 697 return false; |
638 } | 698 } |
639 | 699 |
640 /// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass | 700 /// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass |
641 /// of a mixin application of [y]. | 701 /// of a mixin application of [y]. |
642 bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y) { | 702 bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y) { |
643 assert(isClosed); | 703 assert(isClosed); |
644 x = x.declaration; | 704 assert(x.isDeclaration); |
645 y = y.declaration; | 705 assert(y.isDeclaration); |
646 Map<ClassElement, bool> secondMap = | 706 Map<ClassElement, bool> secondMap = |
647 _subtypeCoveredByCache[x] ??= <ClassElement, bool>{}; | 707 _subtypeCoveredByCache[x] ??= <ClassElement, bool>{}; |
648 return secondMap[y] ??= subtypesOf(x).every((ClassElement cls) => | 708 return secondMap[y] ??= subtypesOf(x).every((ClassElement cls) => |
649 isSubclassOf(cls, y) || isSubclassOfMixinUseOf(cls, y)); | 709 isSubclassOf(cls, y) || isSubclassOfMixinUseOf(cls, y)); |
650 } | 710 } |
651 | 711 |
652 /// Returns `true` if any subclass of [superclass] implements [type]. | 712 /// Returns `true` if any subclass of [superclass] implements [type]. |
653 bool hasAnySubclassThatImplements( | 713 bool hasAnySubclassThatImplements( |
654 ClassElement superclass, ClassElement type) { | 714 ClassElement superclass, ClassElement type) { |
655 assert(isClosed); | 715 assert(isClosed); |
656 Set<ClassElement> subclasses = typesImplementedBySubclassesOf(superclass); | 716 Set<ClassElement> subclasses = typesImplementedBySubclassesOf(superclass); |
657 if (subclasses == null) return false; | 717 if (subclasses == null) return false; |
658 return subclasses.contains(type); | 718 return subclasses.contains(type); |
659 } | 719 } |
660 | 720 |
| 721 @override |
| 722 bool hasElementIn(ClassElement cls, Selector selector, Element element) { |
| 723 // Use [:implementation:] of [element] |
| 724 // because our function set only stores declarations. |
| 725 Element result = findMatchIn(cls, selector); |
| 726 return result == null |
| 727 ? false |
| 728 : result.implementation == element.implementation; |
| 729 } |
| 730 |
| 731 Element findMatchIn(ClassElement cls, Selector selector) { |
| 732 // Use the [:implementation] of [cls] in case the found [element] |
| 733 // is in the patch class. |
| 734 var result = cls.implementation.lookupByName(selector.memberName); |
| 735 return result; |
| 736 } |
| 737 |
| 738 /// Returns whether a [selector] call on an instance of [cls] |
| 739 /// will hit a method at runtime, and not go through [noSuchMethod]. |
| 740 bool hasConcreteMatch(ClassElement cls, Selector selector) { |
| 741 assert(invariant(cls, isInstantiated(cls), |
| 742 message: '$cls has not been instantiated.')); |
| 743 Element element = findMatchIn(cls, selector); |
| 744 if (element == null) return false; |
| 745 |
| 746 if (element.isAbstract) { |
| 747 ClassElement enclosingClass = element.enclosingClass; |
| 748 return hasConcreteMatch(enclosingClass.superclass, selector); |
| 749 } |
| 750 return selector.appliesUntyped(element); |
| 751 } |
| 752 |
| 753 @override |
| 754 bool needsNoSuchMethod( |
| 755 ClassElement base, Selector selector, ClassQuery query) { |
| 756 /// Returns `true` if [cls] is an instantiated class that does not have |
| 757 /// a concrete method matching [selector]. |
| 758 bool needsNoSuchMethod(ClassElement cls) { |
| 759 // We can skip uninstantiated subclasses. |
| 760 if (!isInstantiated(cls)) { |
| 761 return false; |
| 762 } |
| 763 // We can just skip abstract classes because we know no |
| 764 // instance of them will be created at runtime, and |
| 765 // therefore there is no instance that will require |
| 766 // [noSuchMethod] handling. |
| 767 return !cls.isAbstract && !hasConcreteMatch(cls, selector); |
| 768 } |
| 769 |
| 770 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); |
| 771 if (query == ClassQuery.EXACT || baseNeedsNoSuchMethod) { |
| 772 return baseNeedsNoSuchMethod; |
| 773 } |
| 774 |
| 775 Iterable<ClassElement> subclassesToCheck; |
| 776 if (query == ClassQuery.SUBTYPE) { |
| 777 subclassesToCheck = strictSubtypesOf(base); |
| 778 } else { |
| 779 assert(query == ClassQuery.SUBCLASS); |
| 780 subclassesToCheck = strictSubclassesOf(base); |
| 781 } |
| 782 |
| 783 return subclassesToCheck != null && |
| 784 subclassesToCheck.any(needsNoSuchMethod); |
| 785 } |
| 786 |
661 final Compiler _compiler; | 787 final Compiler _compiler; |
662 BackendClasses get backendClasses => _backend.backendClasses; | 788 BackendClasses get backendClasses => _backend.backendClasses; |
663 JavaScriptBackend get _backend => _compiler.backend; | 789 JavaScriptBackend get _backend => _compiler.backend; |
664 CommonMasks get commonMasks => _compiler.commonMasks; | 790 CommonMasks get commonMasks => _compiler.commonMasks; |
665 final FunctionSet allFunctions; | 791 final FunctionSet allFunctions; |
666 final Set<Element> functionsCalledInLoop = new Set<Element>(); | 792 final Set<Element> functionsCalledInLoop = new Set<Element>(); |
667 final Map<Element, SideEffects> sideEffects = new Map<Element, SideEffects>(); | 793 final Map<Element, SideEffects> sideEffects = new Map<Element, SideEffects>(); |
668 | 794 |
669 final Set<TypedefElement> _allTypedefs = new Set<TypedefElement>(); | 795 final Set<TypedefElement> _allTypedefs = new Set<TypedefElement>(); |
670 | 796 |
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1004 return getMightBePassedToApply(element.expression); | 1130 return getMightBePassedToApply(element.expression); |
1005 } | 1131 } |
1006 return functionsThatMightBePassedToApply.contains(element); | 1132 return functionsThatMightBePassedToApply.contains(element); |
1007 } | 1133 } |
1008 | 1134 |
1009 @override | 1135 @override |
1010 bool getCurrentlyKnownMightBePassedToApply(Element element) { | 1136 bool getCurrentlyKnownMightBePassedToApply(Element element) { |
1011 return getMightBePassedToApply(element); | 1137 return getMightBePassedToApply(element); |
1012 } | 1138 } |
1013 } | 1139 } |
| 1140 |
| 1141 /// Enum values defining subset of classes included in queries. |
| 1142 enum ClassQuery { |
| 1143 /// Only the class itself is included. |
| 1144 EXACT, |
| 1145 |
| 1146 /// The class and all subclasses (transitively) are included. |
| 1147 SUBCLASS, |
| 1148 |
| 1149 /// The class and all classes that implement or subclass it (transitively) |
| 1150 /// are included. |
| 1151 SUBTYPE, |
| 1152 } |
OLD | NEW |