| 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 |