OLD | NEW |
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 types; | 5 part of types; |
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 ClassElement base; |
18 final int flags; | 18 final int flags; |
19 | 19 |
20 FlatTypeMask(ClassElement base, int kind, bool isNullable) | 20 FlatTypeMask(ClassElement 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) | 23 FlatTypeMask.exact(ClassElement base) : this.internal(base, (EXACT << 1) | 1); |
24 : this.internal(base, (EXACT << 1) | 1); | |
25 FlatTypeMask.subclass(ClassElement base) | 24 FlatTypeMask.subclass(ClassElement base) |
26 : this.internal(base, (SUBCLASS << 1) | 1); | 25 : this.internal(base, (SUBCLASS << 1) | 1); |
27 FlatTypeMask.subtype(ClassElement base) | 26 FlatTypeMask.subtype(ClassElement base) |
28 : this.internal(base, (SUBTYPE << 1) | 1); | 27 : this.internal(base, (SUBTYPE << 1) | 1); |
29 | 28 |
30 const FlatTypeMask.nonNullEmpty(): base = null, flags = 0; | 29 const FlatTypeMask.nonNullEmpty() |
31 const FlatTypeMask.empty() : base = null, flags = 1; | 30 : base = null, |
| 31 flags = 0; |
| 32 const FlatTypeMask.empty() |
| 33 : base = null, |
| 34 flags = 1; |
32 | 35 |
33 FlatTypeMask.nonNullExact(ClassElement base) | 36 FlatTypeMask.nonNullExact(ClassElement base) |
34 : this.internal(base, EXACT << 1); | 37 : this.internal(base, EXACT << 1); |
35 FlatTypeMask.nonNullSubclass(ClassElement base) | 38 FlatTypeMask.nonNullSubclass(ClassElement base) |
36 : this.internal(base, SUBCLASS << 1); | 39 : this.internal(base, SUBCLASS << 1); |
37 FlatTypeMask.nonNullSubtype(ClassElement base) | 40 FlatTypeMask.nonNullSubtype(ClassElement base) |
38 : this.internal(base, SUBTYPE << 1); | 41 : this.internal(base, SUBTYPE << 1); |
39 | 42 |
40 FlatTypeMask.internal(this.base, this.flags) { | 43 FlatTypeMask.internal(this.base, this.flags) { |
41 assert(base == null || base.isDeclaration); | 44 assert(base == null || base.isDeclaration); |
(...skipping 11 matching lines...) Expand all Loading... |
53 if (!world.hasAnyStrictSubtype(base) || world.hasOnlySubclasses(base)) { | 56 if (!world.hasAnyStrictSubtype(base) || world.hasOnlySubclasses(base)) { |
54 flags = (flags & 0x1) | (SUBCLASS << 1); | 57 flags = (flags & 0x1) | (SUBCLASS << 1); |
55 } | 58 } |
56 } | 59 } |
57 if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) { | 60 if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) { |
58 flags = (flags & 0x1) | (EXACT << 1); | 61 flags = (flags & 0x1) | (EXACT << 1); |
59 } | 62 } |
60 Map<ClassElement, TypeMask> cachedMasks = | 63 Map<ClassElement, TypeMask> cachedMasks = |
61 world.canonicalizedTypeMasks[flags]; | 64 world.canonicalizedTypeMasks[flags]; |
62 if (cachedMasks == null) { | 65 if (cachedMasks == null) { |
63 world.canonicalizedTypeMasks[flags] = cachedMasks = | 66 world.canonicalizedTypeMasks[flags] = |
64 <ClassElement, TypeMask>{}; | 67 cachedMasks = <ClassElement, TypeMask>{}; |
65 } | 68 } |
66 return cachedMasks.putIfAbsent(base, | 69 return cachedMasks.putIfAbsent( |
67 () => new FlatTypeMask.internal(base, flags)); | 70 base, () => new FlatTypeMask.internal(base, flags)); |
68 } | 71 } |
69 | 72 |
70 bool get isEmpty => isEmptyOrNull && !isNullable; | 73 bool get isEmpty => isEmptyOrNull && !isNullable; |
71 bool get isNull => isEmptyOrNull && isNullable; | 74 bool get isNull => isEmptyOrNull && isNullable; |
72 bool get isEmptyOrNull => (flags >> 1) == EMPTY; | 75 bool get isEmptyOrNull => (flags >> 1) == EMPTY; |
73 bool get isExact => (flags >> 1) == EXACT; | 76 bool get isExact => (flags >> 1) == EXACT; |
74 bool get isNullable => (flags & 1) != 0; | 77 bool get isNullable => (flags & 1) != 0; |
75 | 78 |
76 bool get isUnion => false; | 79 bool get isUnion => false; |
77 bool get isContainer => false; | 80 bool get isContainer => false; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
112 | 115 |
113 bool isSingleImplementationOf(ClassElement cls, ClassWorld classWorld) { | 116 bool isSingleImplementationOf(ClassElement cls, ClassWorld classWorld) { |
114 // Special case basic types so that, for example, JSString is the | 117 // Special case basic types so that, for example, JSString is the |
115 // single implementation of String. | 118 // single implementation of String. |
116 // The general optimization is to realize there is only one class that | 119 // The general optimization is to realize there is only one class that |
117 // implements [base] and [base] is not instantiated. We however do | 120 // implements [base] and [base] is not instantiated. We however do |
118 // not track correctly the list of truly instantiated classes. | 121 // not track correctly the list of truly instantiated classes. |
119 Backend backend = classWorld.backend; | 122 Backend backend = classWorld.backend; |
120 if (containsOnlyString(classWorld)) { | 123 if (containsOnlyString(classWorld)) { |
121 return cls == classWorld.stringClass || | 124 return cls == classWorld.stringClass || |
122 cls == backend.stringImplementation; | 125 cls == backend.stringImplementation; |
123 } | 126 } |
124 if (containsOnlyBool(classWorld)) { | 127 if (containsOnlyBool(classWorld)) { |
125 return cls == classWorld.boolClass || cls == backend.boolImplementation; | 128 return cls == classWorld.boolClass || cls == backend.boolImplementation; |
126 } | 129 } |
127 if (containsOnlyInt(classWorld)) { | 130 if (containsOnlyInt(classWorld)) { |
128 return cls == classWorld.intClass | 131 return cls == classWorld.intClass || |
129 || cls == backend.intImplementation | 132 cls == backend.intImplementation || |
130 || cls == backend.positiveIntImplementation | 133 cls == backend.positiveIntImplementation || |
131 || cls == backend.uint32Implementation | 134 cls == backend.uint32Implementation || |
132 || cls == backend.uint31Implementation; | 135 cls == backend.uint31Implementation; |
133 } | 136 } |
134 if (containsOnlyDouble(classWorld)) { | 137 if (containsOnlyDouble(classWorld)) { |
135 return cls == classWorld.doubleClass | 138 return cls == classWorld.doubleClass || |
136 || cls == backend.doubleImplementation; | 139 cls == backend.doubleImplementation; |
137 } | 140 } |
138 return false; | 141 return false; |
139 } | 142 } |
140 | 143 |
141 bool isInMask(TypeMask other, ClassWorld classWorld) { | 144 bool isInMask(TypeMask other, ClassWorld classWorld) { |
142 if (isEmptyOrNull) return isNullable ? other.isNullable : true; | 145 if (isEmptyOrNull) return isNullable ? other.isNullable : true; |
143 // The empty type contains no classes. | 146 // The empty type contains no classes. |
144 if (other.isEmptyOrNull) return false; | 147 if (other.isEmptyOrNull) return false; |
145 // Quick check whether to handle null. | 148 // Quick check whether to handle null. |
146 if (isNullable && !other.isNullable) return false; | 149 if (isNullable && !other.isNullable) return false; |
147 other = TypeMask.nonForwardingMask(other); | 150 other = TypeMask.nonForwardingMask(other); |
148 // If other is union, delegate to UnionTypeMask.containsMask. | 151 // If other is union, delegate to UnionTypeMask.containsMask. |
149 if (other is! FlatTypeMask) return other.containsMask(this, classWorld); | 152 if (other is! FlatTypeMask) return other.containsMask(this, classWorld); |
150 // The other must be flat, so compare base and flags. | 153 // The other must be flat, so compare base and flags. |
151 FlatTypeMask flatOther = other; | 154 FlatTypeMask flatOther = other; |
152 ClassElement otherBase = flatOther.base; | 155 ClassElement otherBase = flatOther.base; |
153 // If other is exact, it only contains its base. | 156 // If other is exact, it only contains its base. |
154 // TODO(herhut): Get rid of isSingleImplementationOf. | 157 // TODO(herhut): Get rid of isSingleImplementationOf. |
155 if (flatOther.isExact) { | 158 if (flatOther.isExact) { |
156 return (isExact && base == otherBase) | 159 return (isExact && base == otherBase) || |
157 || isSingleImplementationOf(otherBase, classWorld); | 160 isSingleImplementationOf(otherBase, classWorld); |
158 } | 161 } |
159 // If other is subclass, this has to be subclass, as well. Unless | 162 // If other is subclass, this has to be subclass, as well. Unless |
160 // flatOther.base covers all subtypes of this. Currently, we only | 163 // flatOther.base covers all subtypes of this. Currently, we only |
161 // consider object to behave that way. | 164 // consider object to behave that way. |
162 // TODO(herhut): Add check whether flatOther.base is superclass of | 165 // TODO(herhut): Add check whether flatOther.base is superclass of |
163 // all subclasses of this.base. | 166 // all subclasses of this.base. |
164 if (flatOther.isSubclass) { | 167 if (flatOther.isSubclass) { |
165 if (isSubtype) return (otherBase == classWorld.objectClass); | 168 if (isSubtype) return (otherBase == classWorld.objectClass); |
166 return classWorld.isSubclassOf(base, otherBase); | 169 return classWorld.isSubclassOf(base, otherBase); |
167 } | 170 } |
168 assert(flatOther.isSubtype); | 171 assert(flatOther.isSubtype); |
169 // Check whether this TypeMask satisfies otherBase's interface. | 172 // Check whether this TypeMask satisfies otherBase's interface. |
170 return satisfies(otherBase, classWorld); | 173 return satisfies(otherBase, classWorld); |
171 } | 174 } |
172 | 175 |
173 bool containsMask(TypeMask other, ClassWorld classWorld) { | 176 bool containsMask(TypeMask other, ClassWorld classWorld) { |
174 return other.isInMask(this, classWorld); | 177 return other.isInMask(this, classWorld); |
175 } | 178 } |
176 | 179 |
177 bool containsOnlyInt(ClassWorld classWorld) { | 180 bool containsOnlyInt(ClassWorld classWorld) { |
178 Backend backend = classWorld.backend; | 181 Backend backend = classWorld.backend; |
179 return base == classWorld.intClass | 182 return base == classWorld.intClass || |
180 || base == backend.intImplementation | 183 base == backend.intImplementation || |
181 || base == backend.positiveIntImplementation | 184 base == backend.positiveIntImplementation || |
182 || base == backend.uint31Implementation | 185 base == backend.uint31Implementation || |
183 || base == backend.uint32Implementation; | 186 base == backend.uint32Implementation; |
184 } | 187 } |
185 | 188 |
186 bool containsOnlyDouble(ClassWorld classWorld) { | 189 bool containsOnlyDouble(ClassWorld classWorld) { |
187 Backend backend = classWorld.backend; | 190 Backend backend = classWorld.backend; |
188 return base == classWorld.doubleClass | 191 return base == classWorld.doubleClass || |
189 || base == backend.doubleImplementation; | 192 base == backend.doubleImplementation; |
190 } | 193 } |
191 | 194 |
192 bool containsOnlyNum(ClassWorld classWorld) { | 195 bool containsOnlyNum(ClassWorld classWorld) { |
193 Backend backend = classWorld.backend; | 196 Backend backend = classWorld.backend; |
194 return containsOnlyInt(classWorld) | 197 return containsOnlyInt(classWorld) || |
195 || containsOnlyDouble(classWorld) | 198 containsOnlyDouble(classWorld) || |
196 || base == classWorld.numClass | 199 base == classWorld.numClass || |
197 || base == backend.numImplementation; | 200 base == backend.numImplementation; |
198 } | 201 } |
199 | 202 |
200 bool containsOnlyBool(ClassWorld classWorld) { | 203 bool containsOnlyBool(ClassWorld classWorld) { |
201 Backend backend = classWorld.backend; | 204 Backend backend = classWorld.backend; |
202 return base == classWorld.boolClass | 205 return base == classWorld.boolClass || base == backend.boolImplementation; |
203 || base == backend.boolImplementation; | |
204 } | 206 } |
205 | 207 |
206 bool containsOnlyString(ClassWorld classWorld) { | 208 bool containsOnlyString(ClassWorld classWorld) { |
207 Backend backend = classWorld.backend; | 209 Backend backend = classWorld.backend; |
208 return base == classWorld.stringClass | 210 return base == classWorld.stringClass || |
209 || base == backend.stringImplementation; | 211 base == backend.stringImplementation; |
210 } | 212 } |
211 | 213 |
212 bool containsOnly(ClassElement cls) { | 214 bool containsOnly(ClassElement cls) { |
213 assert(cls.isDeclaration); | 215 assert(cls.isDeclaration); |
214 return base == cls; | 216 return base == cls; |
215 } | 217 } |
216 | 218 |
217 bool satisfies(ClassElement cls, ClassWorld classWorld) { | 219 bool satisfies(ClassElement cls, ClassWorld classWorld) { |
218 assert(cls.isDeclaration); | 220 assert(cls.isDeclaration); |
219 if (isEmptyOrNull) return false; | 221 if (isEmptyOrNull) return false; |
220 if (classWorld.isSubtypeOf(base, cls)) return true; | 222 if (classWorld.isSubtypeOf(base, cls)) return true; |
221 return false; | 223 return false; |
222 } | 224 } |
223 | 225 |
224 /** | 226 /** |
225 * Returns the [ClassElement] if this type represents a single class, | 227 * Returns the [ClassElement] if this type represents a single class, |
226 * otherwise returns `null`. This method is conservative. | 228 * otherwise returns `null`. This method is conservative. |
227 */ | 229 */ |
228 ClassElement singleClass(ClassWorld classWorld) { | 230 ClassElement singleClass(ClassWorld classWorld) { |
229 if (isEmptyOrNull) return null; | 231 if (isEmptyOrNull) return null; |
230 if (isNullable) return null; // It is Null and some other class. | 232 if (isNullable) return null; // It is Null and some other class. |
231 if (isExact) { | 233 if (isExact) { |
232 return base; | 234 return base; |
233 } else if (isSubclass) { | 235 } else if (isSubclass) { |
234 return classWorld.hasAnyStrictSubclass(base) ? null : base; | 236 return classWorld.hasAnyStrictSubclass(base) ? null : base; |
235 } else { | 237 } else { |
236 assert(isSubtype); | 238 assert(isSubtype); |
237 return null; | 239 return null; |
238 } | 240 } |
239 } | 241 } |
240 | 242 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 } | 387 } |
386 | 388 |
387 static bool _isDisjointHelper( | 389 static bool _isDisjointHelper( |
388 FlatTypeMask a, FlatTypeMask b, ClassWorld classWorld) { | 390 FlatTypeMask a, FlatTypeMask b, ClassWorld classWorld) { |
389 if (!a.isSubclass && b.isSubclass) { | 391 if (!a.isSubclass && b.isSubclass) { |
390 return _isDisjointHelper(b, a, classWorld); | 392 return _isDisjointHelper(b, a, classWorld); |
391 } | 393 } |
392 assert(a.isSubclass || a.isSubtype); | 394 assert(a.isSubclass || a.isSubtype); |
393 assert(b.isSubtype); | 395 assert(b.isSubtype); |
394 var elements = a.isSubclass | 396 var elements = a.isSubclass |
395 ? classWorld.strictSubclassesOf(a.base) | 397 ? classWorld.strictSubclassesOf(a.base) |
396 : classWorld.strictSubtypesOf(a.base); | 398 : classWorld.strictSubtypesOf(a.base); |
397 for (var element in elements) { | 399 for (var element in elements) { |
398 if (classWorld.isSubtypeOf(element, b.base)) return false; | 400 if (classWorld.isSubtypeOf(element, b.base)) return false; |
399 } | 401 } |
400 return true; | 402 return true; |
401 } | 403 } |
402 | 404 |
403 TypeMask intersectionSame(FlatTypeMask other, ClassWorld classWorld) { | 405 TypeMask intersectionSame(FlatTypeMask other, ClassWorld classWorld) { |
404 assert(base == other.base); | 406 assert(base == other.base); |
405 // The two masks share the base type, so we must chose the most | 407 // The two masks share the base type, so we must chose the most |
406 // constraining kind (the lowest) of the two. Only if both masks | 408 // constraining kind (the lowest) of the two. Only if both masks |
407 // are nullable, will the result be nullable too. | 409 // are nullable, will the result be nullable too. |
408 // The result will be normalized, as the two inputs are normalized, too. | 410 // The result will be normalized, as the two inputs are normalized, too. |
409 int combined = (flags < other.flags) | 411 int combined = (flags < other.flags) |
410 ? flags & ((other.flags & 1) | ~1) | 412 ? flags & ((other.flags & 1) | ~1) |
411 : other.flags & ((flags & 1) | ~1); | 413 : other.flags & ((flags & 1) | ~1); |
412 if (flags == combined) { | 414 if (flags == combined) { |
413 return this; | 415 return this; |
414 } else if (other.flags == combined) { | 416 } else if (other.flags == combined) { |
415 return other; | 417 return other; |
416 } else { | 418 } else { |
417 return new FlatTypeMask.normalized(base, combined, classWorld); | 419 return new FlatTypeMask.normalized(base, combined, classWorld); |
418 } | 420 } |
419 } | 421 } |
420 | 422 |
421 TypeMask intersectionStrictSubclass(FlatTypeMask other, | 423 TypeMask intersectionStrictSubclass( |
422 ClassWorld classWorld) { | 424 FlatTypeMask other, ClassWorld classWorld) { |
423 assert(base != other.base); | 425 assert(base != other.base); |
424 assert(classWorld.isSubclassOf(other.base, base)); | 426 assert(classWorld.isSubclassOf(other.base, base)); |
425 // If this mask isn't at least a subclass mask, then the | 427 // If this mask isn't at least a subclass mask, then the |
426 // intersection with the other mask is empty. | 428 // intersection with the other mask is empty. |
427 if (isExact) return intersectionEmpty(other); | 429 if (isExact) return intersectionEmpty(other); |
428 // Only the other mask puts constraints on the intersection mask, | 430 // Only the other mask puts constraints on the intersection mask, |
429 // so base the combined flags on the other mask. Only if both | 431 // so base the combined flags on the other mask. Only if both |
430 // masks are nullable, will the result be nullable too. | 432 // masks are nullable, will the result be nullable too. |
431 // The result is guaranteed to be normalized, as the other type | 433 // The result is guaranteed to be normalized, as the other type |
432 // was normalized. | 434 // was normalized. |
433 int combined = other.flags & ((flags & 1) | ~1); | 435 int combined = other.flags & ((flags & 1) | ~1); |
434 if (other.flags == combined) { | 436 if (other.flags == combined) { |
435 return other; | 437 return other; |
436 } else { | 438 } else { |
437 return new FlatTypeMask.normalized(other.base, combined, classWorld); | 439 return new FlatTypeMask.normalized(other.base, combined, classWorld); |
438 } | 440 } |
439 } | 441 } |
440 | 442 |
441 TypeMask intersectionStrictSubtype(FlatTypeMask other, | 443 TypeMask intersectionStrictSubtype( |
442 ClassWorld classWorld) { | 444 FlatTypeMask other, ClassWorld classWorld) { |
443 assert(base != other.base); | 445 assert(base != other.base); |
444 assert(classWorld.isSubtypeOf(other.base, base)); | 446 assert(classWorld.isSubtypeOf(other.base, base)); |
445 if (!isSubtype) return intersectionHelper(other, classWorld); | 447 if (!isSubtype) return intersectionHelper(other, classWorld); |
446 // Only the other mask puts constraints on the intersection mask, | 448 // Only the other mask puts constraints on the intersection mask, |
447 // so base the combined flags on the other mask. Only if both | 449 // so base the combined flags on the other mask. Only if both |
448 // masks are nullable, will the result be nullable too. | 450 // masks are nullable, will the result be nullable too. |
449 // The result is guaranteed to be normalized, as the other type | 451 // The result is guaranteed to be normalized, as the other type |
450 // was normalized. | 452 // was normalized. |
451 int combined = other.flags & ((flags & 1) | ~1); | 453 int combined = other.flags & ((flags & 1) | ~1); |
452 if (other.flags == combined) { | 454 if (other.flags == combined) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 // result will only be nullable if both masks are nullable. We have | 500 // result will only be nullable if both masks are nullable. We have |
499 // to normalize here, as we generate types based on new base classes. | 501 // to normalize here, as we generate types based on new base classes. |
500 int combined = (kind << 1) | (flags & other.flags & 1); | 502 int combined = (kind << 1) | (flags & other.flags & 1); |
501 Iterable<TypeMask> masks = candidates.map((ClassElement cls) { | 503 Iterable<TypeMask> masks = candidates.map((ClassElement cls) { |
502 return new FlatTypeMask.normalized(cls, combined, classWorld); | 504 return new FlatTypeMask.normalized(cls, combined, classWorld); |
503 }); | 505 }); |
504 return UnionTypeMask.unionOf(masks, classWorld); | 506 return UnionTypeMask.unionOf(masks, classWorld); |
505 } | 507 } |
506 | 508 |
507 TypeMask intersectionEmpty(FlatTypeMask other) { | 509 TypeMask intersectionEmpty(FlatTypeMask other) { |
508 return isNullable && other.isNullable ? new TypeMask.empty() | 510 return isNullable && other.isNullable |
| 511 ? new TypeMask.empty() |
509 : new TypeMask.nonNullEmpty(); | 512 : new TypeMask.nonNullEmpty(); |
510 } | 513 } |
511 | 514 |
512 /** | 515 /** |
513 * Returns whether [element] will be the one used at runtime when being | 516 * Returns whether [element] will be the one used at runtime when being |
514 * invoked on an instance of [cls]. [selector] is used to ensure library | 517 * invoked on an instance of [cls]. [selector] is used to ensure library |
515 * privacy is taken into account. | 518 * privacy is taken into account. |
516 */ | 519 */ |
517 static bool hasElementIn(ClassElement cls, | 520 static bool hasElementIn( |
518 Selector selector, | 521 ClassElement cls, Selector selector, Element element) { |
519 Element element) { | |
520 // Use [:implementation:] of [element] | 522 // Use [:implementation:] of [element] |
521 // because our function set only stores declarations. | 523 // because our function set only stores declarations. |
522 Element result = findMatchIn(cls, selector); | 524 Element result = findMatchIn(cls, selector); |
523 return result == null | 525 return result == null |
524 ? false | 526 ? false |
525 : result.implementation == element.implementation; | 527 : result.implementation == element.implementation; |
526 } | 528 } |
527 | 529 |
528 static Element findMatchIn(ClassElement cls, | 530 static Element findMatchIn(ClassElement cls, Selector selector) { |
529 Selector selector) { | |
530 // Use the [:implementation] of [cls] in case the found [element] | 531 // Use the [:implementation] of [cls] in case the found [element] |
531 // is in the patch class. | 532 // is in the patch class. |
532 return cls.implementation.lookupByName(selector.memberName); | 533 return cls.implementation.lookupByName(selector.memberName); |
533 } | 534 } |
534 | 535 |
535 /** | 536 /** |
536 * Returns whether [element] is a potential target when being | 537 * Returns whether [element] is a potential target when being |
537 * invoked on this type mask. [selector] is used to ensure library | 538 * invoked on this type mask. [selector] is used to ensure library |
538 * privacy is taken into account. | 539 * privacy is taken into account. |
539 */ | 540 */ |
(...skipping 14 matching lines...) Expand all Loading... |
554 return false; | 555 return false; |
555 } | 556 } |
556 | 557 |
557 ClassElement other = element.enclosingClass; | 558 ClassElement other = element.enclosingClass; |
558 if (other == backend.nullImplementation) { | 559 if (other == backend.nullImplementation) { |
559 return isNullable; | 560 return isNullable; |
560 } else if (isExact) { | 561 } else if (isExact) { |
561 return hasElementIn(self, selector, element); | 562 return hasElementIn(self, selector, element); |
562 } else if (isSubclass) { | 563 } else if (isSubclass) { |
563 assert(classWorld.isClosed); | 564 assert(classWorld.isClosed); |
564 return hasElementIn(self, selector, element) | 565 return hasElementIn(self, selector, element) || |
565 || other.isSubclassOf(self) | 566 other.isSubclassOf(self) || |
566 || classWorld.hasAnySubclassThatMixes(self, other); | 567 classWorld.hasAnySubclassThatMixes(self, other); |
567 } else { | 568 } else { |
568 assert(isSubtype); | 569 assert(isSubtype); |
569 assert(classWorld.isClosed); | 570 assert(classWorld.isClosed); |
570 bool result = hasElementIn(self, selector, element) | 571 bool result = hasElementIn(self, selector, element) || |
571 || other.implementsInterface(self) | 572 other.implementsInterface(self) || |
572 || classWorld.hasAnySubclassThatImplements(other, base) | 573 classWorld.hasAnySubclassThatImplements(other, base) || |
573 || classWorld.hasAnySubclassOfMixinUseThatImplements(other, base); | 574 classWorld.hasAnySubclassOfMixinUseThatImplements(other, base); |
574 if (result) return true; | 575 if (result) return true; |
575 // If the class is used as a mixin, we have to check if the element | 576 // If the class is used as a mixin, we have to check if the element |
576 // can be hit from any of the mixin applications. | 577 // can be hit from any of the mixin applications. |
577 Iterable<ClassElement> mixinUses = classWorld.mixinUsesOf(self); | 578 Iterable<ClassElement> mixinUses = classWorld.mixinUsesOf(self); |
578 return mixinUses.any((mixinApplication) => | 579 return mixinUses.any((mixinApplication) => |
579 hasElementIn(mixinApplication, selector, element) | 580 hasElementIn(mixinApplication, selector, element) || |
580 || other.isSubclassOf(mixinApplication) | 581 other.isSubclassOf(mixinApplication) || |
581 || classWorld.hasAnySubclassThatMixes(mixinApplication, other)); | 582 classWorld.hasAnySubclassThatMixes(mixinApplication, other)); |
582 } | 583 } |
583 } | 584 } |
584 | 585 |
585 /** | 586 /** |
586 * Returns whether a [selector] call on an instance of [cls] | 587 * Returns whether a [selector] call on an instance of [cls] |
587 * will hit a method at runtime, and not go through [noSuchMethod]. | 588 * will hit a method at runtime, and not go through [noSuchMethod]. |
588 */ | 589 */ |
589 static bool hasConcreteMatch(ClassElement cls, | 590 static bool hasConcreteMatch( |
590 Selector selector, | 591 ClassElement cls, Selector selector, ClassWorld world) { |
591 ClassWorld world) { | |
592 assert(invariant(cls, world.isInstantiated(cls), | 592 assert(invariant(cls, world.isInstantiated(cls), |
593 message: '$cls has not been instantiated.')); | 593 message: '$cls has not been instantiated.')); |
594 Element element = findMatchIn(cls, selector); | 594 Element element = findMatchIn(cls, selector); |
595 if (element == null) return false; | 595 if (element == null) return false; |
596 | 596 |
597 if (element.isAbstract) { | 597 if (element.isAbstract) { |
598 ClassElement enclosingClass = element.enclosingClass; | 598 ClassElement enclosingClass = element.enclosingClass; |
599 return hasConcreteMatch(enclosingClass.superclass, selector, world); | 599 return hasConcreteMatch(enclosingClass.superclass, selector, world); |
600 } | 600 } |
601 return selector.appliesUntyped(element, world); | 601 return selector.appliesUntyped(element, world); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
654 bool needsNoSuchMethod(ClassElement cls) { | 654 bool needsNoSuchMethod(ClassElement cls) { |
655 // We can skip uninstantiated subclasses. | 655 // We can skip uninstantiated subclasses. |
656 // TODO(johnniwinther): Put filtering into the (Class)World. | 656 // TODO(johnniwinther): Put filtering into the (Class)World. |
657 if (!classWorld.isInstantiated(cls)) { | 657 if (!classWorld.isInstantiated(cls)) { |
658 return false; | 658 return false; |
659 } | 659 } |
660 // We can just skip abstract classes because we know no | 660 // We can just skip abstract classes because we know no |
661 // instance of them will be created at runtime, and | 661 // instance of them will be created at runtime, and |
662 // therefore there is no instance that will require | 662 // therefore there is no instance that will require |
663 // [noSuchMethod] handling. | 663 // [noSuchMethod] handling. |
664 return !cls.isAbstract | 664 return !cls.isAbstract && !hasConcreteMatch(cls, selector, classWorld); |
665 && !hasConcreteMatch(cls, selector, classWorld); | |
666 } | 665 } |
667 | 666 |
668 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); | 667 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); |
669 if (isExact || baseNeedsNoSuchMethod) { | 668 if (isExact || baseNeedsNoSuchMethod) { |
670 return baseNeedsNoSuchMethod; | 669 return baseNeedsNoSuchMethod; |
671 } | 670 } |
672 | 671 |
673 Iterable<ClassElement> subclassesToCheck; | 672 Iterable<ClassElement> subclassesToCheck; |
674 if (isSubtype) { | 673 if (isSubtype) { |
675 subclassesToCheck = classWorld.strictSubtypesOf(base); | 674 subclassesToCheck = classWorld.strictSubtypesOf(base); |
676 } else { | 675 } else { |
677 assert(isSubclass); | 676 assert(isSubclass); |
678 subclassesToCheck = classWorld.strictSubclassesOf(base); | 677 subclassesToCheck = classWorld.strictSubclassesOf(base); |
679 } | 678 } |
680 | 679 |
681 return subclassesToCheck != null && | 680 return subclassesToCheck != null && |
682 subclassesToCheck.any(needsNoSuchMethod); | 681 subclassesToCheck.any(needsNoSuchMethod); |
683 } | 682 } |
684 | 683 |
685 Element locateSingleElement(Selector selector, | 684 Element locateSingleElement( |
686 TypeMask mask, | 685 Selector selector, TypeMask mask, Compiler compiler) { |
687 Compiler compiler) { | |
688 if (isEmptyOrNull) return null; | 686 if (isEmptyOrNull) return null; |
689 Iterable<Element> targets = | 687 Iterable<Element> targets = |
690 compiler.world.allFunctions.filter(selector, mask); | 688 compiler.world.allFunctions.filter(selector, mask); |
691 if (targets.length != 1) return null; | 689 if (targets.length != 1) return null; |
692 Element result = targets.first; | 690 Element result = targets.first; |
693 ClassElement enclosing = result.enclosingClass; | 691 ClassElement enclosing = result.enclosingClass; |
694 // We only return the found element if it is guaranteed to be | 692 // We only return the found element if it is guaranteed to be |
695 // implemented on the exact receiver type. It could be found in a | 693 // implemented on the exact receiver type. It could be found in a |
696 // subclass or in an inheritance-wise unrelated class in case of | 694 // subclass or in an inheritance-wise unrelated class in case of |
697 // subtype selectors. | 695 // subtype selectors. |
698 return (base.isSubclassOf(enclosing)) ? result : null; | 696 return (base.isSubclassOf(enclosing)) ? result : null; |
699 } | 697 } |
700 | 698 |
701 bool operator ==(var other) { | 699 bool operator ==(var other) { |
702 if (identical(this, other)) return true; | 700 if (identical(this, other)) return true; |
703 if (other is !FlatTypeMask) return false; | 701 if (other is! FlatTypeMask) return false; |
704 FlatTypeMask otherMask = other; | 702 FlatTypeMask otherMask = other; |
705 return (flags == otherMask.flags) && (base == otherMask.base); | 703 return (flags == otherMask.flags) && (base == otherMask.base); |
706 } | 704 } |
707 | 705 |
708 int get hashCode { | 706 int get hashCode { |
709 return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode; | 707 return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode; |
710 } | 708 } |
711 | 709 |
712 String toString() { | 710 String toString() { |
713 if (isEmptyOrNull) return isNullable ? '[null]' : '[empty]'; | 711 if (isEmptyOrNull) return isNullable ? '[null]' : '[empty]'; |
714 StringBuffer buffer = new StringBuffer(); | 712 StringBuffer buffer = new StringBuffer(); |
715 if (isNullable) buffer.write('null|'); | 713 if (isNullable) buffer.write('null|'); |
716 if (isExact) buffer.write('exact='); | 714 if (isExact) buffer.write('exact='); |
717 if (isSubclass) buffer.write('subclass='); | 715 if (isSubclass) buffer.write('subclass='); |
718 if (isSubtype) buffer.write('subtype='); | 716 if (isSubtype) buffer.write('subtype='); |
719 buffer.write(base.name); | 717 buffer.write(base.name); |
720 return "[$buffer]"; | 718 return "[$buffer]"; |
721 } | 719 } |
722 | 720 |
723 static Set<ClassElement> commonContainedClasses(FlatTypeMask x, | 721 static Set<ClassElement> commonContainedClasses( |
724 FlatTypeMask y, | 722 FlatTypeMask x, FlatTypeMask y, ClassWorld classWorld) { |
725 ClassWorld classWorld) { | |
726 Iterable<ClassElement> xSubset = containedSubset(x, classWorld); | 723 Iterable<ClassElement> xSubset = containedSubset(x, classWorld); |
727 if (xSubset == null) return null; | 724 if (xSubset == null) return null; |
728 Iterable<ClassElement> ySubset = containedSubset(y, classWorld); | 725 Iterable<ClassElement> ySubset = containedSubset(y, classWorld); |
729 if (ySubset == null) return null; | 726 if (ySubset == null) return null; |
730 return xSubset.toSet().intersection(ySubset.toSet()); | 727 return xSubset.toSet().intersection(ySubset.toSet()); |
731 } | 728 } |
732 | 729 |
733 static Iterable<ClassElement> containedSubset(FlatTypeMask x, | 730 static Iterable<ClassElement> containedSubset( |
734 ClassWorld classWorld) { | 731 FlatTypeMask x, ClassWorld classWorld) { |
735 ClassElement element = x.base; | 732 ClassElement element = x.base; |
736 if (x.isExact) { | 733 if (x.isExact) { |
737 return null; | 734 return null; |
738 } else if (x.isSubclass) { | 735 } else if (x.isSubclass) { |
739 return classWorld.strictSubclassesOf(element); | 736 return classWorld.strictSubclassesOf(element); |
740 } else { | 737 } else { |
741 assert(x.isSubtype); | 738 assert(x.isSubtype); |
742 return classWorld.strictSubtypesOf(element); | 739 return classWorld.strictSubtypesOf(element); |
743 } | 740 } |
744 } | 741 } |
745 } | 742 } |
OLD | NEW |