| Index: pkg/compiler/lib/src/world.dart
|
| diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart
|
| index 40e73a5bfc5ea6322377393165a163a72f0f1944..8c69baeb5c8c2653543e8988748a292d6d46405e 100644
|
| --- a/pkg/compiler/lib/src/world.dart
|
| +++ b/pkg/compiler/lib/src/world.dart
|
| @@ -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,
|
| +}
|
|
|