Split deferred load entity computation by class, member and local function

Change-Id: Ifb612de6ac945d2ececa96871f0eb3fc5ceb4052
Reviewed-on: https://dart-review.googlesource.com/55896
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index 2064ae8..7418a1a 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -16,11 +16,9 @@
         ConstructedConstantValue,
         DeferredConstantValue,
         DeferredGlobalConstantValue,
-        InstantiationConstantValue,
-        TypeConstantValue;
+        InstantiationConstantValue;
 import 'elements/types.dart';
 import 'elements/entities.dart';
-import 'kernel/kelements.dart' show KLocalFunction;
 import 'universe/use.dart';
 import 'universe/world_impact.dart'
     show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl;
@@ -111,8 +109,15 @@
   /// this map.
   final Map<ImportEntity, String> _importDeferName = <ImportEntity, String>{};
 
-  /// A mapping from elements and constants to their import set.
-  Map<Entity, ImportSet> _elementToSet = new Map<Entity, ImportSet>();
+  /// A mapping from classes to their import set.
+  Map<ClassEntity, ImportSet> _classToSet = new Map<ClassEntity, ImportSet>();
+
+  /// A mapping from members to their import set.
+  Map<MemberEntity, ImportSet> _memberToSet =
+      new Map<MemberEntity, ImportSet>();
+
+  /// A mapping from local functions to their import set.
+  Map<Local, ImportSet> _localFunctionToSet = new Map<Local, ImportSet>();
 
   /// A mapping from constants to their import set.
   Map<ConstantValue, ImportSet> _constantToSet =
@@ -180,69 +185,72 @@
   }
 
   /// Returns every [ImportEntity] that imports [element] into [library].
-  Iterable<ImportEntity> importsTo(Entity element, LibraryEntity library);
+  Iterable<ImportEntity> classImportsTo(
+      ClassEntity element, LibraryEntity library);
+
+  /// Returns every [ImportEntity] that imports [element] into [library].
+  Iterable<ImportEntity> memberImportsTo(
+      MemberEntity element, LibraryEntity library);
+
+  /// Collects all direct dependencies of [element].
+  ///
+  /// The collected dependent elements and constants are are added to
+  /// [elements] and [constants] respectively.
+  void _collectDirectMemberDependencies(
+      MemberEntity element, Dependencies dependencies) {
+    // TODO(sigurdm): We want to be more specific about this - need a better
+    // way to query "liveness".
+    if (!compiler.resolutionWorldBuilder.isMemberUsed(element)) {
+      return;
+    }
+    _collectDependenciesFromImpact(element, dependencies);
+    collectConstantsInBody(element, dependencies);
+  }
 
   /// Finds all elements and constants that [element] depends directly on.
   /// (not the transitive closure.)
   ///
   /// Adds the results to [elements] and [constants].
