Version 2.18.0-21.0.dev

Merge commit '61a4d7ef68f98186d924fb8499b34c4b93e1b07e' into 'dev'
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index e66e57d..10b0b88 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -306,7 +306,7 @@
           inferredParameterTypes[parameterIndex++], '$type'));
     });
 
-    var functionType = environment.getFunctionType(function);
+    final functionType = environment.getFunctionType(function);
     String returnType = '${functionType.returnType}';
 
     String inferredReturnType = '${_resultOfMember(function).returnType}';
@@ -428,29 +428,10 @@
       this.closedWorld, this._globalInferenceResults);
 
   void run() {
-    dumpInfoTask._constantToNode.forEach((constant, node) {
-      // TODO(sigmund): add dependencies on other constants
-      var span = dumpInfoTask._nodeData[node];
-      var info = ConstantInfo(
-          size: span.end - span.start,
-          code: [span],
-          outputUnit: _unitInfoForConstant(constant));
-      _constantToInfo[constant] = info;
-      result.constants.add(info);
-    });
+    // TODO(markzipan): Add CFE constants to `result.constants`.
     component.libraries.forEach(visitLibrary);
   }
 
-  /// Whether to emit information about [entity].
-  ///
-  /// By default we emit information for any entity that contributes to the
-  /// output size. Either because it is a function being emitted or inlined,
-  /// or because it is an entity that holds dependencies to other entities.
-  bool shouldKeep(Entity entity) {
-    return dumpInfoTask.impacts.containsKey(entity) ||
-        dumpInfoTask.inlineCount.containsKey(entity);
-  }
-
   LibraryInfo visitLibrary(ir.Library lib) {
     final libEntity = environment.lookupLibrary(lib.importUri);
     if (libEntity == null) return null;
@@ -460,17 +441,16 @@
       libname = '<unnamed>';
     }
 
