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

Side by Side Diff: pkg/compiler/lib/src/types/flat_type_mask.dart

Issue 2420073002: Decouple TypeMask from ClassElement (Closed)
Patch Set: Updated cf. comments. Created 4 years, 2 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 | pkg/compiler/lib/src/types/forwarding_type_mask.dart » ('j') | 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 part of masks; 5 part of masks;
6 6
7 /** 7 /**
8 * A flat type mask is a type mask that has been flattened to contain a 8 * A flat type mask is a type mask that has been flattened to contain a
9 * base type. 9 * base type.
10 */ 10 */
11 class FlatTypeMask implements TypeMask { 11 class FlatTypeMask implements TypeMask {
12 static const int EMPTY = 0; 12 static const int EMPTY = 0;
13 static const int EXACT = 1; 13 static const int EXACT = 1;
14 static const int SUBCLASS = 2; 14 static const int SUBCLASS = 2;
15 static const int SUBTYPE = 3; 15 static const int SUBTYPE = 3;
16 16
17 final ClassElement base; 17 final Entity base;
18 final int flags; 18 final int flags;
19 19
20 FlatTypeMask(ClassElement base, int kind, bool isNullable) 20 FlatTypeMask(Entity base, int kind, bool isNullable)
21 : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); 21 : this.internal(base, (kind << 1) | (isNullable ? 1 : 0));
22 22
23 FlatTypeMask.exact(ClassElement base) : this.internal(base, (EXACT << 1) | 1); 23 FlatTypeMask.exact(Entity base) : this.internal(base, (EXACT << 1) | 1);
24 FlatTypeMask.subclass(ClassElement base) 24 FlatTypeMask.subclass(Entity base) : this.internal(base, (SUBCLASS << 1) | 1);
25 : this.internal(base, (SUBCLASS << 1) | 1); 25 FlatTypeMask.subtype(Entity base) : this.internal(base, (SUBTYPE << 1) | 1);
26 FlatTypeMask.subtype(ClassElement base)
27 : this.internal(base, (SUBTYPE << 1) | 1);
28 26
29 const FlatTypeMask.nonNullEmpty() 27 const FlatTypeMask.nonNullEmpty()
30 : base = null, 28 : base = null,
31 flags = 0; 29 flags = 0;
32 const FlatTypeMask.empty() 30 const FlatTypeMask.empty()
33 : base = null, 31 : base = null,
34 flags = 1; 32 flags = 1;
35 33
36 FlatTypeMask.nonNullExact(ClassElement base) 34 FlatTypeMask.nonNullExact(Entity base) : this.internal(base, EXACT << 1);
37 : this.internal(base, EXACT << 1); 35 FlatTypeMask.nonNullSubclass(Entity base)
38 FlatTypeMask.nonNullSubclass(ClassElement base)
39 : this.internal(base, SUBCLASS << 1); 36 : this.internal(base, SUBCLASS << 1);
40 FlatTypeMask.nonNullSubtype(ClassElement base) 37 FlatTypeMask.nonNullSubtype(Entity base) : this.internal(base, SUBTYPE << 1);
41 : this.internal(base, SUBTYPE << 1); 38
39 bool _validateBase(ClassElement element) => element.isDeclaration;
42 40
43 FlatTypeMask.internal(this.base, this.flags) { 41 FlatTypeMask.internal(this.base, this.flags) {
44 assert(base == null || base.isDeclaration); 42 assert(base == null || _validateBase(base));
45 } 43 }
46 44
47 /** 45 /**
48 * Ensures that the generated mask is normalized, i.e., a call to 46 * Ensures that the generated mask is normalized, i.e., a call to
49 * [TypeMask.assertIsNormalized] with the factory's result returns `true`. 47 * [TypeMask.assertIsNormalized] with the factory's result returns `true`.
50 */ 48 */
51 factory FlatTypeMask.normalized( 49 factory FlatTypeMask.normalized(Entity base, int flags, ClosedWorld world) {
52 ClassElement base, int flags, ClosedWorld world) {
53 if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { 50 if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) {
54 return new FlatTypeMask.internal(base, flags); 51 return new FlatTypeMask.internal(base, flags);
55 } 52 }
56 if ((flags >> 1) == SUBTYPE) { 53 if ((flags >> 1) == SUBTYPE) {
57 if (!world.hasAnyStrictSubtype(base) || world.hasOnlySubclasses(base)) { 54 if (!world.hasAnyStrictSubtype(base) || world.hasOnlySubclasses(base)) {
58 flags = (flags & 0x1) | (SUBCLASS << 1); 55 flags = (flags & 0x1) | (SUBCLASS << 1);
59 } 56 }
60 } 57 }
61 if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) { 58 if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) {
62 flags = (flags & 0x1) | (EXACT << 1); 59 flags = (flags & 0x1) | (EXACT << 1);
63 } 60 }
64 Map<ClassElement, TypeMask> cachedMasks = 61 return world.getCachedMask(
65 world.canonicalizedTypeMasks[flags]; 62 base, flags, () => new FlatTypeMask.internal(base, flags));
66 if (cachedMasks == null) {
67 world.canonicalizedTypeMasks[flags] =
68 cachedMasks = <ClassElement, TypeMask>{};
69 }
70 return cachedMasks.putIfAbsent(
71 base, () => new FlatTypeMask.internal(base, flags));
72 } 63 }
73 64
74 bool get isEmpty => isEmptyOrNull && !isNullable; 65 bool get isEmpty => isEmptyOrNull && !isNullable;
75 bool get isNull => isEmptyOrNull && isNullable; 66 bool get isNull => isEmptyOrNull && isNullable;
76 bool get isEmptyOrNull => (flags >> 1) == EMPTY; 67 bool get isEmptyOrNull => (flags >> 1) == EMPTY;
77 bool get isExact => (flags >> 1) == EXACT; 68 bool get isExact => (flags >> 1) == EXACT;
78 bool get isNullable => (flags & 1) != 0; 69 bool get isNullable => (flags & 1) != 0;
79 70
80 bool get isUnion => false; 71 bool get isUnion => false;
81 bool get isContainer => false; 72 bool get isContainer => false;
82 bool get isMap => false; 73 bool get isMap => false;
83 bool get isDictionary => false; 74 bool get isDictionary => false;
84 bool get isForwarding => false; 75 bool get isForwarding => false;
85 bool get isValue => false; 76 bool get isValue => false;
86 77
87 // TODO(kasperl): Get rid of these. They should not be a visible 78 // TODO(kasperl): Get rid of these. They should not be a visible
88 // part of the implementation because they make it hard to add 79 // part of the implementation because they make it hard to add
89 // proper union types if we ever want to. 80 // proper union types if we ever want to.
90 bool get isSubclass => (flags >> 1) == SUBCLASS; 81 bool get isSubclass => (flags >> 1) == SUBCLASS;
91 bool get isSubtype => (flags >> 1) == SUBTYPE; 82 bool get isSubtype => (flags >> 1) == SUBTYPE;
92 83
93 TypeMask nullable() { 84 TypeMask nullable() {
94 return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); 85 return isNullable ? this : new FlatTypeMask.internal(base, flags | 1);
95 } 86 }
96 87
97 TypeMask nonNullable() { 88 TypeMask nonNullable() {
98 return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; 89 return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this;
99 } 90 }
100 91
101 bool contains(ClassElement type, ClosedWorld closedWorld) { 92 bool contains(Entity other, ClosedWorld closedWorld) {
102 assert(type.isDeclaration); 93 assert(_validateBase(other));
103 if (isEmptyOrNull) { 94 if (isEmptyOrNull) {
104 return false; 95 return false;
105 } else if (identical(base, type)) { 96 } else if (identical(base, other)) {
106 return true; 97 return true;
107 } else if (isExact) { 98 } else if (isExact) {
108 return false; 99 return false;
109 } else if (isSubclass) { 100 } else if (isSubclass) {
110 return closedWorld.isSubclassOf(type, base); 101 return closedWorld.isSubclassOf(other, base);
111 } else { 102 } else {
112 assert(isSubtype); 103 assert(isSubtype);
113 return closedWorld.isSubtypeOf(type, base); 104 return closedWorld.isSubtypeOf(other, base);
114 } 105 }
115 } 106 }
116 107
117 bool isSingleImplementationOf(ClassElement cls, ClosedWorld closedWorld) { 108 bool isSingleImplementationOf(Entity cls, ClosedWorld closedWorld) {
118 // Special case basic types so that, for example, JSString is the 109 // Special case basic types so that, for example, JSString is the
119 // single implementation of String. 110 // single implementation of String.
120 // The general optimization is to realize there is only one class that 111 // The general optimization is to realize there is only one class that
121 // implements [base] and [base] is not instantiated. We however do 112 // implements [base] and [base] is not instantiated. We however do
122 // not track correctly the list of truly instantiated classes. 113 // not track correctly the list of truly instantiated classes.
123 BackendClasses backendClasses = closedWorld.backendClasses; 114 BackendClasses backendClasses = closedWorld.backendClasses;
124 if (containsOnlyString(closedWorld)) { 115 if (containsOnlyString(closedWorld)) {
125 return cls == closedWorld.coreClasses.stringClass || 116 return cls == closedWorld.coreClasses.stringClass ||
126 cls == backendClasses.stringImplementation; 117 cls == backendClasses.stringImplementation;
127 } 118 }
(...skipping 19 matching lines...) Expand all
147 if (isEmptyOrNull) return isNullable ? other.isNullable : true; 138 if (isEmptyOrNull) return isNullable ? other.isNullable : true;
148 // The empty type contains no classes. 139 // The empty type contains no classes.
149 if (other.isEmptyOrNull) return false; 140 if (other.isEmptyOrNull) return false;
150 // Quick check whether to handle null. 141 // Quick check whether to handle null.
151 if (isNullable && !other.isNullable) return false; 142 if (isNullable && !other.isNullable) return false;
152 other = TypeMask.nonForwardingMask(other); 143 other = TypeMask.nonForwardingMask(other);
153 // If other is union, delegate to UnionTypeMask.containsMask. 144 // If other is union, delegate to UnionTypeMask.containsMask.
154 if (other is! FlatTypeMask) return other.containsMask(this, closedWorld); 145 if (other is! FlatTypeMask) return other.containsMask(this, closedWorld);
155 // The other must be flat, so compare base and flags. 146 // The other must be flat, so compare base and flags.
156 FlatTypeMask flatOther = other; 147 FlatTypeMask flatOther = other;
157 ClassElement otherBase = flatOther.base; 148 Entity otherBase = flatOther.base;
158 // If other is exact, it only contains its base. 149 // If other is exact, it only contains its base.
159 // TODO(herhut): Get rid of isSingleImplementationOf. 150 // TODO(herhut): Get rid of isSingleImplementationOf.
160 if (flatOther.isExact) { 151 if (flatOther.isExact) {
161 return (isExact && base == otherBase) || 152 return (isExact && base == otherBase) ||
162 isSingleImplementationOf(otherBase, closedWorld); 153 isSingleImplementationOf(otherBase, closedWorld);
163 } 154 }
164 // If other is subclass, this has to be subclass, as well. Unless 155 // If other is subclass, this has to be subclass, as well. Unless
165 // flatOther.base covers all subtypes of this. Currently, we only 156 // flatOther.base covers all subtypes of this. Currently, we only
166 // consider object to behave that way. 157 // consider object to behave that way.
167 // TODO(herhut): Add check whether flatOther.base is superclass of 158 // TODO(herhut): Add check whether flatOther.base is superclass of
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 return base == closedWorld.coreClasses.boolClass || 198 return base == closedWorld.coreClasses.boolClass ||
208 base == backendClasses.boolImplementation; 199 base == backendClasses.boolImplementation;
209 } 200 }
210 201
211 bool containsOnlyString(ClosedWorld closedWorld) { 202 bool containsOnlyString(ClosedWorld closedWorld) {
212 BackendClasses backendClasses = closedWorld.backendClasses; 203 BackendClasses backendClasses = closedWorld.backendClasses;
213 return base == closedWorld.coreClasses.stringClass || 204 return base == closedWorld.coreClasses.stringClass ||
214 base == backendClasses.stringImplementation; 205 base == backendClasses.stringImplementation;
215 } 206 }
216 207
217 bool containsOnly(ClassElement cls) { 208 bool containsOnly(Entity cls) {
218 assert(cls.isDeclaration); 209 assert(_validateBase(cls));
219 return base == cls; 210 return base == cls;
220 } 211 }
221 212
222 bool satisfies(ClassElement cls, ClosedWorld closedWorld) { 213 bool satisfies(Entity cls, ClosedWorld closedWorld) {
223 assert(cls.isDeclaration); 214 assert(_validateBase(cls));
224 if (isEmptyOrNull) return false; 215 if (isEmptyOrNull) return false;
225 if (closedWorld.isSubtypeOf(base, cls)) return true; 216 if (closedWorld.isSubtypeOf(base, cls)) return true;
226 return false; 217 return false;
227 } 218 }
228 219
229 /** 220 /// Returns the [Entity] if this type represents a single class, otherwise
230 * Returns the [ClassElement] if this type represents a single class, 221 /// returns `null`. This method is conservative.
231 * otherwise returns `null`. This method is conservative. 222 Entity singleClass(ClosedWorld closedWorld) {
232 */
233 ClassElement singleClass(ClosedWorld closedWorld) {
234 if (isEmptyOrNull) return null; 223 if (isEmptyOrNull) return null;
235 if (isNullable) return null; // It is Null and some other class. 224 if (isNullable) return null; // It is Null and some other class.
236 if (isExact) { 225 if (isExact) {
237 return base; 226 return base;
238 } else if (isSubclass) { 227 } else if (isSubclass) {
239 return closedWorld.hasAnyStrictSubclass(base) ? null : base; 228 return closedWorld.hasAnyStrictSubclass(base) ? null : base;
240 } else { 229 } else {
241 assert(isSubtype); 230 assert(isSubtype);
242 return null; 231 return null;
243 } 232 }
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
472 TypeMask intersectionHelper(FlatTypeMask other, ClosedWorld closedWorld) { 461 TypeMask intersectionHelper(FlatTypeMask other, ClosedWorld closedWorld) {
473 assert(base != other.base); 462 assert(base != other.base);
474 assert(!closedWorld.isSubclassOf(base, other.base)); 463 assert(!closedWorld.isSubclassOf(base, other.base));
475 assert(!closedWorld.isSubclassOf(other.base, base)); 464 assert(!closedWorld.isSubclassOf(other.base, base));
476 // If one of the masks are exact or if both of them are subclass 465 // If one of the masks are exact or if both of them are subclass
477 // masks, then the intersection is empty. 466 // masks, then the intersection is empty.
478 if (isExact || other.isExact) return intersectionEmpty(other); 467 if (isExact || other.isExact) return intersectionEmpty(other);
479 if (isSubclass && other.isSubclass) return intersectionEmpty(other); 468 if (isSubclass && other.isSubclass) return intersectionEmpty(other);
480 assert(isSubtype || other.isSubtype); 469 assert(isSubtype || other.isSubtype);
481 int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; 470 int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE;
471 // TODO(johnniwinther): Move this computation to [ClosedWorld].
482 // Compute the set of classes that are contained in both type masks. 472 // Compute the set of classes that are contained in both type masks.
483 Set<ClassElement> common = commonContainedClasses(this, other, closedWorld); 473 Set<ClassElement> common = commonContainedClasses(this, other, closedWorld);
484 if (common == null || common.isEmpty) return intersectionEmpty(other); 474 if (common == null || common.isEmpty) return intersectionEmpty(other);
485 // Narrow down the candidates by only looking at common classes 475 // Narrow down the candidates by only looking at common classes
486 // that do not have a superclass or supertype that will be a 476 // that do not have a superclass or supertype that will be a
487 // better candidate. 477 // better candidate.
488 Iterable<ClassElement> candidates = common.where((ClassElement each) { 478 Iterable<ClassElement> candidates = common.where((ClassElement each) {
489 bool containsSuperclass = common.contains(each.supertype.element); 479 bool containsSuperclass = common.contains(each.supertype.element);
490 // If the superclass is also a candidate, then we don't want to 480 // If the superclass is also a candidate, then we don't want to
491 // deal with this class. If we're only looking for a subclass we 481 // deal with this class. If we're only looking for a subclass we
492 // know we don't have to look at the list of interfaces because 482 // know we don't have to look at the list of interfaces because
493 // they can never be in the common set. 483 // they can never be in the common set.
494 if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; 484 if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass;
495 // Run through the direct supertypes of the class. If the common 485 // Run through the direct supertypes of the class. If the common
496 // set contains the direct supertype of the class, we ignore the 486 // set contains the direct supertype of the class, we ignore the
497 // the class because the supertype is a better candidate. 487 // the class because the supertype is a better candidate.
498 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { 488 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) {
499 if (common.contains(link.head.element)) return false; 489 if (common.contains(link.head.element)) return false;
500 } 490 }
501 return true; 491 return true;
502 }); 492 });
503 // Run through the list of candidates and compute the union. The 493 // Run through the list of candidates and compute the union. The
504 // result will only be nullable if both masks are nullable. We have 494 // result will only be nullable if both masks are nullable. We have
505 // to normalize here, as we generate types based on new base classes. 495 // to normalize here, as we generate types based on new base classes.
506 int combined = (kind << 1) | (flags & other.flags & 1); 496 int combined = (kind << 1) | (flags & other.flags & 1);
507 Iterable<TypeMask> masks = candidates.map((ClassElement cls) { 497 Iterable<TypeMask> masks = candidates.map((Entity cls) {
508 return new FlatTypeMask.normalized(cls, combined, closedWorld); 498 return new FlatTypeMask.normalized(cls, combined, closedWorld);
509 }); 499 });
510 return UnionTypeMask.unionOf(masks, closedWorld); 500 return UnionTypeMask.unionOf(masks, closedWorld);
511 } 501 }
512 502
513 TypeMask intersectionEmpty(FlatTypeMask other) { 503 TypeMask intersectionEmpty(FlatTypeMask other) {
514 return isNullable && other.isNullable 504 return isNullable && other.isNullable
515 ? new TypeMask.empty() 505 ? new TypeMask.empty()
516 : new TypeMask.nonNullEmpty(); 506 : new TypeMask.nonNullEmpty();
517 } 507 }
518 508
519 /** 509 /**
520 * Returns whether [element] will be the one used at runtime when being
521 * invoked on an instance of [cls]. [selector] is used to ensure library
522 * privacy is taken into account.
523 */
524 static bool hasElementIn(
525 ClassElement cls, Selector selector, Element element) {
526 // Use [:implementation:] of [element]
527 // because our function set only stores declarations.
528 Element result = findMatchIn(cls, selector);
529 return result == null
530 ? false
531 : result.implementation == element.implementation;
532 }
533
534 static Element findMatchIn(ClassElement cls, Selector selector) {
535 // Use the [:implementation] of [cls] in case the found [element]
536 // is in the patch class.
537 return cls.implementation.lookupByName(selector.memberName);
538 }
539
540 /**
541 * Returns whether [element] is a potential target when being 510 * Returns whether [element] is a potential target when being
542 * invoked on this type mask. [selector] is used to ensure library 511 * invoked on this type mask. [selector] is used to ensure library
543 * privacy is taken into account. 512 * privacy is taken into account.
544 */ 513 */
545 bool canHit(Element element, Selector selector, ClosedWorld closedWorld) { 514 bool canHit(Element element, Selector selector, ClosedWorld closedWorld) {
546 BackendClasses backendClasses = closedWorld.backendClasses; 515 BackendClasses backendClasses = closedWorld.backendClasses;
547 assert(element.name == selector.name); 516 assert(element.name == selector.name);
548 if (isEmpty) return false; 517 if (isEmpty) return false;
549 if (isNull) { 518 if (isNull) {
550 return hasElementIn(backendClasses.nullImplementation, selector, element); 519 return closedWorld.hasElementIn(
520 backendClasses.nullImplementation, selector, element);
551 } 521 }
552 522
553 // TODO(kasperl): Can't we just avoid creating typed selectors 523 // TODO(kasperl): Can't we just avoid creating typed selectors
554 // based of function types? 524 // based of function types?
555 Element self = base; 525 Element self = base;
556 if (self.isTypedef) { 526 if (self.isTypedef) {
557 // A typedef is a function type that doesn't have any 527 // A typedef is a function type that doesn't have any
558 // user-defined members. 528 // user-defined members.
559 return false; 529 return false;
560 } 530 }
561 531
562 ClassElement other = element.enclosingClass; 532 ClassElement other = element.enclosingClass;
563 if (other == backendClasses.nullImplementation) { 533 if (other == backendClasses.nullImplementation) {
564 return isNullable; 534 return isNullable;
565 } else if (isExact) { 535 } else if (isExact) {
566 return hasElementIn(self, selector, element); 536 return closedWorld.hasElementIn(self, selector, element);
567 } else if (isSubclass) { 537 } else if (isSubclass) {
568 return hasElementIn(self, selector, element) || 538 return closedWorld.hasElementIn(self, selector, element) ||
569 other.isSubclassOf(self) || 539 other.isSubclassOf(self) ||
570 closedWorld.hasAnySubclassThatMixes(self, other); 540 closedWorld.hasAnySubclassThatMixes(self, other);
571 } else { 541 } else {
572 assert(isSubtype); 542 assert(isSubtype);
573 bool result = hasElementIn(self, selector, element) || 543 bool result = closedWorld.hasElementIn(self, selector, element) ||
574 other.implementsInterface(self) || 544 other.implementsInterface(self) ||
575 closedWorld.hasAnySubclassThatImplements(other, base) || 545 closedWorld.hasAnySubclassThatImplements(other, base) ||
576 closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base); 546 closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base);
577 if (result) return true; 547 if (result) return true;
578 // If the class is used as a mixin, we have to check if the element 548 // If the class is used as a mixin, we have to check if the element
579 // can be hit from any of the mixin applications. 549 // can be hit from any of the mixin applications.
580 Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self); 550 Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self);
581 return mixinUses.any((mixinApplication) => 551 return mixinUses.any((mixinApplication) =>
582 hasElementIn(mixinApplication, selector, element) || 552 closedWorld.hasElementIn(mixinApplication, selector, element) ||
583 other.isSubclassOf(mixinApplication) || 553 other.isSubclassOf(mixinApplication) ||
584 closedWorld.hasAnySubclassThatMixes(mixinApplication, other)); 554 closedWorld.hasAnySubclassThatMixes(mixinApplication, other));
585 } 555 }
586 } 556 }
587 557
588 /**
589 * Returns whether a [selector] call on an instance of [cls]
590 * will hit a method at runtime, and not go through [noSuchMethod].
591 */
592 static bool hasConcreteMatch(
593 ClassElement cls, Selector selector, ClosedWorld world) {
594 assert(invariant(cls, world.isInstantiated(cls),
595 message: '$cls has not been instantiated.'));
596 Element element = findMatchIn(cls, selector);
597 if (element == null) return false;
598
599 if (element.isAbstract) {
600 ClassElement enclosingClass = element.enclosingClass;
601 return hasConcreteMatch(enclosingClass.superclass, selector, world);
602 }
603 return selector.appliesUntyped(element);
604 }
605
606 bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) { 558 bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) {
607 // A call on an empty type mask is either dead code, or a call on 559 // A call on an empty type mask is either dead code, or a call on
608 // `null`. 560 // `null`.
609 if (isEmptyOrNull) return false; 561 if (isEmptyOrNull) return false;
610 // A call on an exact mask for an abstract class is dead code. 562 // A call on an exact mask for an abstract class is dead code.
611 if (isExact && base.isAbstract) return false; 563 // TODO(johnniwinther): A type mask cannot be abstract. Remove the need
612 // If the receiver is guaranteed to have a member that 564 // for this noise (currently used for super-calls in inference and mirror
613 // matches what we're looking for, there's no need to 565 // usage).
614 // introduce a noSuchMethod handler. It will never be called. 566 if (isExact && closedWorld.isAbstract(base)) return false;
615 //
616 // As an example, consider this class hierarchy:
617 //
618 // A <-- noSuchMethod
619 // / \
620 // C B <-- foo
621 //
622 // If we know we're calling foo on an object of type B we
623 // don't have to worry about the noSuchMethod method in A
624 // because objects of type B implement foo. On the other hand,
625 // if we end up calling foo on something of type C we have to
626 // add a handler for it.
627 567
628 // If the holders of all user-defined noSuchMethod 568 return closedWorld.needsNoSuchMethod(
629 // implementations that might be applicable to the receiver 569 base,
630 // type have a matching member for the current name and 570 selector,
631 // selector, we avoid introducing a noSuchMethod handler. 571 isExact
632 // 572 ? ClassQuery.EXACT
633 // As an example, consider this class hierarchy: 573 : (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE));
634 //
635 // A <-- foo
636 // / \
637 // noSuchMethod --> B C <-- bar
638 // | |
639 // C D <-- noSuchMethod
640 //
641 // When calling foo on an object of type A, we know that the
642 // implementations of noSuchMethod are in the classes B and D
643 // that also (indirectly) implement foo, so we do not need a
644 // handler for it.
645 //
646 // If we're calling bar on an object of type D, we don't need
647 // the handler either because all objects of type D implement
648 // bar through inheritance.
649 //
650 // If we're calling bar on an object of type A we do need the
651 // handler because we may have to call B.noSuchMethod since B
652 // does not implement bar.
653
654 /// Returns `true` if [cls] is an instantiated class that does not have
655 /// a concrete method matching [selector].
656 bool needsNoSuchMethod(ClassElement cls) {
657 // We can skip uninstantiated subclasses.
658 // TODO(johnniwinther): Put filtering into the (Class)World.
659 if (!closedWorld.isInstantiated(cls)) {
660 return false;
661 }
662 // We can just skip abstract classes because we know no
663 // instance of them will be created at runtime, and
664 // therefore there is no instance that will require
665 // [noSuchMethod] handling.
666 return !cls.isAbstract && !hasConcreteMatch(cls, selector, closedWorld);
667 }
668
669 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base);
670 if (isExact || baseNeedsNoSuchMethod) {
671 return baseNeedsNoSuchMethod;
672 }
673
674 Iterable<ClassElement> subclassesToCheck;
675 if (isSubtype) {
676 subclassesToCheck = closedWorld.strictSubtypesOf(base);
677 } else {
678 assert(isSubclass);
679 subclassesToCheck = closedWorld.strictSubclassesOf(base);
680 }
681
682 return subclassesToCheck != null &&
683 subclassesToCheck.any(needsNoSuchMethod);
684 } 574 }
685 575
686 Element locateSingleElement(Selector selector, Compiler compiler) { 576 Element locateSingleElement(Selector selector, Compiler compiler) {
687 if (isEmptyOrNull) return null; 577 if (isEmptyOrNull) return null;
688 Iterable<Element> targets = 578 Iterable<Element> targets =
689 compiler.closedWorld.allFunctions.filter(selector, this); 579 compiler.closedWorld.allFunctions.filter(selector, this);
690 if (targets.length != 1) return null; 580 if (targets.length != 1) return null;
691 Element result = targets.first; 581 Element result = targets.first;
692 ClassElement enclosing = result.enclosingClass; 582 ClassElement enclosing = result.enclosingClass.declaration;
693 // We only return the found element if it is guaranteed to be implemented on 583 // We only return the found element if it is guaranteed to be implemented on
694 // all classes in the receiver type [this]. It could be found only in a 584 // all classes in the receiver type [this]. It could be found only in a
695 // subclass or in an inheritance-wise unrelated class in case of subtype 585 // subclass or in an inheritance-wise unrelated class in case of subtype
696 // selectors. 586 // selectors.
697 ClosedWorld closedWorld = compiler.closedWorld; 587 ClosedWorld closedWorld = compiler.closedWorld;
698 if (isSubtype) { 588 if (isSubtype) {
699 // if (closedWorld.isUsedAsMixin(enclosing)) { 589 // if (closedWorld.isUsedAsMixin(enclosing)) {
700 if (closedWorld.everySubtypeIsSubclassOfOrMixinUseOf(base, enclosing)) { 590 if (closedWorld.everySubtypeIsSubclassOfOrMixinUseOf(base, enclosing)) {
701 return result; 591 return result;
702 } 592 }
703 //} 593 //}
704 return null; 594 return null;
705 } else { 595 } else {
706 if (base.isSubclassOf(enclosing)) return result; 596 if (closedWorld.isSubclassOf(base, enclosing)) return result;
707 if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result; 597 if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result;
708 } 598 }
709 return null; 599 return null;
710 } 600 }
711 601
712 bool operator ==(var other) { 602 bool operator ==(var other) {
713 if (identical(this, other)) return true; 603 if (identical(this, other)) return true;
714 if (other is! FlatTypeMask) return false; 604 if (other is! FlatTypeMask) return false;
715 FlatTypeMask otherMask = other; 605 FlatTypeMask otherMask = other;
716 return (flags == otherMask.flags) && (base == otherMask.base); 606 return (flags == otherMask.flags) && (base == otherMask.base);
(...skipping 29 matching lines...) Expand all
746 if (x.isExact) { 636 if (x.isExact) {
747 return null; 637 return null;
748 } else if (x.isSubclass) { 638 } else if (x.isSubclass) {
749 return closedWorld.strictSubclassesOf(element); 639 return closedWorld.strictSubclassesOf(element);
750 } else { 640 } else {
751 assert(x.isSubtype); 641 assert(x.isSubtype);
752 return closedWorld.strictSubtypesOf(element); 642 return closedWorld.strictSubtypesOf(element);
753 } 643 }
754 } 644 }
755 } 645 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/types/forwarding_type_mask.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698