| Index: pkg/compiler/lib/src/types/flat_type_mask.dart
|
| diff --git a/pkg/compiler/lib/src/types/flat_type_mask.dart b/pkg/compiler/lib/src/types/flat_type_mask.dart
|
| index 5a3ef8e1ef0e8dda14c446b7847706e3523fe931..ce6cd550f90280b9eee6c68bd7eb73c24070fda4 100644
|
| --- a/pkg/compiler/lib/src/types/flat_type_mask.dart
|
| +++ b/pkg/compiler/lib/src/types/flat_type_mask.dart
|
| @@ -14,17 +14,15 @@ class FlatTypeMask implements TypeMask {
|
| static const int SUBCLASS = 2;
|
| static const int SUBTYPE = 3;
|
|
|
| - final ClassElement base;
|
| + final Entity base;
|
| final int flags;
|
|
|
| - FlatTypeMask(ClassElement base, int kind, bool isNullable)
|
| + FlatTypeMask(Entity base, int kind, bool isNullable)
|
| : this.internal(base, (kind << 1) | (isNullable ? 1 : 0));
|
|
|
| - FlatTypeMask.exact(ClassElement base) : this.internal(base, (EXACT << 1) | 1);
|
| - FlatTypeMask.subclass(ClassElement base)
|
| - : this.internal(base, (SUBCLASS << 1) | 1);
|
| - FlatTypeMask.subtype(ClassElement base)
|
| - : this.internal(base, (SUBTYPE << 1) | 1);
|
| + FlatTypeMask.exact(Entity base) : this.internal(base, (EXACT << 1) | 1);
|
| + FlatTypeMask.subclass(Entity base) : this.internal(base, (SUBCLASS << 1) | 1);
|
| + FlatTypeMask.subtype(Entity base) : this.internal(base, (SUBTYPE << 1) | 1);
|
|
|
| const FlatTypeMask.nonNullEmpty()
|
| : base = null,
|
| @@ -33,23 +31,22 @@ class FlatTypeMask implements TypeMask {
|
| : base = null,
|
| flags = 1;
|
|
|
| - FlatTypeMask.nonNullExact(ClassElement base)
|
| - : this.internal(base, EXACT << 1);
|
| - FlatTypeMask.nonNullSubclass(ClassElement base)
|
| + FlatTypeMask.nonNullExact(Entity base) : this.internal(base, EXACT << 1);
|
| + FlatTypeMask.nonNullSubclass(Entity base)
|
| : this.internal(base, SUBCLASS << 1);
|
| - FlatTypeMask.nonNullSubtype(ClassElement base)
|
| - : this.internal(base, SUBTYPE << 1);
|
| + FlatTypeMask.nonNullSubtype(Entity base) : this.internal(base, SUBTYPE << 1);
|
| +
|
| + bool _validateBase(ClassElement element) => element.isDeclaration;
|
|
|
| FlatTypeMask.internal(this.base, this.flags) {
|
| - assert(base == null || base.isDeclaration);
|
| + assert(base == null || _validateBase(base));
|
| }
|
|
|
| /**
|
| * Ensures that the generated mask is normalized, i.e., a call to
|
| * [TypeMask.assertIsNormalized] with the factory's result returns `true`.
|
| */
|
| - factory FlatTypeMask.normalized(
|
| - ClassElement base, int flags, ClosedWorld world) {
|
| + factory FlatTypeMask.normalized(Entity base, int flags, ClosedWorld world) {
|
| if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) {
|
| return new FlatTypeMask.internal(base, flags);
|
| }
|
| @@ -61,14 +58,8 @@ class FlatTypeMask implements TypeMask {
|
| if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) {
|
| flags = (flags & 0x1) | (EXACT << 1);
|
| }
|
| - Map<ClassElement, TypeMask> cachedMasks =
|
| - world.canonicalizedTypeMasks[flags];
|
| - if (cachedMasks == null) {
|
| - world.canonicalizedTypeMasks[flags] =
|
| - cachedMasks = <ClassElement, TypeMask>{};
|
| - }
|
| - return cachedMasks.putIfAbsent(
|
| - base, () => new FlatTypeMask.internal(base, flags));
|
| + return world.getCachedMask(
|
| + base, flags, () => new FlatTypeMask.internal(base, flags));
|
| }
|
|
|
| bool get isEmpty => isEmptyOrNull && !isNullable;
|
| @@ -98,23 +89,23 @@ class FlatTypeMask implements TypeMask {
|
| return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this;
|
| }
|
|
|
| - bool contains(ClassElement type, ClosedWorld closedWorld) {
|
| - assert(type.isDeclaration);
|
| + bool contains(Entity other, ClosedWorld closedWorld) {
|
| + assert(_validateBase(other));
|
| if (isEmptyOrNull) {
|
| return false;
|
| - } else if (identical(base, type)) {
|
| + } else if (identical(base, other)) {
|
| return true;
|
| } else if (isExact) {
|
| return false;
|
| } else if (isSubclass) {
|
| - return closedWorld.isSubclassOf(type, base);
|
| + return closedWorld.isSubclassOf(other, base);
|
| } else {
|
| assert(isSubtype);
|
| - return closedWorld.isSubtypeOf(type, base);
|
| + return closedWorld.isSubtypeOf(other, base);
|
| }
|
| }
|
|
|
| - bool isSingleImplementationOf(ClassElement cls, ClosedWorld closedWorld) {
|
| + bool isSingleImplementationOf(Entity cls, ClosedWorld closedWorld) {
|
| // Special case basic types so that, for example, JSString is the
|
| // single implementation of String.
|
| // The general optimization is to realize there is only one class that
|
| @@ -154,7 +145,7 @@ class FlatTypeMask implements TypeMask {
|
| if (other is! FlatTypeMask) return other.containsMask(this, closedWorld);
|
| // The other must be flat, so compare base and flags.
|
| FlatTypeMask flatOther = other;
|
| - ClassElement otherBase = flatOther.base;
|
| + Entity otherBase = flatOther.base;
|
| // If other is exact, it only contains its base.
|
| // TODO(herhut): Get rid of isSingleImplementationOf.
|
| if (flatOther.isExact) {
|
| @@ -214,23 +205,21 @@ class FlatTypeMask implements TypeMask {
|
| base == backendClasses.stringImplementation;
|
| }
|
|
|
| - bool containsOnly(ClassElement cls) {
|
| - assert(cls.isDeclaration);
|
| + bool containsOnly(Entity cls) {
|
| + assert(_validateBase(cls));
|
| return base == cls;
|
| }
|
|
|
| - bool satisfies(ClassElement cls, ClosedWorld closedWorld) {
|
| - assert(cls.isDeclaration);
|
| + bool satisfies(Entity cls, ClosedWorld closedWorld) {
|
| + assert(_validateBase(cls));
|
| if (isEmptyOrNull) return false;
|
| if (closedWorld.isSubtypeOf(base, cls)) return true;
|
| return false;
|
| }
|
|
|
| - /**
|
| - * Returns the [ClassElement] if this type represents a single class,
|
| - * otherwise returns `null`. This method is conservative.
|
| - */
|
| - ClassElement singleClass(ClosedWorld closedWorld) {
|
| + /// Returns the [Entity] if this type represents a single class, otherwise
|
| + /// returns `null`. This method is conservative.
|
| + Entity singleClass(ClosedWorld closedWorld) {
|
| if (isEmptyOrNull) return null;
|
| if (isNullable) return null; // It is Null and some other class.
|
| if (isExact) {
|
| @@ -479,6 +468,7 @@ class FlatTypeMask implements TypeMask {
|
| if (isSubclass && other.isSubclass) return intersectionEmpty(other);
|
| assert(isSubtype || other.isSubtype);
|
| int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE;
|
| + // TODO(johnniwinther): Move this computation to [ClosedWorld].
|
| // Compute the set of classes that are contained in both type masks.
|
| Set<ClassElement> common = commonContainedClasses(this, other, closedWorld);
|
| if (common == null || common.isEmpty) return intersectionEmpty(other);
|
| @@ -504,7 +494,7 @@ class FlatTypeMask implements TypeMask {
|
| // result will only be nullable if both masks are nullable. We have
|
| // to normalize here, as we generate types based on new base classes.
|
| int combined = (kind << 1) | (flags & other.flags & 1);
|
| - Iterable<TypeMask> masks = candidates.map((ClassElement cls) {
|
| + Iterable<TypeMask> masks = candidates.map((Entity cls) {
|
| return new FlatTypeMask.normalized(cls, combined, closedWorld);
|
| });
|
| return UnionTypeMask.unionOf(masks, closedWorld);
|
| @@ -517,27 +507,6 @@ class FlatTypeMask implements TypeMask {
|
| }
|
|
|
| /**
|
| - * Returns whether [element] will be the one used at runtime when being
|
| - * invoked on an instance of [cls]. [selector] is used to ensure library
|
| - * privacy is taken into account.
|
| - */
|
| - static bool hasElementIn(
|
| - ClassElement cls, Selector selector, Element element) {
|
| - // Use [:implementation:] of [element]
|
| - // because our function set only stores declarations.
|
| - Element result = findMatchIn(cls, selector);
|
| - return result == null
|
| - ? false
|
| - : result.implementation == element.implementation;
|
| - }
|
| -
|
| - static Element findMatchIn(ClassElement cls, Selector selector) {
|
| - // Use the [:implementation] of [cls] in case the found [element]
|
| - // is in the patch class.
|
| - return cls.implementation.lookupByName(selector.memberName);
|
| - }
|
| -
|
| - /**
|
| * Returns whether [element] is a potential target when being
|
| * invoked on this type mask. [selector] is used to ensure library
|
| * privacy is taken into account.
|
| @@ -547,7 +516,8 @@ class FlatTypeMask implements TypeMask {
|
| assert(element.name == selector.name);
|
| if (isEmpty) return false;
|
| if (isNull) {
|
| - return hasElementIn(backendClasses.nullImplementation, selector, element);
|
| + return closedWorld.hasElementIn(
|
| + backendClasses.nullImplementation, selector, element);
|
| }
|
|
|
| // TODO(kasperl): Can't we just avoid creating typed selectors
|
| @@ -563,14 +533,14 @@ class FlatTypeMask implements TypeMask {
|
| if (other == backendClasses.nullImplementation) {
|
| return isNullable;
|
| } else if (isExact) {
|
| - return hasElementIn(self, selector, element);
|
| + return closedWorld.hasElementIn(self, selector, element);
|
| } else if (isSubclass) {
|
| - return hasElementIn(self, selector, element) ||
|
| + return closedWorld.hasElementIn(self, selector, element) ||
|
| other.isSubclassOf(self) ||
|
| closedWorld.hasAnySubclassThatMixes(self, other);
|
| } else {
|
| assert(isSubtype);
|
| - bool result = hasElementIn(self, selector, element) ||
|
| + bool result = closedWorld.hasElementIn(self, selector, element) ||
|
| other.implementsInterface(self) ||
|
| closedWorld.hasAnySubclassThatImplements(other, base) ||
|
| closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base);
|
| @@ -579,108 +549,28 @@ class FlatTypeMask implements TypeMask {
|
| // can be hit from any of the mixin applications.
|
| Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self);
|
| return mixinUses.any((mixinApplication) =>
|
| - hasElementIn(mixinApplication, selector, element) ||
|
| + closedWorld.hasElementIn(mixinApplication, selector, element) ||
|
| other.isSubclassOf(mixinApplication) ||
|
| closedWorld.hasAnySubclassThatMixes(mixinApplication, other));
|
| }
|
| }
|
|
|
| - /**
|
| - * Returns whether a [selector] call on an instance of [cls]
|
| - * will hit a method at runtime, and not go through [noSuchMethod].
|
| - */
|
| - static bool hasConcreteMatch(
|
| - ClassElement cls, Selector selector, ClosedWorld world) {
|
| - assert(invariant(cls, world.isInstantiated(cls),
|
| - message: '$cls has not been instantiated.'));
|
| - Element element = findMatchIn(cls, selector);
|
| - if (element == null) return false;
|
| -
|
| - if (element.isAbstract) {
|
| - ClassElement enclosingClass = element.enclosingClass;
|
| - return hasConcreteMatch(enclosingClass.superclass, selector, world);
|
| - }
|
| - return selector.appliesUntyped(element);
|
| - }
|
| -
|
| bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) {
|
| // A call on an empty type mask is either dead code, or a call on
|
| // `null`.
|
| if (isEmptyOrNull) return false;
|
| // A call on an exact mask for an abstract class is dead code.
|
| - if (isExact && base.isAbstract) return false;
|
| - // If the receiver is guaranteed to have a member that
|
| - // matches what we're looking for, there's no need to
|
| - // introduce a noSuchMethod handler. It will never be called.
|
| - //
|
| - // As an example, consider this class hierarchy:
|
| - //
|
| - // A <-- noSuchMethod
|
| - // / \
|
| - // C B <-- foo
|
| - //
|
| - // If we know we're calling foo on an object of type B we
|
| - // don't have to worry about the noSuchMethod method in A
|
| - // because objects of type B implement foo. On the other hand,
|
| - // if we end up calling foo on something of type C we have to
|
| - // add a handler for it.
|
| -
|
| - // If the holders of all user-defined noSuchMethod
|
| - // implementations that might be applicable to the receiver
|
| - // type have a matching member for the current name and
|
| - // selector, we avoid introducing a noSuchMethod handler.
|
| - //
|
| - // As an example, consider this class hierarchy:
|
| - //
|
| - // A <-- foo
|
| - // / \
|
| - // noSuchMethod --> B C <-- bar
|
| - // | |
|
| - // C D <-- noSuchMethod
|
| - //
|
| - // When calling foo on an object of type A, we know that the
|
| - // implementations of noSuchMethod are in the classes B and D
|
| - // that also (indirectly) implement foo, so we do not need a
|
| - // handler for it.
|
| - //
|
| - // If we're calling bar on an object of type D, we don't need
|
| - // the handler either because all objects of type D implement
|
| - // bar through inheritance.
|
| - //
|
| - // If we're calling bar on an object of type A we do need the
|
| - // handler because we may have to call B.noSuchMethod since B
|
| - // does not implement bar.
|
| -
|
| - /// Returns `true` if [cls] is an instantiated class that does not have
|
| - /// a concrete method matching [selector].
|
| - bool needsNoSuchMethod(ClassElement cls) {
|
| - // We can skip uninstantiated subclasses.
|
| - // TODO(johnniwinther): Put filtering into the (Class)World.
|
| - if (!closedWorld.isInstantiated(cls)) {
|
| - return false;
|
| - }
|
| - // We can just skip abstract classes because we know no
|
| - // instance of them will be created at runtime, and
|
| - // therefore there is no instance that will require
|
| - // [noSuchMethod] handling.
|
| - return !cls.isAbstract && !hasConcreteMatch(cls, selector, closedWorld);
|
| - }
|
| -
|
| - bool baseNeedsNoSuchMethod = needsNoSuchMethod(base);
|
| - if (isExact || baseNeedsNoSuchMethod) {
|
| - return baseNeedsNoSuchMethod;
|
| - }
|
| -
|
| - Iterable<ClassElement> subclassesToCheck;
|
| - if (isSubtype) {
|
| - subclassesToCheck = closedWorld.strictSubtypesOf(base);
|
| - } else {
|
| - assert(isSubclass);
|
| - subclassesToCheck = closedWorld.strictSubclassesOf(base);
|
| - }
|
| + // TODO(johnniwinther): A type mask cannot be abstract. Remove the need
|
| + // for this noise (currently used for super-calls in inference and mirror
|
| + // usage).
|
| + if (isExact && closedWorld.isAbstract(base)) return false;
|
|
|
| - return subclassesToCheck != null &&
|
| - subclassesToCheck.any(needsNoSuchMethod);
|
| + return closedWorld.needsNoSuchMethod(
|
| + base,
|
| + selector,
|
| + isExact
|
| + ? ClassQuery.EXACT
|
| + : (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE));
|
| }
|
|
|
| Element locateSingleElement(Selector selector, Compiler compiler) {
|
| @@ -689,7 +579,7 @@ class FlatTypeMask implements TypeMask {
|
| compiler.closedWorld.allFunctions.filter(selector, this);
|
| if (targets.length != 1) return null;
|
| Element result = targets.first;
|
| - ClassElement enclosing = result.enclosingClass;
|
| + ClassElement enclosing = result.enclosingClass.declaration;
|
| // We only return the found element if it is guaranteed to be implemented on
|
| // all classes in the receiver type [this]. It could be found only in a
|
| // subclass or in an inheritance-wise unrelated class in case of subtype
|
| @@ -703,7 +593,7 @@ class FlatTypeMask implements TypeMask {
|
| //}
|
| return null;
|
| } else {
|
| - if (base.isSubclassOf(enclosing)) return result;
|
| + if (closedWorld.isSubclassOf(base, enclosing)) return result;
|
| if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result;
|
| }
|
| return null;
|
|
|