-    int size = dumpInfoTask.sizeOf(libEntity);
-    LibraryInfo info = LibraryInfo(libname, lib.importUri, null, size);
+    LibraryInfo info = LibraryInfo(libname, lib.importUri, null, null);
     _entityToInfo[libEntity] = info;
 
     lib.members.forEach((ir.Member member) {
       final memberEntity =
           environment.lookupLibraryMember(libEntity, member.name.text);
       if (memberEntity == null) return;
-
       if (member.function != null) {
-        FunctionInfo functionInfo = visitFunction(memberEntity);
+        FunctionInfo functionInfo =
+            visitFunction(member.function, functionEntity: memberEntity);
         if (functionInfo != null) {
           info.topLevelFunctions.add(functionInfo);
           functionInfo.parent = info;
@@ -488,7 +468,7 @@
       final classEntity = environment.lookupClass(libEntity, clazz.name);
       if (classEntity == null) return;
 
-      ClassTypeInfo classTypeInfo = visitClassType(classEntity);
+      ClassTypeInfo classTypeInfo = visitClassType(clazz);
       if (classTypeInfo != null) {
         info.classTypes.add(classTypeInfo);
         classTypeInfo.parent = info;
@@ -501,19 +481,265 @@
       }
     });
 
-    if (info.isEmpty && !shouldKeep(libEntity)) return null;
     result.libraries.add(info);
     return info;
   }
 
+  AbstractValue _resultOfParameter(Local e) =>
+      _globalInferenceResults.resultOfParameter(e);
+
+  FieldInfo visitField(ir.Field field, {FieldEntity fieldEntity}) {
+    FieldInfo info = FieldInfo(
+        name: field.name.text,
+        type: field.type.toStringInternal(),
+        inferredType: null,
+        code: null,
+        outputUnit: null,
+        isConst: field.isConst,
+        size: null);
+    _entityToInfo[fieldEntity] = info;
+
+    if (compiler.options.experimentCallInstrumentation) {
+      // We use field.hashCode because it is globally unique and it is
+      // available while we are doing codegen.
+      info.coverageId = '${field.hashCode}';
+    }
+
+    result.fields.add(info);
+    return info;
+  }
+
+  ClassTypeInfo visitClassType(ir.Class clazz) {
+    ClassTypeInfo classTypeInfo = ClassTypeInfo(name: clazz.name);
+    result.classTypes.add(classTypeInfo);
+    return classTypeInfo;
+  }
+
+  ClassInfo visitClass(ir.Class clazz, {ClassEntity classEntity}) {
+    // Omit class if it is not needed.
+    ClassInfo classInfo = ClassInfo(
+        name: clazz.name, isAbstract: clazz.isAbstract, outputUnit: null);
+    _entityToInfo[classEntity] = classInfo;
+
+    clazz.members.forEach((ir.Member member) {
+      // clazz.members includes constructors
+      MemberEntity memberEntity =
+          environment.lookupLocalClassMember(classEntity, member.name.text) ??
+              environment.lookupConstructor(classEntity, member.name.text);
+      if (memberEntity == null) return;
+      // Multiple kernel members can map to single JWorld member
+      // (e.g., when one of a getter/field pair are tree-shaken),
+      // so avoid duplicating the downstream info object.
+      if (_entityToInfo.containsKey(memberEntity)) {
+        return;
+      }
+
+      if (member.function != null) {
+        FunctionInfo functionInfo =
+            visitFunction(member.function, functionEntity: memberEntity);
+        if (functionInfo != null) {
+          classInfo.functions.add(functionInfo);
+          functionInfo.parent = classInfo;
+        }
+      } else {
+        FieldInfo fieldInfo = visitField(member, fieldEntity: memberEntity);
+        if (fieldInfo != null) {
+          classInfo.fields.add(fieldInfo);
+          fieldInfo.parent = classInfo;
+        }
+      }
+    });
+
+    result.classes.add(classInfo);
+    return classInfo;
+  }
+
+  FunctionInfo visitFunction(ir.FunctionNode function,
+      {FunctionEntity functionEntity}) {
+    var parent = function.parent;
+    String name = parent.toStringInternal();
+    bool isConstructor = parent is ir.Constructor;
+    bool isFactory = parent is ir.Procedure && parent.isFactory;
+    // Kernel `isStatic` refers to static members, constructors, and top-level
+    // members.
+    bool isTopLevel = ((parent is ir.Field && parent.isStatic) ||
+            (parent is ir.Procedure && parent.isStatic)) &&
+        (parent is ir.Member && parent.enclosingClass == null);
+    bool isStaticMember = ((parent is ir.Field && parent.isStatic) ||
+            (parent is ir.Procedure && parent.isStatic)) &&
+        (parent is ir.Member && parent.enclosingClass != null) &&
+        !isConstructor &&
+        !isFactory;
+    bool isConst = parent is ir.Member && parent.isConst;
+    bool isExternal = parent is ir.Member && parent.isExternal;
+    bool isMethod = parent is ir.Member && parent.enclosingClass != null;
+    bool isGetter = parent is ir.Procedure && parent.isGetter;
+    bool isSetter = parent is ir.Procedure && parent.isSetter;
+    int kind;
+    if (isTopLevel) {
+      kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND;
+    } else if (isMethod) {
+      kind = FunctionInfo.METHOD_FUNCTION_KIND;
+    }
+    if (isConstructor || isFactory) {
+      kind = FunctionInfo.CONSTRUCTOR_FUNCTION_KIND;
+      String functionName = function.toStringInternal();
+      name = functionName.isEmpty ? '$name' : '$name$functionName';
+    } else {
+      if (parent.parent is ir.Class && name.contains('.')) {
+        name = name.split('.')[1];
+      }
+    }
+    if (name.endsWith('.')) name = name.substring(0, name.length - 1);
+
+    FunctionModifiers modifiers = FunctionModifiers(
+      isStatic: isStaticMember,
+      isConst: isConst,
+      isFactory: isFactory,
+      isExternal: isExternal,
+      isGetter: isGetter,
+      isSetter: isSetter,
+    );
+
+    List<ParameterInfo> parameters = <ParameterInfo>[];
+    List<String> inferredParameterTypes = <String>[];
+
+    closedWorld.elementEnvironment.forEachParameterAsLocal(
+        _globalInferenceResults.globalLocalsMap, functionEntity, (parameter) {
+      inferredParameterTypes.add('${_resultOfParameter(parameter)}');
+    });
+
+    int parameterIndex = 0;
+    closedWorld.elementEnvironment.forEachParameter(functionEntity,
+        (type, name, _) {
+      // Synthesized parameters have no name. This can happen on parameters of
+      // setters derived from lowering late fields.
+      parameters.add(ParameterInfo(name ?? '#t${parameterIndex}',
+          inferredParameterTypes[parameterIndex++], '$type'));
+    });
+
+    // TODO(markzipan): Determine if it's safe to default to nonNullable here.
+    final nullability = parent is ir.Member
+        ? parent.enclosingLibrary.nonNullable
+        : ir.Nullability.nonNullable;
+    final functionType = function.computeFunctionType(nullability);
+
+    FunctionInfo info = FunctionInfo(
+        name: name,
+        functionKind: kind,
+        modifiers: modifiers,
+        returnType: function.returnType.toStringInternal(),
+        inferredReturnType: null,
+        parameters: parameters,
+        sideEffects: null,
+        inlinedCount: null,
+        code: null,
+        type: functionType.toStringInternal(),
+        outputUnit: null);
+    _entityToInfo[functionEntity] = info;
+
+    if (compiler.options.experimentCallInstrumentation) {
+      // We use function.hashCode because it is globally unique and it is
+      // available while we are doing codegen.
+      info.coverageId = '${function.hashCode}';
+    }
+
+    result.functions.add(info);
+    return info;
+  }
+}
+
+/// Annotates [KernelInfoCollector] with info extracted from closed-world
+/// analysis.
+class DumpInfoAnnotator {
+  final KernelInfoCollector kernelInfo;
+  final Compiler compiler;
+  final JClosedWorld closedWorld;
+  final GlobalTypeInferenceResults _globalInferenceResults;
+  final DumpInfoTask dumpInfoTask;
+
+  JElementEnvironment get environment => closedWorld.elementEnvironment;
+
+  DumpInfoAnnotator(this.kernelInfo, this.compiler, this.dumpInfoTask,
+      this.closedWorld, this._globalInferenceResults);
+
+  void run() {
+    dumpInfoTask._constantToNode.forEach((constant, node) {
+      // TODO(sigmund): add dependencies on other constants
+      var span = dumpInfoTask._nodeData[node];
+      var info = ConstantInfo(
+          size: span.end - span.start,
+          code: [span],
+          outputUnit: _unitInfoForConstant(constant));
+      kernelInfo._constantToInfo[constant] = info;
+      info.treeShakenStatus = TreeShakenStatus.Live;
+      kernelInfo.result.constants.add(info);
+    });
+    environment.libraries.forEach(visitLibrary);
+  }
+
+  /// Whether to emit information about [entity].
+  ///
+  /// By default we emit information for any entity that contributes to the
+  /// output size. Either because it is a function being emitted or inlined,
+  /// or because it is an entity that holds dependencies to other entities.
+  bool shouldKeep(Entity entity) {
+    return dumpInfoTask.impacts.containsKey(entity) ||
+        dumpInfoTask.inlineCount.containsKey(entity);
+  }
+
+  LibraryInfo visitLibrary(LibraryEntity lib) {
+    var kLibraryInfos = kernelInfo.result.libraries
+        .where((i) => '${i.uri}' == '${lib.canonicalUri}');
+    assert(
+        kLibraryInfos.length == 1,
+        'Ambiguous library resolution. '
+        'Expected singleton, found $kLibraryInfos');
+    var kLibraryInfo = kLibraryInfos.first;
+
+    String libname = environment.getLibraryName(lib);
+    if (libname.isEmpty) {
+      libname = '<unnamed>';
+    }
+    assert(kLibraryInfo.name == libname);
+    kLibraryInfo.size = dumpInfoTask.sizeOf(lib);
+
+    environment.forEachLibraryMember(lib, (MemberEntity member) {
+      if (member.isFunction || member.isGetter || member.isSetter) {
+        visitFunction(member, libname);
+      } else if (member.isField) {
+        visitField(member, libname);
+      } else {
+        throw StateError('Class member not a function or field');
+      }
+    });
+
+    environment.forEachClass(lib, (ClassEntity clazz) {
+      visitClassType(clazz, libname);
+      visitClass(clazz, libname);
+    });
+
+    bool hasLiveFields = [
+      ...kLibraryInfo.topLevelFunctions,
+      ...kLibraryInfo.topLevelVariables,
+      ...kLibraryInfo.classes,
+      ...kLibraryInfo.classTypes
+    ].any((i) => i.treeShakenStatus == TreeShakenStatus.Live);
+    if (!hasLiveFields && !shouldKeep(lib)) return null;
+    kLibraryInfo.treeShakenStatus = TreeShakenStatus.Live;
+    return kLibraryInfo;
+  }
+
   GlobalTypeInferenceMemberResult _resultOfMember(MemberEntity e) =>
       _globalInferenceResults.resultOfMember(e);
 
   AbstractValue _resultOfParameter(Local e) =>
       _globalInferenceResults.resultOfParameter(e);
 
-  FieldInfo visitField(ir.Field field, {FieldEntity fieldEntity}) {
-    AbstractValue inferredType = _resultOfMember(fieldEntity).type;
+  // TODO(markzipan): [parentName] is used for disambiguation, but this might
+  // not always be valid. Check and validate later.
+  FieldInfo visitField(FieldEntity field, String parentName) {
+    AbstractValue inferredType = _resultOfMember(field).type;
     // If a field has an empty inferred type it is never used.
     if (inferredType == null ||
         closedWorld.abstractValueDomain
@@ -522,123 +748,114 @@
       return null;
     }
 
-    int size = dumpInfoTask.sizeOf(fieldEntity);
-    List<CodeSpan> code = dumpInfoTask.codeOf(fieldEntity);
+    final kFieldInfos = kernelInfo.result.fields
+        .where((f) => f.name == field.name && f.parent.name == parentName)
+        .toList();
+    assert(
+        kFieldInfos.length == 1,
+        'Ambiguous field resolution. '
+        'Expected singleton, found $kFieldInfos');
+    final kFieldInfo = kFieldInfos.first;
+
+    int size = dumpInfoTask.sizeOf(field);
+    List<CodeSpan> code = dumpInfoTask.codeOf(field);
 
     // TODO(het): Why doesn't `size` account for the code size already?
     if (code != null) size += code.length;
 
-    FieldInfo info = FieldInfo(
-        name: field.name.text,
-        type: field.type.toStringInternal(),
-        inferredType: '$inferredType',
-        code: code,
-        outputUnit: _unitInfoForMember(fieldEntity),
-        isConst: field.isConst);
-    _entityToInfo[fieldEntity] = info;
-    FieldAnalysisData fieldData =
-        closedWorld.fieldAnalysis.getFieldData(fieldEntity);
+    kFieldInfo.outputUnit = _unitInfoForMember(field);
+    kFieldInfo.inferredType = '$inferredType';
+    kFieldInfo.code = code;
+    kFieldInfo.treeShakenStatus = TreeShakenStatus.Live;
+
+    FieldAnalysisData fieldData = closedWorld.fieldAnalysis.getFieldData(field);
     if (fieldData.initialValue != null) {
-      info.initializer = _constantToInfo[fieldData.initialValue];
+      kFieldInfo.initializer =
+          kernelInfo._constantToInfo[fieldData.initialValue];
     }
 
-    if (compiler.options.experimentCallInstrumentation) {
-      // We use field.hashCode because it is globally unique and it is
-      // available while we are doing codegen.
-      info.coverageId = '${field.hashCode}';
-    }
-
-    int closureSize = _addClosureInfo(info, fieldEntity);
-    info.size = size + closureSize;
-
-    result.fields.add(info);
-    return info;
+    int closureSize = _addClosureInfo(kFieldInfo, field);
+    kFieldInfo.size = size + closureSize;
+    return kFieldInfo;
   }
 
-  ClassTypeInfo visitClassType(ClassEntity clazz) {
-    // Omit class type if it is not needed.
-    ClassTypeInfo classTypeInfo = ClassTypeInfo(
-        name: clazz.name, outputUnit: _unitInfoForClassType(clazz));
+  // TODO(markzipan): [parentName] is used for disambiguation, but this might
+  // not always be valid. Check and validate later.
+  ClassTypeInfo visitClassType(ClassEntity clazz, String parentName) {
+    var kClassTypeInfos = kernelInfo.result.classTypes
+        .where((i) => i.name == clazz.name && i.parent.name == parentName);
+    assert(
+        kClassTypeInfos.length == 1,
+        'Ambiguous class type resolution. '
+        'Expected singleton, found $kClassTypeInfos');
+    var kClassTypeInfo = kClassTypeInfos.first;
 
     // TODO(joshualitt): Get accurate size information for class types.
-    classTypeInfo.size = 0;
+    kClassTypeInfo.size = 0;
 
+    // Omit class type if it is not needed.
     bool isNeeded =
         compiler.backendStrategy.emitterTask.neededClassTypes.contains(clazz);
-    if (!isNeeded) {
-      return null;
-    }
+    if (!isNeeded) return null;
 
-    result.classTypes.add(classTypeInfo);
-    return classTypeInfo;
+    assert(kClassTypeInfo.name == clazz.name);
+    kClassTypeInfo.outputUnit = _unitInfoForClassType(clazz);
+    kClassTypeInfo.treeShakenStatus = TreeShakenStatus.Live;
+    return kClassTypeInfo;
   }
 
-  ClassInfo visitClass(ir.Class clazz, {ClassEntity classEntity}) {
-    // Omit class if it is not needed.
-    ClassInfo classInfo = ClassInfo(
-        name: clazz.name,
-        isAbstract: clazz.isAbstract,
-        outputUnit: _unitInfoForClass(classEntity));
-    _entityToInfo[classEntity] = classInfo;
+  // TODO(markzipan): [parentName] is used for disambiguation, but this might
+  // not always be valid. Check and validate later.
+  ClassInfo visitClass(ClassEntity clazz, String parentName) {
+    final kClassInfos = kernelInfo.result.classes
+        .where((i) => i.name == clazz.name && i.parent.name == parentName)
+        .toList();
+    assert(
+        kClassInfos.length == 1,
+        'Ambiguous class resolution. '
+        'Expected singleton, found $kClassInfos');
+    final kClassInfo = kClassInfos.first;
 
-    int size = dumpInfoTask.sizeOf(classEntity);
-    clazz.members.forEach((ir.Member member) {
-      MemberEntity memberEntity =
-          environment.lookupClassMember(classEntity, member.name.text);
-      if (memberEntity == null) return;
-      // Note: JWorld representations of a kernel member may omit the field or
-      // getter. Filter those cases here.
-      if ((member is ir.Field && memberEntity is! FieldEntity) ||
-          (member is ir.Procedure && memberEntity is! FunctionEntity)) {
-        return;
-      }
-
-      if (member.function != null) {
-        FunctionInfo functionInfo = visitFunction(memberEntity);
+    int size = dumpInfoTask.sizeOf(clazz);
+    environment.forEachLocalClassMember(clazz, (member) {
+      if (member.isFunction || member.isGetter || member.isSetter) {
+        FunctionInfo functionInfo = visitFunction(member, clazz.name);
         if (functionInfo != null) {
-          classInfo.functions.add(functionInfo);
-          functionInfo.parent = classInfo;
           for (var closureInfo in functionInfo.closures) {
             size += closureInfo.size;
           }
         }
-      } else {
-        FieldInfo fieldInfo = visitField(member, fieldEntity: memberEntity);
+      } else if (member.isField) {
+        FieldInfo fieldInfo = visitField(member, clazz.name);
         if (fieldInfo != null) {
-          classInfo.fields.add(fieldInfo);
-          fieldInfo.parent = classInfo;
           for (var closureInfo in fieldInfo.closures) {
             size += closureInfo.size;
           }
         }
+      } else {
+        throw StateError('Class member not a function or field');
       }
     });
-
-    clazz.constructors.forEach((ir.Constructor constructor) {
-      final constructorEntity =
-          environment.lookupConstructor(classEntity, constructor.name.text);
-      if (constructorEntity == null) return;
-      FunctionInfo functionInfo = visitFunction(constructorEntity);
+    environment.forEachConstructor(clazz, (constructor) {
+      FunctionInfo functionInfo = visitFunction(constructor, clazz.name);
       if (functionInfo != null) {
-        classInfo.functions.add(functionInfo);
-        functionInfo.parent = classInfo;
         for (var closureInfo in functionInfo.closures) {
           size += closureInfo.size;
         }
       }
     });
+    kClassInfo.size = size;
 
-    classInfo.size = size;
-
-    if (!compiler.backendStrategy.emitterTask.neededClasses
-            .contains(classEntity) &&
-        classInfo.fields.isEmpty &&
-        classInfo.functions.isEmpty) {
+    bool hasLiveFields = [...kClassInfo.fields, ...kClassInfo.functions]
+        .any((i) => i.treeShakenStatus == TreeShakenStatus.Live);
+    if (!compiler.backendStrategy.emitterTask.neededClasses.contains(clazz) &&
+        !hasLiveFields) {
       return null;
     }
 
-    result.classes.add(classInfo);
-    return classInfo;
+    kClassInfo.outputUnit = _unitInfoForClass(clazz);
+    kClassInfo.treeShakenStatus = TreeShakenStatus.Live;
+    return kClassInfo;
   }
 
   ClosureInfo visitClosureClass(ClassEntity element) {
@@ -646,54 +863,53 @@
         name: element.name,
         outputUnit: _unitInfoForClass(element),
         size: dumpInfoTask.sizeOf(element));
-    _entityToInfo[element] = closureInfo;
+    kernelInfo._entityToInfo[element] = closureInfo;
 
     FunctionEntity callMethod = closedWorld.elementEnvironment
         .lookupClassMember(element, Identifiers.call);
 
-    FunctionInfo functionInfo = visitFunction(callMethod);
+    FunctionInfo functionInfo = visitFunction(callMethod, element.name);
     if (functionInfo == null) return null;
+
     closureInfo.function = functionInfo;
     functionInfo.parent = closureInfo;
+    closureInfo.treeShakenStatus = TreeShakenStatus.Live;
 
-    result.closures.add(closureInfo);
+    kernelInfo.result.closures.add(closureInfo);
     return closureInfo;
   }
 
-  FunctionInfo visitFunction(FunctionEntity function) {
+  // TODO(markzipan): [parentName] is used for disambiguation, but this might
+  // not always be valid. Check and validate later.
+  FunctionInfo visitFunction(FunctionEntity function, String parentName) {
     int size = dumpInfoTask.sizeOf(function);
     // TODO(sigmund): consider adding a small info to represent unreachable
     // code here.
     if (size == 0 && !shouldKeep(function)) return null;
 
-    // TODO(het): use 'toString' instead of 'text'? It will add '=' for setters
-    String name = function.memberName.text;
-    int kind;
-    if (function.isStatic || function.isTopLevel) {
-      kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND;
-    } else if (function.enclosingClass != null) {
-      kind = FunctionInfo.METHOD_FUNCTION_KIND;
-    }
-
+    var compareName = function.name;
     if (function.isConstructor) {
-      name = name == ""
+      compareName = compareName == ""
           ? "${function.enclosingClass.name}"
           : "${function.enclosingClass.name}.${function.name}";
-      kind = FunctionInfo.CONSTRUCTOR_FUNCTION_KIND;
     }
 
-    assert(kind != null);
+    // Multiple kernel members members can sometimes map to a single JElement.
+    // [isSetter] and [isGetter] are required for disambiguating these cases.
+    final kFunctionInfos = kernelInfo.result.functions
+        .where((i) =>
+            i.name == compareName &&
+            i.parent.name == parentName &&
+            !(function.isGetter ^ i.modifiers.isGetter) &&
+            !(function.isSetter ^ i.modifiers.isSetter))
+        .toList();
+    assert(
+        kFunctionInfos.length == 1,
+        'Ambiguous function resolution. '
+        'Expected singleton, found $kFunctionInfos');
+    final kFunctionInfo = kFunctionInfos.first;
 
-    FunctionModifiers modifiers = FunctionModifiers(
-      isStatic: function.isStatic,
-      isConst: function.isConst,
-      isFactory: function.isConstructor
-          ? (function as ConstructorEntity).isFactoryConstructor
-          : false,
-      isExternal: function.isExternal,
-    );
     List<CodeSpan> code = dumpInfoTask.codeOf(function);
-
     List<ParameterInfo> parameters = <ParameterInfo>[];
     List<String> inferredParameterTypes = <String>[];
 
@@ -709,49 +925,28 @@
           inferredParameterTypes[parameterIndex++], '$type'));
     });
 
-    var functionType = environment.getFunctionType(function);
-    String returnType = '${functionType.returnType}';
-
     String inferredReturnType = '${_resultOfMember(function).returnType}';
     String sideEffects =
         '${_globalInferenceResults.inferredData.getSideEffectsOfElement(function)}';
+    int inlinedCount = dumpInfoTask.inlineCount[function] ?? 0;
 
-    int inlinedCount = dumpInfoTask.inlineCount[function];
-    if (inlinedCount == null) inlinedCount = 0;
+    kFunctionInfo.inferredReturnType = inferredReturnType;
+    kFunctionInfo.sideEffects = sideEffects;
+    kFunctionInfo.inlinedCount = inlinedCount;
+    kFunctionInfo.code = code;
+    kFunctionInfo.outputUnit = _unitInfoForMember(function);
 
-    FunctionInfo info = FunctionInfo(
-        name: name,
-        functionKind: kind,
-        modifiers: modifiers,
-        returnType: returnType,
-        inferredReturnType: inferredReturnType,
-        parameters: parameters,
-        sideEffects: sideEffects,
-        inlinedCount: inlinedCount,
-        code: code,
-        type: functionType.toString(),
-        outputUnit: _unitInfoForMember(function));
-    _entityToInfo[function] = info;
+    int closureSize = _addClosureInfo(kFunctionInfo, function);
+    kFunctionInfo.size = size + closureSize;
 
-    int closureSize = _addClosureInfo(info, function);
-    size += closureSize;
-
-    if (compiler.options.experimentCallInstrumentation) {
-      // We use function.hashCode because it is globally unique and it is
-      // available while we are doing codegen.
-      info.coverageId = '${function.hashCode}';
-    }
-
-    info.size = size;
-
-    result.functions.add(info);
-    return info;
+    kFunctionInfo.treeShakenStatus = TreeShakenStatus.Live;
+    return kFunctionInfo;
   }
 
   /// Adds closure information to [info], using all nested closures in [member].
   ///
   /// Returns the total size of the nested closures, to add to the info size.
-  int _addClosureInfo(Info info, MemberEntity member) {
+  int _addClosureInfo(BasicInfo info, MemberEntity member) {
     assert(info is FunctionInfo || info is FieldInfo);
     int size = 0;
     List<ClosureInfo> nestedClosures = <ClosureInfo>[];
@@ -759,6 +954,7 @@
       ClosureInfo closureInfo = visitClosureClass(closure.enclosingClass);
       if (closureInfo != null) {
         closureInfo.parent = info;
+        closureInfo.treeShakenStatus = info.treeShakenStatus;
         nestedClosures.add(closureInfo);
         size += closureInfo.size;
       }
@@ -770,7 +966,7 @@
   }
 
   OutputUnitInfo _infoFromOutputUnit(OutputUnit outputUnit) {
-    return _outputToInfo.putIfAbsent(outputUnit, () {
+    return kernelInfo._outputToInfo.putIfAbsent(outputUnit, () {
       // Dump-info currently only works with the full emitter. If another
       // emitter is used it will fail here.
       JsBackendStrategy backendStrategy = compiler.backendStrategy;
@@ -780,9 +976,10 @@
           : deferredPartFileName(compiler.options, outputUnit.name);
       OutputUnitInfo info = OutputUnitInfo(filename, outputUnit.name,
           backendStrategy.emitterTask.emitter.generatedSize(outputUnit));
+      info.treeShakenStatus = TreeShakenStatus.Live;
       info.imports
           .addAll(closedWorld.outputUnitData.getImportNames(outputUnit));
-      result.outputUnits.add(info);
+      kernelInfo.result.outputUnits.add(info);
       return info;
     });
   }
@@ -1003,11 +1200,15 @@
           component, compiler, this, closedWorld, globalInferenceResults)
         ..run();
 
+      DumpInfoAnnotator(kernelInfoCollector, compiler, this, closedWorld,
+          globalInferenceResults)
+        ..run();
+
       var allInfo = buildDumpInfoDataNew(closedWorld, kernelInfoCollector);
       // TODO(markzipan): Filter DumpInfo here instead of passing a filter
       // filter flag through the serializers.
       if (useBinaryFormat) {
-        dumpInfoBinary(allInfo, filterTreeshaken: true);
+        dumpInfoBinary(allInfo);
       } else {
         dumpInfoJson(allInfo, filterTreeshaken: true);
       }
diff --git a/pkg/dart2js_info/lib/info.dart b/pkg/dart2js_info/lib/info.dart
index d5d64bf..ad45c6f 100644
--- a/pkg/dart2js_info/lib/info.dart
+++ b/pkg/dart2js_info/lib/info.dart
@@ -467,12 +467,17 @@
   final bool isConst;
   final bool isFactory;
   final bool isExternal;
+  final bool isGetter;
+  final bool isSetter;
 
-  FunctionModifiers(
-      {this.isStatic = false,
-      this.isConst = false,
-      this.isFactory = false,
-      this.isExternal = false});
+  FunctionModifiers({
+    this.isStatic = false,
+    this.isConst = false,
+    this.isFactory = false,
+    this.isExternal = false,
+    this.isGetter,
+    this.isSetter,
+  });
 }
 
 /// Possible values of the `kind` field in the serialized infos.
diff --git a/tools/VERSION b/tools/VERSION
index c45d641..3e0b84e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 20
+PRERELEASE 21
 PRERELEASE_PATCH 0
\ No newline at end of file