mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:19:48 +00:00
Decouple TypeMask from ClassElement
- by typing FlatTypeMask.base with Entity - moving needsNoSuchMethodHandling to ClosedWorld - and testing needsNoSuchMethodHandling R=sigmund@google.com Review URL: https://codereview.chromium.org/2420073002 .
This commit is contained in:
parent
c1b1151f54
commit
74b74a13f7
|
@ -14,17 +14,15 @@ class FlatTypeMask implements TypeMask {
|
||||||
static const int SUBCLASS = 2;
|
static const int SUBCLASS = 2;
|
||||||
static const int SUBTYPE = 3;
|
static const int SUBTYPE = 3;
|
||||||
|
|
||||||
final ClassElement base;
|
final Entity base;
|
||||||
final int flags;
|
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));
|
: this.internal(base, (kind << 1) | (isNullable ? 1 : 0));
|
||||||
|
|
||||||
FlatTypeMask.exact(ClassElement base) : this.internal(base, (EXACT << 1) | 1);
|
FlatTypeMask.exact(Entity base) : this.internal(base, (EXACT << 1) | 1);
|
||||||
FlatTypeMask.subclass(ClassElement base)
|
FlatTypeMask.subclass(Entity base) : this.internal(base, (SUBCLASS << 1) | 1);
|
||||||
: this.internal(base, (SUBCLASS << 1) | 1);
|
FlatTypeMask.subtype(Entity base) : this.internal(base, (SUBTYPE << 1) | 1);
|
||||||
FlatTypeMask.subtype(ClassElement base)
|
|
||||||
: this.internal(base, (SUBTYPE << 1) | 1);
|
|
||||||
|
|
||||||
const FlatTypeMask.nonNullEmpty()
|
const FlatTypeMask.nonNullEmpty()
|
||||||
: base = null,
|
: base = null,
|
||||||
|
@ -33,23 +31,22 @@ class FlatTypeMask implements TypeMask {
|
||||||
: base = null,
|
: base = null,
|
||||||
flags = 1;
|
flags = 1;
|
||||||
|
|
||||||
FlatTypeMask.nonNullExact(ClassElement base)
|
FlatTypeMask.nonNullExact(Entity base) : this.internal(base, EXACT << 1);
|
||||||
: this.internal(base, EXACT << 1);
|
FlatTypeMask.nonNullSubclass(Entity base)
|
||||||
FlatTypeMask.nonNullSubclass(ClassElement base)
|
|
||||||
: this.internal(base, SUBCLASS << 1);
|
: this.internal(base, SUBCLASS << 1);
|
||||||
FlatTypeMask.nonNullSubtype(ClassElement base)
|
FlatTypeMask.nonNullSubtype(Entity base) : this.internal(base, SUBTYPE << 1);
|
||||||
: this.internal(base, SUBTYPE << 1);
|
|
||||||
|
bool _validateBase(ClassElement element) => element.isDeclaration;
|
||||||
|
|
||||||
FlatTypeMask.internal(this.base, this.flags) {
|
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
|
* Ensures that the generated mask is normalized, i.e., a call to
|
||||||
* [TypeMask.assertIsNormalized] with the factory's result returns `true`.
|
* [TypeMask.assertIsNormalized] with the factory's result returns `true`.
|
||||||
*/
|
*/
|
||||||
factory FlatTypeMask.normalized(
|
factory FlatTypeMask.normalized(Entity base, int flags, ClosedWorld world) {
|
||||||
ClassElement base, int flags, ClosedWorld world) {
|
|
||||||
if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) {
|
if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) {
|
||||||
return new FlatTypeMask.internal(base, flags);
|
return new FlatTypeMask.internal(base, flags);
|
||||||
}
|
}
|
||||||
|
@ -61,14 +58,8 @@ class FlatTypeMask implements TypeMask {
|
||||||
if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) {
|
if (((flags >> 1) == SUBCLASS) && !world.hasAnyStrictSubclass(base)) {
|
||||||
flags = (flags & 0x1) | (EXACT << 1);
|
flags = (flags & 0x1) | (EXACT << 1);
|
||||||
}
|
}
|
||||||
Map<ClassElement, TypeMask> cachedMasks =
|
return world.getCachedMask(
|
||||||
world.canonicalizedTypeMasks[flags];
|
base, flags, () => new FlatTypeMask.internal(base, flags));
|
||||||
if (cachedMasks == null) {
|
|
||||||
world.canonicalizedTypeMasks[flags] =
|
|
||||||
cachedMasks = <ClassElement, TypeMask>{};
|
|
||||||
}
|
|
||||||
return cachedMasks.putIfAbsent(
|
|
||||||
base, () => new FlatTypeMask.internal(base, flags));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isEmpty => isEmptyOrNull && !isNullable;
|
bool get isEmpty => isEmptyOrNull && !isNullable;
|
||||||
|
@ -98,23 +89,23 @@ class FlatTypeMask implements TypeMask {
|
||||||
return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this;
|
return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(ClassElement type, ClosedWorld closedWorld) {
|
bool contains(Entity other, ClosedWorld closedWorld) {
|
||||||
assert(type.isDeclaration);
|
assert(_validateBase(other));
|
||||||
if (isEmptyOrNull) {
|
if (isEmptyOrNull) {
|
||||||
return false;
|
return false;
|
||||||
} else if (identical(base, type)) {
|
} else if (identical(base, other)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (isExact) {
|
} else if (isExact) {
|
||||||
return false;
|
return false;
|
||||||
} else if (isSubclass) {
|
} else if (isSubclass) {
|
||||||
return closedWorld.isSubclassOf(type, base);
|
return closedWorld.isSubclassOf(other, base);
|
||||||
} else {
|
} else {
|
||||||
assert(isSubtype);
|
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
|
// Special case basic types so that, for example, JSString is the
|
||||||
// single implementation of String.
|
// single implementation of String.
|
||||||
// The general optimization is to realize there is only one class that
|
// 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);
|
if (other is! FlatTypeMask) return other.containsMask(this, closedWorld);
|
||||||
// The other must be flat, so compare base and flags.
|
// The other must be flat, so compare base and flags.
|
||||||
FlatTypeMask flatOther = other;
|
FlatTypeMask flatOther = other;
|
||||||
ClassElement otherBase = flatOther.base;
|
Entity otherBase = flatOther.base;
|
||||||
// If other is exact, it only contains its base.
|
// If other is exact, it only contains its base.
|
||||||
// TODO(herhut): Get rid of isSingleImplementationOf.
|
// TODO(herhut): Get rid of isSingleImplementationOf.
|
||||||
if (flatOther.isExact) {
|
if (flatOther.isExact) {
|
||||||
|
@ -214,23 +205,21 @@ class FlatTypeMask implements TypeMask {
|
||||||
base == backendClasses.stringImplementation;
|
base == backendClasses.stringImplementation;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool containsOnly(ClassElement cls) {
|
bool containsOnly(Entity cls) {
|
||||||
assert(cls.isDeclaration);
|
assert(_validateBase(cls));
|
||||||
return base == cls;
|
return base == cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool satisfies(ClassElement cls, ClosedWorld closedWorld) {
|
bool satisfies(Entity cls, ClosedWorld closedWorld) {
|
||||||
assert(cls.isDeclaration);
|
assert(_validateBase(cls));
|
||||||
if (isEmptyOrNull) return false;
|
if (isEmptyOrNull) return false;
|
||||||
if (closedWorld.isSubtypeOf(base, cls)) return true;
|
if (closedWorld.isSubtypeOf(base, cls)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Returns the [Entity] if this type represents a single class, otherwise
|
||||||
* Returns the [ClassElement] if this type represents a single class,
|
/// returns `null`. This method is conservative.
|
||||||
* otherwise returns `null`. This method is conservative.
|
Entity singleClass(ClosedWorld closedWorld) {
|
||||||
*/
|
|
||||||
ClassElement singleClass(ClosedWorld closedWorld) {
|
|
||||||
if (isEmptyOrNull) return null;
|
if (isEmptyOrNull) return null;
|
||||||
if (isNullable) return null; // It is Null and some other class.
|
if (isNullable) return null; // It is Null and some other class.
|
||||||
if (isExact) {
|
if (isExact) {
|
||||||
|
@ -479,6 +468,7 @@ class FlatTypeMask implements TypeMask {
|
||||||
if (isSubclass && other.isSubclass) return intersectionEmpty(other);
|
if (isSubclass && other.isSubclass) return intersectionEmpty(other);
|
||||||
assert(isSubtype || other.isSubtype);
|
assert(isSubtype || other.isSubtype);
|
||||||
int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE;
|
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.
|
// Compute the set of classes that are contained in both type masks.
|
||||||
Set<ClassElement> common = commonContainedClasses(this, other, closedWorld);
|
Set<ClassElement> common = commonContainedClasses(this, other, closedWorld);
|
||||||
if (common == null || common.isEmpty) return intersectionEmpty(other);
|
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
|
// result will only be nullable if both masks are nullable. We have
|
||||||
// to normalize here, as we generate types based on new base classes.
|
// to normalize here, as we generate types based on new base classes.
|
||||||
int combined = (kind << 1) | (flags & other.flags & 1);
|
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 new FlatTypeMask.normalized(cls, combined, closedWorld);
|
||||||
});
|
});
|
||||||
return UnionTypeMask.unionOf(masks, closedWorld);
|
return UnionTypeMask.unionOf(masks, closedWorld);
|
||||||
|
@ -516,27 +506,6 @@ class FlatTypeMask implements TypeMask {
|
||||||
: new TypeMask.nonNullEmpty();
|
: new TypeMask.nonNullEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Returns whether [element] is a potential target when being
|
||||||
* invoked on this type mask. [selector] is used to ensure library
|
* invoked on this type mask. [selector] is used to ensure library
|
||||||
|
@ -547,7 +516,8 @@ class FlatTypeMask implements TypeMask {
|
||||||
assert(element.name == selector.name);
|
assert(element.name == selector.name);
|
||||||
if (isEmpty) return false;
|
if (isEmpty) return false;
|
||||||
if (isNull) {
|
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
|
// TODO(kasperl): Can't we just avoid creating typed selectors
|
||||||
|
@ -563,14 +533,14 @@ class FlatTypeMask implements TypeMask {
|
||||||
if (other == backendClasses.nullImplementation) {
|
if (other == backendClasses.nullImplementation) {
|
||||||
return isNullable;
|
return isNullable;
|
||||||
} else if (isExact) {
|
} else if (isExact) {
|
||||||
return hasElementIn(self, selector, element);
|
return closedWorld.hasElementIn(self, selector, element);
|
||||||
} else if (isSubclass) {
|
} else if (isSubclass) {
|
||||||
return hasElementIn(self, selector, element) ||
|
return closedWorld.hasElementIn(self, selector, element) ||
|
||||||
other.isSubclassOf(self) ||
|
other.isSubclassOf(self) ||
|
||||||
closedWorld.hasAnySubclassThatMixes(self, other);
|
closedWorld.hasAnySubclassThatMixes(self, other);
|
||||||
} else {
|
} else {
|
||||||
assert(isSubtype);
|
assert(isSubtype);
|
||||||
bool result = hasElementIn(self, selector, element) ||
|
bool result = closedWorld.hasElementIn(self, selector, element) ||
|
||||||
other.implementsInterface(self) ||
|
other.implementsInterface(self) ||
|
||||||
closedWorld.hasAnySubclassThatImplements(other, base) ||
|
closedWorld.hasAnySubclassThatImplements(other, base) ||
|
||||||
closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base);
|
closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base);
|
||||||
|
@ -579,108 +549,28 @@ class FlatTypeMask implements TypeMask {
|
||||||
// can be hit from any of the mixin applications.
|
// can be hit from any of the mixin applications.
|
||||||
Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self);
|
Iterable<ClassElement> mixinUses = closedWorld.mixinUsesOf(self);
|
||||||
return mixinUses.any((mixinApplication) =>
|
return mixinUses.any((mixinApplication) =>
|
||||||
hasElementIn(mixinApplication, selector, element) ||
|
closedWorld.hasElementIn(mixinApplication, selector, element) ||
|
||||||
other.isSubclassOf(mixinApplication) ||
|
other.isSubclassOf(mixinApplication) ||
|
||||||
closedWorld.hasAnySubclassThatMixes(mixinApplication, other));
|
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) {
|
bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) {
|
||||||
// A call on an empty type mask is either dead code, or a call on
|
// A call on an empty type mask is either dead code, or a call on
|
||||||
// `null`.
|
// `null`.
|
||||||
if (isEmptyOrNull) return false;
|
if (isEmptyOrNull) return false;
|
||||||
// A call on an exact mask for an abstract class is dead code.
|
// A call on an exact mask for an abstract class is dead code.
|
||||||
if (isExact && base.isAbstract) return false;
|
// TODO(johnniwinther): A type mask cannot be abstract. Remove the need
|
||||||
// If the receiver is guaranteed to have a member that
|
// for this noise (currently used for super-calls in inference and mirror
|
||||||
// matches what we're looking for, there's no need to
|
// usage).
|
||||||
// introduce a noSuchMethod handler. It will never be called.
|
if (isExact && closedWorld.isAbstract(base)) return false;
|
||||||
//
|
|
||||||
// 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
|
return closedWorld.needsNoSuchMethod(
|
||||||
// implementations that might be applicable to the receiver
|
base,
|
||||||
// type have a matching member for the current name and
|
selector,
|
||||||
// selector, we avoid introducing a noSuchMethod handler.
|
isExact
|
||||||
//
|
? ClassQuery.EXACT
|
||||||
// As an example, consider this class hierarchy:
|
: (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE));
|
||||||
//
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return subclassesToCheck != null &&
|
|
||||||
subclassesToCheck.any(needsNoSuchMethod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Element locateSingleElement(Selector selector, Compiler compiler) {
|
Element locateSingleElement(Selector selector, Compiler compiler) {
|
||||||
|
@ -689,7 +579,7 @@ class FlatTypeMask implements TypeMask {
|
||||||
compiler.closedWorld.allFunctions.filter(selector, this);
|
compiler.closedWorld.allFunctions.filter(selector, this);
|
||||||
if (targets.length != 1) return null;
|
if (targets.length != 1) return null;
|
||||||
Element result = targets.first;
|
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
|
// 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
|
// 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
|
// subclass or in an inheritance-wise unrelated class in case of subtype
|
||||||
|
@ -703,7 +593,7 @@ class FlatTypeMask implements TypeMask {
|
||||||
//}
|
//}
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
if (base.isSubclassOf(enclosing)) return result;
|
if (closedWorld.isSubclassOf(base, enclosing)) return result;
|
||||||
if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result;
|
if (closedWorld.isSubclassOfMixinUseOf(base, enclosing)) return result;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -54,23 +54,23 @@ abstract class ForwardingTypeMask implements TypeMask {
|
||||||
return forwardTo.containsOnlyString(closedWorld);
|
return forwardTo.containsOnlyString(closedWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool containsOnly(ClassElement element) {
|
bool containsOnly(Entity cls) {
|
||||||
return forwardTo.containsOnly(element);
|
return forwardTo.containsOnly(cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool satisfies(ClassElement cls, ClosedWorld closedWorld) {
|
bool satisfies(Entity cls, ClosedWorld closedWorld) {
|
||||||
return forwardTo.satisfies(cls, closedWorld);
|
return forwardTo.satisfies(cls, closedWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(ClassElement type, ClosedWorld closedWorld) {
|
bool contains(Entity cls, ClosedWorld closedWorld) {
|
||||||
return forwardTo.contains(type, closedWorld);
|
return forwardTo.contains(cls, closedWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool containsAll(ClosedWorld closedWorld) {
|
bool containsAll(ClosedWorld closedWorld) {
|
||||||
return forwardTo.containsAll(closedWorld);
|
return forwardTo.containsAll(closedWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassElement singleClass(ClosedWorld closedWorld) {
|
Entity singleClass(ClosedWorld closedWorld) {
|
||||||
return forwardTo.singleClass(closedWorld);
|
return forwardTo.singleClass(closedWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import '../universe/world_builder.dart'
|
||||||
UniverseSelectorConstraints,
|
UniverseSelectorConstraints,
|
||||||
SelectorConstraintsStrategy;
|
SelectorConstraintsStrategy;
|
||||||
import '../util/util.dart';
|
import '../util/util.dart';
|
||||||
import '../world.dart' show ClosedWorld;
|
import '../world.dart' show ClassQuery, ClosedWorld;
|
||||||
import 'abstract_value_domain.dart' show AbstractValue;
|
import 'abstract_value_domain.dart' show AbstractValue;
|
||||||
|
|
||||||
part 'container_type_mask.dart';
|
part 'container_type_mask.dart';
|
||||||
|
|
|
@ -77,30 +77,30 @@ class TypeMaskStrategy implements SelectorConstraintsStrategy {
|
||||||
*/
|
*/
|
||||||
abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||||
factory TypeMask(
|
factory TypeMask(
|
||||||
ClassElement base, int kind, bool isNullable, ClosedWorld closedWorld) {
|
Entity base, int kind, bool isNullable, ClosedWorld closedWorld) {
|
||||||
return new FlatTypeMask.normalized(
|
return new FlatTypeMask.normalized(
|
||||||
base, (kind << 1) | (isNullable ? 1 : 0), closedWorld);
|
base, (kind << 1) | (isNullable ? 1 : 0), closedWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
const factory TypeMask.empty() = FlatTypeMask.empty;
|
const factory TypeMask.empty() = FlatTypeMask.empty;
|
||||||
|
|
||||||
factory TypeMask.exact(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.exact(Entity base, ClosedWorld closedWorld) {
|
||||||
assert(invariant(base, closedWorld.isInstantiated(base),
|
assert(invariant(base, closedWorld.isInstantiated(base),
|
||||||
message: () => "Cannot create exact type mask for uninstantiated "
|
message: () => "Cannot create exact type mask for uninstantiated "
|
||||||
"class $base.\n${closedWorld.dump(base)}"));
|
"class $base.\n${closedWorld.dump(base)}"));
|
||||||
return new FlatTypeMask.exact(base);
|
return new FlatTypeMask.exact(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory TypeMask.exactOrEmpty(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.exactOrEmpty(Entity base, ClosedWorld closedWorld) {
|
||||||
if (closedWorld.isInstantiated(base)) return new FlatTypeMask.exact(base);
|
if (closedWorld.isInstantiated(base)) return new FlatTypeMask.exact(base);
|
||||||
return const TypeMask.empty();
|
return const TypeMask.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
factory TypeMask.subclass(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.subclass(Entity base, ClosedWorld closedWorld) {
|
||||||
assert(invariant(base, closedWorld.isInstantiated(base),
|
assert(invariant(base, closedWorld.isInstantiated(base),
|
||||||
message: () => "Cannot create subclass type mask for uninstantiated "
|
message: () => "Cannot create subclass type mask for uninstantiated "
|
||||||
"class $base.\n${closedWorld.dump(base)}"));
|
"class $base.\n${closedWorld.dump(base)}"));
|
||||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
Entity topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
||||||
if (topmost == null) {
|
if (topmost == null) {
|
||||||
return new TypeMask.empty();
|
return new TypeMask.empty();
|
||||||
} else if (closedWorld.hasAnyStrictSubclass(topmost)) {
|
} else if (closedWorld.hasAnyStrictSubclass(topmost)) {
|
||||||
|
@ -110,8 +110,8 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
factory TypeMask.subtype(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.subtype(Entity base, ClosedWorld closedWorld) {
|
||||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
Entity topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
||||||
if (topmost == null) {
|
if (topmost == null) {
|
||||||
return new TypeMask.empty();
|
return new TypeMask.empty();
|
||||||
}
|
}
|
||||||
|
@ -127,26 +127,25 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||||
|
|
||||||
const factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty;
|
const factory TypeMask.nonNullEmpty() = FlatTypeMask.nonNullEmpty;
|
||||||
|
|
||||||
factory TypeMask.nonNullExact(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.nonNullExact(Entity base, ClosedWorld closedWorld) {
|
||||||
assert(invariant(base, closedWorld.isInstantiated(base),
|
assert(invariant(base, closedWorld.isInstantiated(base),
|
||||||
message: () => "Cannot create exact type mask for uninstantiated "
|
message: () => "Cannot create exact type mask for uninstantiated "
|
||||||
"class $base.\n${closedWorld.dump(base)}"));
|
"class $base.\n${closedWorld.dump(base)}"));
|
||||||
return new FlatTypeMask.nonNullExact(base);
|
return new FlatTypeMask.nonNullExact(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory TypeMask.nonNullExactOrEmpty(
|
factory TypeMask.nonNullExactOrEmpty(Entity base, ClosedWorld closedWorld) {
|
||||||
ClassElement base, ClosedWorld closedWorld) {
|
|
||||||
if (closedWorld.isInstantiated(base)) {
|
if (closedWorld.isInstantiated(base)) {
|
||||||
return new FlatTypeMask.nonNullExact(base);
|
return new FlatTypeMask.nonNullExact(base);
|
||||||
}
|
}
|
||||||
return const TypeMask.nonNullEmpty();
|
return const TypeMask.nonNullEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
factory TypeMask.nonNullSubclass(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.nonNullSubclass(Entity base, ClosedWorld closedWorld) {
|
||||||
assert(invariant(base, closedWorld.isInstantiated(base),
|
assert(invariant(base, closedWorld.isInstantiated(base),
|
||||||
message: () => "Cannot create subclass type mask for uninstantiated "
|
message: () => "Cannot create subclass type mask for uninstantiated "
|
||||||
"class $base.\n${closedWorld.dump(base)}"));
|
"class $base.\n${closedWorld.dump(base)}"));
|
||||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
Entity topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
||||||
if (topmost == null) {
|
if (topmost == null) {
|
||||||
return new TypeMask.nonNullEmpty();
|
return new TypeMask.nonNullEmpty();
|
||||||
} else if (closedWorld.hasAnyStrictSubclass(topmost)) {
|
} else if (closedWorld.hasAnyStrictSubclass(topmost)) {
|
||||||
|
@ -156,8 +155,8 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
factory TypeMask.nonNullSubtype(ClassElement base, ClosedWorld closedWorld) {
|
factory TypeMask.nonNullSubtype(Entity base, ClosedWorld closedWorld) {
|
||||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
Entity topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
||||||
if (topmost == null) {
|
if (topmost == null) {
|
||||||
return new TypeMask.nonNullEmpty();
|
return new TypeMask.nonNullEmpty();
|
||||||
}
|
}
|
||||||
|
@ -287,7 +286,7 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||||
bool containsOnlyNum(ClosedWorld closedWorld);
|
bool containsOnlyNum(ClosedWorld closedWorld);
|
||||||
bool containsOnlyBool(ClosedWorld closedWorld);
|
bool containsOnlyBool(ClosedWorld closedWorld);
|
||||||
bool containsOnlyString(ClosedWorld closedWorld);
|
bool containsOnlyString(ClosedWorld closedWorld);
|
||||||
bool containsOnly(ClassElement element);
|
bool containsOnly(Entity cls);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two [TypeMask] objects for structural equality.
|
* Compares two [TypeMask] objects for structural equality.
|
||||||
|
@ -316,23 +315,21 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||||
/**
|
/**
|
||||||
* Returns whether this type mask is an instance of [cls].
|
* Returns whether this type mask is an instance of [cls].
|
||||||
*/
|
*/
|
||||||
bool satisfies(ClassElement cls, ClosedWorld closedWorld);
|
bool satisfies(Entity cls, ClosedWorld closedWorld);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not this type mask contains the given type.
|
* Returns whether or not this type mask contains the given class [cls].
|
||||||
*/
|
*/
|
||||||
bool contains(ClassElement type, ClosedWorld closedWorld);
|
bool contains(Entity cls, ClosedWorld closedWorld);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not this type mask contains all types.
|
* Returns whether or not this type mask contains all types.
|
||||||
*/
|
*/
|
||||||
bool containsAll(ClosedWorld closedWorld);
|
bool containsAll(ClosedWorld closedWorld);
|
||||||
|
|
||||||
/**
|
/// Returns the [Entity] if this type represents a single class, otherwise
|
||||||
* Returns the [ClassElement] if this type represents a single class,
|
/// returns `null`. This method is conservative.
|
||||||
* otherwise returns `null`. This method is conservative.
|
Entity singleClass(ClosedWorld closedWorld);
|
||||||
*/
|
|
||||||
ClassElement singleClass(ClosedWorld closedWorld);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a type mask representing the union of [this] and [other].
|
* Returns a type mask representing the union of [this] and [other].
|
||||||
|
|
|
@ -94,15 +94,14 @@ class UnionTypeMask implements TypeMask {
|
||||||
bool useSubclass = masks.every((e) => !e.isSubtype);
|
bool useSubclass = masks.every((e) => !e.isSubtype);
|
||||||
bool isNullable = masks.any((e) => e.isNullable);
|
bool isNullable = masks.any((e) => e.isNullable);
|
||||||
|
|
||||||
List<ClassElement> masksBases = masks.map((mask) => mask.base).toList();
|
List masksBases = masks.map((mask) => mask.base).toList();
|
||||||
Iterable<ClassElement> candidates =
|
Iterable<Entity> candidates = closedWorld.commonSupertypesOf(masksBases);
|
||||||
closedWorld.commonSupertypesOf(masksBases);
|
|
||||||
|
|
||||||
// Compute the best candidate and its kind.
|
// Compute the best candidate and its kind.
|
||||||
ClassElement bestElement;
|
Entity bestElement;
|
||||||
int bestKind;
|
int bestKind;
|
||||||
int bestSize;
|
int bestSize;
|
||||||
for (ClassElement candidate in candidates) {
|
for (Entity candidate in candidates) {
|
||||||
bool isInstantiatedStrictSubclass(cls) =>
|
bool isInstantiatedStrictSubclass(cls) =>
|
||||||
cls != candidate &&
|
cls != candidate &&
|
||||||
closedWorld.isDirectlyInstantiated(cls) &&
|
closedWorld.isDirectlyInstantiated(cls) &&
|
||||||
|
@ -231,14 +230,14 @@ class UnionTypeMask implements TypeMask {
|
||||||
// Check we cover the base class.
|
// Check we cover the base class.
|
||||||
if (!contains(flat.base, closedWorld)) return false;
|
if (!contains(flat.base, closedWorld)) return false;
|
||||||
// Check for other members.
|
// Check for other members.
|
||||||
Iterable<ClassElement> members;
|
Iterable<Entity> members;
|
||||||
if (flat.isSubclass) {
|
if (flat.isSubclass) {
|
||||||
members = closedWorld.strictSubclassesOf(flat.base);
|
members = closedWorld.strictSubclassesOf(flat.base);
|
||||||
} else {
|
} else {
|
||||||
assert(flat.isSubtype);
|
assert(flat.isSubtype);
|
||||||
members = closedWorld.strictSubtypesOf(flat.base);
|
members = closedWorld.strictSubtypesOf(flat.base);
|
||||||
}
|
}
|
||||||
return members.every((ClassElement cls) => this.contains(cls, closedWorld));
|
return members.every((Entity cls) => this.contains(cls, closedWorld));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInMask(TypeMask other, ClosedWorld closedWorld) {
|
bool isInMask(TypeMask other, ClosedWorld closedWorld) {
|
||||||
|
@ -306,23 +305,23 @@ class UnionTypeMask implements TypeMask {
|
||||||
return disjointMasks.every((mask) => mask.containsOnlyString(closedWorld));
|
return disjointMasks.every((mask) => mask.containsOnlyString(closedWorld));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool containsOnly(ClassElement element) {
|
bool containsOnly(Entity element) {
|
||||||
return disjointMasks.every((mask) => mask.containsOnly(element));
|
return disjointMasks.every((mask) => mask.containsOnly(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool satisfies(ClassElement cls, ClosedWorld closedWorld) {
|
bool satisfies(Entity cls, ClosedWorld closedWorld) {
|
||||||
return disjointMasks.every((mask) => mask.satisfies(cls, closedWorld));
|
return disjointMasks.every((mask) => mask.satisfies(cls, closedWorld));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(ClassElement type, ClosedWorld closedWorld) {
|
bool contains(Entity cls, ClosedWorld closedWorld) {
|
||||||
return disjointMasks.any((e) => e.contains(type, closedWorld));
|
return disjointMasks.any((e) => e.contains(cls, closedWorld));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool containsAll(ClosedWorld closedWorld) {
|
bool containsAll(ClosedWorld closedWorld) {
|
||||||
return disjointMasks.any((mask) => mask.containsAll(closedWorld));
|
return disjointMasks.any((mask) => mask.containsAll(closedWorld));
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassElement singleClass(ClosedWorld closedWorld) => null;
|
Entity singleClass(ClosedWorld closedWorld) => null;
|
||||||
|
|
||||||
bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) {
|
bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) {
|
||||||
return disjointMasks
|
return disjointMasks
|
||||||
|
|
|
@ -55,6 +55,10 @@ abstract class ClosedWorld implements World {
|
||||||
/// subclass.
|
/// subclass.
|
||||||
bool isIndirectlyInstantiated(ClassElement cls);
|
bool isIndirectlyInstantiated(ClassElement cls);
|
||||||
|
|
||||||
|
/// Returns `true` if [cls] is abstract and thus can only be instantiated
|
||||||
|
/// through subclasses.
|
||||||
|
bool isAbstract(ClassElement cls);
|
||||||
|
|
||||||
/// Returns `true` if [cls] is implemented by an instantiated class.
|
/// Returns `true` if [cls] is implemented by an instantiated class.
|
||||||
bool isImplemented(ClassElement cls);
|
bool isImplemented(ClassElement cls);
|
||||||
|
|
||||||
|
@ -156,6 +160,52 @@ abstract class ClosedWorld implements World {
|
||||||
/// Returns `true` if any subclass of [superclass] implements [type].
|
/// Returns `true` if any subclass of [superclass] implements [type].
|
||||||
bool hasAnySubclassThatImplements(ClassElement superclass, ClassElement type);
|
bool hasAnySubclassThatImplements(ClassElement superclass, ClassElement type);
|
||||||
|
|
||||||
|
/// Returns `true` if a call of [selector] on [cls] and/or subclasses/subtypes
|
||||||
|
/// need noSuchMethod handling.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
bool needsNoSuchMethod(ClassElement cls, Selector selector, ClassQuery query);
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
bool hasElementIn(ClassElement cls, Selector selector, Element element);
|
||||||
|
|
||||||
/// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
|
/// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
|
||||||
/// of known classes.
|
/// of known classes.
|
||||||
///
|
///
|
||||||
|
@ -170,9 +220,10 @@ abstract class ClosedWorld implements World {
|
||||||
/// methods defined in [ClosedWorld].
|
/// methods defined in [ClosedWorld].
|
||||||
ClassSet getClassSet(ClassElement cls);
|
ClassSet getClassSet(ClassElement cls);
|
||||||
|
|
||||||
// TODO(johnniwinther): Find a better strategy for caching these.
|
/// Return the cached mask for [base] with the given flags, or
|
||||||
@deprecated
|
/// calls [createMask] to create the mask and cache it.
|
||||||
List<Map<ClassElement, TypeMask>> get canonicalizedTypeMasks;
|
// TODO(johnniwinther): Find a better strategy for caching these?
|
||||||
|
TypeMask getCachedMask(ClassElement base, int flags, TypeMask createMask());
|
||||||
|
|
||||||
/// Returns the [FunctionSet] containing all live functions in the closed
|
/// Returns the [FunctionSet] containing all live functions in the closed
|
||||||
/// world.
|
/// world.
|
||||||
|
@ -274,9 +325,15 @@ abstract class OpenWorld implements World {
|
||||||
class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||||
bool _closed = false;
|
bool _closed = false;
|
||||||
|
|
||||||
|
TypeMask getCachedMask(ClassElement base, int flags, TypeMask createMask()) {
|
||||||
|
Map<ClassElement, TypeMask> cachedMasks =
|
||||||
|
_canonicalizedTypeMasks[flags] ??= <ClassElement, TypeMask>{};
|
||||||
|
return cachedMasks.putIfAbsent(base, createMask);
|
||||||
|
}
|
||||||
|
|
||||||
/// Cache of [FlatTypeMask]s grouped by the 8 possible values of the
|
/// Cache of [FlatTypeMask]s grouped by the 8 possible values of the
|
||||||
/// `FlatTypeMask.flags` property.
|
/// `FlatTypeMask.flags` property.
|
||||||
List<Map<ClassElement, TypeMask>> canonicalizedTypeMasks =
|
final List<Map<ClassElement, TypeMask>> _canonicalizedTypeMasks =
|
||||||
new List<Map<ClassElement, TypeMask>>.filled(8, null);
|
new List<Map<ClassElement, TypeMask>>.filled(8, null);
|
||||||
|
|
||||||
bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
|
bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
|
||||||
|
@ -342,6 +399,9 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||||
return node != null && node.isIndirectlyInstantiated;
|
return node != null && node.isIndirectlyInstantiated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isAbstract(ClassElement cls) => cls.isAbstract;
|
||||||
|
|
||||||
/// Returns `true` if [cls] is implemented by an instantiated class.
|
/// Returns `true` if [cls] is implemented by an instantiated class.
|
||||||
bool isImplemented(ClassElement cls) {
|
bool isImplemented(ClassElement cls) {
|
||||||
assert(isClosed);
|
assert(isClosed);
|
||||||
|
@ -622,14 +682,14 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||||
/// Returns `true` if [cls] or any superclass mixes in [mixin].
|
/// Returns `true` if [cls] or any superclass mixes in [mixin].
|
||||||
bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin) {
|
bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin) {
|
||||||
assert(isClosed);
|
assert(isClosed);
|
||||||
|
assert(cls.isDeclaration);
|
||||||
|
assert(mixin.isDeclaration);
|
||||||
if (isUsedAsMixin(mixin)) {
|
if (isUsedAsMixin(mixin)) {
|
||||||
ClassElement current = cls.declaration;
|
ClassElement current = cls;
|
||||||
mixin = mixin.declaration;
|
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
current = current.declaration;
|
|
||||||
if (current.isMixinApplication) {
|
if (current.isMixinApplication) {
|
||||||
MixinApplicationElement application = current;
|
MixinApplicationElement application = current;
|
||||||
if (application.mixin.declaration == mixin) return true;
|
if (application.mixin == mixin) return true;
|
||||||
}
|
}
|
||||||
current = current.superclass;
|
current = current.superclass;
|
||||||
}
|
}
|
||||||
|
@ -641,8 +701,8 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||||
/// of a mixin application of [y].
|
/// of a mixin application of [y].
|
||||||
bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y) {
|
bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y) {
|
||||||
assert(isClosed);
|
assert(isClosed);
|
||||||
x = x.declaration;
|
assert(x.isDeclaration);
|
||||||
y = y.declaration;
|
assert(y.isDeclaration);
|
||||||
Map<ClassElement, bool> secondMap =
|
Map<ClassElement, bool> secondMap =
|
||||||
_subtypeCoveredByCache[x] ??= <ClassElement, bool>{};
|
_subtypeCoveredByCache[x] ??= <ClassElement, bool>{};
|
||||||
return secondMap[y] ??= subtypesOf(x).every((ClassElement cls) =>
|
return secondMap[y] ??= subtypesOf(x).every((ClassElement cls) =>
|
||||||
|
@ -658,6 +718,72 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||||
return subclasses.contains(type);
|
return subclasses.contains(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element findMatchIn(ClassElement cls, Selector selector) {
|
||||||
|
// Use the [:implementation] of [cls] in case the found [element]
|
||||||
|
// is in the patch class.
|
||||||
|
var result = cls.implementation.lookupByName(selector.memberName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether a [selector] call on an instance of [cls]
|
||||||
|
/// will hit a method at runtime, and not go through [noSuchMethod].
|
||||||
|
bool hasConcreteMatch(ClassElement cls, Selector selector) {
|
||||||
|
assert(invariant(cls, 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);
|
||||||
|
}
|
||||||
|
return selector.appliesUntyped(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool needsNoSuchMethod(
|
||||||
|
ClassElement base, Selector selector, ClassQuery query) {
|
||||||
|
/// 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.
|
||||||
|
if (!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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool baseNeedsNoSuchMethod = needsNoSuchMethod(base);
|
||||||
|
if (query == ClassQuery.EXACT || baseNeedsNoSuchMethod) {
|
||||||
|
return baseNeedsNoSuchMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<ClassElement> subclassesToCheck;
|
||||||
|
if (query == ClassQuery.SUBTYPE) {
|
||||||
|
subclassesToCheck = strictSubtypesOf(base);
|
||||||
|
} else {
|
||||||
|
assert(query == ClassQuery.SUBCLASS);
|
||||||
|
subclassesToCheck = strictSubclassesOf(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subclassesToCheck != null &&
|
||||||
|
subclassesToCheck.any(needsNoSuchMethod);
|
||||||
|
}
|
||||||
|
|
||||||
final Compiler _compiler;
|
final Compiler _compiler;
|
||||||
BackendClasses get backendClasses => _backend.backendClasses;
|
BackendClasses get backendClasses => _backend.backendClasses;
|
||||||
JavaScriptBackend get _backend => _compiler.backend;
|
JavaScriptBackend get _backend => _compiler.backend;
|
||||||
|
@ -1011,3 +1137,16 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||||
return getMightBePassedToApply(element);
|
return getMightBePassedToApply(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enum values defining subset of classes included in queries.
|
||||||
|
enum ClassQuery {
|
||||||
|
/// Only the class itself is included.
|
||||||
|
EXACT,
|
||||||
|
|
||||||
|
/// The class and all subclasses (transitively) are included.
|
||||||
|
SUBCLASS,
|
||||||
|
|
||||||
|
/// The class and all classes that implement or subclass it (transitively)
|
||||||
|
/// are included.
|
||||||
|
SUBTYPE,
|
||||||
|
}
|
||||||
|
|
293
tests/compiler/dart2js/needs_no_such_method_test.dart
Normal file
293
tests/compiler/dart2js/needs_no_such_method_test.dart
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:async_helper/async_helper.dart';
|
||||||
|
import 'package:expect/expect.dart';
|
||||||
|
import 'package:compiler/src/common.dart';
|
||||||
|
import 'package:compiler/src/elements/elements.dart'
|
||||||
|
show Element, ClassElement, PublicName;
|
||||||
|
import 'package:compiler/src/universe/call_structure.dart';
|
||||||
|
import 'package:compiler/src/universe/selector.dart';
|
||||||
|
import 'package:compiler/src/world.dart' show ClosedWorld, ClassQuery;
|
||||||
|
import 'type_test_helper.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
asyncTest(() async {
|
||||||
|
await testClassSets();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const String CLASSES = r"""
|
||||||
|
class Superclass {
|
||||||
|
foo() {}
|
||||||
|
}
|
||||||
|
class Subclass extends Superclass {
|
||||||
|
bar() {}
|
||||||
|
}
|
||||||
|
class Subtype implements Superclass {
|
||||||
|
bar() {}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
testClassSets() async {
|
||||||
|
Selector foo, bar, baz;
|
||||||
|
ClosedWorld closedWorld;
|
||||||
|
ClassElement superclass, subclass, subtype;
|
||||||
|
|
||||||
|
Future run(List<String> instantiated) async {
|
||||||
|
StringBuffer main = new StringBuffer();
|
||||||
|
main.write('main() {');
|
||||||
|
for (String cls in instantiated) {
|
||||||
|
main.write('new $cls();');
|
||||||
|
}
|
||||||
|
main.write('}');
|
||||||
|
|
||||||
|
var env = await TypeEnvironment.create(CLASSES,
|
||||||
|
mainSource: main.toString(), useMockCompiler: false);
|
||||||
|
foo = new Selector.call(const PublicName('foo'), CallStructure.NO_ARGS);
|
||||||
|
bar = new Selector.call(const PublicName('bar'), CallStructure.NO_ARGS);
|
||||||
|
baz = new Selector.call(const PublicName('baz'), CallStructure.NO_ARGS);
|
||||||
|
|
||||||
|
closedWorld = env.compiler.closedWorld;
|
||||||
|
superclass = env.getElement('Superclass');
|
||||||
|
subclass = env.getElement('Subclass');
|
||||||
|
subtype = env.getElement('Subtype');
|
||||||
|
}
|
||||||
|
|
||||||
|
void check(ClassElement cls, ClassQuery query, Selector selector,
|
||||||
|
bool expectedResult) {
|
||||||
|
bool result = closedWorld.needsNoSuchMethod(cls, selector, query);
|
||||||
|
Expect.equals(expectedResult, result,
|
||||||
|
'Unexpected result for $selector in $cls ($query)');
|
||||||
|
}
|
||||||
|
|
||||||
|
await run([]);
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(superclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(superclass));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(superclass));
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(subclass));
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(subtype));
|
||||||
|
|
||||||
|
check(superclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(superclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(superclass, ClassQuery.EXACT, baz, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
check(subclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, baz, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
check(subtype, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, baz, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
await run(['Superclass']);
|
||||||
|
|
||||||
|
Expect.isTrue(closedWorld.isDirectlyInstantiated(superclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(superclass));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(superclass));
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(subclass));
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(subtype));
|
||||||
|
|
||||||
|
check(superclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(superclass, ClassQuery.EXACT, bar, true);
|
||||||
|
check(superclass, ClassQuery.EXACT, baz, true);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, bar, true);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, bar, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
check(subclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, baz, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
check(subtype, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, baz, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
await run(['Subclass']);
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(superclass));
|
||||||
|
Expect.isTrue(closedWorld.isIndirectlyInstantiated(superclass));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(superclass));
|
||||||
|
|
||||||
|
Expect.isTrue(closedWorld.isDirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subclass));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(subclass));
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(subtype));
|
||||||
|
|
||||||
|
check(superclass, ClassQuery.EXACT, foo, false);
|
||||||
|
// Should be false since the class is not directly instantiated:
|
||||||
|
check(superclass, ClassQuery.EXACT, bar, true);
|
||||||
|
// Should be false since the class is not directly instantiated:
|
||||||
|
check(superclass, ClassQuery.EXACT, baz, true);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
// Should be false since all live subclasses have a concrete implementation:
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, bar, true);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
// Should be false since all live subtypes have a concrete implementation:
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, bar, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
check(subclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, baz, true);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
check(subtype, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, baz, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
await run(['Subtype']);
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(superclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(superclass));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(superclass));
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isImplemented(subclass));
|
||||||
|
|
||||||
|
Expect.isTrue(closedWorld.isDirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subtype));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(subtype));
|
||||||
|
|
||||||
|
check(superclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(superclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(superclass, ClassQuery.EXACT, baz, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, foo, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
check(subclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, baz, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, baz, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, baz, false);
|
||||||
|
|
||||||
|
check(subtype, ClassQuery.EXACT, foo, true);
|
||||||
|
check(subtype, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, baz, true);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, foo, true);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, foo, true);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
await run(['Subclass', 'Subtype']);
|
||||||
|
|
||||||
|
Expect.isFalse(closedWorld.isDirectlyInstantiated(superclass));
|
||||||
|
Expect.isTrue(closedWorld.isIndirectlyInstantiated(superclass));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(superclass));
|
||||||
|
|
||||||
|
Expect.isTrue(closedWorld.isDirectlyInstantiated(subclass));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subclass));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(subclass));
|
||||||
|
|
||||||
|
Expect.isTrue(closedWorld.isDirectlyInstantiated(subtype));
|
||||||
|
Expect.isFalse(closedWorld.isIndirectlyInstantiated(subtype));
|
||||||
|
Expect.isTrue(closedWorld.isImplemented(subtype));
|
||||||
|
|
||||||
|
check(superclass, ClassQuery.EXACT, foo, false);
|
||||||
|
// Should be false since the class is not directly instantiated:
|
||||||
|
check(superclass, ClassQuery.EXACT, bar, true);
|
||||||
|
// Should be false since the class is not directly instantiated:
|
||||||
|
check(superclass, ClassQuery.EXACT, baz, true);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
// Should be false since all live subclasses have a concrete implementation:
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, bar, true);
|
||||||
|
check(superclass, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, foo, true);
|
||||||
|
// Should be false since all live subtypes have a concrete implementation:
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, bar, true);
|
||||||
|
check(superclass, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
check(subclass, ClassQuery.EXACT, foo, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subclass, ClassQuery.EXACT, baz, true);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, foo, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subclass, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
|
||||||
|
check(subtype, ClassQuery.EXACT, foo, true);
|
||||||
|
check(subtype, ClassQuery.EXACT, bar, false);
|
||||||
|
check(subtype, ClassQuery.EXACT, baz, true);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, foo, true);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBCLASS, baz, true);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, foo, true);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, bar, false);
|
||||||
|
check(subtype, ClassQuery.SUBTYPE, baz, true);
|
||||||
|
}
|
Loading…
Reference in a new issue