mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:35:05 +00:00
Introduce isAbstractlyInstantiated to avoid exact masks of abstract classes.
Classes can be instantiated directly, abstractly or indirectly: When [cls] is directly instantiated: This means that at runtime instances of exactly [cls] are assumed to exist. When [cls] is abstractly instantiated: This means that at runtime instances of [cls] or unknown subclasses of [cls] are assumed to exist. This is used to mark native and/or reflectable classes as instantiated. For native classes we do not know the exact class that instantiates [cls] so [cls] here represents the root of the subclasses. For reflectable classes we need event abstract classes to be 'live' even though they cannot themselves be instantiated. When [cls] is indirectly instantiated: This means that a subclass of [cls] is directly or abstractly instantiated. R=sigmund@google.com Review URL: https://codereview.chromium.org/2443953002 .
This commit is contained in:
parent
bdeb6023af
commit
b0ecbef3cc
|
@ -481,9 +481,7 @@ class SimpleTypeInferrerVisitor<T>
|
|||
});
|
||||
}
|
||||
if (analyzedElement.isGenerativeConstructor && cls.isAbstract) {
|
||||
if (compiler.closedWorld.isDirectlyInstantiated(cls)) {
|
||||
returnType = types.nonNullExact(cls);
|
||||
} else if (compiler.closedWorld.isIndirectlyInstantiated(cls)) {
|
||||
if (compiler.closedWorld.isInstantiated(cls)) {
|
||||
returnType = types.nonNullSubclass(cls);
|
||||
} else {
|
||||
// TODO(johnniwinther): Avoid analyzing [analyzedElement] in this
|
||||
|
|
|
@ -88,6 +88,8 @@ class UnionTypeMask implements TypeMask {
|
|||
}
|
||||
|
||||
static TypeMask flatten(List<FlatTypeMask> masks, ClosedWorld closedWorld) {
|
||||
// TODO(johnniwinther): Move this computation to [ClosedWorld] and use the
|
||||
// class set structures.
|
||||
assert(masks.length > 1);
|
||||
// If either type mask is a subtype type mask, we cannot use a
|
||||
// subclass type mask to represent their union.
|
||||
|
@ -104,7 +106,7 @@ class UnionTypeMask implements TypeMask {
|
|||
for (Entity candidate in candidates) {
|
||||
bool isInstantiatedStrictSubclass(cls) =>
|
||||
cls != candidate &&
|
||||
closedWorld.isDirectlyInstantiated(cls) &&
|
||||
closedWorld.isExplicitlyInstantiated(cls) &&
|
||||
closedWorld.isSubclassOf(cls, candidate);
|
||||
|
||||
int size;
|
||||
|
|
|
@ -6,6 +6,7 @@ library dart2js.world.class_set;
|
|||
|
||||
import 'dart:collection' show IterableBase;
|
||||
|
||||
import '../common.dart';
|
||||
import '../elements/elements.dart' show ClassElement;
|
||||
import '../util/enumset.dart' show EnumSet;
|
||||
import '../util/util.dart' show Link;
|
||||
|
@ -15,6 +16,7 @@ enum Instantiation {
|
|||
UNINSTANTIATED,
|
||||
DIRECTLY_INSTANTIATED,
|
||||
INDIRECTLY_INSTANTIATED,
|
||||
ABSTRACTLY_INSTANTIATED,
|
||||
}
|
||||
|
||||
/// Node for [cls] in a tree forming the subclass relation of [ClassElement]s.
|
||||
|
@ -49,16 +51,18 @@ class ClassHierarchyNode {
|
|||
static final EnumSet<Instantiation> INSTANTIATED =
|
||||
new EnumSet<Instantiation>.fromValues(const <Instantiation>[
|
||||
Instantiation.DIRECTLY_INSTANTIATED,
|
||||
Instantiation.INDIRECTLY_INSTANTIATED
|
||||
Instantiation.INDIRECTLY_INSTANTIATED,
|
||||
Instantiation.ABSTRACTLY_INSTANTIATED,
|
||||
], fixed: true);
|
||||
|
||||
/// Enum set for selecting directly instantiated classes in
|
||||
/// Enum set for selecting directly and abstractly instantiated classes in
|
||||
/// [ClassHierarchyNode.subclassesByMask],
|
||||
/// [ClassHierarchyNode.subclassesByMask] and [ClassSet.subtypesByMask].
|
||||
static final EnumSet<Instantiation> DIRECTLY_INSTANTIATED =
|
||||
new EnumSet<Instantiation>.fromValues(
|
||||
const <Instantiation>[Instantiation.DIRECTLY_INSTANTIATED],
|
||||
fixed: true);
|
||||
static final EnumSet<Instantiation> EXPLICITLY_INSTANTIATED =
|
||||
new EnumSet<Instantiation>.fromValues(const <Instantiation>[
|
||||
Instantiation.DIRECTLY_INSTANTIATED,
|
||||
Instantiation.ABSTRACTLY_INSTANTIATED
|
||||
], fixed: true);
|
||||
|
||||
/// Enum set for selecting all classes in
|
||||
/// [ClassHierarchyNode.subclassesByMask],
|
||||
|
@ -72,7 +76,8 @@ class ClassHierarchyNode {
|
|||
static EnumSet<Instantiation> createMask(
|
||||
{bool includeDirectlyInstantiated: true,
|
||||
bool includeIndirectlyInstantiated: true,
|
||||
bool includeUninstantiated: true}) {
|
||||
bool includeUninstantiated: true,
|
||||
bool includeAbstractlyInstantiated: true}) {
|
||||
EnumSet<Instantiation> mask = new EnumSet<Instantiation>();
|
||||
if (includeDirectlyInstantiated) {
|
||||
mask.add(Instantiation.DIRECTLY_INSTANTIATED);
|
||||
|
@ -83,6 +88,9 @@ class ClassHierarchyNode {
|
|||
if (includeUninstantiated) {
|
||||
mask.add(Instantiation.UNINSTANTIATED);
|
||||
}
|
||||
if (includeAbstractlyInstantiated) {
|
||||
mask.add(Instantiation.ABSTRACTLY_INSTANTIATED);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
@ -106,23 +114,54 @@ class ClassHierarchyNode {
|
|||
|
||||
void set isDirectlyInstantiated(bool value) {
|
||||
if (value != isDirectlyInstantiated) {
|
||||
ClassHierarchyNode parent = parentNode;
|
||||
if (value) {
|
||||
_mask.remove(Instantiation.UNINSTANTIATED);
|
||||
_mask.add(Instantiation.DIRECTLY_INSTANTIATED);
|
||||
while (parent != null) {
|
||||
parent._updateInstantiatedSubclassCount(1);
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
} else {
|
||||
_mask.remove(Instantiation.DIRECTLY_INSTANTIATED);
|
||||
if (_mask.isEmpty) {
|
||||
_mask.add(Instantiation.UNINSTANTIATED);
|
||||
}
|
||||
while (parent != null) {
|
||||
parent._updateInstantiatedSubclassCount(-1);
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
_updateParentInstantiatedSubclassCount(
|
||||
Instantiation.DIRECTLY_INSTANTIATED,
|
||||
add: value);
|
||||
}
|
||||
}
|
||||
|
||||
/// `true` if [cls] has been abstractly instantiated. This means that at
|
||||
/// runtime instances of [cls] or unknown subclasses of [cls] are assumed to
|
||||
/// exist.
|
||||
///
|
||||
/// This is used to mark native and/or reflectable classes as instantiated.
|
||||
/// For native classes we do not know the exact class that instantiates [cls]
|
||||
/// so [cls] here represents the root of the subclasses. For reflectable
|
||||
/// classes we need event abstract classes to be 'live' even though they
|
||||
/// cannot themselves be instantiated.
|
||||
bool get isAbstractlyInstantiated =>
|
||||
_mask.contains(Instantiation.ABSTRACTLY_INSTANTIATED);
|
||||
|
||||
void set isAbstractlyInstantiated(bool value) {
|
||||
if (value != isAbstractlyInstantiated) {
|
||||
_updateParentInstantiatedSubclassCount(
|
||||
Instantiation.ABSTRACTLY_INSTANTIATED,
|
||||
add: value);
|
||||
}
|
||||
}
|
||||
|
||||
/// `true` if [cls] is either directly or abstractly instantiated.
|
||||
bool get isExplicitlyInstantiated =>
|
||||
isDirectlyInstantiated || isAbstractlyInstantiated;
|
||||
|
||||
void _updateParentInstantiatedSubclassCount(Instantiation instantiation,
|
||||
{bool add}) {
|
||||
ClassHierarchyNode parent = parentNode;
|
||||
if (add) {
|
||||
_mask.remove(Instantiation.UNINSTANTIATED);
|
||||
_mask.add(instantiation);
|
||||
while (parent != null) {
|
||||
parent._updateInstantiatedSubclassCount(1);
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
} else {
|
||||
_mask.remove(instantiation);
|
||||
if (_mask.isEmpty) {
|
||||
_mask.add(Instantiation.UNINSTANTIATED);
|
||||
}
|
||||
while (parent != null) {
|
||||
parent._updateInstantiatedSubclassCount(-1);
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,8 +227,9 @@ class ClassHierarchyNode {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// `true` if [cls] has been directly or indirectly instantiated.
|
||||
bool get isInstantiated => isDirectlyInstantiated || isIndirectlyInstantiated;
|
||||
/// `true` if [cls] has been directly, indirectly, or abstractly instantiated.
|
||||
bool get isInstantiated =>
|
||||
isExplicitlyInstantiated || isIndirectlyInstantiated;
|
||||
|
||||
/// Returns an [Iterable] of the subclasses of [cls] possibly including [cls].
|
||||
///
|
||||
|
@ -270,9 +310,12 @@ class ClassHierarchyNode {
|
|||
}
|
||||
|
||||
ClassElement _computeLeastUpperInstantiatedSubclass() {
|
||||
if (isDirectlyInstantiated) {
|
||||
if (isExplicitlyInstantiated) {
|
||||
return cls;
|
||||
}
|
||||
if (!isInstantiated) {
|
||||
return null;
|
||||
}
|
||||
ClassHierarchyNode subclass;
|
||||
for (Link<ClassHierarchyNode> link = _directSubclasses;
|
||||
!link.isEmpty;
|
||||
|
@ -311,6 +354,9 @@ class ClassHierarchyNode {
|
|||
if (isIndirectlyInstantiated) {
|
||||
sb.write(' indirectly');
|
||||
}
|
||||
if (isAbstractlyInstantiated) {
|
||||
sb.write(' abstractly');
|
||||
}
|
||||
sb.write(' [');
|
||||
if (_directSubclasses.isEmpty) {
|
||||
sb.write(']');
|
||||
|
@ -439,7 +485,7 @@ class ClassSet {
|
|||
int count = node.instantiatedSubclassCount;
|
||||
if (_subtypes != null) {
|
||||
for (ClassHierarchyNode subtypeNode in _subtypes) {
|
||||
if (subtypeNode.isDirectlyInstantiated) {
|
||||
if (subtypeNode.isExplicitlyInstantiated) {
|
||||
count++;
|
||||
}
|
||||
count += subtypeNode.instantiatedSubclassCount;
|
||||
|
@ -647,7 +693,7 @@ class ClassSet {
|
|||
}
|
||||
|
||||
ClassElement _computeLeastUpperInstantiatedSubtype() {
|
||||
if (node.isDirectlyInstantiated) {
|
||||
if (node.isExplicitlyInstantiated) {
|
||||
return cls;
|
||||
}
|
||||
if (_subtypes == null) {
|
||||
|
|
|
@ -10,6 +10,8 @@ import '../common.dart';
|
|||
import '../compiler.dart' show Compiler;
|
||||
import '../dart_types.dart';
|
||||
import '../elements/elements.dart';
|
||||
import '../universe/class_set.dart' show Instantiation;
|
||||
import '../util/enumset.dart';
|
||||
import '../util/util.dart';
|
||||
import '../world.dart' show World, ClosedWorld, OpenWorld;
|
||||
import 'selector.dart' show Selector;
|
||||
|
@ -156,6 +158,10 @@ abstract class ResolutionWorldBuilder implements WorldBuilder {
|
|||
|
||||
/// Set of all fields that are statically known to be written to.
|
||||
Iterable<Element> get fieldSetters;
|
||||
|
||||
/// Call [f] for all directly or abstractly instantiated classes.
|
||||
void forEachInstantiatedClass(
|
||||
f(ClassElement cls, EnumSet<Instantiation> instantiations));
|
||||
}
|
||||
|
||||
class ResolutionWorldBuilderImpl implements ResolutionWorldBuilder {
|
||||
|
@ -166,8 +172,8 @@ class ResolutionWorldBuilderImpl implements ResolutionWorldBuilder {
|
|||
/// Invariant: Elements are declaration elements.
|
||||
// TODO(johnniwinther): [_directlyInstantiatedClasses] and
|
||||
// [_instantiatedTypes] sets should be merged.
|
||||
final Set<ClassElement> _directlyInstantiatedClasses =
|
||||
new Set<ClassElement>();
|
||||
final Map<ClassElement, EnumSet<Instantiation>> _directlyInstantiatedClasses =
|
||||
<ClassElement, EnumSet<Instantiation>>{};
|
||||
|
||||
/// The set of all directly instantiated types, that is, the types of the
|
||||
/// directly instantiated classes.
|
||||
|
@ -237,7 +243,7 @@ class ResolutionWorldBuilderImpl implements ResolutionWorldBuilder {
|
|||
/// super-call.
|
||||
// TODO(johnniwinther): Improve semantic precision.
|
||||
Iterable<ClassElement> get directlyInstantiatedClasses {
|
||||
return _directlyInstantiatedClasses;
|
||||
return _directlyInstantiatedClasses.keys;
|
||||
}
|
||||
|
||||
/// All directly instantiated types, that is, the types of the directly
|
||||
|
@ -280,7 +286,13 @@ class ResolutionWorldBuilderImpl implements ResolutionWorldBuilder {
|
|||
// TODO(herhut): Track classes required by mirrors seperately.
|
||||
||
|
||||
byMirrors) {
|
||||
_directlyInstantiatedClasses.add(cls);
|
||||
EnumSet<Instantiation> instantiations = _directlyInstantiatedClasses
|
||||
.putIfAbsent(cls, () => new EnumSet<Instantiation>());
|
||||
if (isNative || byMirrors) {
|
||||
instantiations.add(Instantiation.ABSTRACTLY_INSTANTIATED);
|
||||
} else {
|
||||
instantiations.add(Instantiation.DIRECTLY_INSTANTIATED);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Replace this by separate more specific mappings that
|
||||
|
@ -295,6 +307,12 @@ class ResolutionWorldBuilderImpl implements ResolutionWorldBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void forEachInstantiatedClass(
|
||||
f(ClassElement cls, EnumSet<Instantiation> instantiations)) {
|
||||
_directlyInstantiatedClasses.forEach(f);
|
||||
}
|
||||
|
||||
bool _hasMatchingSelector(Map<Selector, SelectorConstraints> selectors,
|
||||
Element member, OpenWorld world) {
|
||||
if (selectors == null) return false;
|
||||
|
|
|
@ -25,6 +25,7 @@ import 'universe/class_set.dart';
|
|||
import 'universe/function_set.dart' show FunctionSet;
|
||||
import 'universe/selector.dart' show Selector;
|
||||
import 'universe/side_effects.dart' show SideEffects;
|
||||
import 'util/enumset.dart';
|
||||
import 'util/util.dart' show Link;
|
||||
|
||||
/// Common superinterface for [OpenWorld] and [ClosedWorld].
|
||||
|
@ -48,9 +49,26 @@ abstract class ClosedWorld implements World {
|
|||
/// Returns `true` if [cls] is either directly or indirectly instantiated.
|
||||
bool isInstantiated(ClassElement cls);
|
||||
|
||||
/// Returns `true` if [cls] is directly instantiated.
|
||||
/// Returns `true` if [cls] is directly instantiated. This means that at
|
||||
/// runtime instances of exactly [cls] are assumed to exist.
|
||||
bool isDirectlyInstantiated(ClassElement cls);
|
||||
|
||||
/// Returns `true` if [cls] is abstractly instantiated. This means that at
|
||||
/// runtime instances of [cls] or unknown subclasses of [cls] are assumed to
|
||||
/// exist.
|
||||
///
|
||||
/// This is used to mark native and/or reflectable classes as instantiated.
|
||||
/// For native classes we do not know the exact class that instantiates [cls]
|
||||
/// so [cls] here represents the root of the subclasses. For reflectable
|
||||
/// classes we need event abstract classes to be 'live' even though they
|
||||
/// cannot themselves be instantiated.
|
||||
bool isAbstractlyInstantiated(ClassElement cls);
|
||||
|
||||
/// Returns `true` if [cls] is either directly or abstractly instantiated.
|
||||
///
|
||||
/// See [isDirectlyInstantiated] and [isAbstractlyInstantiated].
|
||||
bool isExplicitlyInstantiated(ClassElement cls);
|
||||
|
||||
/// Returns `true` if [cls] is indirectly instantiated, that is through a
|
||||
/// subclass.
|
||||
bool isIndirectlyInstantiated(ClassElement cls);
|
||||
|
@ -392,6 +410,20 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
return node != null && node.isDirectlyInstantiated;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAbstractlyInstantiated(ClassElement cls) {
|
||||
assert(isClosed);
|
||||
ClassHierarchyNode node = _classHierarchyNodes[cls.declaration];
|
||||
return node != null && node.isAbstractlyInstantiated;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isExplicitlyInstantiated(ClassElement cls) {
|
||||
assert(isClosed);
|
||||
ClassHierarchyNode node = _classHierarchyNodes[cls.declaration];
|
||||
return node != null && node.isExplicitlyInstantiated;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isIndirectlyInstantiated(ClassElement cls) {
|
||||
assert(isClosed);
|
||||
|
@ -414,7 +446,8 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
assert(isClosed);
|
||||
ClassHierarchyNode hierarchy = _classHierarchyNodes[cls.declaration];
|
||||
if (hierarchy == null) return const <ClassElement>[];
|
||||
return hierarchy.subclassesByMask(ClassHierarchyNode.DIRECTLY_INSTANTIATED);
|
||||
return hierarchy
|
||||
.subclassesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
|
||||
}
|
||||
|
||||
/// Returns an iterable over the directly instantiated classes that extend
|
||||
|
@ -423,7 +456,8 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
assert(isClosed);
|
||||
ClassHierarchyNode subclasses = _classHierarchyNodes[cls.declaration];
|
||||
if (subclasses == null) return const <ClassElement>[];
|
||||
return subclasses.subclassesByMask(ClassHierarchyNode.DIRECTLY_INSTANTIATED,
|
||||
return subclasses.subclassesByMask(
|
||||
ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
|
||||
strict: true);
|
||||
}
|
||||
|
||||
|
@ -443,7 +477,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
assert(isClosed);
|
||||
ClassHierarchyNode subclasses = _classHierarchyNodes[cls.declaration];
|
||||
if (subclasses == null) return;
|
||||
subclasses.forEachSubclass(f, ClassHierarchyNode.DIRECTLY_INSTANTIATED,
|
||||
subclasses.forEachSubclass(f, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
|
||||
strict: true);
|
||||
}
|
||||
|
||||
|
@ -454,7 +488,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
ClassHierarchyNode subclasses = _classHierarchyNodes[cls.declaration];
|
||||
if (subclasses == null) return false;
|
||||
return subclasses.anySubclass(
|
||||
predicate, ClassHierarchyNode.DIRECTLY_INSTANTIATED,
|
||||
predicate, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
|
||||
strict: true);
|
||||
}
|
||||
|
||||
|
@ -466,7 +500,8 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
if (classSet == null) {
|
||||
return const <ClassElement>[];
|
||||
} else {
|
||||
return classSet.subtypesByMask(ClassHierarchyNode.DIRECTLY_INSTANTIATED);
|
||||
return classSet
|
||||
.subtypesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,7 +513,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
if (classSet == null) {
|
||||
return const <ClassElement>[];
|
||||
} else {
|
||||
return classSet.subtypesByMask(ClassHierarchyNode.DIRECTLY_INSTANTIATED,
|
||||
return classSet.subtypesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
|
||||
strict: true);
|
||||
}
|
||||
}
|
||||
|
@ -499,7 +534,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
assert(isClosed);
|
||||
ClassSet classSet = _classSets[cls.declaration];
|
||||
if (classSet == null) return;
|
||||
classSet.forEachSubtype(f, ClassHierarchyNode.DIRECTLY_INSTANTIATED,
|
||||
classSet.forEachSubtype(f, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
|
||||
strict: true);
|
||||
}
|
||||
|
||||
|
@ -510,7 +545,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
ClassSet classSet = _classSets[cls.declaration];
|
||||
if (classSet == null) return false;
|
||||
return classSet.anySubtype(
|
||||
predicate, ClassHierarchyNode.DIRECTLY_INSTANTIATED,
|
||||
predicate, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
|
||||
strict: true);
|
||||
}
|
||||
|
||||
|
@ -769,7 +804,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
// The root subclass has a concrete implementation so no subclass needs
|
||||
// noSuchMethod handling.
|
||||
return false;
|
||||
} else if (rootNode.isDirectlyInstantiated) {
|
||||
} else if (rootNode.isExplicitlyInstantiated) {
|
||||
// The root class need noSuchMethod handling.
|
||||
return true;
|
||||
}
|
||||
|
@ -781,7 +816,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
// Stop fast - we found a need for noSuchMethod handling.
|
||||
return IterationStep.STOP;
|
||||
}
|
||||
}, ClassHierarchyNode.DIRECTLY_INSTANTIATED, strict: true);
|
||||
}, ClassHierarchyNode.EXPLICITLY_INSTANTIATED, strict: true);
|
||||
// We stopped fast so we need noSuchMethod handling.
|
||||
return result == IterationStep.STOP;
|
||||
}
|
||||
|
@ -943,19 +978,22 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
}
|
||||
|
||||
void _updateClassHierarchyNodeForClass(ClassElement cls,
|
||||
{bool directlyInstantiated: false}) {
|
||||
{bool directlyInstantiated: false, bool abstractlyInstantiated: false}) {
|
||||
ClassHierarchyNode node = getClassHierarchyNode(cls);
|
||||
_updateSuperClassHierarchyNodeForClass(node);
|
||||
if (directlyInstantiated) {
|
||||
node.isDirectlyInstantiated = true;
|
||||
}
|
||||
if (abstractlyInstantiated) {
|
||||
node.isAbstractlyInstantiated = true;
|
||||
}
|
||||
}
|
||||
|
||||
ClosedWorld closeWorld() {
|
||||
/// Updates the `isDirectlyInstantiated` and `isIndirectlyInstantiated`
|
||||
/// properties of the [ClassHierarchyNode] for [cls].
|
||||
|
||||
void addSubtypes(ClassElement cls) {
|
||||
void addSubtypes(ClassElement cls, EnumSet<Instantiation> instantiations) {
|
||||
if (_compiler.options.hasIncrementalSupport &&
|
||||
!alreadyPopulated.add(cls)) {
|
||||
return;
|
||||
|
@ -965,7 +1003,11 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
reporter.internalError(cls, 'Class "${cls.name}" is not resolved.');
|
||||
}
|
||||
|
||||
_updateClassHierarchyNodeForClass(cls, directlyInstantiated: true);
|
||||
_updateClassHierarchyNodeForClass(cls,
|
||||
directlyInstantiated:
|
||||
instantiations.contains(Instantiation.DIRECTLY_INSTANTIATED),
|
||||
abstractlyInstantiated:
|
||||
instantiations.contains(Instantiation.ABSTRACTLY_INSTANTIATED));
|
||||
|
||||
// Walk through the superclasses, and record the types
|
||||
// implemented by that type on the superclasses.
|
||||
|
@ -985,7 +1027,7 @@ class WorldImpl implements ClosedWorld, ClosedWorldRefiner, OpenWorld {
|
|||
// classes: if the superclass of these classes require RTI, then
|
||||
// they also need RTI, so that a constructor passes the type
|
||||
// variables to the super constructor.
|
||||
_compiler.resolverWorld.directlyInstantiatedClasses.forEach(addSubtypes);
|
||||
_compiler.resolverWorld.forEachInstantiatedClass(addSubtypes);
|
||||
|
||||
_closed = true;
|
||||
return this;
|
||||
|
|
|
@ -529,7 +529,7 @@ testForEach() async {
|
|||
checkForEach(X, [X, A, B, D, C, G, F, I, H, E],
|
||||
skipSubclasses: [D], forEachSubtype: true);
|
||||
checkForEach(X, [A, D, C, G, F, I, H, E],
|
||||
forEachSubtype: true, mask: ClassHierarchyNode.DIRECTLY_INSTANTIATED);
|
||||
forEachSubtype: true, mask: ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
|
||||
checkForEach(X, [A, B, D, C, G, F, I, H, E],
|
||||
forEachSubtype: true, mask: ClassHierarchyNode.INSTANTIATED);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ void main() {
|
|||
testClasses() async {
|
||||
test(String mainSource,
|
||||
{List<String> directlyInstantiated: const <String>[],
|
||||
List<String> abstractlyInstantiated: const <String>[],
|
||||
List<String> indirectlyInstantiated: const <String>[]}) async {
|
||||
TypeEnvironment env = await TypeEnvironment.create(
|
||||
r"""
|
||||
|
@ -120,6 +121,13 @@ $mainSource
|
|||
"Expected $name to be directly instantiated in `${mainSource}`:"
|
||||
"\n${world.dump(cls)}");
|
||||
}
|
||||
if (abstractlyInstantiated.contains(name)) {
|
||||
isInstantiated = true;
|
||||
Expect.isTrue(
|
||||
world.isAbstractlyInstantiated(cls),
|
||||
"Expected $name to be abstractly instantiated in `${mainSource}`:"
|
||||
"\n${world.dump(cls)}");
|
||||
}
|
||||
if (indirectlyInstantiated.contains(name)) {
|
||||
isInstantiated = true;
|
||||
Expect.isTrue(
|
||||
|
@ -139,19 +147,19 @@ $mainSource
|
|||
await test('main() {}');
|
||||
|
||||
await test('main() => newA();',
|
||||
directlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
indirectlyInstantiated: ['Object', 'Interceptor', 'JavaScriptObject']);
|
||||
|
||||
await test('main() => newB();',
|
||||
directlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
indirectlyInstantiated: ['Object', 'Interceptor', 'JavaScriptObject']);
|
||||
|
||||
await test('main() => newC();',
|
||||
directlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
indirectlyInstantiated: ['Object', 'Interceptor', 'JavaScriptObject']);
|
||||
|
||||
await test('main() => newD();',
|
||||
directlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
indirectlyInstantiated: ['Object', 'Interceptor', 'JavaScriptObject']);
|
||||
|
||||
await test('main() => newE();', directlyInstantiated: ['E']);
|
||||
|
@ -159,6 +167,7 @@ $mainSource
|
|||
await test('main() => newF();', directlyInstantiated: ['F']);
|
||||
|
||||
await test('main() => [newD(), newE()];',
|
||||
directlyInstantiated: ['A', 'B', 'C', 'D', 'E'],
|
||||
directlyInstantiated: ['E'],
|
||||
abstractlyInstantiated: ['A', 'B', 'C', 'D'],
|
||||
indirectlyInstantiated: ['Object', 'Interceptor', 'JavaScriptObject']);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ library world_test;
|
|||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'type_test_helper.dart';
|
||||
import 'package:compiler/src/common.dart';
|
||||
import 'package:compiler/src/elements/elements.dart' show Element, ClassElement;
|
||||
import 'package:compiler/src/common/names.dart';
|
||||
import 'package:compiler/src/elements/elements.dart'
|
||||
show Element, ClassElement, LibraryElement;
|
||||
import 'package:compiler/src/universe/class_set.dart';
|
||||
import 'package:compiler/src/world.dart' show ClosedWorld;
|
||||
|
||||
|
@ -16,6 +17,7 @@ void main() {
|
|||
asyncTest(() async {
|
||||
await testClassSets();
|
||||
await testProperties();
|
||||
await testNativeClasses();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,6 +35,7 @@ testClassSets() async {
|
|||
class X {}
|
||||
""",
|
||||
mainSource: r"""
|
||||
import 'dart:html' as html;
|
||||
main() {
|
||||
new A();
|
||||
new B();
|
||||
|
@ -41,6 +44,8 @@ testClassSets() async {
|
|||
new E();
|
||||
new F();
|
||||
new G();
|
||||
html.window;
|
||||
new html.Worker('');
|
||||
}
|
||||
""",
|
||||
useMockCompiler: false);
|
||||
|
@ -298,3 +303,210 @@ testProperties() async {
|
|||
check("H3", hasStrictSubtype: false, hasOnlySubclasses: true);
|
||||
check("H4", hasStrictSubtype: false, hasOnlySubclasses: true);
|
||||
}
|
||||
|
||||
testNativeClasses() async {
|
||||
var env = await TypeEnvironment.create('',
|
||||
mainSource: r"""
|
||||
import 'dart:html' as html;
|
||||
main() {
|
||||
html.window; // Creates 'Window'.
|
||||
new html.Worker(''); // Creates 'Worker'.
|
||||
new html.CanvasElement() // Creates CanvasElement
|
||||
..getContext(''); // Creates CanvasRenderingContext2D
|
||||
}
|
||||
""",
|
||||
useMockCompiler: false);
|
||||
ClosedWorld closedWorld = env.compiler.closedWorld;
|
||||
LibraryElement dart_html =
|
||||
env.compiler.libraryLoader.lookupLibrary(Uris.dart_html);
|
||||
|
||||
ClassElement clsEventTarget = dart_html.findExported('EventTarget');
|
||||
ClassElement clsWindow = dart_html.findExported('Window');
|
||||
ClassElement clsAbstractWorker = dart_html.findExported('AbstractWorker');
|
||||
ClassElement clsWorker = dart_html.findExported('Worker');
|
||||
ClassElement clsCanvasElement = dart_html.findExported('CanvasElement');
|
||||
ClassElement clsCanvasRenderingContext =
|
||||
dart_html.findExported('CanvasRenderingContext');
|
||||
ClassElement clsCanvasRenderingContext2D =
|
||||
dart_html.findExported('CanvasRenderingContext2D');
|
||||
|
||||
List<ClassElement> allClasses = [
|
||||
clsEventTarget,
|
||||
clsWindow,
|
||||
clsAbstractWorker,
|
||||
clsWorker,
|
||||
clsCanvasElement,
|
||||
clsCanvasRenderingContext,
|
||||
clsCanvasRenderingContext2D
|
||||
];
|
||||
|
||||
check(ClassElement cls,
|
||||
{bool isDirectlyInstantiated,
|
||||
bool isAbstractlyInstantiated,
|
||||
bool isIndirectlyInstantiated,
|
||||
bool hasStrictSubtype,
|
||||
bool hasOnlySubclasses,
|
||||
ClassElement lubOfInstantiatedSubclasses,
|
||||
ClassElement lubOfInstantiatedSubtypes,
|
||||
int instantiatedSubclassCount,
|
||||
int instantiatedSubtypeCount,
|
||||
List<ClassElement> subclasses: const <ClassElement>[],
|
||||
List<ClassElement> subtypes: const <ClassElement>[]}) {
|
||||
ClassSet classSet = closedWorld.getClassSet(cls);
|
||||
ClassHierarchyNode node = classSet.node;
|
||||
|
||||
String dumpText = '\n${closedWorld.dump(cls)}';
|
||||
|
||||
Expect.equals(
|
||||
isDirectlyInstantiated,
|
||||
closedWorld.isDirectlyInstantiated(cls),
|
||||
"Unexpected isDirectlyInstantiated property on $cls.$dumpText");
|
||||
Expect.equals(
|
||||
isAbstractlyInstantiated,
|
||||
closedWorld.isAbstractlyInstantiated(cls),
|
||||
"Unexpected isAbstractlyInstantiated property on $cls.$dumpText");
|
||||
Expect.equals(
|
||||
isIndirectlyInstantiated,
|
||||
closedWorld.isIndirectlyInstantiated(cls),
|
||||
"Unexpected isIndirectlyInstantiated property on $cls.$dumpText");
|
||||
Expect.equals(hasStrictSubtype, closedWorld.hasAnyStrictSubtype(cls),
|
||||
"Unexpected hasAnyStrictSubtype property on $cls.$dumpText");
|
||||
Expect.equals(hasOnlySubclasses, closedWorld.hasOnlySubclasses(cls),
|
||||
"Unexpected hasOnlySubclasses property on $cls.$dumpText");
|
||||
Expect.equals(
|
||||
lubOfInstantiatedSubclasses,
|
||||
node.getLubOfInstantiatedSubclasses(),
|
||||
"Unexpected getLubOfInstantiatedSubclasses() result on $cls.$dumpText");
|
||||
Expect.equals(
|
||||
lubOfInstantiatedSubtypes,
|
||||
classSet.getLubOfInstantiatedSubtypes(),
|
||||
"Unexpected getLubOfInstantiatedSubtypes() result on $cls.$dumpText");
|
||||
if (instantiatedSubclassCount != null) {
|
||||
Expect.equals(instantiatedSubclassCount, node.instantiatedSubclassCount,
|
||||
"Unexpected instantiatedSubclassCount property on $cls.$dumpText");
|
||||
}
|
||||
if (instantiatedSubtypeCount != null) {
|
||||
Expect.equals(instantiatedSubtypeCount, classSet.instantiatedSubtypeCount,
|
||||
"Unexpected instantiatedSubtypeCount property on $cls.$dumpText");
|
||||
}
|
||||
for (ClassElement other in allClasses) {
|
||||
if (other == cls) continue;
|
||||
if (!closedWorld.isExplicitlyInstantiated(other)) continue;
|
||||
Expect.equals(
|
||||
subclasses.contains(other),
|
||||
closedWorld.isSubclassOf(other, cls),
|
||||
"Unexpected subclass relation between $other and $cls.");
|
||||
Expect.equals(
|
||||
subtypes.contains(other),
|
||||
closedWorld.isSubtypeOf(other, cls),
|
||||
"Unexpected subtype relation between $other and $cls.");
|
||||
}
|
||||
|
||||
Set<ClassElement> strictSubclasses = new Set<ClassElement>();
|
||||
closedWorld.forEachStrictSubclassOf(cls, (ClassElement other) {
|
||||
if (allClasses.contains(other)) {
|
||||
strictSubclasses.add(other);
|
||||
}
|
||||
});
|
||||
Expect.setEquals(subclasses, strictSubclasses,
|
||||
"Unexpected strict subclasses of $cls: ${strictSubclasses}.");
|
||||
|
||||
Set<ClassElement> strictSubtypes = new Set<ClassElement>();
|
||||
closedWorld.forEachStrictSubtypeOf(cls, (ClassElement other) {
|
||||
if (allClasses.contains(other)) {
|
||||
strictSubtypes.add(other);
|
||||
}
|
||||
});
|
||||
Expect.setEquals(subtypes, strictSubtypes,
|
||||
"Unexpected strict subtypes of $cls: $strictSubtypes.");
|
||||
}
|
||||
|
||||
// Extended by Window.
|
||||
check(clsEventTarget,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: false,
|
||||
isIndirectlyInstantiated: true,
|
||||
hasStrictSubtype: true,
|
||||
hasOnlySubclasses: true,
|
||||
lubOfInstantiatedSubclasses: clsEventTarget,
|
||||
lubOfInstantiatedSubtypes: clsEventTarget,
|
||||
// May vary with implementation, do no test.
|
||||
instantiatedSubclassCount: null,
|
||||
instantiatedSubtypeCount: null,
|
||||
subclasses: [clsWindow, clsCanvasElement, clsWorker],
|
||||
subtypes: [clsWindow, clsCanvasElement, clsWorker]);
|
||||
|
||||
// Created by 'html.window'.
|
||||
check(clsWindow,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: true,
|
||||
isIndirectlyInstantiated: false,
|
||||
hasStrictSubtype: false,
|
||||
hasOnlySubclasses: true,
|
||||
lubOfInstantiatedSubclasses: clsWindow,
|
||||
lubOfInstantiatedSubtypes: clsWindow,
|
||||
instantiatedSubclassCount: 0,
|
||||
instantiatedSubtypeCount: 0);
|
||||
|
||||
// Implemented by 'Worker'.
|
||||
check(clsAbstractWorker,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: false,
|
||||
isIndirectlyInstantiated: false,
|
||||
hasStrictSubtype: true,
|
||||
hasOnlySubclasses: false,
|
||||
lubOfInstantiatedSubclasses: null,
|
||||
lubOfInstantiatedSubtypes: clsWorker,
|
||||
instantiatedSubclassCount: 0,
|
||||
instantiatedSubtypeCount: 1,
|
||||
subtypes: [clsWorker]);
|
||||
|
||||
// Created by 'new html.Worker'.
|
||||
check(clsWorker,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: true,
|
||||
isIndirectlyInstantiated: false,
|
||||
hasStrictSubtype: false,
|
||||
hasOnlySubclasses: true,
|
||||
lubOfInstantiatedSubclasses: clsWorker,
|
||||
lubOfInstantiatedSubtypes: clsWorker,
|
||||
instantiatedSubclassCount: 0,
|
||||
instantiatedSubtypeCount: 0);
|
||||
|
||||
// Created by 'new html.CanvasElement'.
|
||||
check(clsCanvasElement,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: true,
|
||||
isIndirectlyInstantiated: false,
|
||||
hasStrictSubtype: false,
|
||||
hasOnlySubclasses: true,
|
||||
lubOfInstantiatedSubclasses: clsCanvasElement,
|
||||
lubOfInstantiatedSubtypes: clsCanvasElement,
|
||||
instantiatedSubclassCount: 0,
|
||||
instantiatedSubtypeCount: 0);
|
||||
|
||||
// Implemented by CanvasRenderingContext2D and RenderingContext.
|
||||
check(clsCanvasRenderingContext,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: false,
|
||||
isIndirectlyInstantiated: false,
|
||||
hasStrictSubtype: true,
|
||||
hasOnlySubclasses: false,
|
||||
lubOfInstantiatedSubclasses: null,
|
||||
lubOfInstantiatedSubtypes: clsCanvasRenderingContext,
|
||||
instantiatedSubclassCount: 0,
|
||||
instantiatedSubtypeCount: 2,
|
||||
subtypes: [clsCanvasRenderingContext2D]);
|
||||
|
||||
// Created by 'html.CanvasElement.getContext'.
|
||||
check(clsCanvasRenderingContext2D,
|
||||
isDirectlyInstantiated: false,
|
||||
isAbstractlyInstantiated: true,
|
||||
isIndirectlyInstantiated: false,
|
||||
hasStrictSubtype: false,
|
||||
hasOnlySubclasses: true,
|
||||
lubOfInstantiatedSubclasses: clsCanvasRenderingContext2D,
|
||||
lubOfInstantiatedSubtypes: clsCanvasRenderingContext2D,
|
||||
instantiatedSubclassCount: 0,
|
||||
instantiatedSubtypeCount: 0);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue