mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:01:30 +00:00
Extract ClosedWorldBase from ClosedWorldImpl
This is step towards making (parts of) ClosedWorldImpl reusable for kernel based elements. R=efortuna@google.com Review-Url: https://codereview.chromium.org/2813503005 .
This commit is contained in:
parent
5c0566c476
commit
aef0c45019
1 changed files with 278 additions and 246 deletions
|
@ -17,8 +17,7 @@ import 'elements/elements.dart'
|
|||
FunctionElement,
|
||||
MemberElement,
|
||||
MixinApplicationElement,
|
||||
TypedefElement,
|
||||
FieldElement;
|
||||
TypedefElement;
|
||||
import 'elements/resolution_types.dart';
|
||||
import 'js_backend/backend.dart' show JavaScriptBackend;
|
||||
import 'js_backend/interceptor_data.dart' show InterceptorData;
|
||||
|
@ -121,11 +120,11 @@ abstract class ClosedWorld implements World {
|
|||
|
||||
/// Returns an iterable over the directly instantiated that implement [cls]
|
||||
/// possibly including [cls] itself, if it is live.
|
||||
Iterable<ClassElement> subtypesOf(ClassEntity cls);
|
||||
Iterable<ClassEntity> subtypesOf(ClassEntity cls);
|
||||
|
||||
/// Returns an iterable over the live classes that implement [cls] _not_
|
||||
/// including [cls] if it is live.
|
||||
Iterable<ClassElement> strictSubtypesOf(ClassEntity cls);
|
||||
Iterable<ClassEntity> strictSubtypesOf(ClassEntity cls);
|
||||
|
||||
/// Returns the number of live classes that implement [cls] _not_
|
||||
/// including [cls] itself.
|
||||
|
@ -183,8 +182,8 @@ abstract class ClosedWorld implements World {
|
|||
/// commonSubclasses(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE)
|
||||
///
|
||||
/// return the set {C} because [D] is implied by [C].
|
||||
Iterable<ClassEntity> commonSubclasses(ClassElement cls1, ClassQuery query1,
|
||||
ClassElement cls2, ClassQuery query2);
|
||||
Iterable<ClassEntity> commonSubclasses(
|
||||
ClassEntity cls1, ClassQuery query1, ClassEntity cls2, ClassQuery query2);
|
||||
|
||||
/// Returns an iterable over the live mixin applications that mixin [cls].
|
||||
Iterable<ClassEntity> mixinUsesOf(ClassEntity cls);
|
||||
|
@ -388,7 +387,7 @@ enum ClassQuery {
|
|||
SUBTYPE,
|
||||
}
|
||||
|
||||
class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
||||
abstract class ClosedWorldBase implements ClosedWorld, ClosedWorldRefiner {
|
||||
final JavaScriptBackend _backend;
|
||||
BackendClasses get backendClasses => _backend.backendClasses;
|
||||
InterceptorData get interceptorData => _backend.interceptorData;
|
||||
|
@ -425,7 +424,7 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
|
||||
final ResolutionWorldBuilder _resolverWorld;
|
||||
|
||||
ClosedWorldImpl(
|
||||
ClosedWorldBase(
|
||||
{JavaScriptBackend backend,
|
||||
this.commonElements,
|
||||
ResolutionWorldBuilder resolutionWorldBuilder,
|
||||
|
@ -470,47 +469,7 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
return cachedMasks.putIfAbsent(base, createMask);
|
||||
}
|
||||
|
||||
bool _checkClass(ClassElement cls) => cls.isDeclaration;
|
||||
|
||||
bool checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
|
||||
return invariant(cls, cls.isDeclaration,
|
||||
message: '$cls must be the declaration.') &&
|
||||
invariant(cls, cls.isResolved,
|
||||
message:
|
||||
'$cls must be resolved.') /* &&
|
||||
// TODO(johnniwinther): Reinsert this or similar invariant.
|
||||
(!mustBeInstantiated ||
|
||||
invariant(cls, isInstantiated(cls),
|
||||
message: '$cls is not instantiated.'))*/
|
||||
;
|
||||
}
|
||||
|
||||
/// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
|
||||
/// instance of [y].
|
||||
bool isSubtypeOf(ClassElement x, ClassElement y) {
|
||||
assert(checkInvariants(x));
|
||||
assert(checkInvariants(y, mustBeInstantiated: false));
|
||||
|
||||
if (y == commonElements.objectClass) return true;
|
||||
if (x == commonElements.objectClass) return false;
|
||||
if (x.asInstanceOf(y) != null) return true;
|
||||
if (y != commonElements.functionClass) return false;
|
||||
return x.callType != null;
|
||||
}
|
||||
|
||||
/// Return `true` if [x] is a (non-strict) subclass of [y].
|
||||
bool isSubclassOf(ClassElement x, ClassElement y) {
|
||||
assert(checkInvariants(x));
|
||||
assert(checkInvariants(y));
|
||||
|
||||
if (y == commonElements.objectClass) return true;
|
||||
if (x == commonElements.objectClass) return false;
|
||||
while (x != null && x.hierarchyDepth >= y.hierarchyDepth) {
|
||||
if (x == y) return true;
|
||||
x = x.superclass;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool _checkClass(ClassEntity cls);
|
||||
|
||||
@override
|
||||
bool isInstantiated(ClassEntity cls) {
|
||||
|
@ -732,13 +691,275 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
return classSet != null ? classSet.getLubOfInstantiatedSubtypes() : null;
|
||||
}
|
||||
|
||||
Set<ClassEntity> _commonContainedClasses(ClassEntity cls1, ClassQuery query1,
|
||||
ClassEntity cls2, ClassQuery query2) {
|
||||
Iterable<ClassEntity> xSubset = _containedSubset(cls1, query1);
|
||||
if (xSubset == null) return null;
|
||||
Iterable<ClassEntity> ySubset = _containedSubset(cls2, query2);
|
||||
if (ySubset == null) return null;
|
||||
return xSubset.toSet().intersection(ySubset.toSet());
|
||||
}
|
||||
|
||||
Iterable<ClassEntity> _containedSubset(ClassEntity cls, ClassQuery query) {
|
||||
switch (query) {
|
||||
case ClassQuery.EXACT:
|
||||
return null;
|
||||
case ClassQuery.SUBCLASS:
|
||||
return strictSubclassesOf(cls);
|
||||
case ClassQuery.SUBTYPE:
|
||||
return strictSubtypesOf(cls);
|
||||
}
|
||||
throw new ArgumentError('Unexpected query: $query.');
|
||||
}
|
||||
|
||||
/// Returns `true` if [cls] is mixed into a live class.
|
||||
bool isUsedAsMixin(ClassEntity cls) {
|
||||
return !mixinUsesOf(cls).isEmpty;
|
||||
}
|
||||
|
||||
/// Returns `true` if any live class that mixes in [cls] implements [type].
|
||||
bool hasAnySubclassOfMixinUseThatImplements(
|
||||
ClassEntity cls, ClassEntity type) {
|
||||
return mixinUsesOf(cls)
|
||||
.any((use) => hasAnySubclassThatImplements(use, type));
|
||||
}
|
||||
|
||||
/// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass
|
||||
/// of a mixin application of [y].
|
||||
bool everySubtypeIsSubclassOfOrMixinUseOf(ClassEntity x, ClassEntity y) {
|
||||
assert(_checkClass(x));
|
||||
assert(_checkClass(y));
|
||||
Map<ClassEntity, bool> secondMap =
|
||||
_subtypeCoveredByCache[x] ??= <ClassEntity, bool>{};
|
||||
return secondMap[y] ??= subtypesOf(x).every((ClassEntity cls) =>
|
||||
isSubclassOf(cls, y) || isSubclassOfMixinUseOf(cls, y));
|
||||
}
|
||||
|
||||
/// Returns `true` if any subclass of [superclass] implements [type].
|
||||
bool hasAnySubclassThatImplements(ClassEntity superclass, ClassEntity type) {
|
||||
assert(_checkClass(superclass));
|
||||
Set<ClassEntity> subclasses = _typesImplementedBySubclasses[superclass];
|
||||
if (subclasses == null) return false;
|
||||
return subclasses.contains(type);
|
||||
}
|
||||
|
||||
/// Returns whether a [selector] call on an instance of [cls]
|
||||
/// will hit a method at runtime, and not go through [noSuchMethod].
|
||||
bool hasConcreteMatch(ClassEntity cls, Selector selector,
|
||||
{ClassEntity stopAtSuperclass});
|
||||
|
||||
@override
|
||||
bool needsNoSuchMethod(
|
||||
ClassEntity base, Selector selector, ClassQuery query) {
|
||||
/// Returns `true` if subclasses in the [rootNode] tree needs noSuchMethod
|
||||
/// handling.
|
||||
bool subclassesNeedNoSuchMethod(ClassHierarchyNode rootNode) {
|
||||
if (!rootNode.isInstantiated) {
|
||||
// No subclass needs noSuchMethod handling since they are all
|
||||
// uninstantiated.
|
||||
return false;
|
||||
}
|
||||
ClassEntity rootClass = rootNode.cls;
|
||||
if (hasConcreteMatch(rootClass, selector)) {
|
||||
// The root subclass has a concrete implementation so no subclass needs
|
||||
// noSuchMethod handling.
|
||||
return false;
|
||||
} else if (rootNode.isExplicitlyInstantiated) {
|
||||
// The root class need noSuchMethod handling.
|
||||
return true;
|
||||
}
|
||||
IterationStep result = rootNode.forEachSubclass((ClassEntity subclass) {
|
||||
if (hasConcreteMatch(subclass, selector, stopAtSuperclass: rootClass)) {
|
||||
// Found a match - skip all subclasses.
|
||||
return IterationStep.SKIP_SUBCLASSES;
|
||||
} else {
|
||||
// Stop fast - we found a need for noSuchMethod handling.
|
||||
return IterationStep.STOP;
|
||||
}
|
||||
}, ClassHierarchyNode.EXPLICITLY_INSTANTIATED, strict: true);
|
||||
// We stopped fast so we need noSuchMethod handling.
|
||||
return result == IterationStep.STOP;
|
||||
}
|
||||
|
||||
ClassSet classSet = getClassSet(base);
|
||||
ClassHierarchyNode node = classSet.node;
|
||||
if (query == ClassQuery.EXACT) {
|
||||
return node.isExplicitlyInstantiated && !hasConcreteMatch(base, selector);
|
||||
} else if (query == ClassQuery.SUBCLASS) {
|
||||
return subclassesNeedNoSuchMethod(node);
|
||||
} else {
|
||||
if (subclassesNeedNoSuchMethod(node)) return true;
|
||||
for (ClassHierarchyNode subtypeNode in classSet.subtypeNodes) {
|
||||
if (subclassesNeedNoSuchMethod(subtypeNode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
|
||||
/// of known classes.
|
||||
///
|
||||
/// This method is only provided for testing. For queries on classes, use the
|
||||
/// methods defined in [ClosedWorld].
|
||||
ClassHierarchyNode getClassHierarchyNode(ClassEntity cls) {
|
||||
assert(_checkClass(cls));
|
||||
return _classHierarchyNodes[cls];
|
||||
}
|
||||
|
||||
/// Returns [ClassSet] for [cls] used to model the extends and implements
|
||||
/// relations of known classes.
|
||||
///
|
||||
/// This method is only provided for testing. For queries on classes, use the
|
||||
/// methods defined in [ClosedWorld].
|
||||
ClassSet getClassSet(ClassEntity cls) {
|
||||
assert(_checkClass(cls));
|
||||
return _classSets[cls];
|
||||
}
|
||||
|
||||
Iterable<TypedefElement> get allTypedefs => _allTypedefs;
|
||||
|
||||
bool hasAnyUserDefinedGetter(Selector selector, TypeMask mask) {
|
||||
return allFunctions.filter(selector, mask).any((each) => each.isGetter);
|
||||
}
|
||||
|
||||
FieldEntity locateSingleField(Selector selector, TypeMask mask) {
|
||||
MemberEntity result = locateSingleElement(selector, mask);
|
||||
return (result != null && result.isField) ? result : null;
|
||||
}
|
||||
|
||||
MemberEntity locateSingleElement(Selector selector, TypeMask mask) {
|
||||
mask ??= commonMasks.dynamicType;
|
||||
return mask.locateSingleElement(selector, this);
|
||||
}
|
||||
|
||||
TypeMask extendMaskIfReachesAll(Selector selector, TypeMask mask) {
|
||||
bool canReachAll = true;
|
||||
if (mask != null) {
|
||||
canReachAll = _backend.backendUsage.isInvokeOnUsed &&
|
||||
mask.needsNoSuchMethodHandling(selector, this);
|
||||
}
|
||||
return canReachAll ? commonMasks.dynamicType : mask;
|
||||
}
|
||||
|
||||
bool fieldNeverChanges(MemberEntity element) {
|
||||
if (!element.isField) return false;
|
||||
if (nativeData.isNativeMember(element)) {
|
||||
// Some native fields are views of data that may be changed by operations.
|
||||
// E.g. node.firstChild depends on parentNode.removeBefore(n1, n2).
|
||||
// TODO(sra): Refine the effect classification so that native effects are
|
||||
// distinct from ordinary Dart effects.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!element.isAssignable) {
|
||||
return true;
|
||||
}
|
||||
if (element.isInstanceMember) {
|
||||
return !_resolverWorld.hasInvokedSetter(element) &&
|
||||
!_resolverWorld.fieldSetters.contains(element);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SideEffects getSideEffectsOfSelector(Selector selector, TypeMask mask) {
|
||||
// We're not tracking side effects of closures.
|
||||
if (selector.isClosureCall) return new SideEffects();
|
||||
SideEffects sideEffects = new SideEffects.empty();
|
||||
for (MemberElement e in allFunctions.filter(selector, mask)) {
|
||||
if (e.isField) {
|
||||
if (selector.isGetter) {
|
||||
if (!fieldNeverChanges(e)) {
|
||||
sideEffects.setDependsOnInstancePropertyStore();
|
||||
}
|
||||
} else if (selector.isSetter) {
|
||||
sideEffects.setChangesInstanceProperty();
|
||||
} else {
|
||||
assert(selector.isCall);
|
||||
sideEffects.setAllSideEffects();
|
||||
sideEffects.setDependsOnSomething();
|
||||
}
|
||||
} else {
|
||||
sideEffects.add(getSideEffectsOfElement(e));
|
||||
}
|
||||
}
|
||||
return sideEffects;
|
||||
}
|
||||
}
|
||||
|
||||
class ClosedWorldImpl extends ClosedWorldBase {
|
||||
ClosedWorldImpl(
|
||||
{JavaScriptBackend backend,
|
||||
CommonElements commonElements,
|
||||
ResolutionWorldBuilder resolutionWorldBuilder,
|
||||
FunctionSetBuilder functionSetBuilder,
|
||||
Iterable<TypedefElement> allTypedefs,
|
||||
Map<ClassEntity, Set<ClassEntity>> mixinUses,
|
||||
Map<ClassEntity, Set<ClassEntity>> typesImplementedBySubclasses,
|
||||
Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes,
|
||||
Map<ClassEntity, ClassSet> classSets})
|
||||
: super(
|
||||
backend: backend,
|
||||
commonElements: commonElements,
|
||||
resolutionWorldBuilder: resolutionWorldBuilder,
|
||||
functionSetBuilder: functionSetBuilder,
|
||||
allTypedefs: allTypedefs,
|
||||
mixinUses: mixinUses,
|
||||
typesImplementedBySubclasses: typesImplementedBySubclasses,
|
||||
classHierarchyNodes: classHierarchyNodes,
|
||||
classSets: classSets);
|
||||
|
||||
bool _checkClass(ClassElement cls) => cls.isDeclaration;
|
||||
|
||||
bool _checkInvariants(ClassElement cls, {bool mustBeInstantiated: true}) {
|
||||
return invariant(cls, cls.isDeclaration,
|
||||
message: '$cls must be the declaration.') &&
|
||||
invariant(cls, cls.isResolved,
|
||||
message:
|
||||
'$cls must be resolved.') /* &&
|
||||
// TODO(johnniwinther): Reinsert this or similar invariant. Currently
|
||||
// various call sites use uninstantiated classes for isSubtypeOf or
|
||||
// isSubclassOf. Some are valid, some are not. Work out better invariants
|
||||
// to catch the latter.
|
||||
(!mustBeInstantiated ||
|
||||
invariant(cls, isInstantiated(cls),
|
||||
message: '$cls is not instantiated.'))*/
|
||||
;
|
||||
}
|
||||
|
||||
/// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
|
||||
/// instance of [y].
|
||||
bool isSubtypeOf(ClassElement x, ClassElement y) {
|
||||
assert(_checkInvariants(x));
|
||||
assert(_checkInvariants(y, mustBeInstantiated: false));
|
||||
|
||||
if (y == commonElements.objectClass) return true;
|
||||
if (x == commonElements.objectClass) return false;
|
||||
if (x.asInstanceOf(y) != null) return true;
|
||||
if (y != commonElements.functionClass) return false;
|
||||
return x.callType != null;
|
||||
}
|
||||
|
||||
/// Return `true` if [x] is a (non-strict) subclass of [y].
|
||||
bool isSubclassOf(ClassElement x, ClassElement y) {
|
||||
assert(_checkInvariants(x));
|
||||
assert(_checkInvariants(y));
|
||||
|
||||
if (y == commonElements.objectClass) return true;
|
||||
if (x == commonElements.objectClass) return false;
|
||||
while (x != null && x.hierarchyDepth >= y.hierarchyDepth) {
|
||||
if (x == y) return true;
|
||||
x = x.superclass;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns an iterable over the common supertypes of the [classes].
|
||||
Iterable<ClassElement> commonSupertypesOf(Iterable<ClassElement> classes) {
|
||||
Iterator<ClassElement> iterator = classes.iterator;
|
||||
if (!iterator.moveNext()) return const <ClassElement>[];
|
||||
|
||||
ClassElement cls = iterator.current;
|
||||
assert(checkInvariants(cls));
|
||||
assert(_checkInvariants(cls));
|
||||
OrderedTypeSet typeSet = cls.allSupertypesAndSelf;
|
||||
if (!iterator.moveNext()) return typeSet.types.map((type) => type.element);
|
||||
|
||||
|
@ -746,7 +967,7 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
Link<OrderedTypeSet> otherTypeSets = const Link<OrderedTypeSet>();
|
||||
do {
|
||||
ClassElement otherClass = iterator.current;
|
||||
assert(checkInvariants(otherClass));
|
||||
assert(_checkInvariants(otherClass));
|
||||
OrderedTypeSet otherTypeSet = otherClass.allSupertypesAndSelf;
|
||||
otherTypeSets = otherTypeSets.prepend(otherTypeSet);
|
||||
if (otherTypeSet.maxDepth < depth) {
|
||||
|
@ -804,27 +1025,6 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
});
|
||||
}
|
||||
|
||||
Set<ClassEntity> _commonContainedClasses(ClassEntity cls1, ClassQuery query1,
|
||||
ClassEntity cls2, ClassQuery query2) {
|
||||
Iterable<ClassEntity> xSubset = _containedSubset(cls1, query1);
|
||||
if (xSubset == null) return null;
|
||||
Iterable<ClassEntity> ySubset = _containedSubset(cls2, query2);
|
||||
if (ySubset == null) return null;
|
||||
return xSubset.toSet().intersection(ySubset.toSet());
|
||||
}
|
||||
|
||||
Iterable<ClassEntity> _containedSubset(ClassEntity cls, ClassQuery query) {
|
||||
switch (query) {
|
||||
case ClassQuery.EXACT:
|
||||
return null;
|
||||
case ClassQuery.SUBCLASS:
|
||||
return strictSubclassesOf(cls);
|
||||
case ClassQuery.SUBTYPE:
|
||||
return strictSubtypesOf(cls);
|
||||
}
|
||||
throw new ArgumentError('Unexpected query: $query.');
|
||||
}
|
||||
|
||||
/// Returns an iterable over the live mixin applications that mixin [cls].
|
||||
Iterable<ClassEntity> mixinUsesOf(ClassEntity cls) {
|
||||
if (_liveMixinUses == null) {
|
||||
|
@ -853,18 +1053,6 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
return uses != null ? uses : const <ClassEntity>[];
|
||||
}
|
||||
|
||||
/// Returns `true` if [cls] is mixed into a live class.
|
||||
bool isUsedAsMixin(ClassEntity cls) {
|
||||
return !mixinUsesOf(cls).isEmpty;
|
||||
}
|
||||
|
||||
/// Returns `true` if any live class that mixes in [cls] implements [type].
|
||||
bool hasAnySubclassOfMixinUseThatImplements(
|
||||
ClassEntity cls, ClassEntity type) {
|
||||
return mixinUsesOf(cls)
|
||||
.any((use) => hasAnySubclassThatImplements(use, type));
|
||||
}
|
||||
|
||||
/// Returns `true` if any live class that mixes in [mixin] is also a subclass
|
||||
/// of [superclass].
|
||||
bool hasAnySubclassThatMixes(ClassEntity superclass, ClassEntity mixin) {
|
||||
|
@ -890,25 +1078,6 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Returns `true` if every subtype of [x] is a subclass of [y] or a subclass
|
||||
/// of a mixin application of [y].
|
||||
bool everySubtypeIsSubclassOfOrMixinUseOf(ClassEntity x, ClassEntity y) {
|
||||
assert(_checkClass(x));
|
||||
assert(_checkClass(y));
|
||||
Map<ClassEntity, bool> secondMap =
|
||||
_subtypeCoveredByCache[x] ??= <ClassEntity, bool>{};
|
||||
return secondMap[y] ??= subtypesOf(x).every((ClassEntity cls) =>
|
||||
isSubclassOf(cls, y) || isSubclassOfMixinUseOf(cls, y));
|
||||
}
|
||||
|
||||
/// Returns `true` if any subclass of [superclass] implements [type].
|
||||
bool hasAnySubclassThatImplements(ClassEntity superclass, ClassEntity type) {
|
||||
assert(_checkClass(superclass));
|
||||
Set<ClassEntity> subclasses = _typesImplementedBySubclasses[superclass];
|
||||
if (subclasses == null) return false;
|
||||
return subclasses.contains(type);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasElementIn(ClassEntity cls, Selector selector, Element element) {
|
||||
// Use [:implementation:] of [element]
|
||||
|
@ -943,74 +1112,6 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
return selector.appliesUntyped(element);
|
||||
}
|
||||
|
||||
@override
|
||||
bool needsNoSuchMethod(
|
||||
ClassElement base, Selector selector, ClassQuery query) {
|
||||
/// Returns `true` if subclasses in the [rootNode] tree needs noSuchMethod
|
||||
/// handling.
|
||||
bool subclassesNeedNoSuchMethod(ClassHierarchyNode rootNode) {
|
||||
if (!rootNode.isInstantiated) {
|
||||
// No subclass needs noSuchMethod handling since they are all
|
||||
// uninstantiated.
|
||||
return false;
|
||||
}
|
||||
ClassElement rootClass = rootNode.cls;
|
||||
if (hasConcreteMatch(rootClass, selector)) {
|
||||
// The root subclass has a concrete implementation so no subclass needs
|
||||
// noSuchMethod handling.
|
||||
return false;
|
||||
} else if (rootNode.isExplicitlyInstantiated) {
|
||||
// The root class need noSuchMethod handling.
|
||||
return true;
|
||||
}
|
||||
IterationStep result = rootNode.forEachSubclass((ClassElement subclass) {
|
||||
if (hasConcreteMatch(subclass, selector, stopAtSuperclass: rootClass)) {
|
||||
// Found a match - skip all subclasses.
|
||||
return IterationStep.SKIP_SUBCLASSES;
|
||||
} else {
|
||||
// Stop fast - we found a need for noSuchMethod handling.
|
||||
return IterationStep.STOP;
|
||||
}
|
||||
}, ClassHierarchyNode.EXPLICITLY_INSTANTIATED, strict: true);
|
||||
// We stopped fast so we need noSuchMethod handling.
|
||||
return result == IterationStep.STOP;
|
||||
}
|
||||
|
||||
ClassSet classSet = getClassSet(base);
|
||||
ClassHierarchyNode node = classSet.node;
|
||||
if (query == ClassQuery.EXACT) {
|
||||
return node.isExplicitlyInstantiated && !hasConcreteMatch(base, selector);
|
||||
} else if (query == ClassQuery.SUBCLASS) {
|
||||
return subclassesNeedNoSuchMethod(node);
|
||||
} else {
|
||||
if (subclassesNeedNoSuchMethod(node)) return true;
|
||||
for (ClassHierarchyNode subtypeNode in classSet.subtypeNodes) {
|
||||
if (subclassesNeedNoSuchMethod(subtypeNode)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
|
||||
/// of known classes.
|
||||
///
|
||||
/// This method is only provided for testing. For queries on classes, use the
|
||||
/// methods defined in [ClosedWorld].
|
||||
ClassHierarchyNode getClassHierarchyNode(ClassEntity cls) {
|
||||
assert(_checkClass(cls));
|
||||
return _classHierarchyNodes[cls];
|
||||
}
|
||||
|
||||
/// Returns [ClassSet] for [cls] used to model the extends and implements
|
||||
/// relations of known classes.
|
||||
///
|
||||
/// This method is only provided for testing. For queries on classes, use the
|
||||
/// methods defined in [ClosedWorld].
|
||||
ClassSet getClassSet(ClassEntity cls) {
|
||||
assert(_checkClass(cls));
|
||||
return _classSets[cls];
|
||||
}
|
||||
|
||||
void registerClosureClass(ClosureClassElement cls) {
|
||||
ClassHierarchyNode parentNode = getClassHierarchyNode(cls.superclass);
|
||||
ClassHierarchyNode node = _classHierarchyNodes[cls] =
|
||||
|
@ -1038,8 +1139,6 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
}
|
||||
}
|
||||
|
||||
Iterable<TypedefElement> get allTypedefs => _allTypedefs;
|
||||
|
||||
@override
|
||||
String dump([ClassElement cls]) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
@ -1053,57 +1152,6 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
bool hasAnyUserDefinedGetter(Selector selector, TypeMask mask) {
|
||||
return allFunctions.filter(selector, mask).any((each) => each.isGetter);
|
||||
}
|
||||
|
||||
FieldElement locateSingleField(Selector selector, TypeMask mask) {
|
||||
Element result = locateSingleElement(selector, mask);
|
||||
return (result != null && result.isField) ? result : null;
|
||||
}
|
||||
|
||||
MemberElement locateSingleElement(Selector selector, TypeMask mask) {
|
||||
mask ??= commonMasks.dynamicType;
|
||||
return mask.locateSingleElement(selector, this);
|
||||
}
|
||||
|
||||
TypeMask extendMaskIfReachesAll(Selector selector, TypeMask mask) {
|
||||
bool canReachAll = true;
|
||||
if (mask != null) {
|
||||
canReachAll = _backend.backendUsage.isInvokeOnUsed &&
|
||||
mask.needsNoSuchMethodHandling(selector, this);
|
||||
}
|
||||
return canReachAll ? commonMasks.dynamicType : mask;
|
||||
}
|
||||
|
||||
void addFunctionCalledInLoop(Element element) {
|
||||
functionsCalledInLoop.add(element.declaration);
|
||||
}
|
||||
|
||||
bool isCalledInLoop(Element element) {
|
||||
return functionsCalledInLoop.contains(element.declaration);
|
||||
}
|
||||
|
||||
bool fieldNeverChanges(MemberElement element) {
|
||||
if (!element.isField) return false;
|
||||
if (nativeData.isNativeMember(element)) {
|
||||
// Some native fields are views of data that may be changed by operations.
|
||||
// E.g. node.firstChild depends on parentNode.removeBefore(n1, n2).
|
||||
// TODO(sra): Refine the effect classification so that native effects are
|
||||
// distinct from ordinary Dart effects.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (element.isFinal || element.isConst) {
|
||||
return true;
|
||||
}
|
||||
if (element.isInstanceMember) {
|
||||
return !_resolverWorld.hasInvokedSetter(element) &&
|
||||
!_resolverWorld.fieldSetters.contains(element);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SideEffects getSideEffectsOfElement(Element element) {
|
||||
// The type inferrer (where the side effects are being computed),
|
||||
// does not see generative constructor bodies because they are
|
||||
|
@ -1133,28 +1181,12 @@ class ClosedWorldImpl implements ClosedWorld, ClosedWorldRefiner {
|
|||
sideEffectsFreeElements.add(element);
|
||||
}
|
||||
|
||||
SideEffects getSideEffectsOfSelector(Selector selector, TypeMask mask) {
|
||||
// We're not tracking side effects of closures.
|
||||
if (selector.isClosureCall) return new SideEffects();
|
||||
SideEffects sideEffects = new SideEffects.empty();
|
||||
for (MemberElement e in allFunctions.filter(selector, mask)) {
|
||||
if (e.isField) {
|
||||
if (selector.isGetter) {
|
||||
if (!fieldNeverChanges(e)) {
|
||||
sideEffects.setDependsOnInstancePropertyStore();
|
||||
}
|
||||
} else if (selector.isSetter) {
|
||||
sideEffects.setChangesInstanceProperty();
|
||||
} else {
|
||||
assert(selector.isCall);
|
||||
sideEffects.setAllSideEffects();
|
||||
sideEffects.setDependsOnSomething();
|
||||
}
|
||||
} else {
|
||||
sideEffects.add(getSideEffectsOfElement(e));
|
||||
}
|
||||
}
|
||||
return sideEffects;
|
||||
void addFunctionCalledInLoop(Element element) {
|
||||
functionsCalledInLoop.add(element.declaration);
|
||||
}
|
||||
|
||||
bool isCalledInLoop(Element element) {
|
||||
return functionsCalledInLoop.contains(element.declaration);
|
||||
}
|
||||
|
||||
void registerCannotThrow(Element element) {
|
||||
|
|
Loading…
Reference in a new issue