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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698