Version 2.13.0-149.0.dev

Merge commit '7c29dd9583604083ecdae3f05517967aba7dc35e' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 48079d1..5c095eda 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-03-15T10:48:35.022974",
+  "generated": "2021-03-17T11:06:05.181429",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -139,6 +139,12 @@
       "languageVersion": "2.10"
     },
     {
+      "name": "browser_launcher",
+      "rootUri": "../third_party/pkg/browser_launcher",
+      "packageUri": "lib/",
+      "languageVersion": "2.2"
+    },
+    {
       "name": "build_integration",
       "rootUri": "../pkg/build_integration",
       "packageUri": "lib/",
@@ -788,6 +794,12 @@
       "languageVersion": "2.0"
     },
     {
+      "name": "webkit_inspection_protocol",
+      "rootUri": "../third_party/pkg/webkit_inspection_protocol",
+      "packageUri": "lib/",
+      "languageVersion": "2.0"
+    },
+    {
       "name": "yaml",
       "rootUri": "../third_party/pkg/yaml",
       "packageUri": "lib/",
diff --git a/.packages b/.packages
index 3587c4f..5fc05a8 100644
--- a/.packages
+++ b/.packages
@@ -20,6 +20,7 @@
 bazel_worker:third_party/pkg/bazel_worker/lib
 benchmark_harness:third_party/pkg/benchmark_harness/lib
 boolean_selector:third_party/pkg/boolean_selector/lib
+browser_launcher:third_party/pkg/browser_launcher/lib
 build_integration:pkg/build_integration/lib
 charcode:third_party/pkg/charcode/lib
 cli_util:third_party/pkg/cli_util/lib
@@ -118,6 +119,7 @@
 wasm:pkg/wasm/lib
 watcher:third_party/pkg/watcher/lib
 webdriver:third_party/pkg/webdriver/lib
+webkit_inspection_protocol:third_party/pkg/webkit_inspection_protocol/lib
 web_components:third_party/pkg/web_components/lib
 web_socket_channel:third_party/pkg/web_socket_channel/lib
 yaml:third_party/pkg/yaml/lib
diff --git a/DEPS b/DEPS
index 184913f..50bd12c 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
   # has.
-  "co19_rev": "88b4f0556b0889067efcac45f2b96bc630e5b96c",
+  "co19_rev": "8cf837f5abf0a30ed91ebf480df58e5c0480947e",
   "co19_2_rev": "cf6eed0535e45413672bb5bb6e65df9f59846372",
 
   # The internal benchmarks to use. See go/dart-benchmarks-internal
@@ -101,6 +101,7 @@
   "dart_style_tag": "2cf810073e0cc7e7ea05d3de51830e6fa6d62924",
 
   "chromedriver_tag": "83.0.4103.39",
+  "browser_launcher_rev": "74949f0c599095e6fac3cb4581ed4769167576c7",
   "dartdoc_rev" : "174021f3d76c2a8500b54977c80f32c5bbc099f1",
   "ffi_rev": "f3346299c55669cc0db48afae85b8110088bf8da",
   "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
@@ -168,6 +169,7 @@
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "76931ea1b81ba71e8319330c35285d3e88566315",
   "WebCore_rev": "fb11e887f77919450e497344da570d780e078bc8",
+  "webkit_inspection_protocol_rev": "6b15729292d030f2e5c5861022da4c5a4c11961c",
   "yaml_rev": "b4c4411631bda556ce9a45af1ab0eecaf9f3ac53",
   "zlib_rev": "bf44340d1b6be1af8950bbdf664fec0cf5a831cc",
   "crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
@@ -313,6 +315,9 @@
   Var("dart_root") + "/third_party/pkg/boolean_selector":
       Var("dart_git") + "boolean_selector.git" +
       "@" + Var("boolean_selector_rev"),
+  Var("dart_root") + "/third_party/pkg/browser_launcher":
+      Var("dart_git") + "browser_launcher.git" +
+      "@" + Var("browser_launcher_rev"),
   Var("dart_root") + "/third_party/pkg/charcode":
       Var("dart_git") + "charcode.git" + "@" + Var("charcode_rev"),
   Var("dart_root") + "/third_party/pkg/cli_util":
@@ -461,6 +466,9 @@
   Var("dart_root") + "/third_party/pkg/webdriver":
       Var("dart_git") + "external/github.com/google/webdriver.dart.git" +
       "@" + Var("webdriver_rev"),
+  Var("dart_root") + "/third_party/pkg/webkit_inspection_protocol":
+      Var("dart_git") + "external/github.com/google/webkit_inspection_protocol.dart.git" +
+      "@" + Var("webkit_inspection_protocol_rev"),
 
   Var("dart_root") + "/third_party/pkg/web_socket_channel":
       Var("dart_git") + "web_socket_channel.git" +
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
index cfa4242e..e610849 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/src/services/pub/pub_package_service.dart';
 import 'package:analysis_server/src/utilities/extensions/yaml.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/util/yaml.dart';
 import 'package:yaml/yaml.dart';
 
 /// A completion generator that can produce completion suggestions for files
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
index 58abf49..29e814f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
@@ -6,8 +6,8 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_parser.dart';
-import 'package:analysis_server/src/utilities/extensions/yaml.dart';
 import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/util/yaml.dart';
 import 'package:yaml/yaml.dart';
 
 /// A parser used to parse the content of a configuration file.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index b0c122c..21e7d1d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -19,8 +19,8 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/variable_scope.dart';
-import 'package:analysis_server/src/utilities/extensions/yaml.dart';
 import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/util/yaml.dart';
 import 'package:meta/meta.dart';
 import 'package:yaml/yaml.dart';
 
diff --git a/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart b/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
index 6ce971a..1199a2f 100644
--- a/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
+++ b/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
@@ -4,30 +4,6 @@
 
 import 'package:yaml/yaml.dart';
 
-extension YamlMapExtensions on YamlMap {
-  /// Return the node representing the key that corresponds to the value
-  /// represented by the [value] node.
-  YamlNode keyAtValue(YamlNode value) {
-    for (var entry in nodes.entries) {
-      if (entry.value == value) {
-        return entry.key;
-      }
-    }
-    return null;
-  }
-
-  /// Return the value associated with the key whose value matches the given
-  /// [key], or `null` if there is no matching key.
-  YamlNode valueAt(String key) {
-    for (var keyNode in nodes.keys) {
-      if (keyNode is YamlScalar && keyNode.value == key) {
-        return nodes[key];
-      }
-    }
-    return null;
-  }
-}
-
 extension YamlNodeExtensions on YamlNode {
   /// Return the child of this node that contains the given [offset], or `null`
   /// if none of the children contains the offset.
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index fd9f767..9395186ea 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -476,6 +476,9 @@
   FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
   FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
   FfiCode.NON_SIZED_TYPE_ARGUMENT,
+  FfiCode.PACKED_ANNOTATION,
+  FfiCode.PACKED_ANNOTATION_ALIGNMENT,
+  FfiCode.PACKED_NESTING_NON_PACKED,
   FfiCode.SIZE_ANNOTATION_DIMENSIONS,
   FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
   FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS,
diff --git a/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart b/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart
index 6193114..3e512c7 100644
--- a/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart
+++ b/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart
@@ -62,7 +62,7 @@
   /// Return an empty options map if the file does not exist.
   YamlMap getOptionsFromSource(Source source) {
     YamlMap options = getOptionsFromString(_readAnalysisOptions(source));
-    var node = getValue(options, AnalyzerOptions.include);
+    var node = options.valueAt(AnalyzerOptions.include);
     var sourceFactory = this.sourceFactory;
     if (sourceFactory != null && node is YamlScalar) {
       var path = node.value;
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
index 6b341e8..986e5a0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
@@ -433,10 +433,10 @@
             .getOptionsFromFile(optionsFile);
 
         if (doc is YamlMap) {
-          var analyzerOptions = getValue(doc, AnalyzerOptions.analyzer);
+          var analyzerOptions = doc.valueAt(AnalyzerOptions.analyzer);
           if (analyzerOptions is YamlMap) {
             var excludeOptions =
-                getValue(analyzerOptions, AnalyzerOptions.exclude);
+                analyzerOptions.valueAt(AnalyzerOptions.exclude);
             if (excludeOptions is YamlList) {
               List<String>? excludeList = toStringList(excludeOptions);
               if (excludeList != null) {
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
index 08f603f..dc8e106 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
@@ -209,6 +209,35 @@
   /**
    * No parameters.
    */
+  static const FfiCode PACKED_ANNOTATION = FfiCode(
+      name: 'PACKED_ANNOTATION',
+      message: "Structs must have at most one 'Packed' annotation.",
+      correction: "Try removing extra 'Packed' annotations.");
+
+  /**
+   * No parameters.
+   */
+  static const FfiCode PACKED_ANNOTATION_ALIGNMENT = FfiCode(
+      name: 'PACKED_ANNOTATION_ALIGNMENT',
+      message: "Only packing to 1, 2, 4, 8, and 16 bytes is supported.",
+      correction:
+          "Try changing the 'Packed' annotation alignment to 1, 2, 4, 8, or 16.");
+
+  /**
+   * Parameters:
+   * 0: the name of the outer struct
+   * 1: the name of the struct being nested
+   */
+  static const FfiCode PACKED_NESTING_NON_PACKED = FfiCode(
+      name: 'PACKED_NESTING_NON_PACKED',
+      message:
+          "Nesting the non-packed or less tightly packed struct '{0}' in a packed struct '{1}' is not supported.",
+      correction:
+          "Try packing the nested struct or packing the nested struct more tightly.");
+
+  /**
+   * No parameters.
+   */
   static const FfiCode SIZE_ANNOTATION_DIMENSIONS = FfiCode(
       name: 'SIZE_ANNOTATION_DIMENSIONS',
       message:
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 92d84fd..7ab99dd 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -13079,6 +13079,20 @@
   // [Understanding null safety](/null-safety/understanding-null-safety#smarter-null-aware-methods)
   // for more details.
   //
+  // The following code produces this diagnostic because `s` can't be `null`.
+  //
+  // ```dart
+  // void f(Object? o) {
+  //   var s = o as String;
+  //   s[!?.!]length;
+  // }
+  // ```
+  //
+  // The reason `s` can't be null, despite the fact that `o` can be `null`, is
+  // because of the cast to `String`, which is a non-nullable type. If `o` ever
+  // has the value `null`, the cast will fail and the invocation of `length`
+  // will not happen.
+  //
   // #### Common fixes
   //
   // Replace the null-aware operator with a non-null-aware equivalent; for
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index d65781b..bd826d4 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -51,12 +51,16 @@
   /// `Struct`.
   bool inStruct = false;
 
+  /// Subclass of `Struct` we are currently visiting, or `null`.
+  ClassDeclaration? struct;
+
   /// Initialize a newly created verifier.
   FfiVerifier(this.typeSystem, this._errorReporter);
 
   @override
   void visitClassDeclaration(ClassDeclaration node) {
     inStruct = false;
+    struct = null;
     // Only the Allocator, Opaque and Struct class may be extended.
     var extendsClause = node.extendsClause;
     if (extendsClause != null) {
@@ -66,10 +70,12 @@
         final className = ffiClass.name;
         if (className == _structClassName) {
           inStruct = true;
+          struct = node;
           if (node.declaredElement!.isEmptyStruct) {
             _errorReporter
                 .reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [node.name]);
           }
+          _validatePackedAnnotation(node.metadata);
         } else if (className != _allocatorClassName &&
             className != _opaqueClassName) {
           _errorReporter.reportErrorForNode(
@@ -561,12 +567,20 @@
         }
         final arrayDimensions = declaredType.arrayDimensions;
         _validateSizeOfAnnotation(fieldType, annotations, arrayDimensions);
+        final arrayElement = declaredType.arrayElementType;
+        if (arrayElement.isStructSubtype) {
+          final elementClass = (arrayElement as InterfaceType).element;
+          _validatePackingNesting(struct!.declaredElement!, elementClass,
+              errorNode: fieldType);
+        }
       } else if (declaredType.isStructSubtype) {
         final clazz = (declaredType as InterfaceType).element;
         if (clazz.isEmptyStruct) {
           _errorReporter
               .reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
         }
+        _validatePackingNesting(struct!.declaredElement!, clazz,
+            errorNode: fieldType);
       } else {
         _errorReporter.reportErrorForNode(FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
             fieldType, [fieldType.toSource()]);
@@ -664,6 +678,54 @@
     }
   }
 
+  /// Validate that the [annotations] include at most one packed annotation.
+  void _validatePackedAnnotation(NodeList<Annotation> annotations) {
+    final ffiPackedAnnotations =
+        annotations.where((annotation) => annotation.isPacked).toList();
+
+    if (ffiPackedAnnotations.isEmpty) {
+      return;
+    }
+
+    if (ffiPackedAnnotations.length > 1) {
+      final extraAnnotations = ffiPackedAnnotations.skip(1);
+      for (final annotation in extraAnnotations) {
+        _errorReporter.reportErrorForNode(
+            FfiCode.PACKED_ANNOTATION, annotation);
+      }
+    }
+
+    // Check number of dimensions.
+    final annotation = ffiPackedAnnotations.first;
+    final value = annotation.elementAnnotation?.packedMemberAlignment;
+    if (![1, 2, 4, 8, 16].contains(value)) {
+      _errorReporter.reportErrorForNode(
+          FfiCode.PACKED_ANNOTATION_ALIGNMENT, annotation);
+    }
+  }
+
+  void _validatePackingNesting(ClassElement outer, ClassElement nested,
+      {required TypeAnnotation errorNode}) {
+    final outerPacking = outer.structPacking;
+    if (outerPacking == null) {
+      // No packing for outer class, so we're done.
+      return;
+    }
+    bool error = false;
+    final nestedPacking = nested.structPacking;
+    if (nestedPacking == null) {
+      // The outer struct packs, but the nested struct does not.
+      error = true;
+    } else if (outerPacking < nestedPacking) {
+      // The outer struct packs tighter than the nested struct.
+      error = true;
+    }
+    if (error) {
+      _errorReporter.reportErrorForNode(FfiCode.PACKED_NESTING_NON_PACKED,
+          errorNode, [nested.name, outer.name]);
+    }
+  }
+
   void _validateRefIndexed(IndexExpression node) {
     var targetType = node.realTarget.staticType;
     if (!_isValidFfiNativeType(targetType,
@@ -774,6 +836,30 @@
   none,
 }
 
+extension on Annotation {
+  bool get isPacked {
+    final element = this.element;
+    return element is ConstructorElement &&
+        element.ffiClass != null &&
+        element.enclosingElement.name == 'Packed';
+  }
+}
+
+extension on ElementAnnotation {
+  bool get isPacked {
+    final element = this.element;
+    return element is ConstructorElement &&
+        element.ffiClass != null &&
+        element.enclosingElement.name == 'Packed';
+  }
+
+  int get packedMemberAlignment {
+    assert(isPacked);
+    final value = computeConstantValue();
+    return value!.getField('memberAlignment')!.toIntValue()!;
+  }
+}
+
 extension on Element? {
   /// Return `true` if this represents the extension `AllocatorAlloc`.
   bool get isAllocatorExtension {
@@ -855,6 +941,17 @@
   bool get isFfiClass {
     return library.name == FfiVerifier._dartFfiLibraryName;
   }
+
+  int? get structPacking {
+    final packedAnnotations =
+        metadata.where((annotation) => annotation.isPacked);
+
+    if (packedAnnotations.isEmpty) {
+      return null;
+    }
+
+    return packedAnnotations.first.packedMemberAlignment;
+  }
 }
 
 extension on ExtensionElement {
@@ -886,6 +983,16 @@
     return dimensions;
   }
 
+  DartType get arrayElementType {
+    DartType iterator = this;
+    while (iterator is InterfaceType &&
+        iterator.element.name == FfiVerifier._arrayClassName &&
+        iterator.element.isFfiClass) {
+      iterator = iterator.typeArguments.single;
+    }
+    return iterator;
+  }
+
   bool get isPointer {
     final self = this;
     return self is InterfaceType && self.element.isPointer;
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index 270cc84..ff6050c 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -93,7 +93,7 @@
     return buffer.toString();
   }
 
-  /// If the given [keyword] is not `null`, append it to the given [builder],
+  /// If the given [keyword] is not `null`, append it to the given [buffer],
   /// prefixing it with a space if [needsSpace] is `true`. Return `true` if
   /// subsequent keywords need to be prefixed with a space.
   bool _appendKeyword(StringBuffer buffer, bool needsSpace, Token? keyword) {
diff --git a/pkg/analyzer/lib/src/lint/config.dart b/pkg/analyzer/lib/src/lint/config.dart
index 4b0dd66..288bf06 100644
--- a/pkg/analyzer/lib/src/lint/config.dart
+++ b/pkg/analyzer/lib/src/lint/config.dart
@@ -9,7 +9,7 @@
 /// Return `null` if [optionsMap] is `null` or does not have `linter` map.
 LintConfig? parseConfig(YamlMap? optionsMap) {
   if (optionsMap != null) {
-    var options = getValue(optionsMap, 'linter');
+    var options = optionsMap.valueAt('linter');
     // Quick check of basic contract.
     if (options is YamlMap) {
       return LintConfig.parseMap(options);
diff --git a/pkg/analyzer/lib/src/lint/linter.dart b/pkg/analyzer/lib/src/lint/linter.dart
index 3a4d2a3..0fb77cf 100644
--- a/pkg/analyzer/lib/src/lint/linter.dart
+++ b/pkg/analyzer/lib/src/lint/linter.dart
@@ -849,6 +849,7 @@
         case CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL:
         case CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL:
         case CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT:
+        case CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT:
         case CompileTimeErrorCode.NON_CONSTANT_MAP_KEY:
         case CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE:
         case CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT:
diff --git a/pkg/analyzer/lib/src/lint/options_rule_validator.dart b/pkg/analyzer/lib/src/lint/options_rule_validator.dart
index 71e8620..6f326b1 100644
--- a/pkg/analyzer/lib/src/lint/options_rule_validator.dart
+++ b/pkg/analyzer/lib/src/lint/options_rule_validator.dart
@@ -68,9 +68,9 @@
   @override
   List<AnalysisError> validate(ErrorReporter reporter, YamlMap options) {
     List<AnalysisError> errors = <AnalysisError>[];
-    var node = getValue(options, linter);
+    var node = options.valueAt(linter);
     if (node is YamlMap) {
-      var rules = getValue(node, rulesKey);
+      var rules = node.valueAt(rulesKey);
       validateRules(rules, reporter);
     }
     return errors;
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
index 140cbd2..c7846a4 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
@@ -248,8 +248,8 @@
     if (dependency is YamlMap) {
       var pathEntry = _asString(dependency[PATH_FIELD]);
       if (pathEntry != null) {
-        YamlNode pathKey() => getKey(dependency, PATH_FIELD)!;
-        YamlNode pathValue() => getValue(dependency, PATH_FIELD)!;
+        YamlNode pathKey() => dependency.getKey(PATH_FIELD)!;
+        YamlNode pathValue() => dependency.valueAt(PATH_FIELD)!;
 
         if (pathEntry.contains(r'\')) {
           _reportErrorForNode(reporter, pathValue(),
@@ -280,7 +280,7 @@
 
       var gitEntry = dependency[GIT_FIELD];
       if (gitEntry != null && checkForPathAndGitDeps) {
-        _reportErrorForNode(reporter, getKey(dependency, GIT_FIELD)!,
+        _reportErrorForNode(reporter, dependency.getKey(GIT_FIELD)!,
             PubspecWarningCode.INVALID_DEPENDENCY, [GIT_FIELD]);
       }
     }
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 5ed06e0..61d2688 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -21,6 +21,7 @@
 import 'package:analyzer/src/lint/registry.dart';
 import 'package:analyzer/src/plugin/options.dart';
 import 'package:analyzer/src/util/yaml.dart';
+import 'package:analyzer/src/utilities/extensions/string.dart';
 import 'package:source_span/source_span.dart';
 import 'package:yaml/yaml.dart';
 
@@ -57,7 +58,7 @@
       errors.addAll(validationErrors);
     }
 
-    var node = getValue(options, AnalyzerOptions.include);
+    var node = options.valueAt(AnalyzerOptions.include);
     if (node == null) {
       return;
     }
@@ -171,10 +172,14 @@
   /// Supported `analyzer` language options.
   static const List<String> languageOptions = [strictInference, strictRawTypes];
 
-  // Supported 'analyzer' optional checks options.
+  /// Supported 'analyzer' optional checks options.
   static const List<String> optionalChecksOptions = [
     chromeOsManifestChecks,
   ];
+
+  /// Proposed values for a `true` or `false` option.
+  static String get trueOrFalseProposal =>
+      AnalyzerOptions.trueOrFalse.quotedAndCommaSeparatedWithAnd;
 }
 
 /// Validates `analyzer` options.
@@ -203,15 +208,11 @@
 
 /// Validates `analyzer` language configuration options.
 class EnabledExperimentsValidator extends OptionsValidator {
-  ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.languageOptions);
-  ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
-
   @override
   void validate(ErrorReporter reporter, YamlMap options) {
-    var analyzer = getValue(options, AnalyzerOptions.analyzer);
+    var analyzer = options.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
-      var experimentNames =
-          getValue(analyzer, AnalyzerOptions.enableExperiment);
+      var experimentNames = analyzer.valueAt(AnalyzerOptions.enableExperiment);
       if (experimentNames is YamlList) {
         var flags =
             experimentNames.nodes.map((node) => node.toString()).toList();
@@ -242,34 +243,39 @@
 
 /// Builds error reports with value proposals.
 class ErrorBuilder {
-  String? proposal;
-  late final AnalysisOptionsWarningCode code;
+  static AnalysisOptionsWarningCode get noProposalCode =>
+      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES;
+
+  static AnalysisOptionsWarningCode get pluralProposalCode =>
+      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
+
+  static AnalysisOptionsWarningCode get singularProposalCode =>
+      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
+
+  final String proposal;
+
+  final AnalysisOptionsWarningCode code;
 
   /// Create a builder for the given [supportedOptions].
-  ErrorBuilder(List<String> supportedOptions) {
+  factory ErrorBuilder(List<String> supportedOptions) {
+    var proposal = supportedOptions.quotedAndCommaSeparatedWithAnd;
     if (supportedOptions.isEmpty) {
-      code = noProposalCode;
+      return ErrorBuilder._(proposal: proposal, code: noProposalCode);
     } else if (supportedOptions.length == 1) {
-      proposal = "'${supportedOptions.join()}'";
-      code = singularProposalCode;
+      return ErrorBuilder._(proposal: proposal, code: singularProposalCode);
     } else {
-      proposal = StringUtilities.printListOfQuotedNames(supportedOptions);
-      code = pluralProposalCode;
+      return ErrorBuilder._(proposal: proposal, code: pluralProposalCode);
     }
   }
 
-  AnalysisOptionsWarningCode get noProposalCode =>
-      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES;
-
-  AnalysisOptionsWarningCode get pluralProposalCode =>
-      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
-
-  AnalysisOptionsWarningCode get singularProposalCode =>
-      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
+  ErrorBuilder._({
+    required this.proposal,
+    required this.code,
+  });
 
   /// Report an unsupported [node] value, defined in the given [scopeName].
   void reportError(ErrorReporter reporter, String scopeName, YamlNode node) {
-    if (proposal != null) {
+    if (proposal.isNotEmpty) {
       reporter.reportErrorForSpan(
           code, node.span, [scopeName, node.value, proposal]);
     } else {
@@ -313,9 +319,9 @@
 
   @override
   void validate(ErrorReporter reporter, YamlMap options) {
-    var analyzer = getValue(options, AnalyzerOptions.analyzer);
+    var analyzer = options.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
-      var filters = getValue(analyzer, AnalyzerOptions.errors);
+      var filters = analyzer.valueAt(AnalyzerOptions.errors);
       if (filters is YamlMap) {
         String? value;
         filters.nodes.forEach((k, v) {
@@ -350,14 +356,13 @@
 
 /// Validates `analyzer` language configuration options.
 class LanguageOptionValidator extends OptionsValidator {
-  ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.languageOptions);
-  ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
+  final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.languageOptions);
 
   @override
   void validate(ErrorReporter reporter, YamlMap options) {
-    var analyzer = getValue(options, AnalyzerOptions.analyzer);
+    var analyzer = options.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
-      var language = getValue(analyzer, AnalyzerOptions.language);
+      var language = analyzer.valueAt(AnalyzerOptions.language);
       if (language is YamlMap) {
         language.nodes.forEach((k, v) {
           String? key, value;
@@ -373,7 +378,7 @@
                   AnalysisOptionsHintCode.SUPER_MIXINS_SETTING_DEPRECATED,
                   k.span);
             } else if (!AnalyzerOptions.languageOptions.contains(key)) {
-              builder.reportError(reporter, AnalyzerOptions.language, k);
+              _builder.reportError(reporter, AnalyzerOptions.language, k);
             } else {
               // If we have a valid key, go on and check the value.
               validKey = true;
@@ -382,7 +387,10 @@
           if (validKey && v is YamlScalar) {
             value = toLowerCase(v.value);
             if (!AnalyzerOptions.trueOrFalse.contains(value)) {
-              trueOrFalseBuilder.reportError(reporter, key!, v);
+              reporter.reportErrorForSpan(
+                  AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
+                  v.span,
+                  [key, v.value, AnalyzerOptions.trueOrFalseProposal]);
             }
           }
         });
@@ -409,18 +417,18 @@
 
 /// Validates `analyzer` optional-checks value configuration options.
 class OptionalChecksValueValidator extends OptionsValidator {
-  ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.optionalChecksOptions);
-  ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
+  final ErrorBuilder _builder =
+      ErrorBuilder(AnalyzerOptions.optionalChecksOptions);
 
   @override
   void validate(ErrorReporter reporter, YamlMap options) {
-    var analyzer = getValue(options, AnalyzerOptions.analyzer);
+    var analyzer = options.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
-      var v = getValue(analyzer, AnalyzerOptions.optionalChecks);
+      var v = analyzer.valueAt(AnalyzerOptions.optionalChecks);
       if (v is YamlScalar) {
         var value = toLowerCase(v.value);
         if (value != AnalyzerOptions.chromeOsManifestChecks) {
-          builder.reportError(
+          _builder.reportError(
               reporter, AnalyzerOptions.chromeOsManifestChecks, v);
         }
       } else if (v is YamlMap) {
@@ -429,12 +437,15 @@
           if (k is YamlScalar) {
             key = k.value?.toString();
             if (key != AnalyzerOptions.chromeOsManifestChecks) {
-              builder.reportError(
+              _builder.reportError(
                   reporter, AnalyzerOptions.chromeOsManifestChecks, k);
             } else {
               value = toLowerCase(v.value);
               if (!AnalyzerOptions.trueOrFalse.contains(value)) {
-                trueOrFalseBuilder.reportError(reporter, key!, v);
+                reporter.reportErrorForSpan(
+                    AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
+                    v.span,
+                    [key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
               }
             }
           }
@@ -471,19 +482,22 @@
 
 /// Validates `analyzer` strong-mode value configuration options.
 class StrongModeOptionValueValidator extends OptionsValidator {
-  ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.strongModeOptions);
-  ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
+  final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.strongModeOptions);
 
   @override
   void validate(ErrorReporter reporter, YamlMap options) {
-    var analyzer = getValue(options, AnalyzerOptions.analyzer);
+    var analyzer = options.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
-      var v = getValue(analyzer, AnalyzerOptions.strong_mode);
+      var v = analyzer.valueAt(AnalyzerOptions.strong_mode);
       if (v is YamlScalar) {
         var value = toLowerCase(v.value);
         if (!AnalyzerOptions.trueOrFalse.contains(value)) {
-          trueOrFalseBuilder.reportError(
-              reporter, AnalyzerOptions.strong_mode, v);
+          reporter.reportErrorForSpan(
+              AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, v.span, [
+            AnalyzerOptions.strong_mode,
+            v.value,
+            AnalyzerOptions.trueOrFalseProposal
+          ]);
         } else if (value == 'false') {
           reporter.reportErrorForSpan(
               AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, v.span);
@@ -498,7 +512,7 @@
           if (k is YamlScalar) {
             key = k.value?.toString();
             if (!AnalyzerOptions.strongModeOptions.contains(key)) {
-              builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
+              _builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
             } else if (key == AnalyzerOptions.declarationCasts) {
               reporter.reportErrorForSpan(
                   AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
@@ -512,7 +526,10 @@
           if (validKey && v is YamlScalar) {
             value = toLowerCase(v.value);
             if (!AnalyzerOptions.trueOrFalse.contains(value)) {
-              trueOrFalseBuilder.reportError(reporter, key!, v);
+              reporter.reportErrorForSpan(
+                  AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
+                  v.span,
+                  [key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
             }
           }
         });
@@ -533,25 +550,19 @@
 class TopLevelOptionValidator extends OptionsValidator {
   final String pluginName;
   final List<String> supportedOptions;
-  late final String _valueProposal;
-  late final AnalysisOptionsWarningCode _warningCode;
+  final String _valueProposal;
+  final AnalysisOptionsWarningCode _warningCode;
 
-  TopLevelOptionValidator(this.pluginName, this.supportedOptions) {
-    assert(supportedOptions.isNotEmpty);
-    if (supportedOptions.length > 1) {
-      _valueProposal = StringUtilities.printListOfQuotedNames(supportedOptions);
-      _warningCode =
-          AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
-    } else {
-      _valueProposal = "'${supportedOptions.join()}'";
-      _warningCode =
-          AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
-    }
-  }
+  TopLevelOptionValidator(this.pluginName, this.supportedOptions)
+      : assert(supportedOptions.isNotEmpty),
+        _valueProposal = supportedOptions.quotedAndCommaSeparatedWithAnd,
+        _warningCode = supportedOptions.length == 1
+            ? AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE
+            : AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
 
   @override
   void validate(ErrorReporter reporter, YamlMap options) {
-    var node = getValue(options, pluginName);
+    var node = options.valueAt(pluginName);
     if (node is YamlMap) {
       node.nodes.forEach((k, v) {
         if (k is YamlScalar) {
@@ -566,15 +577,6 @@
   }
 }
 
-/// An error-builder that knows about `true` and `false` legal values.
-class TrueOrFalseValueErrorBuilder extends ErrorBuilder {
-  TrueOrFalseValueErrorBuilder() : super(AnalyzerOptions.trueOrFalse);
-
-  @override
-  AnalysisOptionsWarningCode get pluralProposalCode =>
-      AnalysisOptionsWarningCode.UNSUPPORTED_VALUE;
-}
-
 class _OptionsProcessor {
   /// Apply the options in the given [optionMap] to the given analysis
   /// [options].
@@ -582,19 +584,18 @@
     if (optionMap == null) {
       return;
     }
-    var analyzer = getValue(optionMap, AnalyzerOptions.analyzer);
+    var analyzer = optionMap.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
       // Process strong mode option.
-      var strongMode = getValue(analyzer, AnalyzerOptions.strong_mode);
+      var strongMode = analyzer.valueAt(AnalyzerOptions.strong_mode);
       _applyStrongOptions(options, strongMode);
 
       // Process filters.
-      var filters = getValue(analyzer, AnalyzerOptions.errors);
+      var filters = analyzer.valueAt(AnalyzerOptions.errors);
       _applyProcessors(options, filters);
 
       // Process enabled experiments.
-      var experimentNames =
-          getValue(analyzer, AnalyzerOptions.enableExperiment);
+      var experimentNames = analyzer.valueAt(AnalyzerOptions.enableExperiment);
       if (experimentNames is YamlList) {
         List<String> enabledExperiments = <String>[];
         for (var element in experimentNames.nodes) {
@@ -610,19 +611,19 @@
       }
 
       // Process optional checks options.
-      var optionalChecks = getValue(analyzer, AnalyzerOptions.optionalChecks);
+      var optionalChecks = analyzer.valueAt(AnalyzerOptions.optionalChecks);
       _applyOptionalChecks(options, optionalChecks);
 
       // Process language options.
-      var language = getValue(analyzer, AnalyzerOptions.language);
+      var language = analyzer.valueAt(AnalyzerOptions.language);
       _applyLanguageOptions(options, language);
 
       // Process excludes.
-      var excludes = getValue(analyzer, AnalyzerOptions.exclude);
+      var excludes = analyzer.valueAt(AnalyzerOptions.exclude);
       _applyExcludes(options, excludes);
 
       // Process plugins.
-      var names = getValue(analyzer, AnalyzerOptions.plugins);
+      var names = analyzer.valueAt(AnalyzerOptions.plugins);
       List<String> pluginNames = <String>[];
       var pluginName = _toString(names);
       if (pluginName != null) {
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index f43dcc2..a0ea939 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -677,6 +677,12 @@
 
 class Struct extends NativeType {}
 
+class Packed {
+  final int memberAlignment;
+
+  const Packed(this.memberAlignment);
+}
+
 abstract class DynamicLibrary {}
 
 extension DynamicLibraryExtension on DynamicLibrary {
diff --git a/pkg/analyzer/lib/src/util/yaml.dart b/pkg/analyzer/lib/src/util/yaml.dart
index 98f91e8..642dcc1 100644
--- a/pkg/analyzer/lib/src/util/yaml.dart
+++ b/pkg/analyzer/lib/src/util/yaml.dart
@@ -8,28 +8,6 @@
 import 'package:yaml/src/event.dart';
 import 'package:yaml/yaml.dart';
 
-/// Given a [map], return the [YamlNode] associated with the given [key], or
-/// `null` if there is no matching key.
-YamlNode? getKey(YamlMap map, String key) {
-  for (YamlNode k in map.nodes.keys) {
-    if (k is YamlScalar && k.value == key) {
-      return k;
-    }
-  }
-  return null;
-}
-
-/// Given a [map], return the value associated with the key whose value matches
-/// the given [key], or `null` if there is no matching key.
-YamlNode? getValue(YamlMap map, String key) {
-  for (var k in map.nodes.keys) {
-    if (k is YamlScalar && k.value == key) {
-      return map.nodes[k];
-    }
-  }
-  return null;
-}
-
 /// If all of the elements of [list] are strings, return a list of strings
 /// containing the same elements. Otherwise, return `null`.
 List<String>? toStringList(List? list) {
@@ -82,9 +60,9 @@
       return YamlMap.internal(map, o1.span, CollectionStyle.BLOCK);
     }
 
-    if (isListOfString(o1) && isMapToBools(o2)) {
+    if (_isListOfString(o1) && _isMapToBools(o2)) {
       o1 = listToMap(o1 as YamlList);
-    } else if (isMapToBools(o1) && isListOfString(o2)) {
+    } else if (_isMapToBools(o1) && _isListOfString(o2)) {
       o2 = listToMap(o2 as YamlList);
     }
 
@@ -117,11 +95,8 @@
     m1.nodes.forEach((k, v) {
       merged[k] = v;
     });
-    m2.nodes.forEach((k, v) {
-      // TODO(srawlins): The key should always be a [YamlNode], but is not (see
-      // https://pub.dev/documentation/yaml/latest/yaml/YamlMap/nodes.html).
-      // We could write an extension method which does this cast for us.
-      var value = (k as YamlNode).value;
+    m2.nodeMap.forEach((k, v) {
+      var value = k.value;
       var mergedKey =
           merged.keys.firstWhere((key) => key.value == value, orElse: () => k)
               as YamlScalar;
@@ -135,11 +110,49 @@
     return YamlMap.internal(merged, m1.span, CollectionStyle.BLOCK);
   }
 
-  static bool isListOfString(Object? o) =>
+  static bool _isListOfString(Object? o) =>
       o is YamlList &&
       o.nodes.every((e) => e is YamlScalar && e.value is String);
 
-  static bool isMapToBools(Object? o) =>
+  static bool _isMapToBools(Object? o) =>
       o is YamlMap &&
       o.nodes.values.every((v) => v is YamlScalar && v.value is bool);
 }
+
+extension YamlMapExtensions on YamlMap {
+  /// Return the value associated with the key whose value matches the given
+  /// [key], or `null` if there is no matching key.
+  YamlNode? valueAt(String key) {
+    for (var keyNode in nodes.keys) {
+      if (keyNode is YamlScalar && keyNode.value == key) {
+        return nodes[keyNode];
+      }
+    }
+    return null;
+  }
+
+  /// Return the [YamlNode] representing the key that corresponds to the value
+  /// represented by the [value] node.
+  YamlNode? keyAtValue(YamlNode value) {
+    for (var entry in nodes.entries) {
+      if (entry.value == value) {
+        return entry.key;
+      }
+    }
+    return null;
+  }
+
+  /// Return the [YamlNode] associated with the given [key], or `null` if there
+  /// is no matching key.
+  YamlNode? getKey(String key) {
+    for (YamlNode k in nodes.keys) {
+      if (k is YamlScalar && k.value == key) {
+        return k;
+      }
+    }
+    return null;
+  }
+
+  /// Return [nodes] as a Map with [YamlNode] keys.
+  Map<YamlNode, YamlNode> get nodeMap => nodes.cast<YamlNode, YamlNode>();
+}
diff --git a/pkg/analyzer/lib/src/utilities/extensions/string.dart b/pkg/analyzer/lib/src/utilities/extensions/string.dart
index d583029..6651cd7 100644
--- a/pkg/analyzer/lib/src/utilities/extensions/string.dart
+++ b/pkg/analyzer/lib/src/utilities/extensions/string.dart
@@ -14,24 +14,46 @@
   String get commaSeparatedWithOr => _commaSeparated('or');
 
   /// Produce a comma-separated representation of this list, with the last
+  /// element preceded by 'and' when there are more than two elements in this
+  /// list, and a pair of single quotes surrounding each element.
+  String get quotedAndCommaSeparatedWithAnd =>
+      _commaSeparated('and', quoted: true);
+
+  /// Produce a comma-separated representation of this list, with the last
   /// element preceded by the [conjunction] when there are more than two
   /// elements in this list.
-  String _commaSeparated(String conjunction) {
+  ///
+  /// Each element is surrounded by a pair of single quotes if [quoted] is true.
+  String _commaSeparated(String conjunction, {bool quoted = false}) {
     if (length == 0) {
       return '';
     } else if (length == 1) {
-      return this[0];
+      return quoted ? "'${this[0]}'" : this[0];
     } else if (length == 2) {
-      return '${this[0]} $conjunction ${this[1]}';
+      return quoted
+          ? "'${this[0]}' $conjunction '${this[1]}'"
+          : '${this[0]} $conjunction ${this[1]}';
     } else {
       var buffer = StringBuffer();
       for (int i = 0; i < length - 1; i++) {
+        if (quoted) {
+          buffer.write("'");
+        }
         buffer.write(this[i]);
+        if (quoted) {
+          buffer.write("'");
+        }
         buffer.write(', ');
       }
       buffer.write(conjunction);
       buffer.write(' ');
+      if (quoted) {
+        buffer.write("'");
+      }
       buffer.write(this[length - 1]);
+      if (quoted) {
+        buffer.write("'");
+      }
       return buffer.toString();
     }
   }
diff --git a/pkg/analyzer/test/source/analysis_options_provider_test.dart b/pkg/analyzer/test/source/analysis_options_provider_test.dart
index ac4ea13..64cc0a4 100644
--- a/pkg/analyzer/test/source/analysis_options_provider_test.dart
+++ b/pkg/analyzer/test/source/analysis_options_provider_test.dart
@@ -131,9 +131,9 @@
     YamlMap options = _getOptions('/foo/bar');
     expect(options, hasLength(1));
     {
-      var analyzer = getValue(options, 'analyzer') as YamlMap;
+      var analyzer = options.valueAt('analyzer') as YamlMap;
       expect(analyzer, isNotNull);
-      expect(getValue(analyzer, 'ignore'), unorderedEquals(['bar']));
+      expect(analyzer.valueAt('ignore'), unorderedEquals(['bar']));
     }
   }
 
@@ -152,9 +152,9 @@
     YamlMap options = _getOptions('/foo/bar/baz');
     expect(options, hasLength(1));
     {
-      var analyzer = getValue(options, 'analyzer') as YamlMap;
+      var analyzer = options.valueAt('analyzer') as YamlMap;
       expect(analyzer, isNotNull);
-      expect(getValue(analyzer, 'ignore'), unorderedEquals(['bar']));
+      expect(analyzer.valueAt('ignore'), unorderedEquals(['bar']));
     }
   }
 
@@ -184,10 +184,10 @@
     YamlMap options = _getOptions('/');
     expect(options, hasLength(2));
     {
-      var analyzer = getValue(options, 'analyzer') as YamlMap;
+      var analyzer = options.valueAt('analyzer') as YamlMap;
       expect(analyzer, hasLength(1));
       {
-        var ignore = getValue(analyzer, 'ignore') as YamlList;
+        var ignore = analyzer.valueAt('ignore') as YamlList;
         expect(ignore, hasLength(2));
         expect(ignore[0], 'ignoreme.dart');
         expect(ignore[1], 'sdk_ext/**');
@@ -219,10 +219,10 @@
     YamlMap options = _getOptions('/');
     expect(options, hasLength(1));
     {
-      var analyzer = getValue(options, 'analyzer') as YamlMap;
+      var analyzer = options.valueAt('analyzer') as YamlMap;
       expect(analyzer, hasLength(1));
       {
-        var ignore = getValue(analyzer, 'ignore') as YamlList;
+        var ignore = analyzer.valueAt('ignore') as YamlList;
         expect(ignore, hasLength(2));
         expect(ignore[0], 'ignoreme.dart');
         expect(ignore[1], 'sdk_ext/**');
diff --git a/pkg/analyzer/test/src/diagnostics/packed_annotation_alignment_test.dart b/pkg/analyzer/test/src/diagnostics/packed_annotation_alignment_test.dart
new file mode 100644
index 0000000..b5e9223
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/packed_annotation_alignment_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(PackedAnnotationAlignment);
+  });
+}
+
+@reflectiveTest
+class PackedAnnotationAlignment extends PubPackageResolutionTest {
+  test_error() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(3)
+class C extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+''', [
+      error(FfiCode.PACKED_ANNOTATION_ALIGNMENT, 20, 10),
+    ]);
+  }
+
+  test_no_error() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+class C extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/packed_annotation_test.dart b/pkg/analyzer/test/src/diagnostics/packed_annotation_test.dart
new file mode 100644
index 0000000..5ebf701
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/packed_annotation_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(PackedAnnotation);
+  });
+}
+
+@reflectiveTest
+class PackedAnnotation extends PubPackageResolutionTest {
+  test_error() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+@Packed(1)
+class C extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+''', [
+      error(FfiCode.PACKED_ANNOTATION, 31, 10),
+    ]);
+  }
+
+  test_no_error_1() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+''');
+  }
+
+  test_no_error_2() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+class C extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/packed_nesting_non_packed_test.dart b/pkg/analyzer/test/src/diagnostics/packed_nesting_non_packed_test.dart
new file mode 100644
index 0000000..9751139
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/packed_nesting_non_packed_test.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(PackedAnnotationNestingNonPacked);
+  });
+}
+
+@reflectiveTest
+class PackedAnnotationNestingNonPacked extends PubPackageResolutionTest {
+  test_error_1() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class TestStruct1603 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1603Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  external TestStruct1603 nestedNotPacked;
+}
+''', [
+      error(FfiCode.PACKED_NESTING_NON_PACKED, 200, 14),
+    ]);
+  }
+
+  test_error_2() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1604Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  external TestStruct1604 nestedLooselyPacked;
+}
+''', [
+      error(FfiCode.PACKED_NESTING_NON_PACKED, 211, 14),
+    ]);
+  }
+
+  test_error_3() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class TestStruct1603 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1605Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  @Array(2)
+  external Array<TestStruct1603> nestedNotPacked;
+}
+''', [
+      error(FfiCode.PACKED_NESTING_NON_PACKED, 212, 21),
+    ]);
+  }
+
+  test_error_4() async {
+    await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  @Array(2)
+  external Array<TestStruct1604> nestedLooselyPacked;
+}
+''', [
+      error(FfiCode.PACKED_NESTING_NON_PACKED, 223, 21),
+    ]);
+  }
+
+  test_no_error() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+class TestStruct1604 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  @Array(2)
+  external Array<TestStruct1604> nestedLooselyPacked;
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 3916b75..9d666bb 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -498,6 +498,9 @@
     as override_on_non_overriding_method;
 import 'override_on_non_overriding_setter_test.dart'
     as override_on_non_overriding_setter;
+import 'packed_annotation_alignment_test.dart' as packed_annotation_alignment;
+import 'packed_annotation_test.dart' as packed_annotation;
+import 'packed_nesting_non_packed_test.dart' as packed_nesting_non_packed;
 import 'part_of_different_library_test.dart' as part_of_different_library;
 import 'part_of_non_part_test.dart' as part_of_non_part;
 import 'prefix_collides_with_top_level_member_test.dart'
@@ -1000,6 +1003,9 @@
     override_on_non_overriding_getter.main();
     override_on_non_overriding_method.main();
     override_on_non_overriding_setter.main();
+    packed_annotation.main();
+    packed_annotation_alignment.main();
+    packed_nesting_non_packed.main();
     part_of_different_library.main();
     part_of_non_part.main();
     prefix_collides_with_top_level_member.main();
diff --git a/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart b/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
index f7e2b7d..99cce62 100644
--- a/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
+++ b/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
@@ -311,6 +311,13 @@
     expect(context.canBeConst(node), expectedResult);
   }
 
+  void test_listLiteral_false_forElement() async {
+    await resolve('''
+f() => [for (var i = 0; i < 10; i++) i];
+''');
+    assertCanBeConst('[for', false);
+  }
+
   void test_listLiteral_false_methodInvocation() async {
     await resolve('''
 f() => [g()];
@@ -354,6 +361,14 @@
     assertCanBeConst('[', true);
   }
 
+  void test_listLiteral_true_ifElement() async {
+    await resolve('''
+const a = true;
+f() => [if (a) 0 else 1];
+''');
+    assertCanBeConst('[if', true);
+  }
+
   void test_listLiteral_true_integerLiteral() async {
     await resolve('''
 f() => [1, 2, 3];
@@ -361,6 +376,13 @@
     assertCanBeConst('[', true);
   }
 
+  void test_mapLiteral_false_forElement() async {
+    await resolve('''
+f() => {for (var i = 0; i < 10; i++) i: 0};
+''');
+    assertCanBeConst('{', false);
+  }
+
   void test_mapLiteral_false_methodInvocation_key() async {
     await resolve('''
 f() => {g(): 0};
@@ -377,6 +399,14 @@
     assertCanBeConst('{', false);
   }
 
+  void test_mapLiteral_true_ifElement() async {
+    await resolve('''
+const a = true;
+f() => {if (a) 0: 0 else 1: 1};
+''');
+    assertCanBeConst('{', true);
+  }
+
   void test_mapLiteral_true_integerLiteral() async {
     await resolve('''
 f() => {1: 2, 3: 4};
@@ -384,6 +414,13 @@
     assertCanBeConst('{', true);
   }
 
+  void test_setLiteral_false_forElement() async {
+    await resolve('''
+f() => {for (var i = 0; i < 10; i++) i};
+''');
+    assertCanBeConst('{for', false);
+  }
+
   void test_setLiteral_false_methodInvocation() async {
     await resolve('''
 f() => {g()};
@@ -392,6 +429,14 @@
     assertCanBeConst('{', false);
   }
 
+  void test_setLiteral_true_ifElement() async {
+    await resolve('''
+const a = true;
+f() => {if (a) 0 else 1};
+''');
+    assertCanBeConst('{', true);
+  }
+
   void test_setLiteral_true_integerLiteral() async {
     await resolve('''
 f() => {1, 2, 3};
diff --git a/pkg/analyzer/test/src/utilities/extensions/string_test.dart b/pkg/analyzer/test/src/utilities/extensions/string_test.dart
index 2d9d373..ccf3226 100644
--- a/pkg/analyzer/test/src/utilities/extensions/string_test.dart
+++ b/pkg/analyzer/test/src/utilities/extensions/string_test.dart
@@ -32,7 +32,7 @@
   }
 
   void test_commaSeparatedWithAnd_zero() {
-    expect(<String>[].commaSeparatedWithOr, isEmpty);
+    expect(<String>[].commaSeparatedWithAnd, isEmpty);
   }
 
   void test_commaSeparatedWithOr_five() {
@@ -55,4 +55,21 @@
   void test_commaSeparatedWithOr_zero() {
     expect(<String>[].commaSeparatedWithOr, isEmpty);
   }
+
+  void test_quotedAndCommaSeparatedWithAnd_one() {
+    expect(<String>['a'].quotedAndCommaSeparatedWithAnd, "'a'");
+  }
+
+  void test_quotedAndCommaSeparatedWithAnd_three() {
+    expect(<String>['a', 'b', 'c'].quotedAndCommaSeparatedWithAnd,
+        "'a', 'b', and 'c'");
+  }
+
+  void test_quotedAndCommaSeparatedWithAnd_two() {
+    expect(<String>['a', 'b'].quotedAndCommaSeparatedWithAnd, "'a' and 'b'");
+  }
+
+  void test_quotedAndCommaSeparatedWithAnd_zero() {
+    expect(<String>[].quotedAndCommaSeparatedWithAnd, isEmpty);
+  }
 }
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 6778017..39761ec 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -5260,6 +5260,20 @@
 [Understanding null safety](/null-safety/understanding-null-safety#smarter-null-aware-methods)
 for more details.
 
+The following code produces this diagnostic because `s` can't be `null`.
+
+{% prettify dart tag=pre+code %}
+void f(Object? o) {
+  var s = o as String;
+  s[!?.!]length;
+}
+{% endprettify %}
+
+The reason `s` can't be null, despite the fact that `o` can be `null`, is
+because of the cast to `String`, which is a non-nullable type. If `o` ever
+has the value `null`, the cast will fail and the invocation of `length`
+will not happen.
+
 #### Common fixes
 
 Replace the null-aware operator with a non-null-aware equivalent; for
diff --git a/pkg/analyzer_cli/lib/src/driver.dart b/pkg/analyzer_cli/lib/src/driver.dart
index 357b5d7..5a2a4fd 100644
--- a/pkg/analyzer_cli/lib/src/driver.dart
+++ b/pkg/analyzer_cli/lib/src/driver.dart
@@ -48,8 +48,8 @@
 
 /// Test this option map to see if it specifies lint rules.
 bool containsLintRuleEntry(YamlMap options) {
-  var linterNode = getValue(options, 'linter');
-  return linterNode is YamlMap && getValue(linterNode, 'rules') != null;
+  var linterNode = options.valueAt('linter');
+  return linterNode is YamlMap && linterNode.valueAt('rules') != null;
 }
 
 class Driver implements CommandLineStarter {
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index 2a65eb6..dedd502 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -80,13 +80,6 @@
     return name.compareTo(other.name);
   }
 
-  void merge(OutputUnit that) {
-    assert(this != that);
-    // We don't currently support merging code into the main output unit.
-    assert(!isMainOutput);
-    this.imports.addAll(that.imports);
-  }
-
   @override
   String toString() => "OutputUnit($name, $imports)";
 }
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index e12068e..aebbf7b 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -921,10 +921,19 @@
 
   @override
   TypeInformation visitEqualsNull(ir.EqualsNull node) {
-    // TODO(johnniwinther). This triggers the computation of the mask for the
-    // receiver of the call to `==`, which doesn't happen in this case. Remove
-    // this when the ssa builder recognized `== null` directly.
-    _typeOfReceiver(node, node.expression);
+    visit(node.expression);
+    if (node.fileOffset < node.expression.fileOffset) {
+      // Hack to detect `null == o`.
+      // TODO(johnniwinther): Remove this after the new method invocation has
+      //  landed stably. This is only included to make the transition a no-op.
+      KernelGlobalTypeInferenceElementData data = _memberData;
+      data.setReceiverTypeMask(node, _closedWorld.abstractValueDomain.nullType);
+    } else {
+      // TODO(johnniwinther). This triggers the computation of the mask for the
+      // receiver of the call to `==`, which doesn't happen in this case. Remove
+      // this when the ssa builder recognized `== null` directly.
+      _typeOfReceiver(node, node.expression);
+    }
     _potentiallyAddNullCheck(node, node.expression);
     return _types.boolType;
   }
@@ -941,8 +950,8 @@
       _checkIfExposesThis(
           selector, _types.newTypedSelector(receiverType, mask));
     }
-    TypeInformation type = handleDynamicInvoke(
-        CallType.access, node, selector, mask, receiverType, arguments);
+    TypeInformation type = handleDynamicInvoke(CallType.access, node, selector,
+        mask, receiverType, arguments, _getVariableDeclaration(receiver));
     if (interfaceTarget != null) {
       if (interfaceTarget is ir.Procedure &&
           (interfaceTarget.kind == ir.ProcedureKind.Method ||
@@ -1018,6 +1027,17 @@
   }
 
   @override
+  TypeInformation visitInstanceGetterInvocation(
+      ir.InstanceGetterInvocation node) {
+    Selector selector = _elementMap.getSelector(node);
+    ir.Expression receiver = node.receiver;
+    TypeInformation receiverType = visit(receiver);
+    ArgumentsTypes arguments = analyzeArguments(node.arguments);
+    return _handleMethodInvocation(node, node.receiver, receiverType, selector,
+        arguments, node.interfaceTarget);
+  }
+
+  @override
   TypeInformation visitDynamicInvocation(ir.DynamicInvocation node) {
     Selector selector = _elementMap.getSelector(node);
     ir.Expression receiver = node.receiver;
@@ -1063,13 +1083,18 @@
         arguments, node.interfaceTarget);
   }
 
+  ir.VariableDeclaration _getVariableDeclaration(ir.Expression node) {
+    return node is ir.VariableGet ? node.variable : null;
+  }
+
   TypeInformation _handleDynamic(
       CallType callType,
       ir.Node node,
       Selector selector,
       AbstractValue mask,
       TypeInformation receiverType,
-      ArgumentsTypes arguments) {
+      ArgumentsTypes arguments,
+      ir.VariableDeclaration variable) {
     assert(receiverType != null);
     if (_types.selectorNeedsUpdate(receiverType, mask)) {
       mask = receiverType == _types.dynamicType
@@ -1079,18 +1104,6 @@
           _analyzedMember, callType, node, selector, mask);
     }
 
-    ir.VariableDeclaration variable;
-    if (node is ir.MethodInvocation && node.receiver is ir.VariableGet) {
-      ir.VariableGet get = node.receiver;
-      variable = get.variable;
-    } else if (node is ir.PropertyGet && node.receiver is ir.VariableGet) {
-      ir.VariableGet get = node.receiver;
-      variable = get.variable;
-    } else if (node is ir.PropertySet && node.receiver is ir.VariableGet) {
-      ir.VariableGet get = node.receiver;
-      variable = get.variable;
-    }
-
     if (variable != null) {
       Local local = _localsMap.getLocalVariable(variable);
       if (!_capturedVariables.contains(local)) {
@@ -1107,10 +1120,14 @@
         inLoop: inLoop, isConditional: false);
   }
 
-  TypeInformation handleDynamicGet(ir.Node node, Selector selector,
-      AbstractValue mask, TypeInformation receiverType) {
+  TypeInformation handleDynamicGet(
+      ir.Node node,
+      Selector selector,
+      AbstractValue mask,
+      TypeInformation receiverType,
+      ir.VariableDeclaration variable) {
     return _handleDynamic(
-        CallType.access, node, selector, mask, receiverType, null);
+        CallType.access, node, selector, mask, receiverType, null, variable);
   }
 
   TypeInformation handleDynamicSet(
@@ -1118,10 +1135,11 @@
       Selector selector,
       AbstractValue mask,
       TypeInformation receiverType,
-      TypeInformation rhsType) {
+      TypeInformation rhsType,
+      ir.VariableDeclaration variable) {
     ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
-    return _handleDynamic(
-        CallType.access, node, selector, mask, receiverType, arguments);
+    return _handleDynamic(CallType.access, node, selector, mask, receiverType,
+        arguments, variable);
   }
 
   TypeInformation handleDynamicInvoke(
@@ -1130,9 +1148,10 @@
       Selector selector,
       AbstractValue mask,
       TypeInformation receiverType,
-      ArgumentsTypes arguments) {
+      ArgumentsTypes arguments,
+      ir.VariableDeclaration variable) {
     return _handleDynamic(
-        callType, node, selector, mask, receiverType, arguments);
+        callType, node, selector, mask, receiverType, arguments, variable);
   }
 
   @override
@@ -1178,18 +1197,19 @@
       moveNextMask = _memberData.typeOfIteratorMoveNext(node);
 
       iteratorType = handleDynamicInvoke(CallType.forIn, node, iteratorSelector,
-          iteratorMask, expressionType, new ArgumentsTypes.empty());
+          iteratorMask, expressionType, new ArgumentsTypes.empty(), null);
     }
 
     handleDynamicInvoke(CallType.forIn, node, Selectors.moveNext, moveNextMask,
-        iteratorType, new ArgumentsTypes.empty());
+        iteratorType, new ArgumentsTypes.empty(), null);
     TypeInformation currentType = handleDynamicInvoke(
         CallType.forIn,
         node,
         Selectors.current,
         currentMask,
         iteratorType,
-        new ArgumentsTypes.empty());
+        new ArgumentsTypes.empty(),
+        null);
 
     Local variable = _localsMap.getLocalVariable(node.variable);
     DartType variableType = _localsMap.getLocalType(_elementMap, variable);
@@ -1598,7 +1618,8 @@
       _checkIfExposesThis(
           selector, _types.newTypedSelector(receiverType, mask));
     }
-    TypeInformation type = handleDynamicGet(node, selector, mask, receiverType);
+    TypeInformation type = handleDynamicGet(
+        node, selector, mask, receiverType, _getVariableDeclaration(receiver));
     if (interfaceTarget != null) {
       // Pull the type from kernel (instead of from the J-model) because the
       // interface target might be abstract and therefore not part of the
@@ -1674,7 +1695,8 @@
       _checkIfExposesThis(
           selector, _types.newTypedSelector(receiverType, mask));
     }
-    handleDynamicSet(node, selector, mask, receiverType, rhsType);
+    handleDynamicSet(node, selector, mask, receiverType, rhsType,
+        _getVariableDeclaration(receiver));
     return rhsType;
   }
 
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 02ae17c..45dea65 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -651,10 +651,24 @@
     // mimicks that ast inferrer which return `true` for [ast.Send] and
     // non-const [ast.NewExpression].
     if (initializer is ir.MethodInvocation ||
+        initializer is ir.InstanceInvocation ||
+        initializer is ir.InstanceGetterInvocation ||
+        initializer is ir.DynamicInvocation ||
+        initializer is ir.FunctionInvocation ||
+        initializer is ir.LocalFunctionInvocation ||
+        initializer is ir.EqualsNull ||
+        initializer is ir.EqualsCall ||
         initializer is ir.PropertyGet ||
+        initializer is ir.InstanceGet ||
+        initializer is ir.DynamicGet ||
+        initializer is ir.InstanceTearOff ||
+        initializer is ir.FunctionTearOff ||
         initializer is ir.PropertySet ||
+        initializer is ir.InstanceSet ||
+        initializer is ir.DynamicSet ||
         initializer is ir.StaticInvocation ||
         initializer is ir.StaticGet ||
+        initializer is ir.StaticTearOff ||
         initializer is ir.StaticSet ||
         initializer is ir.Let ||
         initializer is ir.ConstructorInvocation && !initializer.isConst) {
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index 1d8b365..803aed8 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -178,7 +178,9 @@
       List<String> namedArguments,
       List<ir.DartType> typeArguments);
 
-  void registerRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+  // TODO(johnniwinther): Change [node] to `InstanceGet` when the old method
+  // invocation encoding is no longer used.
+  void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
       ir.DartType receiverType, ir.DartType argumentType);
 
   // TODO(johnniwinther): Remove these when CFE provides constants.
@@ -595,6 +597,20 @@
   }
 
   @override
+  void handleEqualsNull(ir.EqualsNull node, ir.DartType expressionType) {
+    // TODO(johnniwinther): Remove this after the new method invocation has landed
+    // stably. This is only included to make the transition a no-op.
+    if (node.fileOffset < node.expression.fileOffset) {
+      // Hack to detect `null == o`.
+      expressionType = const ir.NullType();
+    }
+    ClassRelation relation = computeClassRelationFromType(expressionType);
+    registerDynamicInvocation(
+        expressionType, relation, ir.Name.equalsName, 1, const [], const []);
+    registerNullLiteral();
+  }
+
+  @override
   void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
       ir.Name name, ir.DartType resultType) {
     ClassRelation relation = computeClassRelationFromType(receiverType);
@@ -660,8 +676,10 @@
     return super.visitSwitchStatement(node);
   }
 
+  // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+  // invocation encoding is no longer used.
   @override
-  void handleRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+  void handleRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
       ir.DartType receiverType, ir.DartType argumentType) {
     registerRuntimeTypeUse(node, kind, receiverType, argumentType);
   }
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index eecf083..f259a4f 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -418,8 +418,10 @@
     _data._intLiterals.add(value);
   }
 
+  // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+  // invocation encoding is no longer used.
   @override
-  void registerRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+  void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
       ir.DartType receiverType, ir.DartType argumentType) {
     _data._runtimeTypeUses ??= [];
     _data._runtimeTypeUses
@@ -1577,7 +1579,9 @@
 class _RuntimeTypeUse {
   static const String tag = '_RuntimeTypeUse';
 
-  final ir.PropertyGet node;
+  // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+  // invocation encoding is no longer used.
+  final ir.Expression node;
   final RuntimeTypeUseKind kind;
   final ir.DartType receiverType;
   final ir.DartType argumentType;
diff --git a/pkg/compiler/lib/src/ir/runtime_type_analysis.dart b/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
index b62aafc..2fe49b8 100644
--- a/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
+++ b/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
@@ -29,7 +29,9 @@
   final RuntimeTypeUseKind kind;
 
   /// The property get for the left (or single) occurrence of `.runtimeType`.
-  final ir.PropertyGet leftRuntimeTypeExpression;
+  // TODO(johnniwinther): Change this to `InstanceGet` when the old method
+  // invocation encoding is no longer used.
+  final ir.Expression leftRuntimeTypeExpression;
 
   /// The receiver expression.
   final ir.Expression receiver;
@@ -40,7 +42,9 @@
 
   /// The property get for the right occurrence of `.runtimeType` when [kind]
   /// is `RuntimeTypeUseKind.equals`.
-  final ir.PropertyGet rightRuntimeTypeExpression;
+  // TODO(johnniwinther): Change this to `InstanceGet` when the old method
+  // invocation encoding is no longer used.
+  final ir.Expression rightRuntimeTypeExpression;
 
   /// The argument expression if [kind] is `RuntimeTypeUseKind.equals`.
   final ir.Expression argument;
@@ -77,37 +81,43 @@
 ///
 /// [cache] is used to ensure that only one [RuntimeTypeUseData] object is
 /// created per case, even for the `==` case.
+// TODO(johnniwinther): Change [cache] key and [node] to `InstanceGet` when the
+// old method invocation encoding is no longer used.
 RuntimeTypeUseData computeRuntimeTypeUse(
-    Map<ir.PropertyGet, RuntimeTypeUseData> cache, ir.PropertyGet node) {
+    Map<ir.Expression, RuntimeTypeUseData> cache, ir.Expression node) {
   RuntimeTypeUseData receiverData = cache[node];
   if (receiverData != null) return receiverData;
 
   /// Returns `true` if [node] is of the form `e.runtimeType`.
   bool isGetRuntimeType(ir.TreeNode node) {
-    return node is ir.PropertyGet && node.name.text == Identifiers.runtimeType_;
-  }
-
-  /// Returns [node] if [node] is of the form `e.runtimeType` and `null`
-  /// otherwise.
-  ir.PropertyGet asGetRuntimeType(ir.TreeNode node) {
-    return isGetRuntimeType(node) ? node : null;
+    return node is ir.PropertyGet &&
+            node.name.text == Identifiers.runtimeType_ ||
+        node is ir.InstanceGet && node.name.text == Identifiers.runtimeType_ ||
+        node is ir.DynamicGet && node.name.text == Identifiers.runtimeType_;
   }
 
   /// Returns `true` if [node] is of the form `e.toString()`.
   bool isInvokeToString(ir.TreeNode node) {
-    return node is ir.MethodInvocation && node.name.text == 'toString';
+    return node is ir.MethodInvocation && node.name.text == 'toString' ||
+        node is ir.InstanceInvocation && node.name.text == 'toString';
   }
 
   assert(isGetRuntimeType(node));
+  // TODO(johnniwinther): Replace this with `node.receiver` when the old method
+  // invocation encoding is no longer used.
+  _RuntimeTypeAccess runtimeTypeAccess = _getRuntimeTypeAccess(node);
+  assert(runtimeTypeAccess != null);
 
+  // TODO(johnniwinther): Change [receiverGet] and [argumentGet] to
+  // `InstanceGet` when the old method invocation encoding is no longer used.
   // TODO(johnniwinther): Special-case `this.runtimeType`.
-  ir.PropertyGet receiverGet;
+  ir.Expression receiverGet;
   ir.Expression receiver;
-  ir.PropertyGet argumentGet;
+  ir.Expression argumentGet;
   ir.Expression argument;
   RuntimeTypeUseKind kind;
 
-  if (node.receiver is ir.VariableGet &&
+  if (runtimeTypeAccess.receiver is ir.VariableGet &&
       node.parent is ir.ConditionalExpression &&
       node.parent.parent is ir.Let) {
     NullAwareExpression nullAware = getNullAwareExpression(node.parent.parent);
@@ -138,15 +148,16 @@
           receiver = nullAware.receiver;
           receiverGet = node;
         }
-      } else if (nullAware.parent is ir.MethodInvocation) {
-        ir.MethodInvocation methodInvocation = nullAware.parent;
-        if (methodInvocation.receiver == nullAware.let &&
-            methodInvocation.name.text == '==') {
+      } else if (_isObjectMethodInvocation(nullAware.parent)) {
+        _EqualsInvocation equalsInvocation =
+            _getEqualsInvocation(nullAware.parent);
+        if (equalsInvocation != null &&
+            equalsInvocation.left == nullAware.let) {
           // Detected
           //
           //  e0?.runtimeType == other
-          ir.PropertyGet otherGetRuntimeType =
-              asGetRuntimeType(methodInvocation.arguments.positional.first);
+          _RuntimeTypeAccess otherGetRuntimeType =
+              _getRuntimeTypeAccess(equalsInvocation.right);
           if (otherGetRuntimeType != null) {
             // Detected
             //
@@ -161,11 +172,11 @@
             receiver = nullAware.receiver;
             receiverGet = node;
             argument = otherGetRuntimeType.receiver;
-            argumentGet = methodInvocation.arguments.positional.first;
+            argumentGet = otherGetRuntimeType.node;
           }
 
-          NullAwareExpression otherNullAware = getNullAwareExpression(
-              methodInvocation.arguments.positional.first);
+          NullAwareExpression otherNullAware =
+              getNullAwareExpression(equalsInvocation.right);
           if (otherNullAware != null &&
               isGetRuntimeType(otherNullAware.expression)) {
             // Detected
@@ -200,16 +211,17 @@
           receiverGet = node;
         }
       } else if (nullAware.parent is ir.Arguments &&
-          nullAware.parent.parent is ir.MethodInvocation) {
-        ir.MethodInvocation methodInvocation = nullAware.parent.parent;
-        if (methodInvocation.name.text == '==' &&
-            methodInvocation.arguments.positional.first == nullAware.let) {
+          _isObjectMethodInvocation(nullAware.parent.parent)) {
+        _EqualsInvocation equalsInvocation =
+            _getEqualsInvocation(nullAware.parent.parent);
+        if (equalsInvocation != null &&
+            equalsInvocation.right == nullAware.let) {
           // [nullAware] is the right hand side of ==.
 
-          ir.PropertyGet otherGetRuntimeType =
-              asGetRuntimeType(methodInvocation.receiver);
+          _RuntimeTypeAccess otherGetRuntimeType =
+              _getRuntimeTypeAccess(equalsInvocation.left);
           NullAwareExpression otherNullAware =
-              getNullAwareExpression(methodInvocation.receiver);
+              getNullAwareExpression(equalsInvocation.left);
 
           if (otherGetRuntimeType != null) {
             // Detected
@@ -223,7 +235,7 @@
             //                                                  ^
             kind = RuntimeTypeUseKind.equals;
             receiver = otherGetRuntimeType.receiver;
-            receiverGet = otherGetRuntimeType;
+            receiverGet = otherGetRuntimeType.node;
             argument = nullAware.receiver;
             argumentGet = node;
           }
@@ -285,19 +297,18 @@
       //     let #t1 = e.runtimeType in #t1 == null ? null : #t1.toString()
       //                 ^
       kind = RuntimeTypeUseKind.string;
-      receiver = node.receiver;
+      receiver = runtimeTypeAccess.receiver;
       receiverGet = node;
     }
-  } else if (node.parent is ir.MethodInvocation) {
-    ir.MethodInvocation methodInvocation = node.parent;
-    if (methodInvocation.name.text == '==' &&
-        methodInvocation.receiver == node) {
+  } else if (_isObjectMethodInvocation(node.parent)) {
+    _EqualsInvocation equalsInvocation = _getEqualsInvocation(node.parent);
+    if (equalsInvocation != null && equalsInvocation.left == node) {
       // [node] is the left hand side of ==.
 
-      ir.PropertyGet otherGetRuntimeType =
-          asGetRuntimeType(methodInvocation.arguments.positional.first);
+      _RuntimeTypeAccess otherGetRuntimeType =
+          _getRuntimeTypeAccess(equalsInvocation.right);
       NullAwareExpression nullAware =
-          getNullAwareExpression(methodInvocation.arguments.positional.first);
+          getNullAwareExpression(equalsInvocation.right);
       if (otherGetRuntimeType != null) {
         // Detected
         //
@@ -308,10 +319,10 @@
         //     e0.runtimeType.==(e1.runtimeType)
         //        ^
         kind = RuntimeTypeUseKind.equals;
-        receiver = node.receiver;
+        receiver = runtimeTypeAccess.receiver;
         receiverGet = node;
         argument = otherGetRuntimeType.receiver;
-        argumentGet = otherGetRuntimeType;
+        argumentGet = otherGetRuntimeType.node;
       } else if (nullAware != null && isGetRuntimeType(nullAware.expression)) {
         // Detected
         //
@@ -323,7 +334,7 @@
         //        ^
         //         let #t1 = e1 in #t1 == null ? null : #t1.runtimeType)
         kind = RuntimeTypeUseKind.equals;
-        receiver = node.receiver;
+        receiver = runtimeTypeAccess.receiver;
         receiverGet = node;
         argument = nullAware.receiver;
         argumentGet = nullAware.expression;
@@ -334,19 +345,19 @@
       //     e.runtimeType.toString()
       //       ^
       kind = RuntimeTypeUseKind.string;
-      receiver = node.receiver;
+      receiver = runtimeTypeAccess.receiver;
       receiverGet = node;
     }
   } else if (node.parent is ir.Arguments &&
-      node.parent.parent is ir.MethodInvocation) {
-    ir.MethodInvocation methodInvocation = node.parent.parent;
-    if (methodInvocation.name.text == '==' &&
-        methodInvocation.arguments.positional.first == node) {
+      _isObjectMethodInvocation(node.parent.parent)) {
+    _EqualsInvocation _equalsInvocation =
+        _getEqualsInvocation(node.parent.parent);
+    if (_equalsInvocation != null && _equalsInvocation.right == node) {
       // [node] is the right hand side of ==.
-      ir.PropertyGet otherGetRuntimeType =
-          asGetRuntimeType(methodInvocation.receiver);
+      _RuntimeTypeAccess otherGetRuntimeType =
+          _getRuntimeTypeAccess(_equalsInvocation.left);
       NullAwareExpression nullAware =
-          getNullAwareExpression(methodInvocation.receiver);
+          getNullAwareExpression(_equalsInvocation.left);
 
       if (otherGetRuntimeType != null) {
         // Detected
@@ -359,8 +370,8 @@
         //                          ^
         kind = RuntimeTypeUseKind.equals;
         receiver = otherGetRuntimeType.receiver;
-        receiverGet = otherGetRuntimeType;
-        argument = node.receiver;
+        receiverGet = otherGetRuntimeType.node;
+        argument = runtimeTypeAccess.receiver;
         argumentGet = node;
       } else if (nullAware != null && isGetRuntimeType(nullAware.expression)) {
         // Detected
@@ -375,7 +386,7 @@
         kind = RuntimeTypeUseKind.equals;
         receiver = nullAware.receiver;
         receiverGet = nullAware.expression;
-        argument = node.receiver;
+        argument = runtimeTypeAccess.receiver;
         argumentGet = node;
       }
     }
@@ -385,7 +396,7 @@
     //     '${e.runtimeType}'
     //          ^
     kind = RuntimeTypeUseKind.string;
-    receiver = node.receiver;
+    receiver = runtimeTypeAccess.receiver;
     receiverGet = node;
   }
 
@@ -395,7 +406,7 @@
     //     e.runtimeType
     //       ^
     kind = RuntimeTypeUseKind.unknown;
-    receiver = node.receiver;
+    receiver = runtimeTypeAccess.receiver;
     receiverGet = node;
   }
 
@@ -413,3 +424,50 @@
       receiverGet != argumentGet, "Duplicate property get in $data for $node.");
   return data;
 }
+
+/// Returns `true` if [node] is a potential invocation of an Object method.
+bool _isObjectMethodInvocation(ir.TreeNode node) {
+  return node is ir.MethodInvocation ||
+      node is ir.InstanceInvocation ||
+      node is ir.EqualsCall;
+}
+
+/// Returns the [_RuntimeTypeAccess] corresponding to [node] if it is an access
+/// of `.runtimeType`, and `null` otherwise.
+_RuntimeTypeAccess _getRuntimeTypeAccess(ir.TreeNode node) {
+  if (node is ir.PropertyGet && node.name.text == 'runtimeType') {
+    return _RuntimeTypeAccess(node, node.receiver);
+  } else if (node is ir.InstanceGet && node.name.text == 'runtimeType') {
+    return _RuntimeTypeAccess(node, node.receiver);
+  } else if (node is ir.DynamicGet && node.name.text == 'runtimeType') {
+    return _RuntimeTypeAccess(node, node.receiver);
+  }
+  return null;
+}
+
+class _RuntimeTypeAccess {
+  final ir.Expression node;
+  final ir.Expression receiver;
+
+  _RuntimeTypeAccess(this.node, this.receiver);
+}
+
+/// Returns the [_EqualsInvocation] corresponding to [node] if it is a call to
+/// of `==`, and `null` otherwise.
+_EqualsInvocation _getEqualsInvocation(ir.TreeNode node) {
+  if (node is ir.MethodInvocation && node.name.text == '==') {
+    return _EqualsInvocation(
+        node, node.receiver, node.arguments.positional.single);
+  } else if (node is ir.EqualsCall) {
+    return _EqualsInvocation(node, node.left, node.right);
+  }
+  return null;
+}
+
+class _EqualsInvocation {
+  final ir.Expression node;
+  final ir.Expression left;
+  final ir.Expression right;
+
+  _EqualsInvocation(this.node, this.left, this.right);
+}
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 07e82b0..3004f39 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -1070,6 +1070,24 @@
   }
 
   @override
+  EvaluationComplexity visitInstanceGetterInvocation(
+      ir.InstanceGetterInvocation node) {
+    node.receiver = _handleExpression(node.receiver);
+    if (node.arguments.types.isNotEmpty) {
+      ir.TreeNode receiver = node.receiver;
+      assert(
+          !(receiver is ir.VariableGet &&
+              receiver.variable.parent is ir.LocalFunction),
+          "Unexpected local function invocation ${node} "
+          "(${node.runtimeType}).");
+      VariableUse usage = new VariableUse.instanceTypeArgument(node);
+      visitNodesInContext(node.arguments.types, usage);
+    }
+    visitArguments(node.arguments);
+    return const EvaluationComplexity.lazy();
+  }
+
+  @override
   EvaluationComplexity visitDynamicInvocation(ir.DynamicInvocation node) {
     node.receiver = _handleExpression(node.receiver);
     if (node.arguments.types.isNotEmpty) {
@@ -1106,6 +1124,7 @@
   @override
   EvaluationComplexity visitLocalFunctionInvocation(
       ir.LocalFunctionInvocation node) {
+    _markVariableAsUsed(node.variable, VariableUse.explicit);
     if (node.arguments.types.isNotEmpty) {
       VariableUse usage =
           new VariableUse.localTypeArgument(node.localFunction, node);
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 8dcffab..cb063b6 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -73,7 +73,9 @@
 abstract class StaticTypeVisitor extends StaticTypeBase {
   final StaticTypeCacheImpl _staticTypeCache;
   Map<ir.Expression, TypeMap> typeMapsForTesting;
-  Map<ir.PropertyGet, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
+  // TODO(johnniwinther): Change the key to `InstanceGet` when the old method
+  //  invocation encoding is no longer used.
+  Map<ir.Expression, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
 
   final ir.ClassHierarchy hierarchy;
 
@@ -130,13 +132,19 @@
   TypeMap _typeMapWhenTrue;
   TypeMap _typeMapWhenFalse;
 
-  /// Returns the local variable type promotions for when the boolean value of
-  /// the most recent node is not taken into account.
-  TypeMap get typeMap {
+  /// Joins [_typeMapWhenTrue] and [_typeMapWhenFalse] and stores the result
+  /// in [_typeMapBase].
+  void _flattenTypeMap() {
     if (_typeMapBase == null) {
       _typeMapBase = _typeMapWhenTrue.join(_typeMapWhenFalse);
       _typeMapWhenTrue = _typeMapWhenFalse = null;
     }
+  }
+
+  /// Returns the local variable type promotions for when the boolean value of
+  /// the most recent node is not taken into account.
+  TypeMap get typeMap {
+    _flattenTypeMap();
     return _typeMapBase;
   }
 
@@ -282,13 +290,22 @@
     return const ir.DynamicType();
   }
 
+  /// Replaces [original] with [replacement] in the AST and removes cached
+  /// expression type information for [original].
+  void _replaceExpression(ir.Expression original, ir.Expression replacement) {
+    original.replaceWith(replacement);
+    _staticTypeCache._expressionTypes.remove(original);
+  }
+
   void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
       ir.Name name, ir.DartType resultType) {}
 
   void handleInstanceGet(ir.Expression node, ir.DartType receiverType,
       ir.Member interfaceTarget, ir.DartType resultType) {}
 
-  void handleRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+  // TODO(johnniwinther): Change [node] to `InstanceGet` when the old method
+  // invocation encoding is no longer used.
+  void handleRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
       ir.DartType receiverType, ir.DartType argumentType) {}
 
   @override
@@ -343,13 +360,25 @@
       resultType = _computeInstanceGetType(receiverType, interfaceTarget);
       ir.InstanceGet instanceGet = ir.InstanceGet(
           ir.InstanceAccessKind.Instance, node.receiver, node.name,
-          interfaceTarget: interfaceTarget, resultType: resultType);
-      node.replaceWith(instanceGet);
+          interfaceTarget: interfaceTarget, resultType: resultType)
+        ..fileOffset = node.fileOffset;
+      _replaceExpression(node, instanceGet);
       handleInstanceGet(instanceGet, receiverType, interfaceTarget, resultType);
+    } else if (node.name == ir.Name.callName &&
+        (receiverType is ir.FunctionType ||
+            (receiverType is ir.InterfaceType &&
+                receiverType.classNode == typeEnvironment.functionClass))) {
+      ir.FunctionTearOff functionTearOff = ir.FunctionTearOff(node.receiver)
+        ..fileOffset = node.fileOffset;
+      _replaceExpression(node, functionTearOff);
+      handleDynamicGet(
+          functionTearOff, receiverType, ir.Name.callName, resultType);
+      resultType = receiverType;
     } else {
       handleDynamicGet(node, receiverType, node.name, resultType);
     }
     if (node.name.text == Identifiers.runtimeType_) {
+      // This handles `runtimeType` access on `Never`.
       handleRuntimeTypeGet(receiverType, node);
     }
     return resultType;
@@ -358,9 +387,17 @@
   @override
   ir.DartType visitInstanceGet(ir.InstanceGet node) {
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType resultType = super.visitInstanceGet(node);
+    // We compute the function type instead of reading it of [node] since the
+    // receiver and argument types might have improved through inference of
+    // effectively final variable types and type promotion.
+    ir.DartType resultType =
+        _computeInstanceGetType(receiverType, node.interfaceTarget);
+    node.resultType = resultType;
+    receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
     handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
     if (node.name.text == Identifiers.runtimeType_) {
+      // This handles `runtimeType` access on non-Never types, like in
+      // `(throw 'foo').runtimeType`.
       handleRuntimeTypeGet(receiverType, node);
     }
     return resultType;
@@ -369,8 +406,14 @@
   @override
   ir.DartType visitInstanceTearOff(ir.InstanceTearOff node) {
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType resultType = super.visitInstanceTearOff(node);
-    assert(node.name.text == Identifiers.runtimeType_,
+    // We compute the function type instead of reading it of [node] since the
+    // receiver and argument types might have improved through inference of
+    // effectively final variable types and type promotion.
+    ir.DartType resultType =
+        _computeInstanceGetType(receiverType, node.interfaceTarget);
+    node.resultType = resultType;
+    receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
+    assert(node.name.text != Identifiers.runtimeType_,
         "Unexpected .runtimeType instance tear-off.");
     handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
     return resultType;
@@ -379,9 +422,8 @@
   @override
   ir.DartType visitFunctionTearOff(ir.FunctionTearOff node) {
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType resultType = super.visitFunctionTearOff(node);
-    handleDynamicGet(node, receiverType, ir.Name.callName, resultType);
-    return resultType;
+    handleDynamicGet(node, receiverType, ir.Name.callName, receiverType);
+    return receiverType;
   }
 
   void handleDynamicSet(ir.Expression node, ir.DartType receiverType,
@@ -452,7 +494,7 @@
   @override
   ir.DartType visitDynamicSet(ir.DynamicSet node) {
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType valueType = super.visitDynamicSet(node);
+    ir.DartType valueType = visitNode(node.value);
     ir.Member interfaceTarget = _resolveDynamicSet(receiverType, node.name);
     if (interfaceTarget != null) {
       ir.DartType setterType =
@@ -470,7 +512,7 @@
       ir.InstanceSet instanceSet = ir.InstanceSet(
           ir.InstanceAccessKind.Instance, node.receiver, node.name, value,
           interfaceTarget: interfaceTarget);
-      node.replaceWith(instanceSet);
+      _replaceExpression(node, instanceSet);
       receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
       handleInstanceSet(node, receiverType, interfaceTarget, valueType);
     } else {
@@ -482,7 +524,7 @@
   @override
   ir.DartType visitInstanceSet(ir.InstanceSet node) {
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType valueType = super.visitInstanceSet(node);
+    ir.DartType valueType = visitNode(node.value);
     handleInstanceSet(node, receiverType, node.interfaceTarget, valueType);
     return valueType;
   }
@@ -522,74 +564,77 @@
     return receiverType;
   }
 
-  /// Returns `true` if [member] can be called with the structure of
-  /// [arguments].
-  bool _isApplicable(ir.Arguments arguments, ir.Member member) {
-    /// Returns `true` if [arguments] are applicable to the function type
-    /// structure.
-    bool isFunctionTypeApplicable(
-        int typeParameterCount,
-        int requiredParameterCount,
-        int positionalParameterCount,
-        Iterable<String> Function() getNamedParameters) {
-      if (arguments.types.isNotEmpty &&
-          arguments.types.length != typeParameterCount) {
-        return false;
-      }
-      if (arguments.positional.length < requiredParameterCount) {
-        return false;
-      }
-      if (arguments.positional.length > positionalParameterCount) {
-        return false;
-      }
-      Iterable<String> namedParameters = getNamedParameters();
-      if (arguments.named.length > namedParameters.length) {
-        return false;
-      }
-      if (arguments.named.isNotEmpty) {
-        for (ir.NamedExpression namedArguments in arguments.named) {
-          if (!namedParameters.contains(namedArguments.name)) {
-            return false;
-          }
-        }
-      }
-      return true;
-    }
-
-    /// Returns `true` if [arguments] are applicable to a value of the static
-    /// [type].
-    bool isTypeApplicable(ir.DartType type) {
-      if (type is ir.DynamicType) return true;
-      if (type == typeEnvironment.coreTypes.functionLegacyRawType ||
-          type == typeEnvironment.coreTypes.functionNullableRawType ||
-          type == typeEnvironment.coreTypes.functionNonNullableRawType)
-        return true;
-      if (type is ir.FunctionType) {
-        return isFunctionTypeApplicable(
-            type.typeParameters.length,
-            type.requiredParameterCount,
-            type.positionalParameters.length,
-            () => type.namedParameters.map((p) => p.name).toSet());
-      }
+  /// Returns `true` if [arguments] are applicable to the function type
+  /// structure.
+  bool _isApplicableToFunctionType(
+      ir.Arguments arguments,
+      int typeParameterCount,
+      int requiredParameterCount,
+      int positionalParameterCount,
+      Iterable<String> Function() getNamedParameters) {
+    if (arguments.types.isNotEmpty &&
+        arguments.types.length != typeParameterCount) {
       return false;
     }
+    if (arguments.positional.length < requiredParameterCount) {
+      return false;
+    }
+    if (arguments.positional.length > positionalParameterCount) {
+      return false;
+    }
+    Iterable<String> namedParameters = getNamedParameters();
+    if (arguments.named.length > namedParameters.length) {
+      return false;
+    }
+    if (arguments.named.isNotEmpty) {
+      for (ir.NamedExpression namedArguments in arguments.named) {
+        if (!namedParameters.contains(namedArguments.name)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
 
+  /// Returns `true` if [arguments] are applicable to a value of the static
+  /// [type].
+  bool _isApplicableToType(ir.Arguments arguments, ir.DartType type) {
+    if (type is ir.DynamicType) return true;
+    if (type == typeEnvironment.coreTypes.functionLegacyRawType ||
+        type == typeEnvironment.coreTypes.functionNullableRawType ||
+        type == typeEnvironment.coreTypes.functionNonNullableRawType)
+      return true;
+    if (type is ir.FunctionType) {
+      return _isApplicableToFunctionType(
+          arguments,
+          type.typeParameters.length,
+          type.requiredParameterCount,
+          type.positionalParameters.length,
+          () => type.namedParameters.map((p) => p.name).toSet());
+    }
+    return false;
+  }
+
+  /// Returns `true` if [member] can be called with the structure of
+  /// [arguments].
+  bool _isApplicableToMember(ir.Arguments arguments, ir.Member member) {
     if (member is ir.Procedure) {
       if (member.kind == ir.ProcedureKind.Setter ||
           member.kind == ir.ProcedureKind.Factory) {
         return false;
       } else if (member.kind == ir.ProcedureKind.Getter) {
-        return isTypeApplicable(member.getterType);
+        return _isApplicableToType(arguments, member.getterType);
       } else if (member.kind == ir.ProcedureKind.Method ||
           member.kind == ir.ProcedureKind.Operator) {
-        return isFunctionTypeApplicable(
+        return _isApplicableToFunctionType(
+            arguments,
             member.function.typeParameters.length,
             member.function.requiredParameterCount,
             member.function.positionalParameters.length,
             () => member.function.namedParameters.map((p) => p.name).toSet());
       }
     } else if (member is ir.Field) {
-      return isTypeApplicable(member.type);
+      return _isApplicableToType(arguments, member.type);
     }
     return false;
   }
@@ -703,18 +748,41 @@
     if (receiverType is ir.InterfaceType) {
       ir.Member member =
           hierarchy.getInterfaceMember(receiverType.classNode, name);
-      if (_isApplicable(arguments, member)) {
+      if (_isApplicableToMember(arguments, member)) {
         return member;
       }
     }
     return null;
   }
 
+  /// Computes the function type of the function invocation of type
+  /// [functionType] with the given [argument].
+  ir.DartType _computeFunctionInvocationType(
+      ir.DartType functionType, ir.Arguments arguments) {
+    if (functionType is ir.FunctionType) {
+      List<ir.DartType> typeArguments = arguments.types;
+      if (functionType.typeParameters.isNotEmpty && typeArguments.isEmpty) {
+        // If this was a dynamic call the invocation does not have the
+        // inferred default type arguments so we need to create them here
+        // to perform a valid substitution.
+        typeArguments =
+            functionType.typeParameters.map((t) => t.defaultType).toList();
+      }
+      return ir.Substitution.fromPairs(
+              functionType.typeParameters, typeArguments)
+          .substituteType(functionType.withoutTypeParameters);
+    }
+    return functionType;
+  }
+
   /// Computes the function type of the instance invocation [node] on a receiver
   /// of type [receiverType] on the [interfaceTarget] with the given
   /// [argumentTypes].
-  ir.DartType _computeInvocationFunctionType(ir.DartType receiverType,
-      ir.Member interfaceTarget, ir.Arguments arguments) {
+  ir.DartType _computeInstanceInvocationType(
+      ir.DartType receiverType,
+      ir.Member interfaceTarget,
+      ir.Arguments arguments,
+      ArgumentTypes argumentTypes) {
     ir.Class superclass = interfaceTarget.enclosingClass;
     ir.Substitution receiverSubstitution = ir.Substitution.fromInterfaceType(
         getTypeAsInstanceOf(receiverType, superclass));
@@ -737,27 +805,20 @@
           ir.Substitution.fromPairs(functionType.typeParameters, typeArguments)
               .substituteType(functionType.withoutTypeParameters);
     }
+    if (isSpecialCasedBinaryOperator(interfaceTarget)) {
+      ir.DartType argumentType = argumentTypes.positional[0];
+      ir.DartType resultType = typeEnvironment
+          .getTypeOfSpecialCasedBinaryOperator(receiverType, argumentType);
+      return new ir.FunctionType(
+          <ir.DartType>[argumentType], resultType, currentLibrary.nonNullable);
+    }
     return getterType;
   }
 
-  /// Computes the result type of the instance invocation [node] on a receiver
-  /// of type [receiverType] on the [interfaceTarget] with the [functionType]
-  /// computed from the [receiverType], [argumentTypes] and the
-  /// [interfaceTarget].
-  ir.DartType _computeInstanceInvocationReturnTypeFromFunctionType(
-      ir.DartType receiverType,
-      ir.Member interfaceTarget,
-      ArgumentTypes argumentTypes,
-      ir.DartType functionType) {
-    if (isSpecialCasedBinaryOperator(interfaceTarget)) {
-      ir.DartType argumentType = argumentTypes.positional[0];
-      return typeEnvironment.getTypeOfSpecialCasedBinaryOperator(
-          receiverType, argumentType);
-    } else if (functionType is ir.FunctionType) {
-      return functionType.returnType;
-    } else {
-      return const ir.DynamicType();
-    }
+  ir.DartType _getFunctionReturnType(ir.DartType functionType) {
+    return functionType is ir.FunctionType
+        ? functionType.returnType
+        : const ir.DynamicType();
   }
 
   /// Computes the result type of the dynamic invocation [node] on a receiver of
@@ -858,8 +919,8 @@
             receiverType, node.name, node.arguments);
     ir.DartType returnType;
     if (interfaceTarget != null) {
-      ir.DartType functionType = _computeInvocationFunctionType(
-          receiverType, interfaceTarget, node.arguments);
+      ir.DartType functionType = _computeInstanceInvocationType(
+          receiverType, interfaceTarget, node.arguments, argumentTypes);
       if (node.interfaceTarget == null) {
         // We change [node] from being a dynamic invocation to an instance
         // invocation, so we need to add static type checks to the arguments to
@@ -868,8 +929,7 @@
         _updateMethodInvocationTarget(node, argumentTypes, functionType);
         node.interfaceTarget = interfaceTarget;
       }
-      returnType = _computeInstanceInvocationReturnTypeFromFunctionType(
-          receiverType, interfaceTarget, argumentTypes, functionType);
+      returnType = _getFunctionReturnType(functionType);
     } else {
       returnType = _computeDynamicInvocationReturnType(node, receiverType);
     }
@@ -914,6 +974,64 @@
   }
 
   @override
+  ir.DartType visitInstanceInvocation(ir.InstanceInvocation node) {
+    ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.Member interfaceTarget = node.interfaceTarget;
+    // We compute the function type instead of reading it of [node] since the
+    // receiver and argument types might have improved through inference of
+    // effectively final variable types and type promotion.
+    ir.DartType functionType = _computeInstanceInvocationType(
+        receiverType, interfaceTarget, node.arguments, argumentTypes);
+    if (functionType != node.functionType) {
+      node.functionType = functionType;
+      // TODO(johnniwinther): To provide the static guarantee that arguments
+      // of a statically typed call have been checked against the parameter
+      // types we need to call [_updateMethodInvocationTarget]. This can create
+      // uses of type variables are not registered with the closure model so
+      // we skip it for now. Note that this invariant is not currently used
+      // in later phases since it wasn't provided for function invocations in
+      // the old method invocation encoding.
+      //_updateMethodInvocationTarget(node, argumentTypes, functionType);
+    }
+    ir.DartType returnType = _getFunctionReturnType(functionType);
+    receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
+    handleInstanceInvocation(
+        node, receiverType, interfaceTarget, argumentTypes);
+    _staticTypeCache._expressionTypes[node] = returnType;
+    return returnType;
+  }
+
+  @override
+  ir.DartType visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+    ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.Member interfaceTarget = node.interfaceTarget;
+    // We compute the function type instead of reading it of [node] since the
+    // receiver and argument types might have improved through inference of
+    // effectively final variable types and type promotion.
+    ir.DartType functionType = _computeInstanceInvocationType(
+        receiverType, interfaceTarget, node.arguments, argumentTypes);
+    if (functionType != node.functionType) {
+      node.functionType = functionType;
+      // TODO(johnniwinther): To provide the static guarantee that arguments
+      // of a statically typed call have been checked against the parameter
+      // types we need to call [_updateMethodInvocationTarget]. This can create
+      // uses of type variables are not registered with the closure model so
+      // we skip it for now. Note that this invariant is not currently used
+      // in later phases since it wasn't provided for function invocations in
+      // the old method invocation encoding.
+      //_updateMethodInvocationTarget(node, argumentTypes, functionType);
+    }
+    ir.DartType returnType = _getFunctionReturnType(functionType);
+    receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
+    handleInstanceInvocation(
+        node, receiverType, interfaceTarget, argumentTypes);
+    _staticTypeCache._expressionTypes[node] = returnType;
+    return returnType;
+  }
+
+  @override
   ir.DartType visitDynamicInvocation(ir.DynamicInvocation node) {
     ArgumentTypes argumentTypes = _visitArguments(node.arguments);
     ir.DartType receiverType = visitNode(node.receiver);
@@ -921,31 +1039,67 @@
         receiverType, node.name, node.arguments);
     if (interfaceTarget != null) {
       // We can turn the dynamic invocation into an instance invocation.
-      ir.DartType functionType = _computeInvocationFunctionType(
-          receiverType, interfaceTarget, node.arguments);
-      ir.InstanceInvocation instanceInvocation = ir.InstanceInvocation(
-          ir.InstanceAccessKind.Instance,
-          node.receiver,
-          node.name,
-          node.arguments,
-          interfaceTarget: interfaceTarget,
-          functionType: functionType);
-      node.replaceWith(instanceInvocation);
-      _updateMethodInvocationTarget(
-          instanceInvocation, argumentTypes, functionType);
-      ir.DartType returnType =
-          _computeInstanceInvocationReturnTypeFromFunctionType(
-              receiverType, interfaceTarget, argumentTypes, functionType);
+      ir.DartType functionType = _computeInstanceInvocationType(
+          receiverType, interfaceTarget, node.arguments, argumentTypes);
+      ir.Expression replacement;
+      if (interfaceTarget is ir.Field ||
+          (interfaceTarget is ir.Procedure && interfaceTarget.isGetter)) {
+        // This should actually be a function invocation of an instance get but
+        // this doesn't work for invocation of js-interop properties. We
+        // therefore use [ir.MethodInvocation] instead.
+        // TODO(johnniwinther): Use [ir.InstanceGetterInvocation] instead.
+        replacement = ir.MethodInvocation(
+            node.receiver, node.name, node.arguments, interfaceTarget)
+          ..fileOffset = node.fileOffset;
+      } else {
+        replacement = ir.InstanceInvocation(ir.InstanceAccessKind.Instance,
+            node.receiver, node.name, node.arguments,
+            interfaceTarget: interfaceTarget, functionType: functionType)
+          ..fileOffset = node.fileOffset;
+      }
+      _replaceExpression(node, replacement);
+      _updateMethodInvocationTarget(replacement, argumentTypes, functionType);
+      ir.DartType resultType = _getFunctionReturnType(functionType);
       receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
       handleInstanceInvocation(
-          instanceInvocation, receiverType, interfaceTarget, argumentTypes);
-      _staticTypeCache._expressionTypes[node] = returnType;
-      return returnType;
+          replacement, receiverType, interfaceTarget, argumentTypes);
+      if (replacement is ir.MethodInvocation) {
+        _staticTypeCache._expressionTypes[replacement] = resultType;
+      }
+      return resultType;
+    } else if (node.name == ir.Name.callName &&
+        (receiverType is ir.FunctionType ||
+            (receiverType is ir.InterfaceType &&
+                receiverType.classNode == typeEnvironment.functionClass)) &&
+        _isApplicableToType(node.arguments, receiverType)) {
+      ir.DartType functionType =
+          _computeFunctionInvocationType(receiverType, node.arguments);
+      bool hasFunctionType = functionType is ir.FunctionType;
+      ir.FunctionInvocation replacement = ir.FunctionInvocation(
+          hasFunctionType
+              ? ir.FunctionAccessKind.FunctionType
+              : ir.FunctionAccessKind.Function,
+          node.receiver,
+          node.arguments,
+          functionType: hasFunctionType ? functionType : null)
+        ..fileOffset = node.fileOffset;
+      ir.DartType resultType = _getFunctionReturnType(functionType);
+      _replaceExpression(node, replacement);
+      _updateMethodInvocationTarget(replacement, argumentTypes, functionType);
+      handleFunctionInvocation(
+          replacement, receiverType, argumentTypes, resultType);
+      return resultType;
     } else {
       ir.DartType returnType =
           _computeDynamicInvocationReturnType(node, receiverType);
       _staticTypeCache._expressionTypes[node] = returnType;
       handleDynamicInvocation(node, receiverType, argumentTypes, returnType);
+      if (operatorFromString(node.name.text) == null &&
+          receiverType is ir.DynamicType) {
+        // We might implicitly call a getter that returns a function.
+        handleFunctionInvocation(
+            node, const ir.DynamicType(), argumentTypes, returnType);
+      }
       return returnType;
     }
   }
@@ -954,15 +1108,30 @@
   ir.DartType visitEqualsCall(ir.EqualsCall node) {
     ir.DartType leftType = visitNode(node.left);
     ir.DartType rightType = visitNode(node.right);
+    // This is accessed to ensure that [typeMapWhenTrue] and [typeMapWhenFalse]
+    // are joined as the result of this node.
+    // This is related to dartbug.com/45053
+    _flattenTypeMap();
+    leftType = _narrowInstanceReceiver(node.interfaceTarget, leftType);
     handleEqualsCall(
         node.left, leftType, node.right, rightType, node.interfaceTarget);
     return super.visitEqualsCall(node);
   }
 
+  // TODO(johnniwinther): Remove this after the new method invocation has landed
+  // stably. This is only included to make the transition a no-op.
+  void handleEqualsNull(ir.EqualsNull node, ir.DartType expressionType) {}
+
   @override
   ir.DartType visitEqualsNull(ir.EqualsNull node) {
-    visitNode(node.expression);
+    ir.DartType expressionType = visitNode(node.expression);
+    if (expressionType is ir.DynamicType) {
+      expressionType = currentLibrary.isNonNullableByDefault
+          ? typeEnvironment.objectNullableRawType
+          : typeEnvironment.objectLegacyRawType;
+    }
     _registerEqualsNull(typeMap, node.expression);
+    handleEqualsNull(node, expressionType);
     return super.visitEqualsNull(node);
   }
 
@@ -970,8 +1139,18 @@
   ir.DartType visitFunctionInvocation(ir.FunctionInvocation node) {
     ArgumentTypes argumentTypes = _visitArguments(node.arguments);
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType returnType = super.visitFunctionInvocation(node);
-    handleFunctionInvocation(node, receiverType, argumentTypes, returnType);
+    ir.DartType functionType =
+        _computeFunctionInvocationType(receiverType, node.arguments);
+    if (functionType is ir.FunctionType) {
+      // We might have improved the known function type through inference of
+      // effectively final variable types and type promotion.
+      node.functionType = functionType;
+    }
+    // We compute the return type instead of reading it of [node] since the
+    // receiver and argument types might have improved through inference of
+    // effectively final variable types and type promotion.
+    ir.DartType returnType = _getFunctionReturnType(functionType);
+    handleFunctionInvocation(node, functionType, argumentTypes, returnType);
     return returnType;
   }
 
@@ -1269,7 +1448,8 @@
 
   @override
   ir.DartType visitBlock(ir.Block node) {
-    assert(_pendingRuntimeTypeUseData.isEmpty);
+    assert(_pendingRuntimeTypeUseData.isEmpty,
+        "Incomplete RuntimeTypeUseData: $_pendingRuntimeTypeUseData");
     ir.DartType type;
     for (ir.Statement statement in node.statements) {
       if (!completes(visitNode(statement))) {
@@ -1770,6 +1950,12 @@
   final List<ir.DartType> named;
 
   ArgumentTypes(this.positional, this.named);
+
+  @override
+  String toString() {
+    return 'ArgumentTypes(position=[${positional.join(',')}],'
+        ' named=[${named.join(',')}])';
+  }
 }
 
 /// Type information collected for a single path for a local variable.
diff --git a/pkg/compiler/lib/src/ir/static_type_base.dart b/pkg/compiler/lib/src/ir/static_type_base.dart
index 7330972..315ee23 100644
--- a/pkg/compiler/lib/src/ir/static_type_base.dart
+++ b/pkg/compiler/lib/src/ir/static_type_base.dart
@@ -262,6 +262,10 @@
       node.getStaticType(staticTypeContext);
 
   @override
+  ir.DartType visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
   ir.DartType visitFunctionTearOff(ir.FunctionTearOff node) =>
       node.getStaticType(staticTypeContext);
 
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index b8c1318..eab6a08 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -118,18 +118,26 @@
     ir.Expression body = node.body;
     if (node.variable.name == null &&
         node.variable.isFinal &&
-        body is ir.ConditionalExpression &&
-        body.condition is ir.MethodInvocation &&
-        isNullLiteral(body.then)) {
-      ir.MethodInvocation invocation = body.condition;
-      ir.Expression receiver = invocation.receiver;
-      if (invocation.name.text == '==' &&
-          receiver is ir.VariableGet &&
-          receiver.variable == node.variable &&
-          isNullLiteral(invocation.arguments.positional.single)) {
-        // We have
-        //   let #t1 = e0 in #t1 == null ? null : e1
-        return new NullAwareExpression(node.variable, body.otherwise);
+        body is ir.ConditionalExpression) {
+      if (body.condition is ir.MethodInvocation && isNullLiteral(body.then)) {
+        ir.MethodInvocation invocation = body.condition;
+        ir.Expression receiver = invocation.receiver;
+        if (invocation.name.text == '==' &&
+            receiver is ir.VariableGet &&
+            receiver.variable == node.variable &&
+            isNullLiteral(invocation.arguments.positional.single)) {
+          // We have
+          //   let #t1 = e0 in #t1 == null ? null : e1
+          return new NullAwareExpression(node.variable, body.otherwise);
+        }
+      } else if (body.condition is ir.EqualsNull) {
+        ir.EqualsNull equalsNull = body.condition;
+        ir.Expression receiver = equalsNull.expression;
+        if (receiver is ir.VariableGet && receiver.variable == node.variable) {
+          // We have
+          //   let #t1 = e0 in #t1 == null ? null : e1
+          return new NullAwareExpression(node.variable, body.otherwise);
+        }
       }
     }
   }
@@ -153,7 +161,16 @@
   //
   //   (let _ = check(prefix) in prefix::field).property
   if (node is ir.StaticGet || node is ir.ConstantExpression) {
-    while (parent is ir.PropertyGet || parent is ir.MethodInvocation) {
+    while (parent is ir.PropertyGet ||
+        parent is ir.InstanceGet ||
+        parent is ir.DynamicGet ||
+        parent is ir.InstanceTearOff ||
+        parent is ir.FunctionTearOff ||
+        parent is ir.MethodInvocation ||
+        parent is ir.InstanceInvocation ||
+        parent is ir.InstanceGetterInvocation ||
+        parent is ir.DynamicInvocation ||
+        parent is ir.FunctionInvocation) {
       parent = parent.parent;
     }
   }
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
index 4802555..d9d65cc 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
@@ -83,17 +83,20 @@
     this.lazyInitializers.addAll(that.lazyInitializers);
     this.nativeSupport.addAll(that.nativeSupport);
     this.successors.remove(that);
+    this.predecessors.remove(that);
     that.successors.forEach((fragment) {
+      if (fragment == this) return;
+      this.successors.add(fragment);
       fragment.predecessors.remove(that);
       fragment.predecessors.add(this);
     });
-    this.successors.addAll(that.successors);
-    that.predecessors.remove(this);
     that.predecessors.forEach((fragment) {
+      if (fragment == this) return;
+      this.predecessors.add(fragment);
       fragment.successors.remove(that);
       fragment.successors.add(this);
     });
-    this.predecessors.addAll(that.predecessors);
+    that.clearAll();
     this.size += that.size;
     return this;
   }
@@ -102,6 +105,7 @@
       Program program, Map<Fragment, FinalizedFragment> fragmentMap) {
     FinalizedFragment finalizedFragment;
     var seedFragment = fragments.first;
+    var seedOutputUnit = seedFragment.outputUnit;
 
     // If we only have a single fragment, then wen just finalize it by itself.
     // Otherwise, we finalize an entire group of fragments into a single
@@ -109,7 +113,7 @@
     if (fragments.length == 1) {
       finalizedFragment = FinalizedFragment(
           seedFragment.outputFileName,
-          seedFragment.outputUnit,
+          [seedOutputUnit],
           seedFragment.libraries,
           classPrototypes.first,
           closurePrototypes.first,
@@ -122,21 +126,22 @@
           staticNonFinalFields.first,
           lazyInitializers.first,
           nativeSupport.first,
-          program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+          program.metadataTypesForOutputUnit(seedOutputUnit));
       fragmentMap[seedFragment] = finalizedFragment;
     } else {
+      List<OutputUnit> outputUnits = [seedOutputUnit];
       List<Library> libraries = [];
       for (var fragment in fragments) {
-        if (seedFragment.outputUnit != fragment.outputUnit) {
-          program.mergeOutputUnitMetadata(
-              seedFragment.outputUnit, fragment.outputUnit);
-          seedFragment.outputUnit.merge(fragment.outputUnit);
+        var fragmentOutputUnit = fragment.outputUnit;
+        if (seedOutputUnit != fragmentOutputUnit) {
+          program.mergeOutputUnitMetadata(seedOutputUnit, fragmentOutputUnit);
+          outputUnits.add(fragmentOutputUnit);
         }
         libraries.addAll(fragment.libraries);
       }
       finalizedFragment = FinalizedFragment(
           seedFragment.outputFileName,
-          seedFragment.outputUnit,
+          outputUnits,
           libraries,
           js.Block(classPrototypes),
           js.Block(closurePrototypes),
@@ -149,7 +154,7 @@
           js.Block(staticNonFinalFields),
           js.Block(lazyInitializers),
           js.Block(nativeSupport),
-          program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+          program.metadataTypesForOutputUnit(seedOutputUnit));
       for (var fragment in fragments) {
         fragmentMap[fragment] = finalizedFragment;
       }
@@ -183,11 +188,31 @@
     }
     return "${outputUnitStrings.join('+')}";
   }
+
+  /// Clears all [PreFragment] data structure and zeros out the size. Should be
+  /// used only after merging to GC internal data structures.
+  void clearAll() {
+    fragments.clear();
+    classPrototypes.clear();
+    closurePrototypes.clear();
+    inheritance.clear();
+    methodAliases.clear();
+    tearOffs.clear();
+    constants.clear();
+    typeRules.clear();
+    variances.clear();
+    staticNonFinalFields.clear();
+    lazyInitializers.clear();
+    nativeSupport.clear();
+    successors.clear();
+    predecessors.clear();
+    size = 0;
+  }
 }
 
 class FinalizedFragment {
   final String outputFileName;
-  final OutputUnit outputUnit;
+  final List<OutputUnit> outputUnits;
   final List<Library> libraries;
   final js.Statement classPrototypes;
   final js.Statement closurePrototypes;
@@ -204,7 +229,7 @@
 
   FinalizedFragment(
       this.outputFileName,
-      this.outputUnit,
+      this.outputUnits,
       this.libraries,
       this.classPrototypes,
       this.closurePrototypes,
@@ -247,6 +272,12 @@
         isEmptyStatement(lazyInitializers) &&
         isEmptyStatement(nativeSupport);
   }
+
+  // The 'main' [OutputUnit] for this [FinalizedFragment].
+  // TODO(joshualitt): Refactor this to more clearly disambiguate between
+  // [OutputUnits](units of deferred merging), fragments(units of emitted code),
+  // and files.
+  OutputUnit get canonicalOutputUnit => outputUnits.first;
 }
 
 class _Partition {
@@ -261,6 +292,7 @@
 
 class FragmentMerger {
   final CompilerOptions _options;
+  int totalSize = 0;
 
   FragmentMerger(this._options);
 
@@ -284,16 +316,63 @@
     return loadMap;
   }
 
-  // Attaches predecessors to each PreFragment. We only care about
-  // direct predecessors.
-  static void attachDependencies(Map<String, List<Fragment>> programLoadMap,
-      Map<Fragment, PreFragment> fragmentMap) {
-    programLoadMap.forEach((loadId, fragments) {
-      for (int i = 0; i < fragments.length - 1; i++) {
-        var fragment = fragmentMap[fragments[i]];
-        var nextFragment = fragmentMap[fragments[i + 1]];
-        fragment.successors.add(nextFragment);
-        nextFragment.predecessors.add(fragment);
+  /// Given a list of OutputUnits sorted by their import entites,
+  /// returns a map of all the direct edges between output units.
+  Map<OutputUnit, Set<OutputUnit>> createDirectEdges(
+      List<OutputUnit> allOutputUnits) {
+    Map<OutputUnit, Set<OutputUnit>> backEdges = {};
+    for (int i = 0; i < allOutputUnits.length; i++) {
+      var a = allOutputUnits[i];
+      var aImports = a.imports;
+      for (int j = i + 1; j < allOutputUnits.length; j++) {
+        var b = allOutputUnits[j];
+        if (b.imports.containsAll(aImports)) {
+          backEdges[b] ??= {};
+
+          // Remove transitive edges from nodes that will reach 'b' from the
+          // edge we just added.
+          // Note: Because we add edges in order (starting from the smallest
+          // sets) we always add transitive edges before the last direct edge.
+          backEdges[b].removeWhere((c) => aImports.containsAll(c.imports));
+
+          // Create an edge to denote that 'b' must be loaded before 'a'.
+          backEdges[b].add(a);
+        }
+      }
+    }
+
+    Map<OutputUnit, Set<OutputUnit>> forwardEdges = {};
+    backEdges.forEach((b, edges) {
+      for (var a in edges) {
+        (forwardEdges[a] ??= {}).add(b);
+      }
+    });
+    return forwardEdges;
+  }
+
+  /// Attachs predecessors and successors to each PreFragment.
+  void attachDependencies(Map<Fragment, PreFragment> fragmentMap,
+      List<PreFragment> preDeferredFragments) {
+    // Create a map of OutputUnit to Fragment.
+    Map<OutputUnit, Fragment> outputUnitMap = {};
+    List<OutputUnit> allOutputUnits = [];
+    for (var preFragment in preDeferredFragments) {
+      var fragment = preFragment.fragments.single;
+      var outputUnit = fragment.outputUnit;
+      outputUnitMap[outputUnit] = fragment;
+      allOutputUnits.add(outputUnit);
+      totalSize += preFragment.size;
+    }
+    allOutputUnits.sort();
+
+    // Get a list of direct edges and then attach them to PreFragments.
+    var allEdges = createDirectEdges(allOutputUnits);
+    allEdges.forEach((outputUnit, edges) {
+      var predecessor = fragmentMap[outputUnitMap[outputUnit]];
+      for (var edge in edges) {
+        var successor = fragmentMap[outputUnitMap[edge]];
+        predecessor.successors.add(successor);
+        successor.predecessors.add(predecessor);
       }
     });
   }
@@ -315,12 +394,6 @@
     });
     int desiredNumberOfFragment = _options.mergeFragmentsThreshold;
 
-    // TODO(joshualitt): Precalculate totalSize when computing dependencies.
-    int totalSize = 0;
-    for (var preFragment in preDeferredFragments) {
-      totalSize += preFragment.size;
-    }
-
     int idealFragmentSize = (totalSize / desiredNumberOfFragment).ceil();
     List<_Partition> partitions = [];
     void add(PreFragment next) {
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index e12027b..943f80e 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -98,6 +98,7 @@
   final JClosedWorld _closedWorld;
   final ConstantOrdering _constantOrdering;
   final SourceInformationStrategy _sourceInformationStrategy;
+  final FragmentMerger fragmentMerger;
 
   // The full code that is written to each hunk part-file.
   final Map<OutputUnit, CodeOutput> emittedOutputBuffers = {};
@@ -128,7 +129,8 @@
       this._sourceInformationStrategy,
       RecipeEncoder rtiRecipeEncoder,
       this._shouldGenerateSourceMap)
-      : _constantOrdering = new ConstantOrdering(_closedWorld.sorter) {
+      : _constantOrdering = new ConstantOrdering(_closedWorld.sorter),
+        fragmentMerger = FragmentMerger(_options) {
     this.constantEmitter = new ConstantEmitter(
         _options,
         _namer,
@@ -227,12 +229,11 @@
       }
     });
 
-    // Attach dependencies to each PreFragment.
-    FragmentMerger.attachDependencies(program.loadMap, preFragmentMap);
-
+    // If we are going to merge, then we attach dependencies to each PreFragment
+    // and merge.
     if (shouldMergeFragments) {
       preDeferredFragments = _task.measureSubtask('merge fragments', () {
-        FragmentMerger fragmentMerger = FragmentMerger(_options);
+        fragmentMerger.attachDependencies(preFragmentMap, preDeferredFragments);
         return fragmentMerger.mergeFragments(preDeferredFragments);
       });
     }
@@ -252,7 +253,7 @@
       if (fragmentCode != null) {
         deferredFragmentsCode[finalizedFragment] = fragmentCode;
       } else {
-        omittedOutputUnits.add(finalizedFragment.outputUnit);
+        omittedOutputUnits.addAll(finalizedFragment.outputUnits);
       }
     }
 
@@ -419,7 +420,9 @@
             hunkPrefix, deferredExtension, OutputType.jsPart),
         outputListeners);
 
-    emittedOutputBuffers[fragment.outputUnit] = output;
+    // TODO(joshualitt): This breaks dump_info when we merge, but fixing it will
+    // require updating the schema.
+    emittedOutputBuffers[fragment.canonicalOutputUnit] = output;
 
     // The [code] contains the function that must be invoked when the deferred
     // hunk is loaded.
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 7a42127..5849d77 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -1249,12 +1249,30 @@
     if (node is ir.PropertyGet) {
       return getGetterSelector(node.name);
     }
+    if (node is ir.InstanceGet) {
+      return getGetterSelector(node.name);
+    }
+    if (node is ir.InstanceTearOff) {
+      return getGetterSelector(node.name);
+    }
+    if (node is ir.DynamicGet) {
+      return getGetterSelector(node.name);
+    }
+    if (node is ir.FunctionTearOff) {
+      return getGetterSelector(ir.Name.callName);
+    }
     if (node is ir.SuperPropertyGet) {
       return getGetterSelector(node.name);
     }
     if (node is ir.PropertySet) {
       return getSetterSelector(node.name);
     }
+    if (node is ir.InstanceSet) {
+      return getSetterSelector(node.name);
+    }
+    if (node is ir.DynamicSet) {
+      return getSetterSelector(node.name);
+    }
     if (node is ir.SuperPropertySet) {
       return getSetterSelector(node.name);
     }
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 60301f2..a0ce422 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -86,7 +86,7 @@
   bool get supportsExplicitGetterCalls => false;
 
   @override
-  bool get supportsNewMethodInvocationEncoding => false;
+  bool get supportsNewMethodInvocationEncoding => true;
 
   @override
   List<String> get extraRequiredLibraries => _requiredLibraries[name];
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index 9a40f68..bd6095b 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.dart
@@ -665,8 +665,10 @@
         const <DartType>[]));
   }
 
+  // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+  // invocation encoding is no longer used.
   @override
-  void registerRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+  void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
       ir.DartType receiverType, ir.DartType argumentType) {
     DartType receiverDartType = elementMap.getDartType(receiverType);
     DartType argumentDartType =
diff --git a/pkg/compiler/lib/src/serialization/node_indexer.dart b/pkg/compiler/lib/src/serialization/node_indexer.dart
index 6846687..663e593 100644
--- a/pkg/compiler/lib/src/serialization/node_indexer.dart
+++ b/pkg/compiler/lib/src/serialization/node_indexer.dart
@@ -166,6 +166,12 @@
   }
 
   @override
+  void visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+    registerNode(node);
+    super.visitInstanceGetterInvocation(node);
+  }
+
+  @override
   void visitDynamicInvocation(ir.DynamicInvocation node) {
     registerNode(node);
     super.visitDynamicInvocation(node);
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 2831f5c..864f6a8 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -5060,6 +5060,9 @@
     if (node is ir.MethodInvocation) {
       invoke.isInvariant = node.isInvariant;
       invoke.isBoundsSafe = node.isBoundsSafe;
+    } else if (node is ir.InstanceInvocation) {
+      invoke.isInvariant = node.isInvariant;
+      invoke.isBoundsSafe = node.isBoundsSafe;
     }
     push(invoke);
   }
@@ -5257,6 +5260,11 @@
   }
 
   @override
+  void visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+    _handleMethodInvocation(node, node.receiver, node.arguments);
+  }
+
+  @override
   void visitDynamicInvocation(ir.DynamicInvocation node) {
     _handleMethodInvocation(node, node.receiver, node.arguments);
   }
@@ -5268,8 +5276,27 @@
 
   @override
   void visitLocalFunctionInvocation(ir.LocalFunctionInvocation node) {
-    _handleMethodInvocation(
-        node, ir.VariableGet(node.variable), node.arguments);
+    Local local = _localsMap.getLocalVariable(node.variable);
+    stack.add(localsHandler.readLocal(local,
+        sourceInformation: _sourceInformationBuilder.buildGet(node)));
+    HInstruction receiverInstruction = pop();
+    Selector selector = _elementMap.getSelector(node);
+    List<DartType> typeArguments = [];
+    selector =
+        _fillDynamicTypeArguments(selector, node.arguments, typeArguments);
+    _pushDynamicInvocation(
+        node,
+        StaticType(_elementMap.getDartType(node.variable.type),
+            computeClassRelationFromType(node.variable.type)),
+        _typeInferenceMap.receiverTypeOfInvocation(node, _abstractValueDomain),
+        selector,
+        [
+          receiverInstruction,
+          ..._visitArgumentsForDynamicTarget(
+              selector, node.arguments, typeArguments)
+        ],
+        typeArguments,
+        _sourceInformationBuilder.buildCall(node, node));
   }
 
   @override
@@ -5293,8 +5320,28 @@
   void visitEqualsNull(ir.EqualsNull node) {
     node.expression.accept(this);
     HInstruction receiverInstruction = pop();
-    return _handleEquals(node, node.expression, receiverInstruction,
-        graph.addConstantNull(closedWorld));
+
+    // Hack to detect `null == o`.
+    // TODO(johnniwinther): Remove this after the new method invocation has
+    // landed stably. This is only included to make the transition a no-op.
+    if (node.fileOffset < node.expression.fileOffset) {
+      _pushDynamicInvocation(
+          node,
+          new StaticType(
+              _elementMap.commonElements.nullType, ClassRelation.subtype),
+          _typeInferenceMap.receiverTypeOfInvocation(
+              node, _abstractValueDomain),
+          Selectors.equals,
+          <HInstruction>[
+            graph.addConstantNull(closedWorld),
+            receiverInstruction
+          ],
+          const <DartType>[],
+          _sourceInformationBuilder.buildCall(node.expression, node));
+    } else {
+      _handleEquals(node, node.expression, receiverInstruction,
+          graph.addConstantNull(closedWorld));
+    }
   }
 
   @override
@@ -7366,6 +7413,16 @@
   }
 
   @override
+  visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+    registerRegularNode();
+    registerReductiveNode();
+    registerCall();
+    visit(node.receiver);
+    skipReductiveNodes(() => visit(node.name));
+    _processArguments(node.arguments, null);
+  }
+
+  @override
   visitDynamicInvocation(ir.DynamicInvocation node) {
     registerRegularNode();
     registerReductiveNode();
diff --git a/pkg/compiler/test/codegen/constant_folding_test.dart b/pkg/compiler/test/codegen/constant_folding_test.dart
index 06d09ad..a3471c3 100644
--- a/pkg/compiler/test/codegen/constant_folding_test.dart
+++ b/pkg/compiler/test/codegen/constant_folding_test.dart
@@ -65,16 +65,16 @@
         NEGATIVE_NUMBER_FOLDING, 'main', new RegExp(r"print\(1\)"));
     await compile(NULL_EQUALS_FOLDING, entry: 'foo', check: (String generated) {
       RegExp regexp = new RegExp(r'a == null');
-      Expect.isTrue(regexp.hasMatch(generated));
+      Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
 
       regexp = new RegExp(r'null == b');
-      Expect.isTrue(regexp.hasMatch(generated));
+      Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
 
       regexp = new RegExp(r'4 === c');
-      Expect.isTrue(regexp.hasMatch(generated));
+      Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
 
       regexp = new RegExp('"foo" === d');
-      Expect.isTrue(regexp.hasMatch(generated));
+      Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
     });
     await compileAndMatch(LIST_LENGTH_FOLDING, 'foo', new RegExp(r"return 3"));
     await compileAndMatch(LIST_INDEX_FOLDING, 'foo', new RegExp(r"return 1"));
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
index d50cc23..58108e9 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [1{lib1, lib2}], usedBy: [2, 3], needs: []},
-  f2: {units: [2{lib1}], usedBy: [], needs: [1]},
-  f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+  f1: {units: [1{lib1, lib2}], usedBy: [], needs: []},
+  f2: {units: [2{lib1}], usedBy: [], needs: []},
+  f3: {units: [3{lib2}], usedBy: [], needs: []}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [1{lib1, lib2}], usedBy: [], needs: [2, 3]},
+  f2: {units: [2{lib1}], usedBy: [1], needs: []},
+  f3: {units: [3{lib2}], usedBy: [1], needs: []}],
  steps=[
   lib1=(f1, f2),
   lib2=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
index d65299a..db1d7a6 100644
--- a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*spec|three-frag.library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
-  f2: {units: [1{lib1}], usedBy: [], needs: [1]},
-  f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+  f1: {units: [2{lib1, lib2}], usedBy: [], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: []},
+  f3: {units: [3{lib2}], usedBy: [], needs: []}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1, f3)]
+*/
+
+/*three-frag.library: 
+ output_units=[
+  f1: {units: [2{lib1, lib2}], usedBy: [], needs: [2, 3]},
+  f2: {units: [1{lib1}], usedBy: [1], needs: []},
+  f3: {units: [3{lib2}], usedBy: [1], needs: []}],
  steps=[
   lib1=(f1, f2),
   lib2=(f1, f3)]
@@ -14,8 +24,8 @@
 
 /*two-frag.library: 
  output_units=[
-  f1: {units: [2{lib1, lib2}, 3{lib2}], usedBy: [2], needs: []},
-  f2: {units: [1{lib1}], usedBy: [], needs: [1]}],
+  f1: {units: [2{lib1, lib2}, 3{lib2}], usedBy: [], needs: [2]},
+  f2: {units: [1{lib1}], usedBy: [1], needs: []}],
  steps=[
   lib1=(f1, f2),
   lib2=(f1)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
index ce14794..4642e97 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
-  f2: {units: [1{b}], usedBy: [], needs: [1]},
-  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+  f1: {units: [2{b, c}], usedBy: [], needs: []},
+  f2: {units: [1{b}], usedBy: [], needs: []},
+  f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [2{b, c}], usedBy: [], needs: [2, 3]},
+  f2: {units: [1{b}], usedBy: [1], needs: []},
+  f3: {units: [3{c}], usedBy: [1], needs: []}],
  steps=[
   b=(f1, f2),
   c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
index 0aba5e2..4f5bd68 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
-  f2: {units: [2{b}], usedBy: [], needs: [1]},
-  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+  f1: {units: [1{b, c}], usedBy: [], needs: []},
+  f2: {units: [2{b}], usedBy: [], needs: []},
+  f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [1{b, c}], usedBy: [], needs: [2, 3]},
+  f2: {units: [2{b}], usedBy: [1], needs: []},
+  f3: {units: [3{c}], usedBy: [1], needs: []}],
  steps=[
   b=(f1, f2),
   c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
index 1629521..f972fc5 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
-  f2: {units: [1{b}], usedBy: [], needs: [1]},
-  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+  f1: {units: [2{b, c}], usedBy: [], needs: []},
+  f2: {units: [1{b}], usedBy: [], needs: []},
+  f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [2{b, c}], usedBy: [], needs: [2, 3]},
+  f2: {units: [1{b}], usedBy: [1], needs: []},
+  f3: {units: [3{c}], usedBy: [1], needs: []}],
  steps=[
   b=(f1, f2),
   c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
index 768282f..f9dd67d 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
-  f2: {units: [2{b}], usedBy: [], needs: [1]},
-  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+  f1: {units: [1{b, c}], usedBy: [], needs: []},
+  f2: {units: [2{b}], usedBy: [], needs: []},
+  f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [1{b, c}], usedBy: [], needs: [2, 3]},
+  f2: {units: [2{b}], usedBy: [1], needs: []},
+  f3: {units: [3{c}], usedBy: [1], needs: []}],
  steps=[
   b=(f1, f2),
   c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart
index 120b83c..4cd9f15 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart
@@ -4,25 +4,18 @@
 
 // @dart = 2.7
 
-/*spec|three-frag.class: Foo:
+/*class: Foo:
  class_unit=1{libB},
  type_unit=3{libA, libB, libC}
 */
-/*two-frag.class: Foo:
- class_unit=1{libB, libA},
- type_unit=3{libA, libB, libC}
-*/
 class Foo {
-  /*spec|three-frag.member: Foo.x:member_unit=1{libB}*/
-  /*two-frag.member: Foo.x:member_unit=1{libB, libA}*/
+  /*member: Foo.x:member_unit=1{libB}*/
   int x;
-  /*spec|three-frag.member: Foo.:member_unit=1{libB}*/
-  /*two-frag.member: Foo.:member_unit=1{libB, libA}*/
+  /*member: Foo.:member_unit=1{libB}*/
   Foo() {
     x = DateTime.now().millisecond;
   }
-  /*spec|three-frag.member: Foo.method:member_unit=1{libB}*/
-  /*two-frag.member: Foo.method:member_unit=1{libB, libA}*/
+  /*member: Foo.method:member_unit=1{libB}*/
   int method() => x;
 }
 
@@ -31,8 +24,7 @@
   return o is Foo;
 }
 
-/*spec|three-frag.member: callFooMethod:member_unit=1{libB}*/
-/*two-frag.member: callFooMethod:member_unit=1{libB, libA}*/
+/*member: callFooMethod:member_unit=1{libB}*/
 int callFooMethod() {
   return Foo().method();
 }
@@ -64,14 +56,10 @@
 /*member: Coo.:member_unit=2{libC}*/
 class Coo<T> {}
 
-/*spec|three-frag.class: Doo:
+/*class: Doo:
  class_unit=2{libC},
  type_unit=5{libB, libC}
 */
-/*two-frag.class: Doo:
- class_unit=2{libC},
- type_unit=5{libB, libC, libA}
-*/
 /*member: Doo.:member_unit=2{libC}*/
 class Doo<T> extends Coo<T> with Boo<T> {}
 
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart
index 7c98f96..20c2423 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart
@@ -6,18 +6,14 @@
 
 import 'lib.dart' as lib;
 
-/*spec|three-frag.member: callFooMethod:member_unit=1{libB}*/
-/*two-frag.member: callFooMethod:member_unit=1{libB, libA}*/
+/*member: callFooMethod:member_unit=1{libB}*/
 int callFooMethod() => lib.callFooMethod();
 
-/*spec|three-frag.member: isFoo:member_unit=1{libB}*/
-/*two-frag.member: isFoo:member_unit=1{libB, libA}*/
+/*member: isFoo:member_unit=1{libB}*/
 bool isFoo(o) => lib.isFoo(o);
 
-/*spec|three-frag.member: isFunFunFoo:member_unit=1{libB}*/
-/*two-frag.member: isFunFunFoo:member_unit=1{libB, libA}*/
+/*member: isFunFunFoo:member_unit=1{libB}*/
 bool isFunFunFoo(o) => lib.isFunFunFoo(o);
 
-/*spec|three-frag.member: isDooFunFunFoo:member_unit=1{libB}*/
-/*two-frag.member: isDooFunFunFoo:member_unit=1{libB, libA}*/
+/*member: isDooFunFunFoo:member_unit=1{libB}*/
 bool isDooFunFunFoo(o) => o is lib.Doo<lib.FunFunFoo>;
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
index 6e80197..15e8022 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
@@ -4,12 +4,12 @@
 
 /*spec.library: 
  output_units=[
-  f1: {units: [3{libA, libB, libC}], usedBy: [2, 4], needs: []},
-  f2: {units: [4{libA, libC}], usedBy: [3, 6], needs: [1, 4]},
-  f3: {units: [6{libA}], usedBy: [], needs: [2]},
-  f4: {units: [5{libB, libC}], usedBy: [5, 2], needs: [1]},
-  f5: {units: [1{libB}], usedBy: [], needs: [4]},
-  f6: {units: [2{libC}], usedBy: [], needs: [2]}],
+  f1: {units: [3{libA, libB, libC}], usedBy: [], needs: []},
+  f2: {units: [4{libA, libC}], usedBy: [], needs: []},
+  f3: {units: [6{libA}], usedBy: [], needs: []},
+  f4: {units: [5{libB, libC}], usedBy: [], needs: []},
+  f5: {units: [1{libB}], usedBy: [], needs: []},
+  f6: {units: [2{libC}], usedBy: [], needs: []}],
  steps=[
   libA=(f1, f2, f3),
   libB=(f1, f4, f5),
@@ -18,9 +18,9 @@
 
 /*two-frag.library: 
  output_units=[
-  f1: {units: [3{libA, libB, libC}], usedBy: [2], needs: []},
-  f2: {units: [5{libB, libC, libA}, 4{libA, libC}, 2{libC}], usedBy: [3], needs: [1]},
-  f3: {units: [1{libB, libA}, 6{libA}], usedBy: [], needs: [2]}],
+  f1: {units: [3{libA, libB, libC}], usedBy: [], needs: [2]},
+  f2: {units: [5{libB, libC}, 4{libA, libC}, 2{libC}], usedBy: [1], needs: [3]},
+  f3: {units: [1{libB}, 6{libA}], usedBy: [2], needs: []}],
  steps=[
   libA=(f1, f2, f3),
   libB=(f1, f2, f3),
@@ -29,10 +29,10 @@
 
 /*three-frag.library: 
  output_units=[
-  f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}], usedBy: [3, 2, 4], needs: []},
-  f2: {units: [6{libA}], usedBy: [], needs: [1]},
-  f3: {units: [1{libB}], usedBy: [], needs: [1]},
-  f4: {units: [2{libC}], usedBy: [], needs: [1]}],
+  f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}], usedBy: [], needs: [4, 3, 2]},
+  f2: {units: [6{libA}], usedBy: [1], needs: []},
+  f3: {units: [1{libB}], usedBy: [1], needs: []},
+  f4: {units: [2{libC}], usedBy: [1], needs: []}],
  steps=[
   libA=(f1, f2),
   libB=(f1, f3),
@@ -45,7 +45,7 @@
 import 'libb.dart' deferred as libB;
 import 'libc.dart' deferred as libC;
 
-/*spec|three-frag.member: foo:
+/*member: foo:
  constants=[
   FunctionConstant(callFooMethod)=1{libB},
   FunctionConstant(createB2)=2{libC},
@@ -62,23 +62,6 @@
   FunctionConstant(isMega)=6{libA}],
  member_unit=main{}
 */
-/*two-frag.member: foo:
- constants=[
-  FunctionConstant(callFooMethod)=1{libB, libA},
-  FunctionConstant(createB2)=2{libC},
-  FunctionConstant(createC3)=2{libC},
-  FunctionConstant(createD3)=2{libC},
-  FunctionConstant(createDooFunFunFoo)=2{libC},
-  FunctionConstant(isDooFunFunFoo)=1{libB, libA},
-  FunctionConstant(isFoo)=1{libB, libA},
-  FunctionConstant(isFoo)=2{libC},
-  FunctionConstant(isFoo)=6{libA},
-  FunctionConstant(isFunFunFoo)=1{libB, libA},
-  FunctionConstant(isFunFunFoo)=2{libC},
-  FunctionConstant(isFunFunFoo)=6{libA},
-  FunctionConstant(isMega)=6{libA}],
- member_unit=main{}
-*/
 void foo() async {
   await libA.loadLibrary();
   await libB.loadLibrary();
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
index 64e287e..7f86fc1 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
@@ -35,13 +35,11 @@
 f_010_11(Set<String> u, int b) => v(u, '01011', b);
 
 @pragma('dart2js:noInline')
-/*spec|three-frag.member: f_011_01:member_unit=8{b1, b3, b4}*/
-/*two-frag.member: f_011_01:member_unit=8{b1, b3, b4, b2, b5}*/
+/*member: f_011_01:member_unit=8{b1, b3, b4}*/
 f_011_01(Set<String> u, int b) => v(u, '01101', b);
 
 @pragma('dart2js:noInline')
-/*spec|two-frag.member: f_011_11:member_unit=9{b1, b2, b3, b4}*/
-/*three-frag.member: f_011_11:member_unit=9{b1, b2, b3, b4, b5}*/
+/*member: f_011_11:member_unit=9{b1, b2, b3, b4}*/
 f_011_11(Set<String> u, int b) => v(u, '01111', b);
 
 @pragma('dart2js:noInline')
@@ -105,8 +103,7 @@
 f_110_10(Set<String> u, int b) => v(u, '11010', b);
 
 @pragma('dart2js:noInline')
-/*spec.member: f_111_10:member_unit=24{b2, b3, b4, b5}*/
-/*two-frag|three-frag.member: f_111_10:member_unit=24{b2, b3, b4, b5, b1}*/
+/*member: f_111_10:member_unit=24{b2, b3, b4, b5}*/
 f_111_10(Set<String> u, int b) => v(u, '11110', b);
 
 @pragma('dart2js:noInline')
@@ -114,8 +111,7 @@
 f_001_00(Set<String> u, int b) => v(u, '00100', b);
 
 @pragma('dart2js:noInline')
-/*spec|two-frag.member: f_011_00:member_unit=26{b3, b4}*/
-/*three-frag.member: f_011_00:member_unit=26{b3, b4, b2, b5, b1}*/
+/*member: f_011_00:member_unit=26{b3, b4}*/
 f_011_00(Set<String> u, int b) => v(u, '01100', b);
 
 @pragma('dart2js:noInline')
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
index 6f86ab9..6cd9224 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
@@ -4,37 +4,37 @@
 
 /*spec.library: 
  output_units=[
-  f10: {units: [7{b1, b2, b4}], usedBy: [11, 29], needs: [9, 8]},
-  f11: {units: [5{b1, b2, b3}], usedBy: [12, 21, 26], needs: [10, 8]},
-  f12: {units: [10{b1, b5}], usedBy: [13, 31], needs: [11, 21]},
-  f13: {units: [6{b1, b4}], usedBy: [14, 30], needs: [12, 22]},
-  f14: {units: [4{b1, b3}], usedBy: [15, 28], needs: [13, 23]},
-  f15: {units: [3{b1, b2}], usedBy: [16, 24], needs: [14, 23]},
-  f16: {units: [2{b1}], usedBy: [], needs: [15]},
-  f17: {units: [24{b2, b3, b4, b5}], usedBy: [3, 2], needs: [1]},
-  f18: {units: [23{b2, b4, b5}], usedBy: [19, 20], needs: [5, 25]},
-  f19: {units: [22{b2, b3, b5}], usedBy: [20, 6], needs: [18, 25]},
-  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2, 17], needs: []},
-  f20: {units: [20{b2, b3, b4}], usedBy: [9, 7, 6], needs: [19, 18]},
-  f21: {units: [21{b2, b5}], usedBy: [22, 12], needs: [11, 26]},
-  f22: {units: [19{b2, b4}], usedBy: [23, 13], needs: [21, 27]},
-  f23: {units: [18{b2, b3}], usedBy: [15, 14], needs: [22, 27]},
-  f24: {units: [17{b2}], usedBy: [], needs: [15]},
-  f25: {units: [28{b3, b4, b5}], usedBy: [19, 18], needs: [5, 4]},
-  f26: {units: [27{b3, b5}], usedBy: [27, 21], needs: [11, 29]},
-  f27: {units: [26{b3, b4}], usedBy: [23, 22], needs: [26, 29]},
-  f28: {units: [25{b3}], usedBy: [], needs: [14]},
-  f29: {units: [30{b4, b5}], usedBy: [27, 26], needs: [10, 9]},
-  f2: {units: [16{b1, b3, b4, b5}], usedBy: [3, 4], needs: [1, 17]},
-  f30: {units: [29{b4}], usedBy: [], needs: [13]},
-  f31: {units: [31{b5}], usedBy: [], needs: [12]},
-  f3: {units: [15{b1, b2, b4, b5}], usedBy: [4, 5], needs: [2, 17]},
-  f4: {units: [13{b1, b2, b3, b5}], usedBy: [5, 25], needs: [3, 2]},
-  f5: {units: [9{b1, b2, b3, b4}], usedBy: [6, 18, 25], needs: [4, 3]},
-  f6: {units: [14{b1, b4, b5}], usedBy: [7, 8], needs: [5, 20, 19]},
-  f7: {units: [12{b1, b3, b5}], usedBy: [8, 9], needs: [6, 20]},
-  f8: {units: [8{b1, b3, b4}], usedBy: [9, 11, 10], needs: [7, 6]},
-  f9: {units: [11{b1, b2, b5}], usedBy: [10, 29], needs: [8, 20, 7]}],
+  f10: {units: [7{b1, b2, b4}], usedBy: [], needs: []},
+  f11: {units: [5{b1, b2, b3}], usedBy: [], needs: []},
+  f12: {units: [10{b1, b5}], usedBy: [], needs: []},
+  f13: {units: [6{b1, b4}], usedBy: [], needs: []},
+  f14: {units: [4{b1, b3}], usedBy: [], needs: []},
+  f15: {units: [3{b1, b2}], usedBy: [], needs: []},
+  f16: {units: [2{b1}], usedBy: [], needs: []},
+  f17: {units: [24{b2, b3, b4, b5}], usedBy: [], needs: []},
+  f18: {units: [23{b2, b4, b5}], usedBy: [], needs: []},
+  f19: {units: [22{b2, b3, b5}], usedBy: [], needs: []},
+  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [], needs: []},
+  f20: {units: [20{b2, b3, b4}], usedBy: [], needs: []},
+  f21: {units: [21{b2, b5}], usedBy: [], needs: []},
+  f22: {units: [19{b2, b4}], usedBy: [], needs: []},
+  f23: {units: [18{b2, b3}], usedBy: [], needs: []},
+  f24: {units: [17{b2}], usedBy: [], needs: []},
+  f25: {units: [28{b3, b4, b5}], usedBy: [], needs: []},
+  f26: {units: [27{b3, b5}], usedBy: [], needs: []},
+  f27: {units: [26{b3, b4}], usedBy: [], needs: []},
+  f28: {units: [25{b3}], usedBy: [], needs: []},
+  f29: {units: [30{b4, b5}], usedBy: [], needs: []},
+  f2: {units: [16{b1, b3, b4, b5}], usedBy: [], needs: []},
+  f30: {units: [29{b4}], usedBy: [], needs: []},
+  f31: {units: [31{b5}], usedBy: [], needs: []},
+  f3: {units: [15{b1, b2, b4, b5}], usedBy: [], needs: []},
+  f4: {units: [13{b1, b2, b3, b5}], usedBy: [], needs: []},
+  f5: {units: [9{b1, b2, b3, b4}], usedBy: [], needs: []},
+  f6: {units: [14{b1, b4, b5}], usedBy: [], needs: []},
+  f7: {units: [12{b1, b3, b5}], usedBy: [], needs: []},
+  f8: {units: [8{b1, b3, b4}], usedBy: [], needs: []},
+  f9: {units: [11{b1, b2, b5}], usedBy: [], needs: []}],
  steps=[
   b1=(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16),
   b2=(f1, f17, f3, f4, f5, f18, f19, f20, f9, f10, f11, f21, f22, f23, f15, f24),
@@ -45,10 +45,10 @@
 
 /*three-frag.library: 
  output_units=[
-  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2], needs: []},
-  f2: {units: [24{b2, b3, b4, b5, b1}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}], usedBy: [3], needs: [1]},
-  f3: {units: [9{b1, b2, b3, b4, b5}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}, 8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}], usedBy: [4], needs: [2]},
-  f4: {units: [26{b3, b4, b2, b5, b1}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [], needs: [3]}],
+  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [], needs: [3, 2]},
+  f2: {units: [24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}], usedBy: [1], needs: [3]},
+  f3: {units: [9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}, 8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}], usedBy: [1, 2], needs: [4]},
+  f4: {units: [26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [3], needs: []}],
  steps=[
   b1=(f1, f2, f3, f4),
   b2=(f1, f2, f3, f4),
@@ -59,9 +59,9 @@
 
 /*two-frag.library: 
  output_units=[
-  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2], needs: []},
-  f2: {units: [24{b2, b3, b4, b5, b1}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}], usedBy: [3], needs: [1]},
-  f3: {units: [8{b1, b3, b4, b2, b5}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [], needs: [2]}],
+  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [], needs: [2]},
+  f2: {units: [24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}], usedBy: [1], needs: [3]},
+  f3: {units: [8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [2], needs: []}],
  steps=[
   b1=(f1, f2, f3),
   b2=(f1, f2, f3),
diff --git a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
index 9c8dd2f..a4f5f5a 100644
--- a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
-  f2: {units: [1{lib1}], usedBy: [], needs: [1]},
-  f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+  f1: {units: [2{lib1, lib2}], usedBy: [], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: []},
+  f3: {units: [3{lib2}], usedBy: [], needs: []}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [2{lib1, lib2}], usedBy: [], needs: [2, 3]},
+  f2: {units: [1{lib1}], usedBy: [1], needs: []},
+  f3: {units: [3{lib2}], usedBy: [1], needs: []}],
  steps=[
   lib1=(f1, f2),
   lib2=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
index 42fed69..13f5f03 100644
--- a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
@@ -2,11 +2,21 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*library: 
+/*spec.library: 
  output_units=[
-  f1: {units: [3{lib1, lib3}], usedBy: [2, 3], needs: []},
-  f2: {units: [1{lib1}], usedBy: [], needs: [1]},
-  f3: {units: [2{lib3}], usedBy: [], needs: [1]}],
+  f1: {units: [3{lib1, lib3}], usedBy: [], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: []},
+  f3: {units: [2{lib3}], usedBy: [], needs: []}],
+ steps=[
+  lib1=(f1, f2),
+  lib3=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library: 
+ output_units=[
+  f1: {units: [3{lib1, lib3}], usedBy: [], needs: [2, 3]},
+  f2: {units: [1{lib1}], usedBy: [1], needs: []},
+  f3: {units: [2{lib3}], usedBy: [1], needs: []}],
  steps=[
   lib1=(f1, f2),
   lib3=(f1, f3)]
diff --git a/pkg/compiler/test/inference/inference_test_helper.dart b/pkg/compiler/test/inference/inference_test_helper.dart
index 701d33d..b51b0e6 100644
--- a/pkg/compiler/test/inference/inference_test_helper.dart
+++ b/pkg/compiler/test/inference/inference_test_helper.dart
@@ -146,11 +146,23 @@
         node is ir.FunctionDeclaration) {
       ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
       return getMemberValue(info.callMethod);
-    } else if (node is ir.MethodInvocation) {
+    } else if (node is ir.MethodInvocation ||
+        node is ir.InstanceInvocation ||
+        node is ir.InstanceGetterInvocation ||
+        node is ir.DynamicInvocation ||
+        node is ir.FunctionInvocation ||
+        node is ir.EqualsNull ||
+        node is ir.EqualsCall) {
       return getTypeMaskValue(result.typeOfReceiver(node));
-    } else if (node is ir.PropertyGet) {
+    } else if (node is ir.PropertyGet ||
+        node is ir.InstanceGet ||
+        node is ir.DynamicGet ||
+        node is ir.InstanceTearOff ||
+        node is ir.FunctionTearOff) {
       return getTypeMaskValue(result.typeOfReceiver(node));
-    } else if (node is ir.PropertySet) {
+    } else if (node is ir.PropertySet ||
+        node is ir.InstanceSet ||
+        node is ir.DynamicSet) {
       return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.ForInStatement) {
       if (id.kind == IdKind.iterator) {
diff --git a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
index e2a2196..51e385e 100644
--- a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
+++ b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
@@ -13,7 +13,7 @@
         final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(_length);
         for (core::int i = 0; i.{core::num::<}(_length); i = i.{core::num::+}(1)) {
           core::int* i = i;
-          _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1));
+          _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1){(core::num*) →* core::int*});
         }
       } =>_list);
     }
diff --git a/pkg/compiler/test/kernel/data/list_generate_3.dart.expect b/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
index 05df05d..9a50e1e 100644
--- a/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
+++ b/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
@@ -34,9 +34,9 @@
   return i;
 }, growable: self::someGrowable);
 static field core::List<core::int*>* list5 = core::List::generate<core::int*>(10, (core::int* i) → core::int* {
-  if(i.{core::int::isEven})
-    return i.{core::num::+}(1);
-  return i.{core::num::-}(1);
+  if(i.{core::int::isEven}{core::bool*})
+    return i.{core::num::+}(1){(core::num*) →* core::int*};
+  return i.{core::num::-}(1){(core::num*) →* core::int*};
 });
 static field core::List<core::int*>* list6 = core::List::generate<core::int*>(10, #C1);
 static field core::List<core::int*>* list7 = core::List::generate<core::int*>(10, self::bar);
diff --git a/pkg/compiler/test/static_type/data/effectively_final_access.dart b/pkg/compiler/test/static_type/data/effectively_final_access.dart
new file mode 100644
index 0000000..352b77c
--- /dev/null
+++ b/pkg/compiler/test/static_type/data/effectively_final_access.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+main() {
+  effectivelyFinalFunctionTyped();
+  effectivelyFinalGenericFunctionTyped(null);
+  effectivelyFinalDynamicallyTyped();
+  effectivelyFinalPartiallyTyped();
+}
+
+effectivelyFinalFunctionTyped() {
+  Function f = (int i) => /*spec.int*/ i;
+  /*spec.int Function(int)*/ f
+      . /*spec.invoke: [int Function(int)]->int*/ call(0);
+  (/*spec.int Function(int)*/ f.call)
+      /*spec.invoke: [int Function(int)]->int*/ (0);
+}
+
+effectivelyFinalGenericFunctionTyped(T Function<T>(T) g) {
+  Function f = /*spec.T Function<T extends Object>(T)*/ g;
+  /*spec.T Function<T extends Object>(T)*/ f
+      . /*spec.invoke: [T Function<T extends Object>(T)]->int*/ call<int>(0);
+  (/*spec.T Function<T extends Object>(T)*/ f.call)
+      .
+      /*spec.invoke: [T Function<T extends Object>(T)]->int*/
+      call<int>(0);
+}
+
+effectivelyFinalDynamicallyTyped() {
+  dynamic list = <int>[0];
+  /*spec.List<int>*/ list.first /*spec.invoke: [int]->int*/ + 0;
+  /*spec.List<int>*/ list /*spec.[List<int>]->int*/
+          [0] /*spec.invoke: [int]->int*/ +
+      0;
+  (/*spec.List<int>*/ list.contains)
+      /*spec.invoke: [bool Function(Object)]->bool*/ (0);
+  /*spec.List<int>*/ list
+      /*spec.update: [List<int>]->void*/ /*spec.[List<int>]->int*/
+      [0] /*spec.invoke: [int]->int*/ ++;
+}
+
+effectivelyFinalPartiallyTyped() {
+  List list = <int>[0];
+  /*spec.List<int>*/ list.first /*spec.invoke: [int]->int*/ + 0;
+  /*spec.List<int>*/ list /*spec.[List<int>]->int*/
+          [0] /*spec.invoke: [int]->int*/ +
+      0;
+  (/*spec.List<int>*/ list.contains)
+      /*spec.invoke: [bool Function(Object)]->bool*/ (0);
+  /*spec.List<int>*/ list
+      /*spec.update: [List<int>]->void*/ /*spec.[List<int>]->int*/
+      [0] /*spec.invoke: [int]->int*/ ++;
+}
diff --git a/pkg/compiler/test/static_type/static_type_test.dart b/pkg/compiler/test/static_type/static_type_test.dart
index 220b990..fca7c8a 100644
--- a/pkg/compiler/test/static_type/static_type_test.dart
+++ b/pkg/compiler/test/static_type/static_type_test.dart
@@ -89,6 +89,29 @@
     } else if (node is ir.MethodInvocation) {
       return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
           '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.InstanceInvocation) {
+      return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.InstanceGetterInvocation) {
+      return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.DynamicInvocation) {
+      return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.FunctionInvocation) {
+      return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.LocalFunctionInvocation) {
+      return '[${typeToText(node.variable.type)}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.EqualsCall) {
+      return '[${typeToText(node.left.accept(staticTypeCache))}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
+    } else if (node is ir.EqualsNull) {
+      // TODO(johnniwinther): Remove this after the new method invocation has
+      // landed stably. This is only included to make the transition a no-op.
+      return '[${typeToText(node.expression.accept(staticTypeCache))}]->'
+          '${typeToText(node.accept(staticTypeCache))}';
     }
     return null;
   }
diff --git a/pkg/compiler/test/static_type/type_promotion_data/equals.dart b/pkg/compiler/test/static_type/type_promotion_data/equals.dart
index cd6ec3c..2438e4d 100644
--- a/pkg/compiler/test/static_type/type_promotion_data/equals.dart
+++ b/pkg/compiler/test/static_type/type_promotion_data/equals.dart
@@ -53,7 +53,7 @@
     if (
         /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
         e1 is List !=
-            /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
+            /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>]}*/
             e2 is List) {
       return
           /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>]}*/
diff --git a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
index d4ef9bf..4d44607 100644
--- a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
+++ b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
@@ -16,9 +16,6 @@
 
 import 'package:kernel/target/targets.dart' show Target;
 
-import '../api_prototype/compiler_options.dart'
-    show CompilerOptions, parseExperimentalFlags;
-
 import '../api_prototype/experimental_flags.dart' show ExperimentalFlag;
 
 import '../api_prototype/file_system.dart' show FileSystem;
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index c0e9971..86e27d1 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -6,8 +6,6 @@
 
 library fasta.named_type_builder;
 
-import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
-
 import 'package:kernel/ast.dart';
 
 import '../fasta_codes.dart'
@@ -30,9 +28,6 @@
 
 import '../identifiers.dart' show Identifier, QualifiedName, flattenName;
 
-import '../messages.dart'
-    show noLength, templateSupertypeIsIllegal, templateSupertypeIsTypeVariable;
-
 import '../problems.dart' show unhandled;
 
 import '../scope.dart';
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index ae2dc15..ecc024e 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -8,8 +8,6 @@
 import 'package:front_end/src/fasta/kernel/kernel_api.dart';
 import 'package:kernel/ast.dart';
 
-import 'package:kernel/type_algebra.dart';
-
 import '../kernel/class_hierarchy_builder.dart';
 import '../kernel/forest.dart';
 import '../kernel/internal_ast.dart';
diff --git a/pkg/front_end/lib/src/fasta/compiler_context.dart b/pkg/front_end/lib/src/fasta/compiler_context.dart
index 913a4e1..67cfc5a 100644
--- a/pkg/front_end/lib/src/fasta/compiler_context.dart
+++ b/pkg/front_end/lib/src/fasta/compiler_context.dart
@@ -24,9 +24,6 @@
 
 import 'command_line_reporting.dart' as command_line_reporting;
 
-import 'fasta_codes.dart'
-    show LocatedMessage, Message, messageInternalProblemMissingContext;
-
 final Object compilerContextKey = new Object();
 
 /// Shared context used throughout the compiler.
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 75da2ac..82ba08c 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -109,14 +109,6 @@
 
 import 'util/textual_outline.dart' show textualOutline;
 
-import 'fasta_codes.dart'
-    show
-        DiagnosticMessageFromJson,
-        templateInitializeFromDillNotSelfContained,
-        templateInitializeFromDillNotSelfContainedNoDump,
-        templateInitializeFromDillUnknownProblem,
-        templateInitializeFromDillUnknownProblemNoDump;
-
 import 'hybrid_file_system.dart' show HybridFileSystem;
 
 import 'kernel/kernel_builder.dart' show ClassHierarchyBuilder;
@@ -127,8 +119,6 @@
 
 import 'library_graph.dart' show LibraryGraph;
 
-import 'messages.dart' show Message;
-
 import 'source/source_library_builder.dart' show SourceLibraryBuilder;
 
 import 'ticker.dart' show Ticker;
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index 9b23f9f..e2fb85b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -12,14 +12,6 @@
 
 import 'package:kernel/type_environment.dart' show StaticTypeContext;
 
-import 'package:kernel/visitor.dart'
-    show
-        ExpressionVisitor,
-        ExpressionVisitor1,
-        Transformer,
-        TreeVisitor,
-        Visitor;
-
 import '../messages.dart'
     show noLength, templateExpectedAfterButGot, templateExpectedButGot;
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index cc156ed..d2e276e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -3413,6 +3413,18 @@
     exprEvaluator.env.addVariableValue(node, value);
     return const ProceedStatus();
   }
+
+  @override
+  ExecutionStatus visitWhileStatement(WhileStatement node) {
+    Constant condition = evaluate(node.condition);
+    while (condition is BoolConstant && condition.value) {
+      final ExecutionStatus status = node.body.accept(this);
+      if (status is! ProceedStatus) return status;
+      condition = evaluate(node.condition);
+    }
+    if (condition is AbortConstant) return new AbortStatus(condition);
+    return const ProceedStatus();
+  }
 }
 
 class ConstantCoverage {
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 4e59fb4..6f89031 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -67,18 +67,6 @@
 
 import 'kernel_api.dart' show NameSystem, printNodeOn, printQualifiedNameOn;
 
-import 'kernel_ast_api.dart'
-    show
-        Arguments,
-        DartType,
-        DynamicType,
-        Expression,
-        Initializer,
-        Member,
-        Name,
-        Procedure,
-        VariableDeclaration;
-
 import 'kernel_builder.dart' show LoadLibraryBuilder;
 
 import 'internal_ast.dart';
diff --git a/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart b/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
index bfaef2c..3cafb52 100644
--- a/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
@@ -5,7 +5,6 @@
 // @dart = 2.9
 
 import 'package:kernel/ast.dart';
-import 'package:kernel/visitor.dart';
 
 import '../type_inference/type_schema.dart';
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index ac5f466..d017229 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -15,8 +15,6 @@
 import 'package:kernel/type_environment.dart'
     show SubtypeCheckMode, TypeEnvironment;
 
-import 'package:kernel/visitor.dart' show Transformer;
-
 import 'collections.dart'
     show
         ControlFlowElement,
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
index 7195f01..253d4ce 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
@@ -14,8 +14,6 @@
 
 import 'package:kernel/type_algebra.dart' show Substitution;
 
-import 'package:kernel/visitor.dart' show Transformer;
-
 import '../source/source_loader.dart' show SourceLoader;
 
 import 'redirecting_factory_body.dart' show RedirectingFactoryBody;
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index bb2c869..8f660b6 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -8,8 +8,6 @@
 
 import 'dart:convert' show jsonEncode;
 
-import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
-
 import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token;
 
 import 'package:_fe_analyzer_shared/src/util/resolve_relative_uri.dart'
diff --git a/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart b/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
index 83721d7..3548229 100644
--- a/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
+++ b/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
@@ -6,8 +6,6 @@
 
 library fasta.stack_listener_impl;
 
-import 'package:_fe_analyzer_shared/src/messages/codes.dart' show Message;
-
 import 'package:_fe_analyzer_shared/src/parser/parser.dart' show Parser;
 
 import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart';
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
index 569d058..7e7f6cd 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
@@ -12,8 +12,6 @@
 
 import 'package:kernel/type_environment.dart';
 
-import 'type_schema.dart' show UnknownType;
-
 import '../names.dart' show callName;
 
 import 'type_schema.dart';
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 78e4c89..fe251d2 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -41,12 +41,6 @@
 
 import '../fasta_codes.dart';
 
-import '../kernel/internal_ast.dart'
-    show
-        VariableDeclarationImpl,
-        getExplicitTypeArguments,
-        getExtensionTypeParameterCount;
-
 import '../kernel/inference_visitor.dart';
 
 import '../kernel/invalid_type.dart';
@@ -2948,7 +2942,7 @@
         VariableDeclaration variable = receiver.variable;
         TreeNode parent = variable.parent;
         if (parent is FunctionDeclaration) {
-          assert(inferredFunctionType != unknownFunction,
+          assert(!identical(inferredFunctionType, unknownFunction),
               "Unknown function type for local function invocation.");
           expression = new LocalFunctionInvocation(variable, arguments,
               functionType: inferredFunctionType)
diff --git a/pkg/front_end/lib/src/fasta/uri_translator.dart b/pkg/front_end/lib/src/fasta/uri_translator.dart
index 55222b2..cf294d2 100644
--- a/pkg/front_end/lib/src/fasta/uri_translator.dart
+++ b/pkg/front_end/lib/src/fasta/uri_translator.dart
@@ -6,8 +6,6 @@
 
 library fasta.uri_translator;
 
-import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
-
 import 'package:package_config/package_config.dart';
 
 import '../base/libraries_specification.dart' show TargetLibrariesSpecification;
diff --git a/pkg/front_end/test/lint_test.status b/pkg/front_end/test/lint_test.status
index 691adfa..4b2a34a 100644
--- a/pkg/front_end/test/lint_test.status
+++ b/pkg/front_end/test/lint_test.status
@@ -15,7 +15,6 @@
 front_end/lib/src/api_prototype/incremental_kernel_generator/Exports: Fail
 front_end/lib/src/api_prototype/language_version/Exports: Fail
 front_end/lib/src/api_prototype/terminal_color_support/Exports: Fail
-front_end/lib/src/api_unstable/bazel_worker/ImportsTwice: Fail
 front_end/lib/src/fasta/fasta_codes/Exports: Fail
 front_end/lib/src/fasta/incremental_compiler/ImportsTwice: Fail
 front_end/lib/src/fasta/kernel/body_builder/ImportsTwice: Fail
@@ -37,7 +36,6 @@
 front_end/lib/src/fasta/source/scope_listener/Exports: Fail
 front_end/lib/src/fasta/source/source_class_builder/ImportsTwice: Fail
 front_end/lib/src/fasta/source/value_kinds/ImportsTwice: Fail
-front_end/lib/src/fasta/type_inference/type_constraint_gatherer/ImportsTwice: Fail
 front_end/lib/src/fasta/type_inference/type_inferrer/ImportsTwice: Fail
 front_end/lib/src/testing/id_testing_helper/Exports: Fail
 kernel/lib/ast/ImportsTwice: Fail
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart
new file mode 100644
index 0000000..cceb2b2
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests while statements for const functions.
+
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+const var2 = fn(3);
+int fn(int a) {
+  int b = a;
+  int i = 0;
+  while (i < 2) {
+    b += a;
+    i++;
+  }
+  return b;
+}
+
+const var3 = fn1(2);
+const var4 = fn1(3);
+int fn1(int a) {
+  int b = a;
+  while (true) {
+    b *= 3;
+    if (b > 10) return b;
+  }
+}
+
+void main() {
+  Expect.equals(var1, 6);
+  Expect.equals(var2, 9);
+  Expect.equals(var3, 18);
+  Expect.equals(var4, 27);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+  core::int b = a;
+  core::int i = 0;
+  while (i.{core::num::<}(2)) {
+    b = b.{core::num::+}(a);
+    i = i.{core::num::+}(1);
+  }
+  return b;
+}
+static method fn1(core::int a) → core::int {
+  core::int b = a;
+  while (true) {
+    b = b.{core::num::*}(3);
+    if(b.{core::num::>}(10))
+      return b;
+  }
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 6);
+  exp::Expect::equals(#C2, 9);
+  exp::Expect::equals(#C3, 18);
+  exp::Expect::equals(#C4, 27);
+}
+
+constants  {
+  #C1 = 6
+  #C2 = 9
+  #C3 = 18
+  #C4 = 27
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.transformed.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.transformed.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+  core::int b = a;
+  core::int i = 0;
+  while (i.{core::num::<}(2)) {
+    b = b.{core::num::+}(a);
+    i = i.{core::num::+}(1);
+  }
+  return b;
+}
+static method fn1(core::int a) → core::int {
+  core::int b = a;
+  while (true) {
+    b = b.{core::num::*}(3);
+    if(b.{core::num::>}(10))
+      return b;
+  }
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 6);
+  exp::Expect::equals(#C2, 9);
+  exp::Expect::equals(#C3, 18);
+  exp::Expect::equals(#C4, 27);
+}
+
+constants  {
+  #C1 = 6
+  #C2 = 9
+  #C3 = 18
+  #C4 = 27
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline.expect
new file mode 100644
index 0000000..2f001f7
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+const var2 = fn(3);
+int fn(int a) {}
+const var3 = fn1(2);
+const var4 = fn1(3);
+int fn1(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d2a9f6b
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+const var2 = fn(3);
+const var3 = fn1(2);
+const var4 = fn1(3);
+int fn(int a) {}
+int fn1(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+  core::int b = a;
+  core::int i = 0;
+  while (i.{core::num::<}(2)) {
+    b = b.{core::num::+}(a);
+    i = i.{core::num::+}(1);
+  }
+  return b;
+}
+static method fn1(core::int a) → core::int {
+  core::int b = a;
+  while (true) {
+    b = b.{core::num::*}(3);
+    if(b.{core::num::>}(10))
+      return b;
+  }
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 6);
+  exp::Expect::equals(#C2, 9);
+  exp::Expect::equals(#C3, 18);
+  exp::Expect::equals(#C4, 27);
+}
+
+constants  {
+  #C1 = 6
+  #C2 = 9
+  #C3 = 18
+  #C4 = 27
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.outline.expect
new file mode 100644
index 0000000..005f16a
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::fn(2);
+static const field core::int var2 = self::fn(3);
+static const field core::int var3 = self::fn1(2);
+static const field core::int var4 = self::fn1(3);
+static method fn(core::int a) → core::int
+  ;
+static method fn1(core::int a) → core::int
+  ;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.transformed.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.transformed.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+  core::int b = a;
+  core::int i = 0;
+  while (i.{core::num::<}(2)) {
+    b = b.{core::num::+}(a);
+    i = i.{core::num::+}(1);
+  }
+  return b;
+}
+static method fn1(core::int a) → core::int {
+  core::int b = a;
+  while (true) {
+    b = b.{core::num::*}(3);
+    if(b.{core::num::>}(10))
+      return b;
+  }
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 6);
+  exp::Expect::equals(#C2, 9);
+  exp::Expect::equals(#C3, 18);
+  exp::Expect::equals(#C4, 27);
+}
+
+constants  {
+  #C1 = 6
+  #C2 = 9
+  #C3 = 18
+  #C4 = 27
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
index c9e4703..1d7d6ef 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
@@ -3,5 +3,5 @@
 import "dart:core" as core;
 
 static method main() → dynamic {
-  core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2));
+  core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2){(core::num) → core::int});
 }
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
index a118ecb..c99286d 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
@@ -8,7 +8,7 @@
     final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
     for (core::int* i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
       core::int i = i;
-      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2));
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2){(core::num) → core::int});
     }
   } =>_list;
 }
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
index c9e4703..1d7d6ef 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
@@ -3,5 +3,5 @@
 import "dart:core" as core;
 
 static method main() → dynamic {
-  core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2));
+  core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2){(core::num) → core::int});
 }
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
index a118ecb..c99286d 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
@@ -8,7 +8,7 @@
     final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
     for (core::int* i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
       core::int i = i;
-      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2));
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2){(core::num) → core::int});
     }
   } =>_list;
 }
diff --git a/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart b/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
new file mode 100644
index 0000000..d30310a
--- /dev/null
+++ b/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--verbose_debug --enable-isolate-groups --experimental-enable-isolate-groups-jit
+import 'dart:async';
+import 'dart:isolate' as dart_isolate;
+
+import 'package:observatory/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 18;
+const int LINE_B = 25;
+const int LINE_C = 29;
+
+foo(args) { // LINE_A
+  final dart_isolate.SendPort sendPort = args[0] as dart_isolate.SendPort;
+  sendPort.send('reply from foo');
+}
+
+testMain() async {
+  final rpResponse = dart_isolate.ReceivePort();
+  debugger(); // LINE_B
+  await dart_isolate.Isolate.spawn(foo, [rpResponse.sendPort]);
+  await rpResponse.first;
+  rpResponse.close();
+  debugger(); // LINE_C
+}
+
+final completerAtFoo = Completer();
+
+final tests = <IsolateTest>[
+  hasPausedAtStart,
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B + 1),
+  (Isolate isolate) async {
+    // Set up a listener to wait for child isolate launch and breakpoint events.
+    final stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var childIsolate;
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) async {
+      switch (event.kind) {
+        case ServiceEvent.kPauseStart:
+          childIsolate = event.isolate!;
+          await childIsolate.reload();
+
+          Library rootLib = await childIsolate.rootLibrary.load() as Library;
+          final foo = rootLib.functions.singleWhere((f) => f.name == 'foo');
+          final bpt = await childIsolate.addBreakpointAtEntry(foo);
+
+          expect(bpt is Breakpoint, isTrue);
+          childIsolate.resume();
+          break;
+        case ServiceEvent.kPauseBreakpoint:
+          if (childIsolate == event.isolate) {
+            ServiceMap stack = await childIsolate.getStack();
+            Frame top = stack['frames'][0];
+            Script script = await top.location!.script.load() as Script;
+            expect(script.tokenToLine(top.location!.tokenPos), equals(LINE_A));
+
+            childIsolate.resume();
+            subscription.cancel();
+            completerAtFoo.complete();
+          }
+          break;
+      }
+    });
+  },
+  resumeIsolate,
+  (Isolate isolate) async {
+    await completerAtFoo.future;
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C + 1),
+  resumeIsolate,
+];
+
+main(args) async {
+  runIsolateTests(args, tests,
+      testeeConcurrent: testMain, pause_on_start: true);
+}
diff --git a/runtime/observatory/tests/service/capture_stdio_test.dart b/runtime/observatory/tests/service/capture_stdio_test.dart
index b02e476..fa4afc7 100644
--- a/runtime/observatory/tests/service/capture_stdio_test.dart
+++ b/runtime/observatory/tests/service/capture_stdio_test.dart
@@ -28,6 +28,7 @@
     var stdoutSub;
     stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
         (ServiceEvent event) {
+      expect(event.isolate != null, isTrue);
       expect(event.kind, equals('WriteEvent'));
       expect(event.bytesAsString, equals('stdout'));
       stdoutSub.cancel().then((_) {
@@ -44,6 +45,7 @@
     int eventNumber = 1;
     stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
         (ServiceEvent event) {
+      expect(event.isolate != null, isTrue);
       expect(event.kind, equals('WriteEvent'));
       if (eventNumber == 1) {
         expect(event.bytesAsString, equals('print'));
@@ -66,6 +68,7 @@
     var stderrSub;
     stderrSub = await isolate.vm.listenEventStream(VM.kStderrStream,
         (ServiceEvent event) {
+      expect(event.isolate != null, isTrue);
       expect(event.kind, equals('WriteEvent'));
       expect(event.bytesAsString, equals('stderr'));
       stderrSub.cancel().then((_) {
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 16e2d93..600d2df 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -58,6 +58,7 @@
 break_on_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
 break_on_async_function_test: SkipByDesign # Debugger is disabled in AOT mode.
 break_on_default_constructor_test: SkipByDesign # Debugger is disabled in AOT mode.
+break_on_function_child_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
 break_on_function_test: SkipByDesign # Debugger is disabled in AOT mode.
 breakpoint_async_break_test: SkipByDesign # Debugger is disabled in AOT mode.
 breakpoint_in_package_parts_class_file_uri_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/observatory_2/tests/service_2/capture_stdio_test.dart b/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
index dc81e5a..ccbdbfd 100644
--- a/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
+++ b/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
@@ -28,6 +28,7 @@
     var stdoutSub;
     stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
         (ServiceEvent event) {
+      expect(event.isolate != null, isTrue);
       expect(event.kind, equals('WriteEvent'));
       expect(event.bytesAsString, equals('stdout'));
       stdoutSub.cancel().then((_) {
@@ -44,6 +45,7 @@
     int eventNumber = 1;
     stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
         (ServiceEvent event) {
+      expect(event.isolate != null, isTrue);
       expect(event.kind, equals('WriteEvent'));
       if (eventNumber == 1) {
         expect(event.bytesAsString, equals('print'));
@@ -66,6 +68,7 @@
     var stderrSub;
     stderrSub = await isolate.vm.listenEventStream(VM.kStderrStream,
         (ServiceEvent event) {
+      expect(event.isolate != null, isTrue);
       expect(event.kind, equals('WriteEvent'));
       expect(event.bytesAsString, equals('stderr'));
       stderrSub.cancel().then((_) {
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index f352aa6..a04fc9c 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -58,6 +58,7 @@
 break_on_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
 break_on_async_function_test: SkipByDesign # Debugger is disabled in AOT mode.
 break_on_default_constructor_test: SkipByDesign # Debugger is disabled in AOT mode.
+break_on_function_child_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
 break_on_function_test: SkipByDesign # Debugger is disabled in AOT mode.
 breakpoint_async_break_test: SkipByDesign # Debugger is disabled in AOT mode.
 breakpoint_in_package_parts_class_file_uri_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/tests/vm/dart/regress_45306_test.dart b/runtime/tests/vm/dart/regress_45306_test.dart
new file mode 100644
index 0000000..2b5e3c0
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45306_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45306.
+// Verifies that ScopeBuilder doesn't crash on an async closure inside
+// instance field initializer.
+
+class X {
+  late final Y y = Y(
+    () async {},
+  );
+
+  final double? a;
+  final double? b;
+  final String? c;
+
+  X({
+    this.a,
+    this.b,
+    this.c,
+  });
+}
+
+typedef Callback = Future<void> Function();
+
+class Y {
+  Y(Callback? f);
+}
+
+void main() {
+  X();
+}
diff --git a/runtime/tests/vm/dart_2/regress_45306_test.dart b/runtime/tests/vm/dart_2/regress_45306_test.dart
new file mode 100644
index 0000000..36af440
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_45306_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45306.
+// Verifies that ScopeBuilder doesn't crash on an async closure inside
+// instance field initializer.
+
+class X {
+  final Y y = Y(
+    () async {},
+  );
+
+  final double a;
+  final double b;
+  final String c;
+
+  X({
+    this.a,
+    this.b,
+    this.c,
+  });
+}
+
+typedef Callback = Future<void> Function();
+
+class Y {
+  Y(Callback f);
+}
+
+void main() {
+  X();
+}
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index c2aae32..2b8fd43 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -636,20 +636,23 @@
   }
 }
 
-static void EmitBranchOnCondition(FlowGraphCompiler* compiler,
-                                  Condition true_condition,
-                                  BranchLabels labels) {
+static void EmitBranchOnCondition(
+    FlowGraphCompiler* compiler,
+    Condition true_condition,
+    BranchLabels labels,
+    compiler::Assembler::JumpDistance jump_distance =
+        compiler::Assembler::kFarJump) {
   if (labels.fall_through == labels.false_label) {
     // If the next block is the false successor, fall through to it.
-    __ j(true_condition, labels.true_label);
+    __ j(true_condition, labels.true_label, jump_distance);
   } else {
     // If the next block is not the false successor, branch to it.
     Condition false_condition = InvertCondition(true_condition);
-    __ j(false_condition, labels.false_label);
+    __ j(false_condition, labels.false_label, jump_distance);
 
     // Fall through or jump to the true successor.
     if (labels.fall_through != labels.true_label) {
-      __ jmp(labels.true_label);
+      __ jmp(labels.true_label, jump_distance);
     }
   }
 }
@@ -815,7 +818,8 @@
   BranchLabels labels = {&is_true, &is_false, &is_false};
   Condition true_condition = EmitComparisonCode(compiler, labels);
   if (true_condition != kInvalidCondition) {
-    EmitBranchOnCondition(compiler, true_condition, labels);
+    EmitBranchOnCondition(compiler, true_condition, labels,
+                          compiler::Assembler::kNearJump);
   }
 
   Register result = locs()->out(0).reg();
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index b0a0802..1bef8cf 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -789,20 +789,23 @@
   }
 }
 
-static void EmitBranchOnCondition(FlowGraphCompiler* compiler,
-                                  Condition true_condition,
-                                  BranchLabels labels) {
+static void EmitBranchOnCondition(
+    FlowGraphCompiler* compiler,
+    Condition true_condition,
+    BranchLabels labels,
+    compiler::Assembler::JumpDistance jump_distance =
+        compiler::Assembler::kFarJump) {
   if (labels.fall_through == labels.false_label) {
     // If the next block is the false successor, fall through to it.
-    __ j(true_condition, labels.true_label);
+    __ j(true_condition, labels.true_label, jump_distance);
   } else {
     // If the next block is not the false successor, branch to it.
     Condition false_condition = InvertCondition(true_condition);
-    __ j(false_condition, labels.false_label);
+    __ j(false_condition, labels.false_label, jump_distance);
 
     // Fall through or jump to the true successor.
     if (labels.fall_through != labels.true_label) {
-      __ jmp(labels.true_label);
+      __ jmp(labels.true_label, jump_distance);
     }
   }
 }
@@ -936,29 +939,20 @@
   Condition true_condition = EmitComparisonCode(compiler, labels);
 
   Register result = locs()->out(0).reg();
-  if (is_true.IsLinked() || is_false.IsLinked()) {
-    if (true_condition != kInvalidCondition) {
-      EmitBranchOnCondition(compiler, true_condition, labels);
-    }
-    compiler::Label done;
-    __ Bind(&is_false);
-    __ LoadObject(result, Bool::False());
-    __ jmp(&done, compiler::Assembler::kNearJump);
-    __ Bind(&is_true);
-    __ LoadObject(result, Bool::True());
-    __ Bind(&done);
-  } else {
-    // If EmitComparisonCode did not use the labels and just returned
-    // a condition we can avoid the branch and use conditional loads.
-    ASSERT(true_condition != kInvalidCondition);
-    __ setcc(InvertCondition(true_condition), ByteRegisterOf(result));
-    __ movzxb(result, result);
-    __ movq(result,
-            compiler::Address(THR, result, TIMES_8,
-                              compiler::target::Thread::bool_true_offset()));
-    ASSERT(compiler::target::Thread::bool_true_offset() + 8 ==
-           compiler::target::Thread::bool_false_offset());
+  if (true_condition != kInvalidCondition) {
+    EmitBranchOnCondition(compiler, true_condition, labels,
+                          compiler::Assembler::kNearJump);
   }
+  // Note: We use branches instead of setcc or cmov even when the branch labels
+  // are otherwise unused, as this runs faster for the x86 processors tested on
+  // our benchmarking server.
+  compiler::Label done;
+  __ Bind(&is_false);
+  __ LoadObject(result, Bool::False());
+  __ jmp(&done, compiler::Assembler::kNearJump);
+  __ Bind(&is_true);
+  __ LoadObject(result, Bool::True());
+  __ Bind(&done);
 }
 
 void ComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
@@ -3892,11 +3886,12 @@
   EMIT_SMI_CHECK;
   Condition true_condition = EmitComparisonCode(compiler, labels);
   ASSERT(true_condition != kInvalidCondition);
-  EmitBranchOnCondition(compiler, true_condition, labels);
+  EmitBranchOnCondition(compiler, true_condition, labels,
+                        compiler::Assembler::kNearJump);
   Register result = locs()->out(0).reg();
   __ Bind(false_label);
   __ LoadObject(result, Bool::False());
-  __ jmp(&done);
+  __ jmp(&done, compiler::Assembler::kNearJump);
   __ Bind(true_label);
   __ LoadObject(result, Bool::True());
   __ Bind(&done);
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 12d1c1e..673bc1f 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -546,15 +546,6 @@
   }
   function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
 
-  // The :sync_op and :async_op continuations are called multiple times. So we
-  // don't want the parameters from the first invocation to get stored in the
-  // context and reused on later invocations with different parameters.
-  if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
-    for (intptr_t i = 0; i < function.NumParameters(); i++) {
-      parsed_function_->ParameterVariable(i)->set_is_forced_stack();
-    }
-  }
-
   // Read (but don't visit) the positional and named parameters, because they've
   // already been added to the scope.
   function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
@@ -1537,7 +1528,11 @@
   if (helper.IsCovariant()) {
     variable->set_is_explicit_covariant_parameter();
   }
-  if (variable->name().ptr() == Symbols::IteratorParameter().ptr()) {
+
+  // The :sync_op and :async_op continuations are called multiple times. So we
+  // don't want the parameters from the first invocation to get stored in the
+  // context and reused on later invocations with different parameters.
+  if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
     variable->set_is_forced_stack();
   }
 
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 712bdb0..1a9d77d 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6269,7 +6269,8 @@
         "Dart_ServiceSendDataEvent expects argument 'bytes_length' to be >= "
         "0.");
   }
-  Service::SendEmbedderEvent(stream_id, event_kind, bytes, bytes_length);
+  Service::SendEmbedderEvent(Isolate::Current(),  // May be NULL
+                             stream_id, event_kind, bytes, bytes_length);
 #endif
   return nullptr;
 }
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 2b5db6a..643e324 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -57,12 +57,14 @@
 #ifndef PRODUCT
 
 // Create an unresolved breakpoint in given token range and script.
-BreakpointLocation::BreakpointLocation(const Script& script,
+BreakpointLocation::BreakpointLocation(Debugger* debugger,
+                                       const Script& script,
                                        TokenPosition token_pos,
                                        TokenPosition end_token_pos,
                                        intptr_t requested_line_number,
                                        intptr_t requested_column_number)
-    : script_(script.ptr()),
+    : debugger_(debugger),
+      script_(script.ptr()),
       url_(script.url()),
       token_pos_(token_pos),
       end_token_pos_(end_token_pos),
@@ -77,10 +79,12 @@
 }
 
 // Create a latent breakpoint at given url and line number.
-BreakpointLocation::BreakpointLocation(const String& url,
+BreakpointLocation::BreakpointLocation(Debugger* debugger,
+                                       const String& url,
                                        intptr_t requested_line_number,
                                        intptr_t requested_column_number)
-    : script_(Script::null()),
+    : debugger_(debugger),
+      script_(Script::null()),
       url_(url.ptr()),
       token_pos_(TokenPosition::kNoSource),
       end_token_pos_(TokenPosition::kNoSource),
@@ -323,7 +327,7 @@
   bpt->set_next(breakpoints());
   set_breakpoints(bpt);
 
-  dbg->SyncBreakpointLocation(this);
+  dbg->group_debugger()->SyncBreakpointLocation(this);
   dbg->SendBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
 }
 
@@ -441,6 +445,11 @@
     }
     return false;
   }
+  return group_debugger()->HasCodeBreakpointInFunction(func);
+}
+
+bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
+  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
   CodeBreakpoint* cbpt = code_breakpoints_;
   while (cbpt != NULL) {
     if (func.ptr() == cbpt->function()) {
@@ -451,7 +460,8 @@
   return false;
 }
 
-bool Debugger::HasBreakpoint(const Code& code) {
+bool GroupDebugger::HasBreakpointInCode(const Code& code) {
+  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
   CodeBreakpoint* cbpt = code_breakpoints_;
   while (cbpt != NULL) {
     if (code.ptr() == cbpt->code_) {
@@ -1446,7 +1456,6 @@
       pc_(pc),
       line_number_(-1),
       is_enabled_(false),
-      bpt_location_(NULL),
       next_(NULL),
       breakpoint_kind_(kind),
       saved_value_(Code::null()) {
@@ -1463,7 +1472,6 @@
 #ifdef DEBUG
   code_ = Code::null();
   pc_ = 0ul;
-  bpt_location_ = NULL;
   next_ = NULL;
   breakpoint_kind_ = UntaggedPcDescriptors::kOther;
 #endif
@@ -1506,12 +1514,57 @@
   ASSERT(!is_enabled_);
 }
 
+bool CodeBreakpoint::HasBreakpointLocation(
+    BreakpointLocation* breakpoint_location) {
+  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+    if (breakpoint_locations_[i] == breakpoint_location) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CodeBreakpoint::FindAndDeleteBreakpointLocation(
+    BreakpointLocation* breakpoint_location) {
+  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+    if (breakpoint_locations_[i] == breakpoint_location) {
+      breakpoint_locations_.EraseAt(i);
+      return true;
+    }
+  }
+  return false;
+}
+
+BreakpointLocation* CodeBreakpoint::FindBreakpointForDebugger(
+    Debugger* debugger) {
+  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+    if (breakpoint_locations_[i]->debugger() == debugger) {
+      return breakpoint_locations_[i];
+    }
+  }
+  return nullptr;
+}
+
+GroupDebugger::GroupDebugger(IsolateGroup* isolate_group)
+    : isolate_group_(isolate_group),
+      code_breakpoints_lock_(new SafepointRwLock()),
+      code_breakpoints_(nullptr),
+      needs_breakpoint_cleanup_(false) {}
+
+GroupDebugger::~GroupDebugger() {
+  while (code_breakpoints_ != nullptr) {
+    CodeBreakpoint* cbpt = code_breakpoints_;
+    code_breakpoints_ = code_breakpoints_->next();
+    ASSERT(!cbpt->IsEnabled());
+    delete cbpt;
+  }
+}
+
 Debugger::Debugger(Isolate* isolate)
     : isolate_(isolate),
       next_id_(1),
       latent_locations_(NULL),
       breakpoint_locations_(NULL),
-      code_breakpoints_(NULL),
       resume_action_(kContinue),
       resume_frame_index_(-1),
       post_deopt_frame_index_(-1),
@@ -1526,7 +1579,6 @@
       async_stepping_fp_(0),
       top_frame_awaiter_(Object::null()),
       skip_next_step_(false),
-      needs_breakpoint_cleanup_(false),
       synthetic_async_breakpoint_(NULL),
       exc_pause_info_(kNoPauseOnExceptions) {}
 
@@ -1534,7 +1586,6 @@
   ASSERT(!IsPaused());
   ASSERT(latent_locations_ == NULL);
   ASSERT(breakpoint_locations_ == NULL);
-  ASSERT(code_breakpoints_ == NULL);
   ASSERT(stack_trace_ == NULL);
   ASSERT(async_causal_stack_trace_ == NULL);
   ASSERT(synthetic_async_breakpoint_ == NULL);
@@ -1546,22 +1597,18 @@
   if (Isolate::IsSystemIsolate(isolate_)) {
     return;
   }
-  while (breakpoint_locations_ != NULL) {
+  while (breakpoint_locations_ != nullptr) {
     BreakpointLocation* loc = breakpoint_locations_;
+    group_debugger()->DisableCodeBreakpointsFor(loc);
     breakpoint_locations_ = breakpoint_locations_->next();
     delete loc;
   }
-  while (latent_locations_ != NULL) {
+  while (latent_locations_ != nullptr) {
     BreakpointLocation* loc = latent_locations_;
+    group_debugger()->DisableCodeBreakpointsFor(loc);
     latent_locations_ = latent_locations_->next();
     delete loc;
   }
-  while (code_breakpoints_ != NULL) {
-    CodeBreakpoint* cbpt = code_breakpoints_;
-    code_breakpoints_ = code_breakpoints_->next();
-    cbpt->Disable();
-    delete cbpt;
-  }
   if (NeedsIsolateEvents()) {
     ServiceEvent event(isolate_, ServiceEvent::kIsolateExit);
     InvokeEventHandler(&event);
@@ -2439,8 +2486,8 @@
   return TokenPosition::kNoSource;
 }
 
-void Debugger::MakeCodeBreakpointAt(const Function& func,
-                                    BreakpointLocation* loc) {
+void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
+                                         BreakpointLocation* loc) {
   ASSERT(loc->token_pos_.IsReal());
   ASSERT((loc != NULL) && loc->IsResolved());
   ASSERT(!func.HasOptimizedCode());
@@ -2464,19 +2511,31 @@
   if (lowest_pc_offset != kUwordMax) {
     uword lowest_pc = code.PayloadStart() + lowest_pc_offset;
     CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
-    if (code_bpt == NULL) {
-      // No code breakpoint for this code exists; create one.
-      code_bpt =
-          new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind);
-      if (FLAG_verbose_debug) {
-        OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px
-                     " offset %#" Px "\n",
-                     loc->token_pos_.ToCString(), lowest_pc,
-                     lowest_pc - code.PayloadStart());
+    if (code_bpt == nullptr) {
+      SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
+      code_bpt = GetCodeBreakpoint(lowest_pc);
+      if (code_bpt == nullptr) {
+        // No code breakpoint for this code exists; create one.
+        code_bpt =
+            new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind);
+        if (FLAG_verbose_debug) {
+          OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px
+                       " offset %#" Px "\n",
+                       loc->token_pos_.ToCString(), lowest_pc,
+                       lowest_pc - code.PayloadStart());
+        }
+        RegisterCodeBreakpoint(code_bpt);
+      } else {
+        if (FLAG_verbose_debug) {
+          OS::PrintErr(
+              "Adding location to existing code breakpoint at pos %s pc %#" Px
+              " offset %#" Px "\n",
+              loc->token_pos_.ToCString(), lowest_pc,
+              lowest_pc - code.PayloadStart());
+        }
       }
-      RegisterCodeBreakpoint(code_bpt);
     }
-    code_bpt->set_bpt_location(loc);
+    code_bpt->AddBreakpointLocation(loc);
     if (loc->AnyEnabled()) {
       code_bpt->Enable();
     }
@@ -2734,7 +2793,7 @@
                                 requested_column);
   }
   if (loc == NULL) {
-    loc = new BreakpointLocation(script, breakpoint_pos, breakpoint_pos,
+    loc = new BreakpointLocation(this, script, breakpoint_pos, breakpoint_pos,
                                  requested_line, requested_column);
     RegisterBreakpointLocation(loc);
   }
@@ -2751,7 +2810,7 @@
   for (intptr_t i = 0; i < num_functions; i++) {
     func ^= functions.At(i);
     ASSERT(func.HasCode());
-    MakeCodeBreakpointAt(func, loc);
+    group_debugger()->MakeCodeBreakpointAt(func, loc);
   }
   if (FLAG_verbose_debug) {
     intptr_t line_number = -1;
@@ -2842,7 +2901,7 @@
   BreakpointLocation* loc =
       GetBreakpointLocation(script, token_pos, -1, requested_column);
   if (loc == NULL) {
-    loc = new BreakpointLocation(script, token_pos, last_token_pos,
+    loc = new BreakpointLocation(this, script, token_pos, last_token_pos,
                                  requested_line, requested_column);
     RegisterBreakpointLocation(loc);
   }
@@ -2851,12 +2910,13 @@
 
 // Synchronize the enabled/disabled state of all code breakpoints
 // associated with the breakpoint location loc.
-void Debugger::SyncBreakpointLocation(BreakpointLocation* loc) {
+void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
   bool any_enabled = loc->AnyEnabled();
 
+  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
   CodeBreakpoint* cbpt = code_breakpoints_;
   while (cbpt != NULL) {
-    if (loc == cbpt->bpt_location()) {
+    if (cbpt->HasBreakpointLocation(loc)) {
       if (any_enabled) {
         cbpt->Enable();
       } else {
@@ -2867,6 +2927,17 @@
   }
 }
 
+void GroupDebugger::DisableCodeBreakpointsFor(BreakpointLocation* location) {
+  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
+  CodeBreakpoint* cbpt = code_breakpoints_;
+  while (cbpt != nullptr) {
+    if (cbpt->HasBreakpointLocation(location)) {
+      cbpt->Disable();
+    }
+    cbpt = cbpt->next();
+  }
+}
+
 Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
                                            bool single_shot) {
   ASSERT(!target_function.IsNull());
@@ -3044,6 +3115,14 @@
   return loc;
 }
 
+void GroupDebugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+  CodeBreakpoint* cbpt = code_breakpoints_;
+  while (cbpt != nullptr) {
+    cbpt->VisitObjectPointers(visitor);
+    cbpt = cbpt->next();
+  }
+}
+
 // static
 void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
   ASSERT(visitor != NULL);
@@ -3057,11 +3136,6 @@
     loc->VisitObjectPointers(visitor);
     loc = loc->next();
   }
-  CodeBreakpoint* cbpt = code_breakpoints_;
-  while (cbpt != NULL) {
-    cbpt->VisitObjectPointers(visitor);
-    cbpt = cbpt->next();
-  }
   visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&top_frame_awaiter_));
 }
 
@@ -3102,10 +3176,14 @@
     }
   }
 
+  group_debugger()->Pause();
+  pause_event_ = nullptr;
+}
+
+void GroupDebugger::Pause() {
   if (needs_breakpoint_cleanup_) {
     RemoveUnlinkedCodeBreakpoints();
   }
-  pause_event_ = NULL;
 }
 
 void Debugger::EnterSingleStepMode() {
@@ -3664,7 +3742,7 @@
   // If there is an active breakpoint at this pc, then we should have
   // already bailed out of this function in the skip_next_step_ test
   // above.
-  ASSERT(!HasActiveBreakpoint(frame->pc()));
+  ASSERT(!group_debugger()->HasActiveBreakpoint(frame->pc()));
 
   if (FLAG_verbose_debug) {
     OS::PrintErr(">>> single step break at %s:%" Pd ":%" Pd
@@ -3701,17 +3779,19 @@
   ASSERT(stack_trace->Length() > 0);
   ActivationFrame* top_frame = stack_trace->FrameAt(0);
   ASSERT(top_frame != nullptr);
-  CodeBreakpoint* cbpt = GetCodeBreakpoint(top_frame->pc());
-  ASSERT(cbpt != nullptr);
-
   if (!Library::Handle(top_frame->Library()).IsDebuggable()) {
     return Error::null();
   }
 
-  BreakpointLocation* bpt_location = cbpt->bpt_location_;
+  CodeBreakpoint* cbpt = nullptr;
+  BreakpointLocation* bpt_location =
+      group_debugger()->GetBreakpointLocationFor(this, top_frame->pc(), &cbpt);
   if (bpt_location == nullptr) {
+    // There might be no breakpoint locations for this isolate/debugger.
     return Error::null();
   }
+  ASSERT(cbpt != nullptr);
+
   Breakpoint* bpt_hit = bpt_location->FindHitBreakpoint(top_frame);
   if (bpt_hit == nullptr) {
     return Error::null();
@@ -3967,7 +4047,7 @@
           bpt = bpt->next();
         }
       }
-      MakeCodeBreakpointAt(func, loc);
+      group_debugger()->MakeCodeBreakpointAt(func, loc);
     }
   }
 }
@@ -4039,9 +4119,9 @@
           if (existing_loc == NULL) {
             // Create and register a new source breakpoint for the
             // latent breakpoint.
-            BreakpointLocation* unresolved_loc =
-                new BreakpointLocation(script, first_token_pos, last_token_pos,
-                                       line_number, column_number);
+            BreakpointLocation* unresolved_loc = new BreakpointLocation(
+                this, script, first_token_pos, last_token_pos, line_number,
+                column_number);
             RegisterBreakpointLocation(unresolved_loc);
 
             // Move breakpoints over.
@@ -4058,7 +4138,7 @@
               }
               bpt = bpt->next();
             }
-            SyncBreakpointLocation(unresolved_loc);
+            group_debugger()->SyncBreakpointLocation(unresolved_loc);
           }
           delete matched_loc;
           // Break out of the iteration over loaded libraries. If the
@@ -4093,12 +4173,12 @@
 
 // TODO(hausner): Could potentially make this faster by checking
 // whether the call target at pc is a debugger stub.
-bool Debugger::HasActiveBreakpoint(uword pc) {
+bool GroupDebugger::HasActiveBreakpoint(uword pc) {
   CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
-  return (cbpt != NULL) && (cbpt->IsEnabled());
+  return (cbpt != nullptr) && (cbpt->IsEnabled());
 }
 
-CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) {
+CodeBreakpoint* GroupDebugger::GetCodeBreakpoint(uword breakpoint_address) {
   CodeBreakpoint* cbpt = code_breakpoints_;
   while (cbpt != NULL) {
     if (cbpt->pc() == breakpoint_address) {
@@ -4106,10 +4186,33 @@
     }
     cbpt = cbpt->next();
   }
-  return NULL;
+  return nullptr;
 }
 
-CodePtr Debugger::GetPatchedStubAddress(uword breakpoint_address) {
+BreakpointLocation* GroupDebugger::GetBreakpointLocationFor(
+    Debugger* debugger,
+    uword breakpoint_address,
+    CodeBreakpoint** pcbpt) {
+  ASSERT(pcbpt != nullptr);
+  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
+  *pcbpt = code_breakpoints_;
+  while (*pcbpt != nullptr) {
+    if ((*pcbpt)->pc() == breakpoint_address) {
+      return (*pcbpt)->FindBreakpointForDebugger(debugger);
+    }
+    *pcbpt = (*pcbpt)->next();
+  }
+  return nullptr;
+}
+
+void GroupDebugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
+  ASSERT(cbpt->next() == NULL);
+  DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter());
+  cbpt->set_next(code_breakpoints_);
+  code_breakpoints_ = cbpt;
+}
+
+CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
   CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
   if (cbpt != NULL) {
     return cbpt->OrigStubAddress();
@@ -4174,7 +4277,7 @@
             // Remove references from code breakpoints to this breakpoint
             // location and disable them.
             // Latent breakpoint locations won't have code breakpoints.
-            UnlinkCodeBreakpoints(curr_loc);
+            group_debugger()->UnlinkCodeBreakpoints(curr_loc);
           }
           BreakpointLocation* next_loc = curr_loc->next();
           delete curr_loc;
@@ -4200,14 +4303,16 @@
 // They will later be deleted when control returns from the pause event
 // callback. Also, disable the breakpoint so it no longer fires if it
 // should be hit before it gets deleted.
-void Debugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
-  ASSERT(bpt_location != NULL);
+void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
+  ASSERT(bpt_location != nullptr);
+  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
   CodeBreakpoint* curr_bpt = code_breakpoints_;
-  while (curr_bpt != NULL) {
-    if (curr_bpt->bpt_location() == bpt_location) {
-      curr_bpt->Disable();
-      curr_bpt->set_bpt_location(NULL);
-      needs_breakpoint_cleanup_ = true;
+  while (curr_bpt != nullptr) {
+    if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
+      if (curr_bpt->HasNoBreakpointLocations()) {
+        curr_bpt->Disable();
+        needs_breakpoint_cleanup_ = true;
+      }
     }
     curr_bpt = curr_bpt->next();
   }
@@ -4215,19 +4320,19 @@
 
 // Remove and delete unlinked code breakpoints, i.e. breakpoints that
 // are not associated with a breakpoint location.
-void Debugger::RemoveUnlinkedCodeBreakpoints() {
-  CodeBreakpoint* prev_bpt = NULL;
+void GroupDebugger::RemoveUnlinkedCodeBreakpoints() {
+  SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
+  CodeBreakpoint* prev_bpt = nullptr;
   CodeBreakpoint* curr_bpt = code_breakpoints_;
-  while (curr_bpt != NULL) {
-    if (curr_bpt->bpt_location() == NULL) {
-      if (prev_bpt == NULL) {
+  while (curr_bpt != nullptr) {
+    if (curr_bpt->HasNoBreakpointLocations()) {
+      if (prev_bpt == nullptr) {
         code_breakpoints_ = code_breakpoints_->next();
       } else {
         prev_bpt->set_next(curr_bpt->next());
       }
       CodeBreakpoint* temp_bpt = curr_bpt;
       curr_bpt = curr_bpt->next();
-      temp_bpt->Disable();
       delete temp_bpt;
     } else {
       prev_bpt = curr_bpt;
@@ -4317,7 +4422,7 @@
     loc = loc->next();
   }
   // No breakpoint for this location requested. Allocate new one.
-  loc = new BreakpointLocation(url, line, column);
+  loc = new BreakpointLocation(this, url, line, column);
   loc->set_next(latent_locations_);
   latent_locations_ = loc;
   return loc;
@@ -4329,12 +4434,6 @@
   breakpoint_locations_ = loc;
 }
 
-void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
-  ASSERT(cbpt->next() == NULL);
-  cbpt->set_next(code_breakpoints_);
-  code_breakpoints_ = cbpt;
-}
-
 #endif  // !PRODUCT
 
 }  // namespace dart
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 6378fb1..c833e5d 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -5,6 +5,8 @@
 #ifndef RUNTIME_VM_DEBUGGER_H_
 #define RUNTIME_VM_DEBUGGER_H_
 
+#include <memory>
+
 #include "include/dart_tools_api.h"
 
 #include "vm/kernel_isolate.h"
@@ -16,6 +18,8 @@
 #include "vm/stack_frame.h"
 #include "vm/stack_trace.h"
 
+#if !defined(PRODUCT)
+
 DECLARE_FLAG(bool, verbose_debug);
 
 // 'Trace Debugger' TD_Print.
@@ -120,13 +124,15 @@
 class BreakpointLocation {
  public:
   // Create a new unresolved breakpoint.
-  BreakpointLocation(const Script& script,
+  BreakpointLocation(Debugger* debugger,
+                     const Script& script,
                      TokenPosition token_pos,
                      TokenPosition end_token_pos,
                      intptr_t requested_line_number,
                      intptr_t requested_column_number);
   // Create a new latent breakpoint.
-  BreakpointLocation(const String& url,
+  BreakpointLocation(Debugger* debugger,
+                     const String& url,
                      intptr_t requested_line_number,
                      intptr_t requested_column_number);
 
@@ -154,6 +160,8 @@
   bool IsResolved() const { return code_token_pos_.IsReal(); }
   bool IsLatent() const { return !token_pos_.IsReal(); }
 
+  Debugger* debugger() { return debugger_; }
+
  private:
   void VisitObjectPointers(ObjectPointerVisitor* visitor);
 
@@ -170,6 +178,7 @@
   // Finds the breakpoint we hit at |location|.
   Breakpoint* FindHitBreakpoint(ActivationFrame* top_frame);
 
+  Debugger* debugger_;
   ScriptPtr script_;
   StringPtr url_;
   TokenPosition token_pos_;
@@ -184,12 +193,16 @@
   TokenPosition code_token_pos_;
 
   friend class Debugger;
+  friend class GroupDebugger;
   DISALLOW_COPY_AND_ASSIGN(BreakpointLocation);
 };
 
 // CodeBreakpoint represents a location in compiled code.
 // There may be more than one CodeBreakpoint for one BreakpointLocation,
 // e.g. when a function gets compiled as a regular function and as a closure.
+// There may be more than one BreakpointLocation associated with CodeBreakpoint,
+// one for for every isolate in a group that sets a breakpoint at particular
+// code location represented by the CodeBreakpoint.
 class CodeBreakpoint {
  public:
   CodeBreakpoint(const Code& code,
@@ -201,6 +214,11 @@
   FunctionPtr function() const;
   uword pc() const { return pc_; }
   TokenPosition token_pos() const { return token_pos_; }
+  bool HasBreakpointLocation(BreakpointLocation* breakpoint_location);
+  bool FindAndDeleteBreakpointLocation(BreakpointLocation* breakpoint_location);
+  bool HasNoBreakpointLocations() {
+    return breakpoint_locations_.length() == 0;
+  }
 
   ScriptPtr SourceCode();
   StringPtr SourceUrl();
@@ -215,8 +233,10 @@
  private:
   void VisitObjectPointers(ObjectPointerVisitor* visitor);
 
-  BreakpointLocation* bpt_location() const { return bpt_location_; }
-  void set_bpt_location(BreakpointLocation* value) { bpt_location_ = value; }
+  BreakpointLocation* FindBreakpointForDebugger(Debugger* debugger);
+  void AddBreakpointLocation(BreakpointLocation* value) {
+    breakpoint_locations_.Add(value);
+  }
 
   void set_next(CodeBreakpoint* value) { next_ = value; }
   CodeBreakpoint* next() const { return this->next_; }
@@ -230,13 +250,16 @@
   intptr_t line_number_;
   bool is_enabled_;
 
-  BreakpointLocation* bpt_location_;
+  // Breakpoint locations from different debuggers/isolates that
+  // point to this code breakpoint.
+  MallocGrowableArray<BreakpointLocation*> breakpoint_locations_;
   CodeBreakpoint* next_;
 
   UntaggedPcDescriptors::Kind breakpoint_kind_;
   CodePtr saved_value_;
 
   friend class Debugger;
+  friend class GroupDebugger;
   DISALLOW_COPY_AND_ASSIGN(CodeBreakpoint);
 };
 
@@ -473,6 +496,52 @@
   kInvalidExceptionPauseInfo
 } Dart_ExceptionPauseInfo;
 
+class GroupDebugger {
+ public:
+  explicit GroupDebugger(IsolateGroup* isolate_group);
+  ~GroupDebugger();
+
+  void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt);
+  // Returns [nullptr] if no breakpoint exists for the given address.
+  CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address);
+  BreakpointLocation* GetBreakpointLocationFor(Debugger* debugger,
+                                               uword breakpoint_address,
+                                               CodeBreakpoint** pcbpt);
+  CodePtr GetPatchedStubAddress(uword breakpoint_address);
+
+  void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location);
+
+  // Returns true if the call at address pc is patched to point to
+  // a debugger stub.
+  bool HasActiveBreakpoint(uword pc);
+  bool HasCodeBreakpointInFunction(const Function& func);
+  bool HasCodeBreakpointInCode(const Code& code);
+
+  bool HasBreakpointInFunction(const Function& func);
+  bool HasBreakpointInCode(const Code& code);
+
+  void SyncBreakpointLocation(BreakpointLocation* loc);
+  void DisableCodeBreakpointsFor(BreakpointLocation* loc);
+
+  void Pause();
+
+  void VisitObjectPointers(ObjectPointerVisitor* visitor);
+
+ private:
+  IsolateGroup* isolate_group_;
+
+  std::unique_ptr<SafepointRwLock> code_breakpoints_lock_;
+  CodeBreakpoint* code_breakpoints_;
+
+  SafepointRwLock* code_breakpoints_lock() {
+    return code_breakpoints_lock_.get();
+  }
+  void RemoveUnlinkedCodeBreakpoints();
+  void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
+
+  bool needs_breakpoint_cleanup_;
+};
+
 class Debugger {
  public:
   enum ResumeAction {
@@ -560,11 +629,6 @@
   // This may be called from different threads, therefore do not use the,
   // debugger's zone.
   bool HasBreakpoint(const Function& func, Zone* zone);
-  bool HasBreakpoint(const Code& code);
-
-  // Returns true if the call at address pc is patched to point to
-  // a debugger stub.
-  bool HasActiveBreakpoint(uword pc);
 
   // Returns a stack trace with frames corresponding to invisible functions
   // omitted. CurrentStackTrace always returns a new trace on the current stack.
@@ -595,14 +659,11 @@
   // Dart.
   void PauseDeveloper(const String& msg);
 
-  CodePtr GetPatchedStubAddress(uword breakpoint_address);
-
   void PrintBreakpointsToJSONArray(JSONArray* jsarr) const;
   void PrintSettingsToJSONObject(JSONObject* jsobj) const;
 
   static bool IsDebuggable(const Function& func);
 
-  // IsolateGroupDebugger::
   static bool IsDebugging(Thread* thread, const Function& func);
 
   intptr_t limitBreakpointId() { return next_id_; }
@@ -621,17 +682,14 @@
 
   void SendBreakpointEvent(ServiceEvent::EventKind kind, Breakpoint* bpt);
 
-  // IsolateGroupDebugger::
   void FindCompiledFunctions(const Script& script,
                              TokenPosition start_pos,
                              TokenPosition end_pos,
                              GrowableObjectArray* code_function_list);
-  // IsolateGroupDebugger::
   bool FindBestFit(const Script& script,
                    TokenPosition token_pos,
                    TokenPosition last_token_pos,
                    Function* best_fit);
-  // IsolateGroupDebugger::
   void DeoptimizeWorld();
   void NotifySingleStepping(bool value) const;
   BreakpointLocation* SetCodeBreakpoints(const Script& script,
@@ -649,24 +707,17 @@
                                     const Function& function);
   bool RemoveBreakpointFromTheList(intptr_t bp_id, BreakpointLocation** list);
   Breakpoint* GetBreakpointByIdInTheList(intptr_t id, BreakpointLocation* list);
-  void RemoveUnlinkedCodeBreakpoints();
-  void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location);
   BreakpointLocation* GetLatentBreakpoint(const String& url,
                                           intptr_t line,
                                           intptr_t column);
   void RegisterBreakpointLocation(BreakpointLocation* bpt);
-  void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
   BreakpointLocation* GetBreakpointLocation(
       const Script& script,
       TokenPosition token_pos,
       intptr_t requested_line,
       intptr_t requested_column,
       TokenPosition code_token_pos = TokenPosition::kNoSource);
-  void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt);
-  // Returns NULL if no breakpoint exists for the given address.
-  CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address);
 
-  void SyncBreakpointLocation(BreakpointLocation* loc);
   void PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt,
                                        JSONArray* jsarr) const;
 
@@ -702,6 +753,8 @@
   void SetAsyncSteppingFramePointer(DebuggerStackTrace* stack_trace);
   void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace);
 
+  GroupDebugger* group_debugger() { return isolate_->group()->debugger(); }
+
   Isolate* isolate_;
 
   // ID number generator.
@@ -709,7 +762,6 @@
 
   BreakpointLocation* latent_locations_;
   BreakpointLocation* breakpoint_locations_;
-  CodeBreakpoint* code_breakpoints_;
 
   // Tells debugger what to do when resuming execution after a breakpoint.
   ResumeAction resume_action_;
@@ -751,8 +803,6 @@
   // breakpoint.
   bool skip_next_step_;
 
-  bool needs_breakpoint_cleanup_;
-
   // We keep this breakpoint alive until after the debugger does the step over
   // async continuation machinery so that we can report that we've stopped
   // at the breakpoint.
@@ -787,4 +837,6 @@
 
 }  // namespace dart
 
+#endif  // !defined(PRODUCT)
+
 #endif  // RUNTIME_VM_DEBUGGER_H_
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 1cb8896..495d026 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -386,7 +386,12 @@
       boxed_field_list_(GrowableObjectArray::null()),
       program_lock_(new SafepointRwLock()),
       active_mutators_monitor_(new Monitor()),
-      max_active_mutators_(Scavenger::MaxMutatorThreadCount()) {
+      max_active_mutators_(Scavenger::MaxMutatorThreadCount())
+#if !defined(PRODUCT)
+      ,
+      debugger_(new GroupDebugger(this))
+#endif
+{
   FlagsCopyFrom(api_flags);
   const bool is_vm_isolate = Dart::VmIsolateNameEquals(source_->name);
   if (!is_vm_isolate) {
@@ -425,6 +430,11 @@
     }
     delete[] obfuscation_map_;
   }
+
+#if !defined(PRODUCT)
+  delete debugger_;
+  debugger_ = nullptr;
+#endif
 }
 
 void IsolateGroup::RegisterIsolate(Isolate* isolate) {
@@ -2528,7 +2538,7 @@
   }
 
   // Cache these two fields, since they are no longer available after the
-  // `delete this` further down.
+  // `delete isolate` further down.
   IsolateGroup* isolate_group = isolate->isolate_group_;
   Dart_IsolateCleanupCallback cleanup = isolate->on_cleanup_callback();
   auto callback_data = isolate->init_callback_data_;
@@ -2811,6 +2821,12 @@
 
   NOT_IN_PRECOMPILED(background_compiler()->VisitPointers(visitor));
 
+#if !defined(PRODUCT)
+  if (debugger() != nullptr) {
+    debugger()->VisitObjectPointers(visitor);
+  }
+#endif
+
 #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
   // Visit objects that are being used for isolate reload.
   if (program_reload_context() != nullptr) {
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index d146eac..90f557a 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -51,6 +51,7 @@
 class Debugger;
 class DeoptContext;
 class ExternalTypedData;
+class GroupDebugger;
 class HandleScope;
 class HandleVisitor;
 class Heap;
@@ -344,6 +345,10 @@
 #endif
   }
 
+#if !defined(PRODUCT)
+  GroupDebugger* debugger() const { return debugger_; }
+#endif
+
   IdleTimeHandler* idle_time_handler() { return &idle_time_handler_; }
 
   // Returns true if this is the first isolate registered.
@@ -938,6 +943,8 @@
   intptr_t active_mutators_ = 0;
   intptr_t waiting_mutators_ = 0;
   intptr_t max_active_mutators_ = 0;
+
+  NOT_IN_PRODUCT(GroupDebugger* debugger_ = nullptr);
 };
 
 // When an isolate sends-and-exits this class represent things that it passed
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e1e0d82..37da0f5 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -16333,17 +16333,7 @@
 #if defined(PRODUCT)
   return false;
 #else
-  // TODO(dartbug.com/36097): We might need to adjust this once we start adding
-  // debugging support to --enable-isolate-groups.
-  auto isolate_group = Thread::Current()->isolate_group();
-
-  bool has_breakpoint = false;
-  isolate_group->ForEachIsolate([&](Isolate* isolate) {
-    if (isolate->debugger()->HasBreakpoint(*this)) {
-      has_breakpoint = true;
-    }
-  });
-  return has_breakpoint;
+  return IsolateGroup::Current()->debugger()->HasBreakpointInCode(*this);
 #endif
 }
 
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 5b562ac..95d3875 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1146,7 +1146,8 @@
   StackFrame* caller_frame = iterator.NextFrame();
   ASSERT(caller_frame != NULL);
   Code& orig_stub = Code::Handle(zone);
-  orig_stub = isolate->debugger()->GetPatchedStubAddress(caller_frame->pc());
+  orig_stub =
+      isolate->group()->debugger()->GetPatchedStubAddress(caller_frame->pc());
   const Error& error =
       Error::Handle(zone, isolate->debugger()->PauseBreakpoint());
   ThrowIfError(error);
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index e56f4eae..ee7a7ec 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4309,11 +4309,12 @@
   Service::HandleEvent(&event);
 }
 
-void Service::SendEmbedderEvent(const char* stream_id,
+void Service::SendEmbedderEvent(Isolate* isolate,
+                                const char* stream_id,
                                 const char* event_kind,
                                 const uint8_t* bytes,
                                 intptr_t bytes_len) {
-  ServiceEvent event(ServiceEvent::kEmbedder);
+  ServiceEvent event(isolate, ServiceEvent::kEmbedder);
   event.set_embedder_kind(event_kind);
   event.set_embedder_stream_id(stream_id);
   event.set_bytes(bytes, bytes_len);
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 99c7e20..00a3f32 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -125,7 +125,8 @@
   static void SendEchoEvent(Isolate* isolate, const char* text);
   static void SendInspectEvent(Isolate* isolate, const Object& inspectee);
 
-  static void SendEmbedderEvent(const char* stream_id,
+  static void SendEmbedderEvent(Isolate* isolate,
+                                const char* stream_id,
                                 const char* event_kind,
                                 const uint8_t* bytes,
                                 intptr_t bytes_len);
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index 0f04e9d..96a4ef6 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -20,6 +20,7 @@
 LanguageFeatures/nnbd/local_variable_read_A01_t03: Skip # Triple shift is not implemented yet
 LanguageFeatures/nnbd/local_variable_read_A02_t03: Skip # Triple shift is not implemented yet
 LanguageFeatures/nnbd/local_variable_read_A03_t03: Skip # Triple shift is not implemented yet
+LibTest/ffi/Array/PointerArray_A01_t01: Skip # https://github.com/dart-lang/co19/issues/1018
 LibTest/io/RawDatagramSocket/*: Skip # https://github.com/dart-lang/co19/issues/195
 
 [ $compiler == dart2analyzer ]
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 1e1adac..07256d4 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -3,19 +3,19 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $compiler == dart2js ]
-Language/Expressions/Constants/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/integer_size_t04: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/literal_number_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t06: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Constants/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/integer_size_t04: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/literal_number_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t06: SkipByDesign # uses integer literal not representable as JavaScript numbert
 Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
-Language/Expressions/Numbers/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t06: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t09: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Numbers/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript number
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript number
 Language/Expressions/Object_Identity/object_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/42222#issuecomment-640431711
-Language/Expressions/Shift/integer_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Shift/integer_t02: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Shift/integer_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Shift/integer_t02: SkipByDesign # uses integer literal not representable as JavaScript numbert
 Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign
 Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # Non-JS-interop external members are not supported
 Language/Libraries_and_Scripts/Scripts/main_optional_parameters_t01: SkipByDesign # https://github.com/dart-lang/co19/issues/952
diff --git a/tests/co19/co19-dartdevc.status b/tests/co19/co19-dartdevc.status
index 7f9fab3..ebc0be6 100644
--- a/tests/co19/co19-dartdevc.status
+++ b/tests/co19/co19-dartdevc.status
@@ -52,21 +52,21 @@
 Language/Classes/Instance_Variables/definition_t02: Skip # Times out
 Language/Classes/Instance_Variables/definition_t04: Skip # Times out
 Language/Classes/Setters/instance_setter_t01: Skip # Times out
-Language/Expressions/Constants/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/integer_size_t04: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/literal_number_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t06: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Constants/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/integer_size_t04: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/literal_number_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t06: SkipByDesign # uses integer literal not representable as JavaScript numbert
 Language/Expressions/Function_Invocation/async_generator_invokation_t08: Skip # Times out
 Language/Expressions/Function_Invocation/async_generator_invokation_t10: Skip # Times out
 Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
-Language/Expressions/Numbers/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t06: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t09: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Numbers/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript numbert
 Language/Expressions/Object_Identity/object_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/42222#issuecomment-640431711
-Language/Expressions/Shift/integer_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Shift/integer_t02: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Shift/integer_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Shift/integer_t02: SkipByDesign # uses integer literal not representable as JavaScript numbert
 Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign # dart:isolate not supported.
 Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # External variables are not supported
 Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # External variables are not supported
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index d54175c..80fa459 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -42,4 +42,5 @@
 LibTest/ffi/*: SkipByDesign # dart:ffi is not supported on sim architectures
 
 [ $compiler == fasta || $runtime == dart_precompiled || $runtime == vm ]
+Language/Expressions/Numbers/syntax_t34: SkipByDesign # expects integer literal error that only occurs in JS
 LibTest/html/*: SkipByDesign # dart:html not supported on VM.
diff --git a/tests/co19_2/co19_2-dart2js.status b/tests/co19_2/co19_2-dart2js.status
index 0d620e8..63158f2 100644
--- a/tests/co19_2/co19_2-dart2js.status
+++ b/tests/co19_2/co19_2-dart2js.status
@@ -4,6 +4,8 @@
 
 [ $compiler == dart2js ]
 Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript number
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript number
 Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign
 Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # Non-JS-interop external members are not supported
 Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # Non-JS-interop external members are not supported
diff --git a/tests/co19_2/co19_2-dartdevc.status b/tests/co19_2/co19_2-dartdevc.status
index 7497344..4ce31b8 100644
--- a/tests/co19_2/co19_2-dartdevc.status
+++ b/tests/co19_2/co19_2-dartdevc.status
@@ -55,6 +55,8 @@
 Language/Expressions/Function_Invocation/async_generator_invokation_t08: Skip # Times out
 Language/Expressions/Function_Invocation/async_generator_invokation_t10: Skip # Times out
 Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript number
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript number
 Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign # dart:isolate not supported.
 Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # External variables are not supported
 Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # External variables are not supported
diff --git a/tests/co19_2/co19_2-runtime.status b/tests/co19_2/co19_2-runtime.status
index a09c881..c9cfe7d 100644
--- a/tests/co19_2/co19_2-runtime.status
+++ b/tests/co19_2/co19_2-runtime.status
@@ -33,4 +33,5 @@
 LibTest/collection/ListMixin/ListMixin_class_A01_t06: SkipSlow # Very slow on sim* architectures.
 
 [ $compiler == fasta || $runtime == dart_precompiled || $runtime == vm ]
+Language/Expressions/Numbers/syntax_t34: SkipByDesign # expects integer literal error that only occurs in JS
 LibTest/html/*: SkipByDesign # dart:html not supported on VM.
diff --git a/tests/language/const_functions/const_functions_while_statements_test.dart b/tests/language/const_functions/const_functions_while_statements_test.dart
new file mode 100644
index 0000000..59ea40c
--- /dev/null
+++ b/tests/language/const_functions/const_functions_while_statements_test.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests while statements for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+//           ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var2 = fn(3);
+//           ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn(int a) {
+  int b = a;
+  int i = 0;
+  while (i < 2) {
+    b += a;
+    i++;
+  }
+  return b;
+}
+
+const var3 = fn1(2);
+//           ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var4 = fn1(3);
+//           ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn1(int a) {
+  int b = a;
+  while (true) {
+    b *= 3;
+    if (b > 10) return b;
+  }
+}
+
+void main() {
+  Expect.equals(var1, 6);
+  Expect.equals(var2, 9);
+  Expect.equals(var3, 18);
+  Expect.equals(var4, 27);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 5a93430..2965fb3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 148
+PRERELEASE 149
 PRERELEASE_PATCH 0
\ No newline at end of file