-  void _collectAllElementsAndConstantsResolvedFrom(Entity element,
-      Set<Entity> elements, Set<ConstantValue> constants, isMirrorUsage) {
-    /// Collects all direct dependencies of [element].
-    ///
-    /// The collected dependent elements and constants are are added to
-    /// [elements] and [constants] respectively.
-    void collectDependencies(Entity element) {
-      if (element is TypedefEntity) {
-        _collectTypeDependencies(
-            elementEnvironment.getTypedefTypeOfTypedef(element), elements);
-        return;
-      }
-
-      // TODO(sigurdm): We want to be more specific about this - need a better
-      // way to query "liveness".
-      if (!compiler.resolutionWorldBuilder.isMemberUsed(element)) {
-        return;
-      }
-      _collectDependenciesFromImpact(element, elements);
-      collectConstantsInBody(element, constants);
+  void _collectAllElementsAndConstantsResolvedFromClass(
+      ClassEntity element, Dependencies dependencies) {
+    // If we see a class, add everything its live instance members refer
+    // to.  Static members are not relevant, unless we are processing
+    // extra dependencies due to mirrors.
+    void addLiveInstanceMember(MemberEntity member) {
+      if (!compiler.resolutionWorldBuilder.isMemberUsed(member)) return;
+      if (!member.isInstanceMember) return;
+      dependencies.members.add(member);
+      _collectDirectMemberDependencies(member, dependencies);
     }
 
+    ClassEntity cls = element;
+    elementEnvironment.forEachLocalClassMember(cls, addLiveInstanceMember);
+    elementEnvironment.forEachSupertype(cls, (InterfaceType type) {
+      _collectTypeDependencies(type, dependencies);
+    });
+    dependencies.classes.add(cls);
+  }
+
+  /// Finds all elements and constants that [element] depends directly on.
+  /// (not the transitive closure.)
+  ///
+  /// Adds the results to [elements] and [constants].
+  void _collectAllElementsAndConstantsResolvedFromMember(
+      MemberEntity element, Dependencies dependencies) {
     if (element is FunctionEntity) {
       _collectTypeDependencies(
-          elementEnvironment.getFunctionType(element), elements);
+          elementEnvironment.getFunctionType(element), dependencies);
     }
-
-    if (element is ClassEntity) {
-      // If we see a class, add everything its live instance members refer
-      // to.  Static members are not relevant, unless we are processing
-      // extra dependencies due to mirrors.
-      void addLiveInstanceMember(_element) {
-        MemberEntity element = _element;
-        if (!compiler.resolutionWorldBuilder.isMemberUsed(element)) return;
-        if (!isMirrorUsage && !element.isInstanceMember) return;
-        elements.add(element);
-        collectDependencies(element);
-      }
-
-      ClassEntity cls = element;
-      elementEnvironment.forEachLocalClassMember(cls, addLiveInstanceMember);
-      elementEnvironment.forEachSupertype(cls, (InterfaceType type) {
-        _collectTypeDependencies(type, elements);
-      });
-      elements.add(cls);
-    } else if (element is MemberEntity &&
-        (element.isStatic || element.isTopLevel || element.isConstructor)) {
-      elements.add(element);
-      collectDependencies(element);
+    if (element.isStatic || element.isTopLevel || element.isConstructor) {
+      dependencies.members.add(element);
+      _collectDirectMemberDependencies(element, dependencies);
     }
     if (element is ConstructorEntity && element.isGenerativeConstructor) {
       // When instantiating a class, we record a reference to the
       // constructor, not the class itself.  We must add all the
       // instance members of the constructor's class.
       ClassEntity cls = element.enclosingClass;
-      _collectAllElementsAndConstantsResolvedFrom(
-          cls, elements, constants, isMirrorUsage);
+      _collectAllElementsAndConstantsResolvedFromClass(cls, dependencies);
     }
 
     // Other elements, in particular instance members, are ignored as
@@ -257,45 +265,55 @@
       Entity element, Set<ConstantValue> constants);
 
   /// Extract the set of constants that are used in the body of [element].
-  void collectConstantsInBody(Entity element, Set<ConstantValue> constants);
+  void collectConstantsInBody(MemberEntity element, Dependencies dependencies);
 
   /// Recursively collects all the dependencies of [type].
-  void _collectTypeDependencies(DartType type, Set<Entity> elements) {
+  void _collectTypeDependencies(DartType type, Dependencies dependencies) {
     // TODO(het): we would like to separate out types that are only needed for
     // rti from types that are needed for their members.
     if (type is FunctionType) {
       for (DartType argumentType in type.parameterTypes) {
-        _collectTypeDependencies(argumentType, elements);
+        _collectTypeDependencies(argumentType, dependencies);
       }
       for (DartType argumentType in type.optionalParameterTypes) {
-        _collectTypeDependencies(argumentType, elements);
+        _collectTypeDependencies(argumentType, dependencies);
       }
       for (DartType argumentType in type.namedParameterTypes) {
-        _collectTypeDependencies(argumentType, elements);
+        _collectTypeDependencies(argumentType, dependencies);
       }
-      _collectTypeDependencies(type.returnType, elements);
+      _collectTypeDependencies(type.returnType, dependencies);
     } else if (type is TypedefType) {
-      type.typeArguments.forEach((t) => _collectTypeDependencies(t, elements));
-      elements.add(type.element);
-      _collectTypeDependencies(type.unaliased, elements);
+      type.typeArguments
+          .forEach((t) => _collectTypeDependencies(t, dependencies));
+      _collectTypeDependencies(type.unaliased, dependencies);
     } else if (type is InterfaceType) {
-      type.typeArguments.forEach((t) => _collectTypeDependencies(t, elements));
-      elements.add(type.element);
+      type.typeArguments
+          .forEach((t) => _collectTypeDependencies(t, dependencies));
+      dependencies.classes.add(type.element);
     }
   }
 
   /// Extract any dependencies that are known from the impact of [element].
-  void _collectDependenciesFromImpact(Entity element, Set<Entity> elements) {
+  void _collectDependenciesFromImpact(
+      MemberEntity element, Dependencies dependencies) {
     WorldImpact worldImpact = compiler.impactCache[element];
     compiler.impactStrategy.visitImpact(
         element,
         worldImpact,
         new WorldImpactVisitorImpl(visitStaticUse: (StaticUse staticUse) {
-          elements.add(staticUse.element);
+          if (staticUse.element is MemberEntity) {
+            dependencies.members.add(staticUse.element);
+          } else {
+            assert(
+                staticUse.element is Local,
+                failedAt(
+                    staticUse.element, "Unexpected static use $staticUse."));
+            dependencies.localFunctions.add(staticUse.element);
+          }
           switch (staticUse.kind) {
             case StaticUseKind.CONSTRUCTOR_INVOKE:
             case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
-              _collectTypeDependencies(staticUse.type, elements);
+              _collectTypeDependencies(staticUse.type, dependencies);
               break;
             case StaticUseKind.INVOKE:
             case StaticUseKind.CLOSURE_CALL:
@@ -305,7 +323,7 @@
               List<DartType> typeArguments = staticUse.typeArguments;
               if (typeArguments != null) {
                 for (DartType typeArgument in typeArguments) {
-                  _collectTypeDependencies(typeArgument, elements);
+                  _collectTypeDependencies(typeArgument, dependencies);
                 }
               }
               break;
@@ -315,12 +333,9 @@
           DartType type = typeUse.type;
           switch (typeUse.kind) {
             case TypeUseKind.TYPE_LITERAL:
-              if (type.isTypedef) {
-                TypedefType typedef = type;
-                elements.add(typedef.element);
-              } else if (type.isInterfaceType) {
+              if (type.isInterfaceType) {
                 InterfaceType interface = type;
-                elements.add(interface.element);
+                dependencies.classes.add(interface.element);
               }
               break;
             case TypeUseKind.INSTANTIATION:
@@ -329,21 +344,21 @@
             case TypeUseKind.IS_CHECK:
             case TypeUseKind.AS_CAST:
             case TypeUseKind.CATCH_TYPE:
-              _collectTypeDependencies(type, elements);
+              _collectTypeDependencies(type, dependencies);
               break;
             case TypeUseKind.IMPLICIT_CAST:
               if (compiler.options.implicitDowncastCheckPolicy.isEmitted) {
-                _collectTypeDependencies(type, elements);
+                _collectTypeDependencies(type, dependencies);
               }
               break;
             case TypeUseKind.PARAMETER_CHECK:
               if (compiler.options.parameterCheckPolicy.isEmitted) {
-                _collectTypeDependencies(type, elements);
+                _collectTypeDependencies(type, dependencies);
               }
               break;
             case TypeUseKind.CHECKED_MODE_CHECK:
               if (compiler.options.assignmentCheckPolicy.isEmitted) {
-                _collectTypeDependencies(type, elements);
+                _collectTypeDependencies(type, dependencies);
               }
               break;
           }
@@ -353,7 +368,7 @@
           List<DartType> typeArguments = dynamicUse.typeArguments;
           if (typeArguments != null) {
             for (DartType typeArgument in typeArguments) {
-              _collectTypeDependencies(typeArgument, elements);
+              _collectTypeDependencies(typeArgument, dependencies);
             }
           }
         }),
@@ -380,18 +395,12 @@
       _constantToSet[constant] = newSet;
       if (constant is ConstructedConstantValue) {
         ClassEntity cls = constant.type.element;
-        _updateElementRecursive(cls, oldSet, newSet, queue);
-      }
-      if (constant is TypeConstantValue) {
-        var type = constant.representedType;
-        if (type is TypedefType) {
-          _updateElementRecursive(type.element, oldSet, newSet, queue);
-        }
+        _updateClassRecursive(cls, oldSet, newSet, queue);
       }
       if (constant is InstantiationConstantValue) {
         for (DartType type in constant.typeArguments) {
           if (type is InterfaceType) {
-            _updateElementRecursive(type.element, oldSet, newSet, queue);
+            _updateClassRecursive(type.element, oldSet, newSet, queue);
           }
         }
       }
@@ -421,77 +430,111 @@
     }
   }
 
-  /// Update the import set of all elements reachable from [element], as long as
-  /// they had the [oldSet]. As soon as we see an element with a different
-  /// import set, we stop and enqueue a new recursive update in [queue].
-  void _updateElementRecursive(
-      Entity element, ImportSet oldSet, ImportSet newSet, WorkQueue queue,
-      {bool isMirrorUsage: false}) {
+  void _updateClassRecursive(ClassEntity element, ImportSet oldSet,
+      ImportSet newSet, WorkQueue queue) {
     if (element == null) return;
-    var currentSet = _elementToSet[element];
+
+    ImportSet currentSet = _classToSet[element];
 
     // Already visited. We may visit some root nodes a second time with
     // [isMirrorUsage] in order to mark static members used reflectively.
-    if (currentSet == newSet && !isMirrorUsage) return;
+    if (currentSet == newSet) return;
 
     // Elements in the main output unit always remain there.
     if (currentSet == importSets.mainSet) return;
 
     if (currentSet == oldSet) {
       // Continue recursively updating from [oldSet] to [newSet].
-      _elementToSet[element] = newSet;
+      _classToSet[element] = newSet;
 
-      Set<Entity> dependentElements = new Set<Entity>();
-      Set<ConstantValue> dependentConstants = new Set<ConstantValue>();
-      _collectAllElementsAndConstantsResolvedFrom(
-          element, dependentElements, dependentConstants, isMirrorUsage);
-
-      // TODO(sigmund): split API to collect data about each kind of entity
-      // separately so we can avoid this ugly pattern.
-      LibraryEntity library;
-      if (element is ClassEntity) {
-        library = element.library;
-      } else if (element is MemberEntity) {
-        library = element.library;
-      } else if (element is TypedefEntity) {
-        library = element.library;
-      } else if (element is KLocalFunction) {
-        // TODO(sigmund): consider adding `Local.library`
-        library = element.memberContext.library;
-      } else {
-        assert(false, "Unexpected entity: ${element.runtimeType}");
-      }
-
-      for (Entity dependency in dependentElements) {
-        Iterable<ImportEntity> imports = importsTo(dependency, library);
-        if (_isExplicitlyDeferred(imports)) {
-          /// New deferred-imports are only discovered when we are visiting the
-          /// main output unit (size == 0) or code reachable from a deferred
-          /// import (size == 1). After that, we are rediscovering the
-          /// same nodes we have already seen.
-          if (newSet.length <= 1) {
-            for (ImportEntity deferredImport in imports) {
-              queue.addElement(
-                  dependency, importSets.singleton(deferredImport));
-            }
-          }
-        } else {
-          _updateElementRecursive(dependency, oldSet, newSet, queue);
-        }
-      }
-
-      for (ConstantValue dependency in dependentConstants) {
-        if (dependency is DeferredConstantValue) {
-          if (newSet.length <= 1) {
-            queue.addConstant(
-                dependency, importSets.singleton(dependency.import));
-          }
-        } else {
-          _updateConstantRecursive(dependency, oldSet, newSet, queue);
-        }
-      }
+      Dependencies dependencies = new Dependencies();
+      _collectAllElementsAndConstantsResolvedFromClass(element, dependencies);
+      LibraryEntity library = element.library;
+      _processDependencies(library, dependencies, oldSet, newSet, queue);
     } else {
-      queue.addElement(element, newSet);
+      queue.addClass(element, newSet);
+    }
+  }
+
+  void _updateMemberRecursive(MemberEntity element, ImportSet oldSet,
+      ImportSet newSet, WorkQueue queue) {
+    if (element == null) return;
+
+    ImportSet currentSet = _memberToSet[element];
+
+    // Already visited. We may visit some root nodes a second time with
+    // [isMirrorUsage] in order to mark static members used reflectively.
+    if (currentSet == newSet) return;
+
+    // Elements in the main output unit always remain there.
+    if (currentSet == importSets.mainSet) return;
+
+    if (currentSet == oldSet) {
+      // Continue recursively updating from [oldSet] to [newSet].
+      _memberToSet[element] = newSet;
+
+      Dependencies dependencies = new Dependencies();
+      _collectAllElementsAndConstantsResolvedFromMember(element, dependencies);
+
+      LibraryEntity library = element.library;
+      _processDependencies(library, dependencies, oldSet, newSet, queue);
+    } else {
+      queue.addMember(element, newSet);
+    }
+  }
+
+  void _processDependencies(LibraryEntity library, Dependencies dependencies,
+      ImportSet oldSet, ImportSet newSet, WorkQueue queue) {
+    for (ClassEntity cls in dependencies.classes) {
+      Iterable<ImportEntity> imports = classImportsTo(cls, library);
+      if (_isExplicitlyDeferred(imports)) {
+        /// New deferred-imports are only discovered when we are visiting the
+        /// main output unit (size == 0) or code reachable from a deferred
+        /// import (size == 1). After that, we are rediscovering the
+        /// same nodes we have already seen.
+        if (newSet.length <= 1) {
+          for (ImportEntity deferredImport in imports) {
+            queue.addClass(cls, importSets.singleton(deferredImport));
+          }
+        }
+      } else {
+        _updateClassRecursive(cls, oldSet, newSet, queue);
+      }
+    }
+
+    for (MemberEntity member in dependencies.members) {
+      Iterable<ImportEntity> imports = memberImportsTo(member, library);
+      if (_isExplicitlyDeferred(imports)) {
+        /// New deferred-imports are only discovered when we are visiting the
+        /// main output unit (size == 0) or code reachable from a deferred
+        /// import (size == 1). After that, we are rediscovering the
+        /// same nodes we have already seen.
+        if (newSet.length <= 1) {
+          for (ImportEntity deferredImport in imports) {
+            queue.addMember(member, importSets.singleton(deferredImport));
+          }
+        }
+      } else {
+        _updateMemberRecursive(member, oldSet, newSet, queue);
+      }
+    }
+
+    for (Local localFunction in dependencies.localFunctions) {
+      // Local function are not updated recursively because the dependencies are
+      // already visited as dependencies of the enclosing member, so we just
+      // assign the [newSet] to each local function.
+      _localFunctionToSet[localFunction] = newSet;
+    }
+
+    for (ConstantValue dependency in dependencies.constants) {
+      if (dependency is DeferredConstantValue) {
+        if (newSet.length <= 1) {
+          queue.addConstant(
+              dependency, importSets.singleton(dependency.import));
+        }
+      } else {
+        _updateConstantRecursive(dependency, oldSet, newSet, queue);
+      }
     }
   }
 
@@ -517,7 +560,9 @@
 
     // Generate an output unit for all import sets that are associated with an
     // element or constant.
-    _elementToSet.values.forEach(addUnit);
+    _classToSet.values.forEach(addUnit);
+    _memberToSet.values.forEach(addUnit);
+    _localFunctionToSet.values.forEach(addUnit);
     _constantToSet.values.forEach(addUnit);
 
     // Sort output units to make the output of the compiler more stable.
@@ -665,7 +710,7 @@
       // Add `main` and their recursive dependencies to the main output unit.
       // We do this upfront to avoid wasting time visiting these elements when
       // analyzing deferred imports.
-      queue.addElement(main, importSets.mainSet);
+      queue.addMember(main, importSets.mainSet);
 
       // Also add "global" dependencies to the main output unit.  These are
       // things that the backend needs but cannot associate with a particular
@@ -673,26 +718,17 @@
       // information.
       for (MemberEntity element
           in closedWorld.backendUsage.globalFunctionDependencies) {
-        queue.addElement(element, importSets.mainSet);
+        queue.addMember(element, importSets.mainSet);
       }
       for (ClassEntity element
           in closedWorld.backendUsage.globalClassDependencies) {
-        queue.addElement(element, importSets.mainSet);
+        queue.addClass(element, importSets.mainSet);
       }
 
       void emptyQueue() {
         while (queue.isNotEmpty) {
-          var item = queue.nextItem();
-          if (item.element != null) {
-            var oldSet = _elementToSet[item.element];
-            var newSet = importSets.union(oldSet, item.newSet);
-            _updateElementRecursive(item.element, oldSet, newSet, queue,
-                isMirrorUsage: item.isMirrorUsage);
-          } else if (item.value != null) {
-            var oldSet = _constantToSet[item.value];
-            var newSet = importSets.union(oldSet, item.newSet);
-            _updateConstantRecursive(item.value, oldSet, newSet, queue);
-          }
+          WorkItem item = queue.nextItem();
+          item.update(this, queue);
         }
       }
 
@@ -710,16 +746,29 @@
   OutputUnitData _buildResult() {
     _createOutputUnits();
     _setupHunksToLoad();
-    Map<Entity, OutputUnit> entityMap = <Entity, OutputUnit>{};
+    Map<ClassEntity, OutputUnit> classMap = <ClassEntity, OutputUnit>{};
+    Map<MemberEntity, OutputUnit> memberMap = <MemberEntity, OutputUnit>{};
+    Map<Local, OutputUnit> localFunctionMap = <Local, OutputUnit>{};
     Map<ConstantValue, OutputUnit> constantMap = <ConstantValue, OutputUnit>{};
-    _elementToSet.forEach((entity, s) => entityMap[entity] = s.unit);
+    _classToSet.forEach((cls, s) => classMap[cls] = s.unit);
+    _memberToSet.forEach((member, s) => memberMap[member] = s.unit);
+    _localFunctionToSet.forEach(
+        (localFunction, s) => localFunctionMap[localFunction] = s.unit);
     _constantToSet.forEach((constant, s) => constantMap[constant] = s.unit);
 
-    _elementToSet = null;
+    _classToSet = null;
+    _memberToSet = null;
+    _localFunctionToSet = null;
     _constantToSet = null;
     cleanup();
-    return new OutputUnitData(this.isProgramSplit && !disableProgramSplit,
-        this.mainOutputUnit, entityMap, constantMap, importSets);
+    return new OutputUnitData(
+        this.isProgramSplit && !disableProgramSplit,
+        this.mainOutputUnit,
+        classMap,
+        memberMap,
+        localFunctionMap,
+        constantMap,
+        importSets);
   }
 
   /// Frees up strategy-specific temporary data.
@@ -808,25 +857,31 @@
   String dump() {
     Map<OutputUnit, List<String>> elementMap = <OutputUnit, List<String>>{};
     Map<OutputUnit, List<String>> constantMap = <OutputUnit, List<String>>{};
-    _elementToSet.forEach((Entity element, ImportSet importSet) {
+    _classToSet.forEach((ClassEntity element, ImportSet importSet) {
       if (ignoreEntityInDump(element)) return;
       var elements = elementMap.putIfAbsent(importSet.unit, () => <String>[]);
       var id = element.name ?? '$element';
-      if (element is MemberEntity) {
-        var cls = element.enclosingClass?.name;
-        if (cls != null) id = '$cls.$id';
-        if (element.isSetter) id = '$id=';
-        id = '$id member';
-      } else if (element is ClassEntity) {
-        id = '$id cls';
-      } else if (element is TypedefEntity) {
-        id = '$id typedef';
-      } else if (element is Local) {
-        var context = (element as dynamic).memberContext.name;
-        id = element.name == null || element.name == '' ? '<anonymous>' : id;
-        id = '$context.$id';
-        id = '$id local';
-      }
+      id = '$id cls';
+      elements.add(id);
+    });
+    _memberToSet.forEach((MemberEntity element, ImportSet importSet) {
+      if (ignoreEntityInDump(element)) return;
+      var elements = elementMap.putIfAbsent(importSet.unit, () => <String>[]);
+      var id = element.name ?? '$element';
+      var cls = element.enclosingClass?.name;
+      if (cls != null) id = '$cls.$id';
+      if (element.isSetter) id = '$id=';
+      id = '$id member';
+      elements.add(id);
+    });
+    _localFunctionToSet.forEach((Local element, ImportSet importSet) {
+      if (ignoreEntityInDump(element)) return;
+      var elements = elementMap.putIfAbsent(importSet.unit, () => <String>[]);
+      var id = element.name ?? '$element';
+      var context = (element as dynamic).memberContext.name;
+      id = element.name == null || element.name == '' ? '<anonymous>' : id;
+      id = '$context.$id';
+      id = '$id local';
       elements.add(id);
     });
     _constantToSet.forEach((ConstantValue value, ImportSet importSet) {
@@ -1027,8 +1082,11 @@
   /// The actual queue of work that needs to be done.
   final Queue<WorkItem> queue = new Queue<WorkItem>();
 
-  /// An index to find work items in the queue corresponding to an entity.
-  final Map<Entity, WorkItem> pendingElements = <Entity, WorkItem>{};
+  /// An index to find work items in the queue corresponding to a class.
+  final Map<ClassEntity, WorkItem> pendingClasses = <ClassEntity, WorkItem>{};
+
+  /// An index to find work items in the queue corresponding to a member.
+  final Map<MemberEntity, WorkItem> pendingMembers = <MemberEntity, WorkItem>{};
 
   /// An index to find work items in the queue corresponding to a constant.
   final Map<ConstantValue, WorkItem> pendingConstants =
@@ -1045,26 +1103,37 @@
   /// Pop the next element in the queue.
   WorkItem nextItem() {
     assert(isNotEmpty);
-    var item = queue.removeFirst();
-    if (item.element != null) pendingElements.remove(item.element);
-    if (item.value != null) pendingConstants.remove(item.value);
-    return item;
+    return queue.removeFirst();
   }
 
   /// Add to the queue that [element] should be updated to include all imports
   /// in [importSet]. If there is already a work item in the queue for
   /// [element], this makes sure that the work item now includes the union of
   /// [importSet] and the existing work item's import set.
-  void addElement(Entity element, ImportSet importSet, {isMirrorUsage: false}) {
-    var item = pendingElements[element];
+  void addClass(ClassEntity element, ImportSet importSet) {
+    var item = pendingClasses[element];
     if (item == null) {
-      item = new WorkItem(element, importSet);
-      pendingElements[element] = item;
+      item = new ClassWorkItem(element, importSet);
+      pendingClasses[element] = item;
       queue.add(item);
     } else {
-      item.newSet = _importSets.union(item.newSet, importSet);
+      item.importsToAdd = _importSets.union(item.importsToAdd, importSet);
     }
-    if (isMirrorUsage) item.isMirrorUsage = true;
+  }
+
+  /// Add to the queue that [element] should be updated to include all imports
+  /// in [importSet]. If there is already a work item in the queue for
+  /// [element], this makes sure that the work item now includes the union of
+  /// [importSet] and the existing work item's import set.
+  void addMember(MemberEntity element, ImportSet importSet) {
+    var item = pendingMembers[element];
+    if (item == null) {
+      item = new MemberWorkItem(element, importSet);
+      pendingMembers[element] = item;
+      queue.add(item);
+    } else {
+      item.importsToAdd = _importSets.union(item.importsToAdd, importSet);
+    }
   }
 
   /// Add to the queue that [constant] should be updated to include all imports
@@ -1074,39 +1143,73 @@
   void addConstant(ConstantValue constant, ImportSet importSet) {
     var item = pendingConstants[constant];
     if (item == null) {
-      item = new WorkItem.constant(constant, importSet);
+      item = new ConstantWorkItem(constant, importSet);
       pendingConstants[constant] = item;
       queue.add(item);
     } else {
-      item.newSet = _importSets.union(item.newSet, importSet);
+      item.importsToAdd = _importSets.union(item.importsToAdd, importSet);
     }
   }
 }
 
-/// Summary of the work that needs to be done on an entity or constant.
-class WorkItem {
-  /// Entity to be recursively updated.
-  final Entity element;
-
-  /// Constant to be recursively updated.
-  final ConstantValue value;
-
+/// Summary of the work that needs to be done on a class, member, or constant.
+abstract class WorkItem {
   /// Additional imports that use [element] or [value] and need to be added by
   /// the algorithm.
   ///
   /// This is non-final in case we add more deferred imports to the set before
   /// the work item is applied (see [WorkQueue.addElement] and
   /// [WorkQueue.addConstant]).
-  ImportSet newSet;
+  ImportSet importsToAdd;
 
-  /// Whether [element] is used via mirrors.
-  ///
-  /// This is non-final in case we later discover that the same [element] is
-  /// used via mirrors (but before the work item is applied).
-  bool isMirrorUsage = false;
+  WorkItem(this.importsToAdd);
 
-  WorkItem(this.element, this.newSet) : value = null;
-  WorkItem.constant(this.value, this.newSet) : element = null;
+  void update(DeferredLoadTask task, WorkQueue queue);
+}
+
+/// Summary of the work that needs to be done on a class.
+class ClassWorkItem extends WorkItem {
+  /// Class to be recursively updated.
+  final ClassEntity cls;
+
+  ClassWorkItem(this.cls, ImportSet newSet) : super(newSet);
+
+  void update(DeferredLoadTask task, WorkQueue queue) {
+    queue.pendingClasses.remove(cls);
+    ImportSet oldSet = task._classToSet[cls];
+    ImportSet newSet = task.importSets.union(oldSet, importsToAdd);
+    task._updateClassRecursive(cls, oldSet, newSet, queue);
+  }
+}
+
+/// Summary of the work that needs to be done on a member.
+class MemberWorkItem extends WorkItem {
+  /// Member to be recursively updated.
+  final MemberEntity member;
+
+  MemberWorkItem(this.member, ImportSet newSet) : super(newSet);
+
+  void update(DeferredLoadTask task, WorkQueue queue) {
+    queue.pendingMembers.remove(member);
+    ImportSet oldSet = task._memberToSet[member];
+    ImportSet newSet = task.importSets.union(oldSet, importsToAdd);
+    task._updateMemberRecursive(member, oldSet, newSet, queue);
+  }
+}
+
+/// Summary of the work that needs to be done on a constant.
+class ConstantWorkItem extends WorkItem {
+  /// Constant to be recursively updated.
+  final ConstantValue constant;
+
+  ConstantWorkItem(this.constant, ImportSet newSet) : super(newSet);
+
+  void update(DeferredLoadTask task, WorkQueue queue) {
+    queue.pendingConstants.remove(constant);
+    ImportSet oldSet = task._constantToSet[constant];
+    ImportSet newSet = task.importSets.union(oldSet, importsToAdd);
+    task._updateConstantRecursive(constant, oldSet, newSet, queue);
+  }
 }
 
 /// Results of the deferred loading algorithm.
@@ -1118,57 +1221,63 @@
 class OutputUnitData {
   final bool isProgramSplit;
   final OutputUnit mainOutputUnit;
-  final Map<Entity, OutputUnit> _entityToUnit;
+  final Map<ClassEntity, OutputUnit> _classToUnit;
+  final Map<MemberEntity, OutputUnit> _memberToUnit;
+  final Map<Local, OutputUnit> _localFunctionToUnit;
   final Map<ConstantValue, OutputUnit> _constantToUnit;
   final ImportSetLattice _importSets;
 
-  OutputUnitData(this.isProgramSplit, this.mainOutputUnit, this._entityToUnit,
-      this._constantToUnit, this._importSets);
+  OutputUnitData(
+      this.isProgramSplit,
+      this.mainOutputUnit,
+      this._classToUnit,
+      this._memberToUnit,
+      this._localFunctionToUnit,
+      this._constantToUnit,
+      this._importSets);
 
   OutputUnitData.from(
       OutputUnitData other,
-      Map<Entity, OutputUnit> Function(Map<Entity, OutputUnit>)
-          convertEntityMap,
+      Map<ClassEntity, OutputUnit> Function(
+              Map<ClassEntity, OutputUnit>, Map<Local, OutputUnit>)
+          convertClassMap,
+      Map<MemberEntity, OutputUnit> Function(
+              Map<MemberEntity, OutputUnit>, Map<Local, OutputUnit>)
+          convertMemberMap,
       Map<ConstantValue, OutputUnit> Function(Map<ConstantValue, OutputUnit>)
           convertConstantMap)
       : isProgramSplit = other.isProgramSplit,
         mainOutputUnit = other.mainOutputUnit,
-        _entityToUnit = convertEntityMap(other._entityToUnit),
+        _memberToUnit =
+            convertMemberMap(other._memberToUnit, other._localFunctionToUnit),
+        _classToUnit =
+            convertClassMap(other._classToUnit, other._localFunctionToUnit),
+        _localFunctionToUnit = const <Local, OutputUnit>{},
         _constantToUnit = convertConstantMap(other._constantToUnit),
         _importSets = other._importSets;
 
-  /// Returns the [OutputUnit] where [element] belongs.
-  OutputUnit outputUnitForEntity(Entity entity) {
-    // TODO(johnniwinther): Support use of entities by splitting maps by
-    // entity kind.
+  /// Returns the [OutputUnit] where [cls] belongs.
+  OutputUnit outputUnitForClass(ClassEntity cls) {
     if (!isProgramSplit) return mainOutputUnit;
-    OutputUnit unit = _entityToUnit[entity];
+    OutputUnit unit = _classToUnit[cls];
+    return unit ?? mainOutputUnit;
+  }
+
+  /// Returns the [OutputUnit] where [member] belongs.
+  OutputUnit outputUnitForMember(MemberEntity member) {
+    if (!isProgramSplit) return mainOutputUnit;
+    OutputUnit unit = _memberToUnit[member];
     if (unit != null) return unit;
-    if (entity is MemberEntity && entity.isInstanceMember) {
-      return outputUnitForEntity(entity.enclosingClass);
+    if (member.isInstanceMember) {
+      return outputUnitForClass(member.enclosingClass);
     }
 
     return mainOutputUnit;
   }
 
-  /// Direct access to the output-unit to element relation used for testing.
-  OutputUnit outputUnitForEntityForTesting(Entity entity) {
-    return _entityToUnit[entity];
-  }
-
   /// Direct access to the output-unit to constants map used for testing.
   Iterable<ConstantValue> get constantsForTesting => _constantToUnit.keys;
 
-  /// Returns the [OutputUnit] where [element] belongs.
-  OutputUnit outputUnitForClass(ClassEntity element) {
-    return outputUnitForEntity(element);
-  }
-
-  /// Returns the [OutputUnit] where [element] belongs.
-  OutputUnit outputUnitForMember(MemberEntity element) {
-    return outputUnitForEntity(element);
-  }
-
   /// Returns the [OutputUnit] where [constant] belongs.
   OutputUnit outputUnitForConstant(ConstantValue constant) {
     if (!isProgramSplit) return mainOutputUnit;
@@ -1176,13 +1285,8 @@
   }
 
   /// Indicates whether [element] is deferred.
-  bool isDeferred(Entity element) {
-    return outputUnitForEntity(element) != mainOutputUnit;
-  }
-
-  /// Indicates whether [element] is deferred.
   bool isDeferredClass(ClassEntity element) {
-    return outputUnitForEntity(element) != mainOutputUnit;
+    return outputUnitForClass(element) != mainOutputUnit;
   }
 
   /// Returns `true` if element [to] is reachable from element [from] without
@@ -1191,9 +1295,9 @@
   /// For example, if we have two deferred libraries `A` and `B` that both
   /// import a library `C`, then even though elements from `A` and `C` end up in
   /// different output units, there is a non-deferred path between `A` and `C`.
-  bool hasOnlyNonDeferredImportPaths(Entity from, Entity to) {
-    OutputUnit outputUnitFrom = outputUnitForEntity(from);
-    OutputUnit outputUnitTo = outputUnitForEntity(to);
+  bool hasOnlyNonDeferredImportPaths(MemberEntity from, MemberEntity to) {
+    OutputUnit outputUnitFrom = outputUnitForMember(from);
+    OutputUnit outputUnitTo = outputUnitForMember(to);
     if (outputUnitTo == mainOutputUnit) return true;
     if (outputUnitFrom == mainOutputUnit) return false;
     return outputUnitTo._imports.containsAll(outputUnitFrom._imports);
@@ -1213,7 +1317,14 @@
   /// [existingEntity];
   void registerColocatedMembers(
       MemberEntity existingEntity, MemberEntity newEntity) {
-    assert(_entityToUnit[newEntity] == null);
-    _entityToUnit[newEntity] = outputUnitForMember(existingEntity);
+    assert(_memberToUnit[newEntity] == null);
+    _memberToUnit[newEntity] = outputUnitForMember(existingEntity);
   }
 }
+
+class Dependencies {
+  final Set<ClassEntity> classes = new Set<ClassEntity>();
+  final Set<MemberEntity> members = new Set<MemberEntity>();
+  final Set<Local> localFunctions = new Set<Local>();
+  final Set<ConstantValue> constants = new Set<ConstantValue>();
+}
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index a975519..2e82222 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -109,7 +109,7 @@
   TypedefInfo visitTypedef(TypedefEntity typdef) {
     var type = environment.getFunctionTypeOfTypedef(typdef);
     TypedefInfo info =
-        new TypedefInfo(typdef.name, '$type', _unitInfoForEntity(typdef));
+        new TypedefInfo(typdef.name, '$type', _unitInfoForTypedef(typdef));
     _entityToInfo[typdef] = info;
     LibraryInfo lib = _entityToInfo[typdef.library];
     lib.typedefs.add(info);
@@ -147,7 +147,7 @@
         type: '${environment.getFieldType(field)}',
         inferredType: '$inferredType',
         code: code,
-        outputUnit: _unitInfoForEntity(field),
+        outputUnit: _unitInfoForMember(field),
         isConst: field.isConst);
     _entityToInfo[field] = info;
     if (codegenWorldBuilder.hasConstantFieldInitializer(field)) {
@@ -179,7 +179,7 @@
     ClassInfo classInfo = new ClassInfo(
         name: clazz.name,
         isAbstract: clazz.isAbstract,
-        outputUnit: _unitInfoForEntity(clazz));
+        outputUnit: _unitInfoForClass(clazz));
     _entityToInfo[clazz] = classInfo;
 
     int size = compiler.dumpInfoTask.sizeOf(clazz);
@@ -232,7 +232,7 @@
   ClosureInfo visitClosureClass(ClassEntity element) {
     ClosureInfo closureInfo = new ClosureInfo(
         name: element.name,
-        outputUnit: _unitInfoForEntity(element),
+        outputUnit: _unitInfoForClass(element),
         size: compiler.dumpInfoTask.sizeOf(element));
     _entityToInfo[element] = closureInfo;
 
@@ -313,7 +313,7 @@
         inlinedCount: inlinedCount,
         code: code,
         type: functionType.toString(),
-        outputUnit: _unitInfoForEntity(function));
+        outputUnit: _unitInfoForMember(function));
     _entityToInfo[function] = info;
 
     int closureSize = _addClosureInfo(info, function);
@@ -366,9 +366,14 @@
     });
   }
 
-  OutputUnitInfo _unitInfoForEntity(Entity entity) {
+  OutputUnitInfo _unitInfoForMember(MemberEntity entity) {
     return _infoFromOutputUnit(
-        compiler.backend.outputUnitData.outputUnitForEntity(entity));
+        compiler.backend.outputUnitData.outputUnitForMember(entity));
+  }
+
+  OutputUnitInfo _unitInfoForClass(ClassEntity entity) {
+    return _infoFromOutputUnit(
+        compiler.backend.outputUnitData.outputUnitForClass(entity));
   }
 
   OutputUnitInfo _unitInfoForConstant(ConstantValue constant) {
@@ -380,6 +385,11 @@
     }
     return _infoFromOutputUnit(outputUnit);
   }
+
+  OutputUnitInfo _unitInfoForTypedef(TypedefEntity entity) {
+    // TODO(johnniwinther): Do we ever emit typedefs?
+    return null;
+  }
 }
 
 class Selection {
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
index ee51d11..3a54cf6 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
@@ -93,7 +93,7 @@
 
     jsAst.Name constructorName = namer.className(classElement);
     OutputUnit outputUnit =
-        compiler.backend.outputUnitData.outputUnitForEntity(classElement);
+        compiler.backend.outputUnitData.outputUnitForClass(classElement);
     emitter.assemblePrecompiledConstructor(
         outputUnit, constructorName, constructorAst, fieldNames);
   }
@@ -317,7 +317,7 @@
     ClassEntity cls = member.enclosingClass;
     jsAst.Name className = namer.className(cls);
     OutputUnit outputUnit =
-        compiler.backend.outputUnitData.outputUnitForEntity(member);
+        compiler.backend.outputUnitData.outputUnitForMember(member);
     emitter
         .cspPrecompiledFunctionFor(outputUnit)
         .add(js('#.prototype.# = #', [className, getterName, function]));
@@ -332,7 +332,7 @@
     ClassEntity cls = member.enclosingClass;
     jsAst.Name className = namer.className(cls);
     OutputUnit outputUnit =
-        compiler.backend.outputUnitData.outputUnitForEntity(member);
+        compiler.backend.outputUnitData.outputUnitForMember(member);
     emitter
         .cspPrecompiledFunctionFor(outputUnit)
         .add(js('#.prototype.# = #', [className, setterName, function]));
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 28e67b6..29a368a 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -102,24 +102,51 @@
       return map.toBackendLibrary(entity);
     }
 
-    // Convert a front-end map containing K-entities keys to a backend map using
-    // J-entities as keys.
-    Map<Entity, OutputUnit> convertEntityMap(Map<Entity, OutputUnit> input) {
-      var result = <Entity, OutputUnit>{};
-      input.forEach((Entity entity, OutputUnit unit) {
-        // Closures have both a class and a call-method, we ensure both are
-        // included in the corresponding output unit.
+    // Convert front-end maps containing K-class and K-local function keys to a
+    // backend map using J-classes as keys.
+    Map<ClassEntity, OutputUnit> convertClassMap(
+        Map<ClassEntity, OutputUnit> classMap,
+        Map<Local, OutputUnit> localFunctionMap) {
+      var result = <ClassEntity, OutputUnit>{};
+      classMap.forEach((ClassEntity entity, OutputUnit unit) {
+        ClassEntity backendEntity = toBackendEntity(entity);
+        if (backendEntity != null) {
+          // If [entity] isn't used it doesn't have a corresponding backend
+          // entity.
+          result[backendEntity] = unit;
+        }
+      });
+      localFunctionMap.forEach((Local entity, OutputUnit unit) {
+        // Ensure closure classes are included in the output unit corresponding
+        // to the local function.
         if (entity is KLocalFunction) {
           var closureInfo = _closureDataLookup.getClosureInfo(entity.node);
           result[closureInfo.closureClassEntity] = unit;
+        }
+      });
+      return result;
+    }
+
+    // Convert front-end maps containing K-member and K-local function keys to
+    // a backend map using J-members as keys.
+    Map<MemberEntity, OutputUnit> convertMemberMap(
+        Map<MemberEntity, OutputUnit> memberMap,
+        Map<Local, OutputUnit> localFunctionMap) {
+      var result = <MemberEntity, OutputUnit>{};
+      memberMap.forEach((MemberEntity entity, OutputUnit unit) {
+        MemberEntity backendEntity = toBackendEntity(entity);
+        if (backendEntity != null) {
+          // If [entity] isn't used it doesn't have a corresponding backend
+          // entity.
+          result[backendEntity] = unit;
+        }
+      });
+      localFunctionMap.forEach((Local entity, OutputUnit unit) {
+        // Ensure closure call-methods are included in the output unit
+        // corresponding to the local function.
+        if (entity is KLocalFunction) {
+          var closureInfo = _closureDataLookup.getClosureInfo(entity.node);
           result[closureInfo.callMethod] = unit;
-        } else {
-          Entity backendEntity = toBackendEntity(entity);
-          if (backendEntity != null) {
-            // If [entity] isn't used it doesn't have a corresponding backend
-            // entity.
-            result[backendEntity] = unit;
-          }
         }
       });
       return result;
@@ -131,7 +158,8 @@
 
     return new OutputUnitData.from(
         data,
-        convertEntityMap,
+        convertClassMap,
+        convertMemberMap,
         (m) => convertMap<ConstantValue, OutputUnit>(
             m, toBackendConstant, (v) => v));
   }
diff --git a/pkg/compiler/lib/src/kernel/deferred_load.dart b/pkg/compiler/lib/src/kernel/deferred_load.dart
index f3f1a9c..b312027 100644
--- a/pkg/compiler/lib/src/kernel/deferred_load.dart
+++ b/pkg/compiler/lib/src/kernel/deferred_load.dart
@@ -20,37 +20,8 @@
 
   KernelDeferredLoadTask(Compiler compiler, this._elementMap) : super(compiler);
 
-  @override
-  Iterable<ImportEntity> importsTo(Entity element, LibraryEntity library) {
-    ir.NamedNode node;
-    String nodeName;
-    ir.Library enclosingLibrary;
-    if (element is ClassEntity) {
-      ClassDefinition definition = _elementMap.getClassDefinition(element);
-      if (definition.kind != ClassKind.regular) {
-        // You can't import closures.
-        return const <ImportEntity>[];
-      }
-      ir.Class _node = definition.node;
-      nodeName = _node.name;
-      enclosingLibrary = _node.enclosingLibrary;
-      node = _node;
-    } else if (element is MemberEntity) {
-      ir.Member _node = _elementMap.getMemberDefinition(element).node;
-      nodeName = _node.name.name;
-      enclosingLibrary = _node.enclosingLibrary;
-      node = _node;
-    } else if (element is Local ||
-        element is LibraryEntity ||
-        element is TypeVariableEntity) {
-      return const <ImportEntity>[];
-    } else if (element is TypedefEntity) {
-      throw new UnimplementedError("KernelDeferredLoadTask.importsTo typedef");
-    } else {
-      throw new UnsupportedError(
-          "KernelDeferredLoadTask.importsTo unexpected entity type: "
-          "${element.runtimeType}");
-    }
+  Iterable<ImportEntity> _findImportsTo(ir.NamedNode node, String nodeName,
+      ir.Library enclosingLibrary, LibraryEntity library) {
     List<ImportEntity> imports = [];
     ir.Library source = _elementMap.getLibraryNode(library);
     for (ir.LibraryDependency dependency in source.dependencies) {
@@ -65,6 +36,25 @@
   }
 
   @override
+  Iterable<ImportEntity> classImportsTo(
+      ClassEntity element, LibraryEntity library) {
+    ClassDefinition definition = _elementMap.getClassDefinition(element);
+    if (definition.kind != ClassKind.regular) {
+      // You can't import closures.
+      return const <ImportEntity>[];
+    }
+    ir.Class node = definition.node;
+    return _findImportsTo(node, node.name, node.enclosingLibrary, library);
+  }
+
+  @override
+  Iterable<ImportEntity> memberImportsTo(
+      Entity element, LibraryEntity library) {
+    ir.Member node = _elementMap.getMemberDefinition(element).node;
+    return _findImportsTo(node, node.name.name, node.enclosingLibrary, library);
+  }
+
+  @override
   void checkForDeferredErrorCases(LibraryEntity library) {
     // Nothing to do. The FE checks for error cases upfront.
   }
@@ -77,14 +67,13 @@
   }
 
   @override
-  void collectConstantsInBody(
-      covariant MemberEntity element, Set<ConstantValue> constants) {
+  void collectConstantsInBody(MemberEntity element, Dependencies dependencies) {
     ir.Member node = _elementMap.getMemberDefinition(element).node;
 
     // Fetch the internal node in order to skip annotations on the member.
     // TODO(sigmund): replace this pattern when the kernel-ast provides a better
     // way to skip annotations (issue 31565).
-    var visitor = new ConstantCollector(_elementMap, constants);
+    var visitor = new ConstantCollector(_elementMap, dependencies);
     if (node is ir.Field) {
       node.initializer?.accept(visitor);
       return;
@@ -135,9 +124,9 @@
 
 class ConstantCollector extends ir.RecursiveVisitor {
   final KernelToElementMapForImpact elementMap;
-  final Set<ConstantValue> constants;
+  final Dependencies dependencies;
 
-  ConstantCollector(this.elementMap, this.constants);
+  ConstantCollector(this.elementMap, this.dependencies);
 
   CommonElements get commonElements => elementMap.commonElements;
 
@@ -145,7 +134,7 @@
     ConstantValue constant =
         elementMap.getConstantValue(node, requireConstant: required);
     if (constant != null) {
-      constants.add(constant);
+      dependencies.constants.add(constant);
     }
   }
 
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index c8a203c..65e1636 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -2839,7 +2839,7 @@
       ConstantValue value = _elementMap.getFieldConstantValue(field);
       if (value != null) {
         if (!field.isAssignable) {
-          var unit = compiler.backend.outputUnitData.outputUnitForEntity(field);
+          var unit = compiler.backend.outputUnitData.outputUnitForMember(field);
           // TODO(sigmund): this is not equivalent to what the old FE does: if
           // there is no prefix the old FE wouldn't treat this in any special
           // way. Also, if the prefix points to a constant in the main output
diff --git a/tests/compiler/dart2js/deferred/custom_element_test.dart b/tests/compiler/dart2js/deferred/custom_element_test.dart
index 01fb615..d2e6236 100644
--- a/tests/compiler/dart2js/deferred/custom_element_test.dart
+++ b/tests/compiler/dart2js/deferred/custom_element_test.dart
@@ -23,15 +23,16 @@
       await runCompiler(memorySourceFiles: MEMORY_SOURCE_FILES);
   Compiler compiler = result.compiler;
   var closedWorld = compiler.backendClosedWorldForTesting;
-  var outputUnitForEntity = compiler.backend.outputUnitData.outputUnitForEntity;
+  var outputUnitForMember = compiler.backend.outputUnitData.outputUnitForMember;
+  var outputUnitForClass = compiler.backend.outputUnitData.outputUnitForClass;
   var mainOutputUnit = compiler.backend.outputUnitData.mainOutputUnit;
   var elementEnvironment = closedWorld.elementEnvironment;
   dynamic lib = elementEnvironment.lookupLibrary(Uri.parse("memory:lib.dart"));
   var customType = elementEnvironment.lookupClass(lib, "CustomType");
   var foo = elementEnvironment.lookupLibraryMember(lib, "foo");
-  Expect.notEquals(mainOutputUnit, outputUnitForEntity(foo));
+  Expect.notEquals(mainOutputUnit, outputUnitForMember(foo));
   // Native elements are not deferred
-  Expect.equals(mainOutputUnit, outputUnitForEntity(customType));
+  Expect.equals(mainOutputUnit, outputUnitForClass(customType));
 }
 
 // The main library imports a file defining a custom element.
diff --git a/tests/compiler/dart2js/deferred/dont_inline_deferred_constants_test.dart b/tests/compiler/dart2js/deferred/dont_inline_deferred_constants_test.dart
index 33da0d2..e65d0c2 100644
--- a/tests/compiler/dart2js/deferred/dont_inline_deferred_constants_test.dart
+++ b/tests/compiler/dart2js/deferred/dont_inline_deferred_constants_test.dart
@@ -25,20 +25,20 @@
       return elementEnvironment.lookupLibrary(Uri.parse(name));
     }
 
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
 
     dynamic lib1 = lookupLibrary("memory:lib1.dart");
     var foo1 = elementEnvironment.lookupLibraryMember(lib1, "foo");
-    var ou_lib1 = outputUnitForEntity(foo1);
+    var ou_lib1 = outputUnitForMember(foo1);
 
     dynamic lib2 = lookupLibrary("memory:lib2.dart");
     var foo2 = elementEnvironment.lookupLibraryMember(lib2, "foo");
-    var ou_lib2 = outputUnitForEntity(foo2);
+    var ou_lib2 = outputUnitForMember(foo2);
 
     dynamic mainApp = elementEnvironment.mainLibrary;
     var fooMain = elementEnvironment.lookupLibraryMember(mainApp, "foo");
-    var ou_lib1_lib2 = outputUnitForEntity(fooMain);
+    var ou_lib1_lib2 = outputUnitForMember(fooMain);
 
     String mainOutput = collector.getOutput("", OutputType.js);
     String lib1Output =
diff --git a/tests/compiler/dart2js/deferred/dont_inline_deferred_globals_test.dart b/tests/compiler/dart2js/deferred/dont_inline_deferred_globals_test.dart
index 7b6db2d..25c48e3 100644
--- a/tests/compiler/dart2js/deferred/dont_inline_deferred_globals_test.dart
+++ b/tests/compiler/dart2js/deferred/dont_inline_deferred_globals_test.dart
@@ -25,12 +25,12 @@
       return elementEnvironment.lookupLibrary(Uri.parse(name));
     }
 
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
 
     dynamic lib1 = lookupLibrary("memory:lib1.dart");
     var foo1 = elementEnvironment.lookupLibraryMember(lib1, "finalVar");
-    var ou_lib1 = outputUnitForEntity(foo1);
+    var ou_lib1 = outputUnitForMember(foo1);
 
     String mainOutput = collector.getOutput("", OutputType.js);
     String lib1Output =
diff --git a/tests/compiler/dart2js/deferred/follow_implicit_super_regression_test.dart b/tests/compiler/dart2js/deferred/follow_implicit_super_regression_test.dart
index f5e7858..f3aecaa 100644
--- a/tests/compiler/dart2js/deferred/follow_implicit_super_regression_test.dart
+++ b/tests/compiler/dart2js/deferred/follow_implicit_super_regression_test.dart
@@ -20,17 +20,17 @@
       return elementEnvironment.lookupLibrary(Uri.parse(name));
     }
 
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
 
     dynamic lib = lookupLibrary("memory:lib.dart");
     var a = elementEnvironment.lookupLibraryMember(lib, "a");
     var b = elementEnvironment.lookupLibraryMember(lib, "b");
     var c = elementEnvironment.lookupLibraryMember(lib, "c");
     var d = elementEnvironment.lookupLibraryMember(lib, "d");
-    Expect.equals(outputUnitForEntity(a), outputUnitForEntity(b));
-    Expect.equals(outputUnitForEntity(a), outputUnitForEntity(c));
-    Expect.equals(outputUnitForEntity(a), outputUnitForEntity(d));
+    Expect.equals(outputUnitForMember(a), outputUnitForMember(b));
+    Expect.equals(outputUnitForMember(a), outputUnitForMember(c));
+    Expect.equals(outputUnitForMember(a), outputUnitForMember(d));
   }
 
   asyncTest(() async {
diff --git a/tests/compiler/dart2js/deferred/inline_restrictions_test.dart b/tests/compiler/dart2js/deferred/inline_restrictions_test.dart
index a296c65e..6c53fcf 100644
--- a/tests/compiler/dart2js/deferred/inline_restrictions_test.dart
+++ b/tests/compiler/dart2js/deferred/inline_restrictions_test.dart
@@ -18,16 +18,16 @@
         memorySourceFiles: MEMORY_SOURCE_FILES, outputProvider: collector);
     Compiler compiler = result.compiler;
     var env = compiler.backendClosedWorldForTesting.elementEnvironment;
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
     lookupLibrary(name) => env.lookupLibrary(Uri.parse(name));
     dynamic lib1 = lookupLibrary("memory:lib1.dart");
     var inlineMeAway = env.lookupLibraryMember(lib1, "inlineMeAway");
-    var ou_lib1 = outputUnitForEntity(inlineMeAway);
+    var ou_lib1 = outputUnitForMember(inlineMeAway);
 
     dynamic lib3 = lookupLibrary("memory:lib3.dart");
     var sameContextInline = env.lookupLibraryMember(lib3, "sameContextInline");
-    var ou_lib3 = outputUnitForEntity(sameContextInline);
+    var ou_lib3 = outputUnitForMember(sameContextInline);
 
     // Test that we actually got different output units.
     Expect.notEquals(ou_lib1.name, ou_lib3.name);
diff --git a/tests/compiler/dart2js/deferred/load_graph_segmentation2_test.dart b/tests/compiler/dart2js/deferred/load_graph_segmentation2_test.dart
index c73eb2e..985ad2a 100644
--- a/tests/compiler/dart2js/deferred/load_graph_segmentation2_test.dart
+++ b/tests/compiler/dart2js/deferred/load_graph_segmentation2_test.dart
@@ -16,15 +16,15 @@
     CompilationResult result =
         await runCompiler(memorySourceFiles: MEMORY_SOURCE_FILES);
     Compiler compiler = result.compiler;
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
     var env = compiler.backendClosedWorldForTesting.elementEnvironment;
     var mainOutputUnit = compiler.backend.outputUnitData.mainOutputUnit;
     dynamic lib = env.lookupLibrary(Uri.parse("memory:lib.dart"));
     var f1 = env.lookupLibraryMember(lib, "f1");
     var f2 = env.lookupLibraryMember(lib, "f2");
-    Expect.notEquals(mainOutputUnit, outputUnitForEntity(f1));
-    Expect.equals(mainOutputUnit, outputUnitForEntity(f2));
+    Expect.notEquals(mainOutputUnit, outputUnitForMember(f1));
+    Expect.equals(mainOutputUnit, outputUnitForMember(f2));
   });
 }
 
diff --git a/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart b/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
index 8135ce0..f20cef3 100644
--- a/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
+++ b/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
@@ -23,8 +23,9 @@
     var main = env.mainFunction;
     Expect.isNotNull(main, "Could not find 'main'");
 
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
+    var outputUnitForClass = compiler.backend.outputUnitData.outputUnitForClass;
 
     var mainOutputUnit = compiler.backend.outputUnitData.mainOutputUnit;
     var backend = compiler.backend;
@@ -40,21 +41,21 @@
     var bar1 = env.lookupLibraryMember(lib4, "bar1");
     var bar2 = env.lookupLibraryMember(lib4, "bar2");
 
-    OutputUnit ou_lib1 = outputUnitForEntity(foo1);
-    OutputUnit ou_lib2 = outputUnitForEntity(foo2);
-    OutputUnit ou_lib1_lib2 = outputUnitForEntity(foo3);
-    OutputUnit ou_lib4_1 = outputUnitForEntity(bar1);
-    OutputUnit ou_lib4_2 = outputUnitForEntity(bar2);
+    OutputUnit ou_lib1 = outputUnitForMember(foo1);
+    OutputUnit ou_lib2 = outputUnitForMember(foo2);
+    OutputUnit ou_lib1_lib2 = outputUnitForMember(foo3);
+    OutputUnit ou_lib4_1 = outputUnitForMember(bar1);
+    OutputUnit ou_lib4_2 = outputUnitForMember(bar2);
 
-    Expect.equals(mainOutputUnit, outputUnitForEntity(main));
-    Expect.notEquals(mainOutputUnit, outputUnitForEntity(foo1));
+    Expect.equals(mainOutputUnit, outputUnitForMember(main));
+    Expect.notEquals(mainOutputUnit, outputUnitForMember(foo1));
     Expect.notEquals(ou_lib1, ou_lib1_lib2);
     Expect.notEquals(ou_lib2, ou_lib1_lib2);
     Expect.notEquals(ou_lib1, ou_lib2);
     Expect.notEquals(ou_lib4_1, ou_lib4_2);
     Expect.notEquals(ou_lib1, ou_lib4_2);
     // InputElement is native, so it should be in the mainOutputUnit.
-    Expect.equals(mainOutputUnit, outputUnitForEntity(inputElement));
+    Expect.equals(mainOutputUnit, outputUnitForClass(inputElement));
 
     var hunksToLoad = compiler.deferredLoadTask.hunksToLoad;
 
diff --git a/tests/compiler/dart2js/deferred/not_in_main_test.dart b/tests/compiler/dart2js/deferred/not_in_main_test.dart
index 4a13b29..a259716 100644
--- a/tests/compiler/dart2js/deferred/not_in_main_test.dart
+++ b/tests/compiler/dart2js/deferred/not_in_main_test.dart
@@ -20,8 +20,8 @@
   asyncTest(() async {
     CompilationResult result = await runCompiler(memorySourceFiles: TEST1);
     Compiler compiler = result.compiler;
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForMember =
+        compiler.backend.outputUnitData.outputUnitForMember;
     var mainOutputUnit = compiler.backend.outputUnitData.mainOutputUnit;
     var env = compiler.backendClosedWorldForTesting.elementEnvironment;
     lookupLibrary(name) => env.lookupLibrary(Uri.parse(name));
@@ -30,7 +30,7 @@
     env.lookupLibraryMember(lib1, "foo1");
     var foo2 = env.lookupLibraryMember(lib2, "foo2");
 
-    Expect.notEquals(mainOutputUnit, outputUnitForEntity(foo2));
+    Expect.notEquals(mainOutputUnit, outputUnitForMember(foo2));
   });
 }
 
@@ -38,16 +38,15 @@
   asyncTest(() async {
     CompilationResult result = await runCompiler(memorySourceFiles: TEST2);
     Compiler compiler = result.compiler;
-    var outputUnitForEntity =
-        compiler.backend.outputUnitData.outputUnitForEntity;
+    var outputUnitForClass = compiler.backend.outputUnitData.outputUnitForClass;
 
     var mainOutputUnit = compiler.backend.outputUnitData.mainOutputUnit;
     var env = compiler.backendClosedWorldForTesting.elementEnvironment;
     lookupLibrary(name) => env.lookupLibrary(Uri.parse(name));
     dynamic shared = lookupLibrary("memory:shared.dart");
-    var a = env.lookupLibraryMember(shared, "A");
+    var a = env.lookupClass(shared, "A");
 
-    Expect.equals(mainOutputUnit, outputUnitForEntity(a));
+    Expect.equals(mainOutputUnit, outputUnitForClass(a));
   });
 }
 
diff --git a/tests/compiler/dart2js/deferred_loading/deferred_loading_test.dart b/tests/compiler/dart2js/deferred_loading/deferred_loading_test.dart
index cef5d1b..ca8c88f 100644
--- a/tests/compiler/dart2js/deferred_loading/deferred_loading_test.dart
+++ b/tests/compiler/dart2js/deferred_loading/deferred_loading_test.dart
@@ -117,7 +117,7 @@
       : super(reporter, actualMap);
 
   String getMemberValue(MemberEntity member) {
-    return outputUnitString(_data.outputUnitForEntity(member));
+    return outputUnitString(_data.outputUnitForMember(member));
   }
 
   @override
@@ -162,7 +162,7 @@
     Compiler compiler, ClassEntity cls, Map<Id, ActualData> actualMap,
     {bool verbose: false}) {
   OutputUnitData data = compiler.backend.outputUnitData;
-  String value = outputUnitString(data.outputUnitForEntity(cls));
+  String value = outputUnitString(data.outputUnitForClass(cls));
 
   KernelBackendStrategy backendStrategy = compiler.backendStrategy;
   KernelToElementMapForBuilding elementMap = backendStrategy.elementMap;