mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +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 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);
|
||||
|
@ -516,27 +506,6 @@ class FlatTypeMask implements TypeMask {
|
|||
: 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
|
||||
* invoked on this type mask. [selector] is used to ensure library
|
||||
|
@ -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.
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -54,23 +54,23 @@ abstract class ForwardingTypeMask implements TypeMask {
|
|||
return forwardTo.containsOnlyString(closedWorld);
|
||||
}
|
||||
|
||||
bool containsOnly(ClassElement element) {
|
||||
return forwardTo.containsOnly(element);
|
||||
bool containsOnly(Entity cls) {
|
||||
return forwardTo.containsOnly(cls);
|
||||
}
|
||||
|
||||
bool satisfies(ClassElement cls, ClosedWorld closedWorld) {
|
||||
bool satisfies(Entity cls, ClosedWorld closedWorld) {
|
||||
return forwardTo.satisfies(cls, closedWorld);
|
||||
}
|
||||
|
||||
bool contains(ClassElement type, ClosedWorld closedWorld) {
|
||||
return forwardTo.contains(type, closedWorld);
|
||||
bool contains(Entity cls, ClosedWorld closedWorld) {
|
||||
return forwardTo.contains(cls, closedWorld);
|
||||
}
|
||||
|
||||
bool containsAll(ClosedWorld closedWorld) {
|
||||
return forwardTo.containsAll(closedWorld);
|
||||
}
|
||||
|
||||
ClassElement singleClass(ClosedWorld closedWorld) {
|
||||
Entity singleClass(ClosedWorld closedWorld) {
|
||||
return forwardTo.singleClass(closedWorld);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import '../universe/world_builder.dart'
|
|||
UniverseSelectorConstraints,
|
||||
SelectorConstraintsStrategy;
|
||||
import '../util/util.dart';
|
||||
import '../world.dart' show ClosedWorld;
|
||||
import '../world.dart' show ClassQuery, ClosedWorld;
|
||||
import 'abstract_value_domain.dart' show AbstractValue;
|
||||
|
||||
part 'container_type_mask.dart';
|
||||
|
|
|
@ -77,30 +77,30 @@ class TypeMaskStrategy implements SelectorConstraintsStrategy {
|
|||
*/
|
||||
abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
||||
factory TypeMask(
|
||||
ClassElement base, int kind, bool isNullable, ClosedWorld closedWorld) {
|
||||
Entity base, int kind, bool isNullable, ClosedWorld closedWorld) {
|
||||
return new FlatTypeMask.normalized(
|
||||
base, (kind << 1) | (isNullable ? 1 : 0), closedWorld);
|
||||
}
|
||||
|
||||
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),
|
||||
message: () => "Cannot create exact type mask for uninstantiated "
|
||||
"class $base.\n${closedWorld.dump(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);
|
||||
return const TypeMask.empty();
|
||||
}
|
||||
|
||||
factory TypeMask.subclass(ClassElement base, ClosedWorld closedWorld) {
|
||||
factory TypeMask.subclass(Entity base, ClosedWorld closedWorld) {
|
||||
assert(invariant(base, closedWorld.isInstantiated(base),
|
||||
message: () => "Cannot create subclass type mask for uninstantiated "
|
||||
"class $base.\n${closedWorld.dump(base)}"));
|
||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
||||
Entity topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
||||
if (topmost == null) {
|
||||
return new TypeMask.empty();
|
||||
} else if (closedWorld.hasAnyStrictSubclass(topmost)) {
|
||||
|
@ -110,8 +110,8 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
|||
}
|
||||
}
|
||||
|
||||
factory TypeMask.subtype(ClassElement base, ClosedWorld closedWorld) {
|
||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
||||
factory TypeMask.subtype(Entity base, ClosedWorld closedWorld) {
|
||||
Entity topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
||||
if (topmost == null) {
|
||||
return new TypeMask.empty();
|
||||
}
|
||||
|
@ -127,26 +127,25 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
|||
|
||||
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),
|
||||
message: () => "Cannot create exact type mask for uninstantiated "
|
||||
"class $base.\n${closedWorld.dump(base)}"));
|
||||
return new FlatTypeMask.nonNullExact(base);
|
||||
}
|
||||
|
||||
factory TypeMask.nonNullExactOrEmpty(
|
||||
ClassElement base, ClosedWorld closedWorld) {
|
||||
factory TypeMask.nonNullExactOrEmpty(Entity base, ClosedWorld closedWorld) {
|
||||
if (closedWorld.isInstantiated(base)) {
|
||||
return new FlatTypeMask.nonNullExact(base);
|
||||
}
|
||||
return const TypeMask.nonNullEmpty();
|
||||
}
|
||||
|
||||
factory TypeMask.nonNullSubclass(ClassElement base, ClosedWorld closedWorld) {
|
||||
factory TypeMask.nonNullSubclass(Entity base, ClosedWorld closedWorld) {
|
||||
assert(invariant(base, closedWorld.isInstantiated(base),
|
||||
message: () => "Cannot create subclass type mask for uninstantiated "
|
||||
"class $base.\n${closedWorld.dump(base)}"));
|
||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
||||
Entity topmost = closedWorld.getLubOfInstantiatedSubclasses(base);
|
||||
if (topmost == null) {
|
||||
return new TypeMask.nonNullEmpty();
|
||||
} else if (closedWorld.hasAnyStrictSubclass(topmost)) {
|
||||
|
@ -156,8 +155,8 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
|||
}
|
||||
}
|
||||
|
||||
factory TypeMask.nonNullSubtype(ClassElement base, ClosedWorld closedWorld) {
|
||||
ClassElement topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
||||
factory TypeMask.nonNullSubtype(Entity base, ClosedWorld closedWorld) {
|
||||
Entity topmost = closedWorld.getLubOfInstantiatedSubtypes(base);
|
||||
if (topmost == null) {
|
||||
return new TypeMask.nonNullEmpty();
|
||||
}
|
||||
|
@ -287,7 +286,7 @@ abstract class TypeMask implements ReceiverConstraint, AbstractValue {
|
|||
bool containsOnlyNum(ClosedWorld closedWorld);
|
||||
bool containsOnlyBool(ClosedWorld closedWorld);
|
||||
bool containsOnlyString(ClosedWorld closedWorld);
|
||||
bool containsOnly(ClassElement element);
|
||||
bool containsOnly(Entity cls);
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
bool containsAll(ClosedWorld closedWorld);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 isNullable = masks.any((e) => e.isNullable);
|
||||
|
||||
List<ClassElement> masksBases = masks.map((mask) => mask.base).toList();
|
||||
Iterable<ClassElement> candidates =
|
||||
closedWorld.commonSupertypesOf(masksBases);
|
||||
List masksBases = masks.map((mask) => mask.base).toList();
|
||||
Iterable<Entity> candidates = closedWorld.commonSupertypesOf(masksBases);
|
||||
|
||||
// Compute the best candidate and its kind.
|
||||
ClassElement bestElement;
|
||||
Entity bestElement;
|
||||
int bestKind;
|
||||
int bestSize;
|
||||
for (ClassElement candidate in candidates) {
|
||||
for (Entity candidate in candidates) {
|
||||
bool isInstantiatedStrictSubclass(cls) =>
|
||||
cls != candidate &&
|
||||
closedWorld.isDirectlyInstantiated(cls) &&
|
||||
|
@ -231,14 +230,14 @@ class UnionTypeMask implements TypeMask {
|
|||
// Check we cover the base class.
|
||||
if (!contains(flat.base, closedWorld)) return false;
|
||||
// Check for other members.
|
||||
Iterable<ClassElement> members;
|
||||
Iterable<Entity> members;
|
||||
if (flat.isSubclass) {
|
||||
members = closedWorld.strictSubclassesOf(flat.base);
|
||||
} else {
|
||||
assert(flat.isSubtype);
|
||||
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) {
|
||||
|
@ -306,23 +305,23 @@ class UnionTypeMask implements TypeMask {
|
|||
return disjointMasks.every((mask) => mask.containsOnlyString(closedWorld));
|
||||
}
|
||||
|
||||
bool containsOnly(ClassElement element) {
|
||||
bool containsOnly(Entity 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));
|
||||
}
|
||||
|
||||
bool contains(ClassElement type, ClosedWorld closedWorld) {
|
||||
return disjointMasks.any((e) => e.contains(type, closedWorld));
|
||||
bool contains(Entity cls, ClosedWorld closedWorld) {
|
||||
return disjointMasks.any((e) => e.contains(cls, closedWorld));
|
||||
}
|
||||
|
||||
bool containsAll(ClosedWorld closedWorld) {
|
||||
return disjointMasks.any((mask) => mask.containsAll(closedWorld));
|
||||
}
|
||||
|
||||
ClassElement singleClass(ClosedWorld closedWorld) => null;
|
||||
Entity singleClass(ClosedWorld closedWorld) => null;
|
||||
|
||||
bool needsNoSuchMethodHandling(Selector selector, ClosedWorld closedWorld) {
|
||||
return disjointMasks
|
||||
|
|
|
@ -55,6 +55,10 @@ abstract class ClosedWorld implements World {
|
|||
/// subclass.
|
||||
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.
|
||||
bool isImplemented(ClassElement cls);
|
||||
|
||||
|
@ -156,6 +160,52 @@ abstract class ClosedWorld implements World {
|
|||
/// Returns `true` if any subclass of [superclass] implements [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
|
||||
/// of known classes.
|
||||
///
|
||||
|
@ -170,9 +220,10 @@ abstract class ClosedWorld implements World {
|
|||
/// methods defined in [ClosedWorld].
|
||||
ClassSet getClassSet(ClassElement cls);
|
||||
|
||||
// TODO(johnniwinther): Find a better strategy for caching these.
|
||||
@deprecated
|
||||
List<Map<ClassElement, TypeMask>> get canonicalizedTypeMasks;
|
||||
/// Return the cached mask for [base] with the given flags, or
|
||||
/// calls [createMask] to create the mask and cache it.
|
||||
// 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
|
||||
/// world.
|
||||
|
@ -274,9 +325,15 @@ abstract class OpenWorld implements World {
|
|||
class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
||||
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
|
||||
/// `FlatTypeMask.flags` property.
|
||||
List<Map<ClassElement, TypeMask>> canonicalizedTypeMasks =
|
||||
final List<Map<ClassElement, TypeMask>> _canonicalizedTypeMasks =
|
||||
new List<Map<ClassElement, TypeMask>>.filled(8, null);
|
||||
|
||||
bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
|
||||
|
@ -342,6 +399,9 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
return node != null && node.isIndirectlyInstantiated;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAbstract(ClassElement cls) => cls.isAbstract;
|
||||
|
||||
/// Returns `true` if [cls] is implemented by an instantiated class.
|
||||
bool isImplemented(ClassElement cls) {
|
||||
assert(isClosed);
|
||||
|
@ -622,14 +682,14 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
/// Returns `true` if [cls] or any superclass mixes in [mixin].
|
||||
bool isSubclassOfMixinUseOf(ClassElement cls, ClassElement mixin) {
|
||||
assert(isClosed);
|
||||
assert(cls.isDeclaration);
|
||||
assert(mixin.isDeclaration);
|
||||
if (isUsedAsMixin(mixin)) {
|
||||
ClassElement current = cls.declaration;
|
||||
mixin = mixin.declaration;
|
||||
ClassElement current = cls;
|
||||
while (current != null) {
|
||||
current = current.declaration;
|
||||
if (current.isMixinApplication) {
|
||||
MixinApplicationElement application = current;
|
||||
if (application.mixin.declaration == mixin) return true;
|
||||
if (application.mixin == mixin) return true;
|
||||
}
|
||||
current = current.superclass;
|
||||
}
|
||||
|
@ -641,8 +701,8 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
/// of a mixin application of [y].
|
||||
bool everySubtypeIsSubclassOfOrMixinUseOf(ClassElement x, ClassElement y) {
|
||||
assert(isClosed);
|
||||
x = x.declaration;
|
||||
y = y.declaration;
|
||||
assert(x.isDeclaration);
|
||||
assert(y.isDeclaration);
|
||||
Map<ClassElement, bool> secondMap =
|
||||
_subtypeCoveredByCache[x] ??= <ClassElement, bool>{};
|
||||
return secondMap[y] ??= subtypesOf(x).every((ClassElement cls) =>
|
||||
|
@ -658,6 +718,72 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
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;
|
||||
BackendClasses get backendClasses => _backend.backendClasses;
|
||||
JavaScriptBackend get _backend => _compiler.backend;
|
||||
|
@ -1011,3 +1137,16 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
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