OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of types; | |
6 | |
7 /** | |
8 * A flat type mask is a type mask that has been flatten to contain a | |
9 * base type. | |
10 */ | |
11 class FlatTypeMask implements TypeMask { | |
12 static const int EMPTY = 0; | |
13 static const int EXACT = 1; | |
14 static const int SUBCLASS = 2; | |
15 static const int SUBTYPE = 3; | |
16 | |
17 final ClassElement base; | |
18 final int flags; | |
19 | |
20 FlatTypeMask(ClassElement base, int kind, bool isNullable) | |
21 : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); | |
22 | |
23 FlatTypeMask.exact(ClassElement base) | |
24 : this.internal(base, (EXACT << 1) | 1); | |
25 FlatTypeMask.subclass(ClassElement base) | |
26 : this.internal(base, (SUBCLASS << 1) | 1); | |
27 FlatTypeMask.subtype(ClassElement base) | |
28 : this.internal(base, (SUBTYPE << 1) | 1); | |
29 | |
30 const FlatTypeMask.nonNullEmpty(): base = null, flags = 0; | |
31 const FlatTypeMask.empty() : base = null, flags = 1; | |
32 | |
33 FlatTypeMask.nonNullExact(ClassElement base) | |
34 : this.internal(base, EXACT << 1); | |
35 FlatTypeMask.nonNullSubclass(ClassElement base) | |
36 : this.internal(base, SUBCLASS << 1); | |
37 FlatTypeMask.nonNullSubtype(ClassElement base) | |
38 : this.internal(base, SUBTYPE << 1); | |
39 | |
40 FlatTypeMask.internal(this.base, this.flags) { | |
41 assert(base == null || base.isDeclaration); | |
42 } | |
43 | |
44 /** | |
45 * Ensures that the generated mask is normalized, i.e., a call to | |
46 * [TypeMask.assertIsNormalized] with the factory's result returns `true`. | |
47 */ | |
48 factory FlatTypeMask.normalized(ClassElement base, int flags, World world) { | |
49 if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { | |
50 return new FlatTypeMask.internal(base, flags); | |
51 } | |
52 if ((flags >> 1) == SUBTYPE) { | |
53 if (!world.hasAnySubtype(base) || world.hasOnlySubclasses(base)) { | |
54 flags = (flags & 0x1) | (SUBCLASS << 1); | |
55 } | |
56 } | |
57 if (((flags >> 1) == SUBCLASS) && !world.hasAnySubclass(base)) { | |
58 flags = (flags & 0x1) | (EXACT << 1); | |
59 } | |
60 return new FlatTypeMask.internal(base, flags); | |
61 } | |
62 | |
63 bool get isEmpty => (flags >> 1) == EMPTY; | |
64 bool get isExact => (flags >> 1) == EXACT; | |
65 bool get isNullable => (flags & 1) != 0; | |
66 | |
67 bool get isUnion => false; | |
68 bool get isContainer => false; | |
69 bool get isMap => false; | |
70 bool get isDictionary => false; | |
71 bool get isForwarding => false; | |
72 bool get isValue => false; | |
73 | |
74 // TODO(kasperl): Get rid of these. They should not be a visible | |
75 // part of the implementation because they make it hard to add | |
76 // proper union types if we ever want to. | |
77 bool get isSubclass => (flags >> 1) == SUBCLASS; | |
78 bool get isSubtype => (flags >> 1) == SUBTYPE; | |
79 | |
80 TypeMask nullable() { | |
81 return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); | |
82 } | |
83 | |
84 TypeMask nonNullable() { | |
85 return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; | |
86 } | |
87 | |
88 bool contains(ClassElement type, ClassWorld classWorld) { | |
89 assert(type.isDeclaration); | |
90 if (isEmpty) { | |
91 return false; | |
92 } else if (identical(base, type)) { | |
93 return true; | |
94 } else if (isExact) { | |
95 return false; | |
96 } else if (isSubclass) { | |
97 return classWorld.isSubclassOf(type, base); | |
98 } else { | |
99 assert(isSubtype); | |
100 return classWorld.isSubtypeOf(type, base); | |
101 } | |
102 } | |
103 | |
104 bool isSingleImplementationOf(ClassElement cls, ClassWorld classWorld) { | |
105 // Special case basic types so that, for example, JSString is the | |
106 // single implementation of String. | |
107 // The general optimization is to realize there is only one class that | |
108 // implements [base] and [base] is not instantiated. We however do | |
109 // not track correctly the list of truly instantiated classes. | |
110 Backend backend = classWorld.backend; | |
111 if (containsOnlyString(classWorld)) { | |
112 return cls == classWorld.stringClass || | |
113 cls == backend.stringImplementation; | |
114 } | |
115 if (containsOnlyBool(classWorld)) { | |
116 return cls == classWorld.boolClass || cls == backend.boolImplementation; | |
117 } | |
118 if (containsOnlyInt(classWorld)) { | |
119 return cls == classWorld.intClass | |
120 || cls == backend.intImplementation | |
121 || cls == backend.positiveIntImplementation | |
122 || cls == backend.uint32Implementation | |
123 || cls == backend.uint31Implementation; | |
124 } | |
125 if (containsOnlyDouble(classWorld)) { | |
126 return cls == classWorld.doubleClass | |
127 || cls == backend.doubleImplementation; | |
128 } | |
129 return false; | |
130 } | |
131 | |
132 bool isInMask(TypeMask other, ClassWorld classWorld) { | |
133 // null is treated separately, so the empty mask might still contain it. | |
134 if (isEmpty) return isNullable ? other.isNullable : true; | |
135 // The empty type contains no classes. | |
136 if (other.isEmpty) return false; | |
137 // Quick check whether to handle null. | |
138 if (isNullable && !other.isNullable) return false; | |
139 other = TypeMask.nonForwardingMask(other); | |
140 // If other is union, delegate to UnionTypeMask.containsMask. | |
141 if (other is! FlatTypeMask) return other.containsMask(this, classWorld); | |
142 // The other must be flat, so compare base and flags. | |
143 FlatTypeMask flatOther = other; | |
144 ClassElement otherBase = flatOther.base; | |
145 // If other is exact, it only contains its base. | |
146 // TODO(herhut): Get rid of isSingleImplementationOf. | |
147 if (flatOther.isExact) { | |
148 return (isExact && base == otherBase) | |
149 || isSingleImplementationOf(otherBase, classWorld); | |
150 } | |
151 // If other is subclass, this has to be subclass, as well. Unless | |
152 // flatOther.base covers all subtypes of this. Currently, we only | |
153 // consider object to behave that way. | |
154 // TODO(herhut): Add check whether flatOther.base is superclass of | |
155 // all subclasses of this.base. | |
156 if (flatOther.isSubclass) { | |
157 if (isSubtype) return (otherBase == classWorld.objectClass); | |
158 return classWorld.isSubclassOf(base, otherBase); | |
159 } | |
160 assert(flatOther.isSubtype); | |
161 // Check whether this TypeMask satisfies otherBase's interface. | |
162 return satisfies(otherBase, classWorld); | |
163 } | |
164 | |
165 bool containsMask(TypeMask other, ClassWorld classWorld) { | |
166 return other.isInMask(this, classWorld); | |
167 } | |
168 | |
169 bool containsOnlyInt(ClassWorld classWorld) { | |
170 Backend backend = classWorld.backend; | |
171 return base == classWorld.intClass | |
172 || base == backend.intImplementation | |
173 || base == backend.positiveIntImplementation | |
174 || base == backend.uint31Implementation | |
175 || base == backend.uint32Implementation; | |
176 } | |
177 | |
178 bool containsOnlyDouble(ClassWorld classWorld) { | |
179 Backend backend = classWorld.backend; | |
180 return base == classWorld.doubleClass | |
181 || base == backend.doubleImplementation; | |
182 } | |
183 | |
184 bool containsOnlyNum(ClassWorld classWorld) { | |
185 Backend backend = classWorld.backend; | |
186 return containsOnlyInt(classWorld) | |
187 || containsOnlyDouble(classWorld) | |
188 || base == classWorld.numClass | |
189 || base == backend.numImplementation; | |
190 } | |
191 | |
192 bool containsOnlyBool(ClassWorld classWorld) { | |
193 Backend backend = classWorld.backend; | |
194 return base == classWorld.boolClass | |
195 || base == backend.boolImplementation; | |
196 } | |
197 | |
198 bool containsOnlyString(ClassWorld classWorld) { | |
199 Backend backend = classWorld.backend; | |
200 return base == classWorld.stringClass | |
201 || base == backend.stringImplementation; | |
202 } | |
203 | |
204 bool containsOnly(ClassElement cls) { | |
205 assert(cls.isDeclaration); | |
206 return base == cls; | |
207 } | |
208 | |
209 bool satisfies(ClassElement cls, ClassWorld classWorld) { | |
210 assert(cls.isDeclaration); | |
211 if (isEmpty) return false; | |
212 if (classWorld.isSubtypeOf(base, cls)) return true; | |
213 return false; | |
214 } | |
215 | |
216 /** | |
217 * Returns the [ClassElement] if this type represents a single class, | |
218 * otherwise returns `null`. This method is conservative. | |
219 */ | |
220 ClassElement singleClass(ClassWorld classWorld) { | |
221 if (isEmpty) return null; | |
222 if (isNullable) return null; // It is Null and some other class. | |
223 if (isExact) { | |
224 return base; | |
225 } else if (isSubclass) { | |
226 return classWorld.hasAnyStrictSubclass(base) ? null : base; | |
227 } else { | |
228 assert(isSubtype); | |
229 return null; | |
230 } | |
231 } | |
232 | |
233 /** | |
234 * Returns whether or not this type mask contains all types. | |
235 */ | |
236 bool containsAll(ClassWorld classWorld) { | |
237 if (isEmpty || isExact) return false; | |
238 return identical(base, classWorld.objectClass); | |
239 } | |
240 | |
241 TypeMask union(TypeMask other, ClassWorld classWorld) { | |
242 assert(other != null); | |
243 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
244 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
245 if (other is! FlatTypeMask) return other.union(this, classWorld); | |
246 FlatTypeMask flatOther = other; | |
247 if (isEmpty) { | |
248 return isNullable ? flatOther.nullable() : flatOther; | |
249 } else if (flatOther.isEmpty) { | |
250 return flatOther.isNullable ? nullable() : this; | |
251 } else if (base == flatOther.base) { | |
252 return unionSame(flatOther, classWorld); | |
253 } else if (classWorld.isSubclassOf(flatOther.base, base)) { | |
254 return unionStrictSubclass(flatOther, classWorld); | |
255 } else if (classWorld.isSubclassOf(base, flatOther.base)) { | |
256 return flatOther.unionStrictSubclass(this, classWorld); | |
257 } else if (classWorld.isSubtypeOf(flatOther.base, base)) { | |
258 return unionStrictSubtype(flatOther, classWorld); | |
259 } else if (classWorld.isSubtypeOf(base, flatOther.base)) { | |
260 return flatOther.unionStrictSubtype(this, classWorld); | |
261 } else { | |
262 return new UnionTypeMask._internal(<FlatTypeMask>[this, flatOther]); | |
263 } | |
264 } | |
265 | |
266 TypeMask unionSame(FlatTypeMask other, ClassWorld classWorld) { | |
267 assert(base == other.base); | |
268 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
269 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
270 // The two masks share the base type, so we must chose the least | |
271 // constraining kind (the highest) of the two. If either one of | |
272 // the masks are nullable the result should be nullable too. | |
273 // As both masks are normalized, the result will be, too. | |
274 int combined = (flags > other.flags) | |
275 ? flags | (other.flags & 1) | |
276 : other.flags | (flags & 1); | |
277 if (flags == combined) { | |
278 return this; | |
279 } else if (other.flags == combined) { | |
280 return other; | |
281 } else { | |
282 return new FlatTypeMask.normalized(base, combined, classWorld); | |
283 } | |
284 } | |
285 | |
286 TypeMask unionStrictSubclass(FlatTypeMask other, ClassWorld classWorld) { | |
287 assert(base != other.base); | |
288 assert(classWorld.isSubclassOf(other.base, base)); | |
289 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
290 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
291 int combined; | |
292 if ((isExact && other.isExact) || base == classWorld.objectClass) { | |
293 // Since the other mask is a subclass of this mask, we need the | |
294 // resulting union to be a subclass too. If either one of the | |
295 // masks are nullable the result should be nullable too. | |
296 combined = (SUBCLASS << 1) | ((flags | other.flags) & 1); | |
297 } else { | |
298 // Both masks are at least subclass masks, so we pick the least | |
299 // constraining kind (the highest) of the two. If either one of | |
300 // the masks are nullable the result should be nullable too. | |
301 combined = (flags > other.flags) | |
302 ? flags | (other.flags & 1) | |
303 : other.flags | (flags & 1); | |
304 } | |
305 // If we weaken the constraint on this type, we have to make sure that | |
306 // the result is normalized. | |
307 return (flags != combined) | |
308 ? new FlatTypeMask.normalized(base, combined, classWorld) | |
309 : this; | |
310 } | |
311 | |
312 TypeMask unionStrictSubtype(FlatTypeMask other, ClassWorld classWorld) { | |
313 assert(base != other.base); | |
314 assert(!classWorld.isSubclassOf(other.base, base)); | |
315 assert(classWorld.isSubtypeOf(other.base, base)); | |
316 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
317 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
318 // Since the other mask is a subtype of this mask, we need the | |
319 // resulting union to be a subtype too. If either one of the masks | |
320 // are nullable the result should be nullable too. | |
321 int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1); | |
322 // We know there is at least one subtype, [other.base], so no need | |
323 // to normalize. | |
324 return (flags != combined) | |
325 ? new FlatTypeMask.normalized(base, combined, classWorld) | |
326 : this; | |
327 } | |
328 | |
329 TypeMask intersection(TypeMask other, ClassWorld classWorld) { | |
330 assert(other != null); | |
331 if (other is! FlatTypeMask) return other.intersection(this, classWorld); | |
332 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
333 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
334 FlatTypeMask flatOther = other; | |
335 if (isEmpty) { | |
336 return flatOther.isNullable ? this : nonNullable(); | |
337 } else if (flatOther.isEmpty) { | |
338 return isNullable ? flatOther : other.nonNullable(); | |
339 } else if (base == flatOther.base) { | |
340 return intersectionSame(flatOther, classWorld); | |
341 } else if (classWorld.isSubclassOf(flatOther.base, base)) { | |
342 return intersectionStrictSubclass(flatOther, classWorld); | |
343 } else if (classWorld.isSubclassOf(base, flatOther.base)) { | |
344 return flatOther.intersectionStrictSubclass(this, classWorld); | |
345 } else if (classWorld.isSubtypeOf(flatOther.base, base)) { | |
346 return intersectionStrictSubtype(flatOther, classWorld); | |
347 } else if (classWorld.isSubtypeOf(base, flatOther.base)) { | |
348 return flatOther.intersectionStrictSubtype(this, classWorld); | |
349 } else { | |
350 return intersectionDisjoint(flatOther, classWorld); | |
351 } | |
352 } | |
353 | |
354 TypeMask intersectionSame(FlatTypeMask other, ClassWorld classWorld) { | |
355 assert(base == other.base); | |
356 // The two masks share the base type, so we must chose the most | |
357 // constraining kind (the lowest) of the two. Only if both masks | |
358 // are nullable, will the result be nullable too. | |
359 // The result will be normalized, as the two inputs are normalized, too. | |
360 int combined = (flags < other.flags) | |
361 ? flags & ((other.flags & 1) | ~1) | |
362 : other.flags & ((flags & 1) | ~1); | |
363 if (flags == combined) { | |
364 return this; | |
365 } else if (other.flags == combined) { | |
366 return other; | |
367 } else { | |
368 return new FlatTypeMask.normalized(base, combined, classWorld); | |
369 } | |
370 } | |
371 | |
372 TypeMask intersectionStrictSubclass(FlatTypeMask other, | |
373 ClassWorld classWorld) { | |
374 assert(base != other.base); | |
375 assert(classWorld.isSubclassOf(other.base, base)); | |
376 // If this mask isn't at least a subclass mask, then the | |
377 // intersection with the other mask is empty. | |
378 if (isExact) return intersectionEmpty(other); | |
379 // Only the other mask puts constraints on the intersection mask, | |
380 // so base the combined flags on the other mask. Only if both | |
381 // masks are nullable, will the result be nullable too. | |
382 // The result is guaranteed to be normalized, as the other type | |
383 // was normalized. | |
384 int combined = other.flags & ((flags & 1) | ~1); | |
385 if (other.flags == combined) { | |
386 return other; | |
387 } else { | |
388 return new FlatTypeMask.normalized(other.base, combined, classWorld); | |
389 } | |
390 } | |
391 | |
392 TypeMask intersectionStrictSubtype(FlatTypeMask other, | |
393 ClassWorld classWorld) { | |
394 assert(base != other.base); | |
395 assert(classWorld.isSubtypeOf(other.base, base)); | |
396 if (!isSubtype) return intersectionHelper(other, classWorld); | |
397 // Only the other mask puts constraints on the intersection mask, | |
398 // so base the combined flags on the other mask. Only if both | |
399 // masks are nullable, will the result be nullable too. | |
400 // The result is guaranteed to be normalized, as the other type | |
401 // was normalized. | |
402 int combined = other.flags & ((flags & 1) | ~1); | |
403 if (other.flags == combined) { | |
404 return other; | |
405 } else { | |
406 return new FlatTypeMask.normalized(other.base, combined, classWorld); | |
407 } | |
408 } | |
409 | |
410 TypeMask intersectionDisjoint(FlatTypeMask other, ClassWorld classWorld) { | |
411 assert(base != other.base); | |
412 assert(!classWorld.isSubtypeOf(base, other.base)); | |
413 assert(!classWorld.isSubtypeOf(other.base, base)); | |
414 return intersectionHelper(other, classWorld); | |
415 } | |
416 | |
417 TypeMask intersectionHelper(FlatTypeMask other, ClassWorld classWorld) { | |
418 assert(base != other.base); | |
419 assert(!classWorld.isSubclassOf(base, other.base)); | |
420 assert(!classWorld.isSubclassOf(other.base, base)); | |
421 // If one of the masks are exact or if both of them are subclass | |
422 // masks, then the intersection is empty. | |
423 if (isExact || other.isExact) return intersectionEmpty(other); | |
424 if (isSubclass && other.isSubclass) return intersectionEmpty(other); | |
425 assert(isSubtype || other.isSubtype); | |
426 int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; | |
427 // Compute the set of classes that are contained in both type masks. | |
428 Set<ClassElement> common = commonContainedClasses(this, other, classWorld); | |
429 if (common == null || common.isEmpty) return intersectionEmpty(other); | |
430 // Narrow down the candidates by only looking at common classes | |
431 // that do not have a superclass or supertype that will be a | |
432 // better candidate. | |
433 Iterable<ClassElement> candidates = common.where((ClassElement each) { | |
434 bool containsSuperclass = common.contains(each.supertype.element); | |
435 // If the superclass is also a candidate, then we don't want to | |
436 // deal with this class. If we're only looking for a subclass we | |
437 // know we don't have to look at the list of interfaces because | |
438 // they can never be in the common set. | |
439 if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; | |
440 // Run through the direct supertypes of the class. If the common | |
441 // set contains the direct supertype of the class, we ignore the | |
442 // the class because the supertype is a better candidate. | |
443 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { | |
444 if (common.contains(link.head.element)) return false; | |
445 } | |
446 return true; | |
447 }); | |
448 // Run through the list of candidates and compute the union. The | |
449 // result will only be nullable if both masks are nullable. We have | |
450 // to normalize here, as we generate types based on new base classes. | |
451 int combined = (kind << 1) | (flags & other.flags & 1); | |
452 Iterable<TypeMask> masks = candidates.map((ClassElement cls) { | |
453 return new FlatTypeMask.normalized(cls, combined, classWorld); | |
454 }); | |
455 return UnionTypeMask.unionOf(masks, classWorld); | |
456 } | |
457 | |
458 TypeMask intersectionEmpty(FlatTypeMask other) { | |
459 return isNullable && other.isNullable ? new TypeMask.empty() | |
460 : new TypeMask.nonNullEmpty(); | |
461 } | |
462 | |
463 /** | |
464 * Returns whether [element] will be the one used at runtime when being | |
465 * invoked on an instance of [cls]. [selector] is used to ensure library | |
466 * privacy is taken into account. | |
467 */ | |
468 static bool hasElementIn(ClassElement cls, | |
469 Selector selector, | |
470 Element element) { | |
471 // Use [:implementation:] of [element] | |
472 // because our function set only stores declarations. | |
473 Element result = findMatchIn(cls, selector); | |
474 return result == null | |
475 ? false | |
476 : result.implementation == element.implementation; | |
477 } | |
478 | |
479 static Element findMatchIn(ClassElement cls, | |
480 Selector selector) { | |
481 // Use the [:implementation] of [cls] in case the found [element] | |
482 // is in the patch class. | |
483 return cls.implementation.lookupSelector(selector); | |
484 } | |
485 | |
486 /** | |
487 * Returns whether [element] is a potential target when being | |
488 * invoked on this type mask. [selector] is used to ensure library | |
489 * privacy is taken into account. | |
490 */ | |
491 bool canHit(Element element, Selector selector, ClassWorld classWorld) { | |
492 Backend backend = classWorld.backend; | |
493 assert(element.name == selector.name); | |
494 if (isEmpty) { | |
495 if (!isNullable) return false; | |
496 return hasElementIn(backend.nullImplementation, selector, element); | |
497 } | |
498 | |
499 // TODO(kasperl): Can't we just avoid creating typed selectors | |
500 // based of function types? | |
501 Element self = base; | |
502 if (self.isTypedef) { | |
503 // A typedef is a function type that doesn't have any | |
504 // user-defined members. | |
505 return false; | |
506 } | |
507 | |
508 ClassElement other = element.enclosingClass; | |
509 if (other == backend.nullImplementation) { | |
510 return isNullable; | |
511 } else if (isExact) { | |
512 return hasElementIn(self, selector, element); | |
513 } else if (isSubclass) { | |
514 assert(classWorld.isClosed); | |
515 return hasElementIn(self, selector, element) | |
516 || other.isSubclassOf(self) | |
517 || classWorld.hasAnySubclassThatMixes(self, other); | |
518 } else { | |
519 assert(isSubtype); | |
520 assert(classWorld.isClosed); | |
521 bool result = hasElementIn(self, selector, element) | |
522 || other.implementsInterface(self) | |
523 || classWorld.hasAnySubclassThatImplements(other, base) | |
524 || classWorld.hasAnySubclassOfMixinUseThatImplements(other, base); | |
525 if (result) return true; | |
526 // If the class is used as a mixin, we have to check if the element | |
527 // can be hit from any of the mixin applications. | |
528 Iterable<ClassElement> mixinUses = classWorld.mixinUsesOf(self); | |
529 return mixinUses.any((mixinApplication) => | |
530 hasElementIn(mixinApplication, selector, element) | |
531 || other.isSubclassOf(mixinApplication) | |
532 || classWorld.hasAnySubclassThatMixes(mixinApplication, other)); | |
533 } | |
534 } | |
535 | |
536 /** | |
537 * Returns whether a [selector] call on an instance of [cls] | |
538 * will hit a method at runtime, and not go through [noSuchMethod]. | |
539 */ | |
540 static bool hasConcreteMatch(ClassElement cls, | |
541 Selector selector, | |
542 World world) { | |
543 assert(invariant(cls, | |
544 world.compiler.resolverWorld.isInstantiated(cls), | |
545 message: '$cls has not been instantiated.')); | |
546 Element element = findMatchIn(cls, selector); | |
547 if (element == null) return false; | |
548 | |
549 if (element.isAbstract) { | |
550 ClassElement enclosingClass = element.enclosingClass; | |
551 return hasConcreteMatch(enclosingClass.superclass, selector, world); | |
552 } | |
553 return selector.appliesUntyped(element, world); | |
554 } | |
555 | |
556 bool needsNoSuchMethodHandling(Selector selector, ClassWorld classWorld) { | |
557 // A call on an empty type mask is either dead code, or a call on | |
558 // `null`. | |
559 if (isEmpty) return false; | |
560 // A call on an exact mask for an abstract class is dead code. | |
561 if (isExact && base.isAbstract) return false; | |
562 // If the receiver is guaranteed to have a member that | |
563 // matches what we're looking for, there's no need to | |
564 // introduce a noSuchMethod handler. It will never be called. | |
565 // | |
566 // As an example, consider this class hierarchy: | |
567 // | |
568 // A <-- noSuchMethod | |
569 // / \ | |
570 // C B <-- foo | |
571 // | |
572 // If we know we're calling foo on an object of type B we | |
573 // don't have to worry about the noSuchMethod method in A | |
574 // because objects of type B implement foo. On the other hand, | |
575 // if we end up calling foo on something of type C we have to | |
576 // add a handler for it. | |
577 | |
578 // If the holders of all user-defined noSuchMethod | |
579 // implementations that might be applicable to the receiver | |
580 // type have a matching member for the current name and | |
581 // selector, we avoid introducing a noSuchMethod handler. | |
582 // | |
583 // As an example, consider this class hierarchy: | |
584 // | |
585 // A <-- foo | |
586 // / \ | |
587 // noSuchMethod --> B C <-- bar | |
588 // | | | |
589 // C D <-- noSuchMethod | |
590 // | |
591 // When calling foo on an object of type A, we know that the | |
592 // implementations of noSuchMethod are in the classes B and D | |
593 // that also (indirectly) implement foo, so we do not need a | |
594 // handler for it. | |
595 // | |
596 // If we're calling bar on an object of type D, we don't need | |
597 // the handler either because all objects of type D implement | |
598 // bar through inheritance. | |
599 // | |
600 // If we're calling bar on an object of type A we do need the | |
601 // handler because we may have to call B.noSuchMethod since B | |
602 // does not implement bar. | |
603 | |
604 /// Returns `true` if [cls] is an instantiated class that does not have | |
605 /// a concrete method matching [selector]. | |
606 bool needsNoSuchMethod(ClassElement cls) { | |
607 // We can skip uninstantiated subclasses. | |
608 // TODO(johnniwinther): Put filtering into the (Class)World. | |
609 if (!classWorld.isInstantiated(cls)) { | |
610 return false; | |
611 } | |
612 // We can just skip abstract classes because we know no | |
613 // instance of them will be created at runtime, and | |
614 // therefore there is no instance that will require | |
615 // [noSuchMethod] handling. | |
616 return !cls.isAbstract | |
617 && !hasConcreteMatch(cls, selector, classWorld); | |
618 } | |
619 | |
620 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); | |
621 if (isExact || baseNeedsNoSuchMethod) { | |
622 return baseNeedsNoSuchMethod; | |
623 } | |
624 | |
625 Iterable<ClassElement> subclassesToCheck; | |
626 if (isSubtype) { | |
627 subclassesToCheck = classWorld.subtypesOf(base); | |
628 } else { | |
629 assert(isSubclass); | |
630 subclassesToCheck = classWorld.subclassesOf(base); | |
631 } | |
632 | |
633 return subclassesToCheck != null && | |
634 subclassesToCheck.any(needsNoSuchMethod); | |
635 } | |
636 | |
637 Element locateSingleElement(Selector selector, Compiler compiler) { | |
638 if (isEmpty) return null; | |
639 Iterable<Element> targets = compiler.world.allFunctions.filter(selector); | |
640 if (targets.length != 1) return null; | |
641 Element result = targets.first; | |
642 ClassElement enclosing = result.enclosingClass; | |
643 // We only return the found element if it is guaranteed to be | |
644 // implemented on the exact receiver type. It could be found in a | |
645 // subclass or in an inheritance-wise unrelated class in case of | |
646 // subtype selectors. | |
647 return (base.isSubclassOf(enclosing)) ? result : null; | |
648 } | |
649 | |
650 bool operator ==(var other) { | |
651 if (other is !FlatTypeMask) return false; | |
652 FlatTypeMask otherMask = other; | |
653 return (flags == otherMask.flags) && (base == otherMask.base); | |
654 } | |
655 | |
656 int get hashCode { | |
657 return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode; | |
658 } | |
659 | |
660 String toString() { | |
661 if (isEmpty) return isNullable ? '[null]' : '[empty]'; | |
662 StringBuffer buffer = new StringBuffer(); | |
663 if (isNullable) buffer.write('null|'); | |
664 if (isExact) buffer.write('exact='); | |
665 if (isSubclass) buffer.write('subclass='); | |
666 if (isSubtype) buffer.write('subtype='); | |
667 buffer.write(base.name); | |
668 return "[$buffer]"; | |
669 } | |
670 | |
671 static Set<ClassElement> commonContainedClasses(FlatTypeMask x, | |
672 FlatTypeMask y, | |
673 ClassWorld classWorld) { | |
674 Iterable<ClassElement> xSubset = containedSubset(x, classWorld); | |
675 if (xSubset == null) return null; | |
676 Iterable<ClassElement> ySubset = containedSubset(y, classWorld); | |
677 if (ySubset == null) return null; | |
678 Iterable<ClassElement> smallSet, largeSet; | |
679 if (xSubset.length <= ySubset.length) { | |
680 smallSet = xSubset; | |
681 largeSet = ySubset; | |
682 } else { | |
683 smallSet = ySubset; | |
684 largeSet = xSubset; | |
685 } | |
686 var result = smallSet.where((ClassElement each) => largeSet.contains(each)); | |
687 return result.toSet(); | |
688 } | |
689 | |
690 static Iterable<ClassElement> containedSubset(FlatTypeMask x, | |
691 ClassWorld classWorld) { | |
692 ClassElement element = x.base; | |
693 if (x.isExact) { | |
694 return null; | |
695 } else if (x.isSubclass) { | |
696 return classWorld.subclassesOf(element); | |
697 } else { | |
698 assert(x.isSubtype); | |
699 return classWorld.subtypesOf(element); | |
700 } | |
701 } | |
702 } | |
OLD | NEW |