Version 2.8.0-dev.2.0

Merge commit 'd1e11f088108b5ec90fcc2faef7ed1437b018f47' into dev
diff --git a/DEPS b/DEPS
index f4b02a2..7b089e9 100644
--- a/DEPS
+++ b/DEPS
@@ -39,7 +39,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": "9e3512c90f9110353909a0da71a871e33324034e",
+  "co19_rev": "6d84d8db719f2076e0c2bbc41db9297e803ab445",
   "co19_2_rev": "368bfa9e877a2df003547f64bb17e30596af10c7",
 
   # As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 580aa15..7a8e5b1 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -8212,6 +8212,56 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)>
+    templateValueForRequiredParameterNotProvidedError =
+    const Template<Message Function(String name)>(
+        messageTemplate:
+            r"""Required named parameter '#name' must be provided.""",
+        withArguments: _withArgumentsValueForRequiredParameterNotProvidedError);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeValueForRequiredParameterNotProvidedError =
+    const Code<Message Function(String name)>(
+  "ValueForRequiredParameterNotProvidedError",
+  templateValueForRequiredParameterNotProvidedError,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsValueForRequiredParameterNotProvidedError(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeValueForRequiredParameterNotProvidedError,
+      message: """Required named parameter '${name}' must be provided.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)>
+    templateValueForRequiredParameterNotProvidedWarning =
+    const Template<Message Function(String name)>(
+        messageTemplate: r"""Missing required named parameter '#name'.""",
+        withArguments:
+            _withArgumentsValueForRequiredParameterNotProvidedWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeValueForRequiredParameterNotProvidedWarning =
+    const Code<Message Function(String name)>(
+        "ValueForRequiredParameterNotProvidedWarning",
+        templateValueForRequiredParameterNotProvidedWarning,
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsValueForRequiredParameterNotProvidedWarning(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeValueForRequiredParameterNotProvidedWarning,
+      message: """Missing required named parameter '${name}'.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeVarAsTypeName = messageVarAsTypeName;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
index b1faa3f..2f33dac 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
@@ -2350,6 +2350,11 @@
     }
     // At this point, `token` is beforeName.
 
+    // Recovery: Inserted ! after method name.
+    if (optional('!', next.next)) {
+      next = next.next;
+    }
+
     next = next.next;
     value = next.stringValue;
     if (getOrSet != null ||
@@ -2411,7 +2416,7 @@
       }
     }
     if (typeInfo == noType) {
-      if (varFinalOrConst == null && lateToken == null) {
+      if (varFinalOrConst == null) {
         reportRecoverableError(name, codes.messageMissingConstFinalVarOrType);
       }
     } else {
@@ -2520,6 +2525,11 @@
   }
 
   Token parseMethodTypeVar(Token name) {
+    if (optional('!', name.next)) {
+      // Recovery
+      name = name.next;
+      reportRecoverableErrorWithToken(name, codes.templateUnexpectedToken);
+    }
     if (!optional('<', name.next)) {
       return noTypeParamOrArg.parseVariables(name, this);
     }
@@ -5180,8 +5190,20 @@
     return token;
   }
 
+  /// Calls handleNonNullAssertExpression when the token points to `!<`.
+  /// Returns the input token if it doesn't point to `!<`, or the next token
+  /// (ready for parsing by e.g. computeMethodTypeArguments) if it does.
+  Token parseBangBeforeTypeArguments(Token token) {
+    if (optional('!', token.next) && optional('<', token.next.next)) {
+      token = token.next;
+      listener.handleNonNullAssertExpression(token);
+    }
+    return token;
+  }
+
   Token parseSend(Token token, IdentifierContext context) {
     Token beginToken = token = ensureIdentifier(token, context);
+    token = parseBangBeforeTypeArguments(token);
     TypeParamOrArgInfo typeArg = computeMethodTypeArguments(token);
     if (typeArg != noTypeParamOrArg) {
       token = typeArg.parseArguments(token, this);
diff --git a/pkg/_fe_analyzer_shared/lib/src/testing/annotated_code_helper.dart b/pkg/_fe_analyzer_shared/lib/src/testing/annotated_code_helper.dart
index d57f969..c9a083f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/testing/annotated_code_helper.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/testing/annotated_code_helper.dart
@@ -12,6 +12,11 @@
 final Pattern commentEnd = new RegExp(r'\*/\s*');
 
 class Annotation {
+  /// The index of the (corresponding) annotation in the annotated code test, or
+  /// `null` if the annotation doesn't correspond to an annotation in the
+  /// annotated code.
+  final int index;
+
   /// 1-based line number of the annotation.
   final int lineNo;
 
@@ -30,13 +35,13 @@
   /// The annotation end text.
   final String suffix;
 
-  Annotation(this.lineNo, this.columnNo, this.offset, this.prefix, this.text,
-      this.suffix)
+  Annotation(this.index, this.lineNo, this.columnNo, this.offset, this.prefix,
+      this.text, this.suffix)
       : assert(offset != null);
 
   String toString() =>
-      'Annotation(lineNo=$lineNo,columnNo=$columnNo,offset=$offset,'
-      'prefix=$prefix,text=$text,suffix=$suffix)';
+      'Annotation(index=$index,lineNo=$lineNo,columnNo=$columnNo,'
+      'offset=$offset,prefix=$prefix,text=$text,suffix=$suffix)';
 }
 
 /// A source code text with annotated positions.
@@ -98,8 +103,8 @@
               annotatedCode.substring(startMatch.start, startMatch.end);
           String text = annotatedCode.substring(startMatch.end, endMatch.start);
           String suffix = annotatedCode.substring(endMatch.start, endMatch.end);
-          annotations.add(
-              new Annotation(lineNo, columnNo, offset, prefix, text, suffix));
+          annotations.add(new Annotation(annotations.length, lineNo, columnNo,
+              offset, prefix, text, suffix));
           index = endMatch.end;
           continue;
         }
@@ -171,8 +176,8 @@
       int lineNo, int columnNo, String prefix, String text, String suffix) {
     _ensureLineStarts();
     int offset = _lineStarts[lineNo - 1] + (columnNo - 1);
-    annotations
-        .add(new Annotation(lineNo, columnNo, offset, prefix, text, suffix));
+    annotations.add(new Annotation(
+        annotations.length, lineNo, columnNo, offset, prefix, text, suffix));
   }
 
   int get lineCount {
@@ -213,7 +218,22 @@
   String toText() {
     StringBuffer sb = new StringBuffer();
     List<Annotation> list = annotations.toList()
-      ..sort((a, b) => a.offset.compareTo(b.offset));
+      ..sort((a, b) {
+        int result = a.offset.compareTo(b.offset);
+        if (result == 0) {
+          if (a.index != null && b.index != null) {
+            result = a.index.compareTo(b.index);
+          } else if (a.index != null) {
+            result = -1;
+          } else if (b.index != null) {
+            result = 1;
+          }
+        }
+        if (result == 0) {
+          result = annotations.indexOf(a).compareTo(annotations.indexOf(b));
+        }
+        return result;
+      });
     int offset = 0;
     for (Annotation annotation in list) {
       sb.write(sourceCode.substring(offset, annotation.offset));
@@ -258,6 +278,7 @@
       if (prefixSet.containsAll(markers)) {
         for (String part in markers) {
           Annotation subAnnotation = new Annotation(
+              annotation.index,
               annotation.lineNo,
               annotation.columnNo,
               annotation.offset,
diff --git a/pkg/_fe_analyzer_shared/lib/src/testing/id_generation.dart b/pkg/_fe_analyzer_shared/lib/src/testing/id_generation.dart
index f722b52..6800762 100644
--- a/pkg/_fe_analyzer_shared/lib/src/testing/id_generation.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/testing/id_generation.dart
@@ -11,14 +11,19 @@
     Map<String, MemberAnnotations<IdValue>> expectedMaps,
     Uri mainUri,
     Map<String, Map<Uri, Map<Id, ActualData<T>>>> actualData,
-    DataInterpreter<T> dataInterpreter) {
+    DataInterpreter<T> dataInterpreter,
+    {Annotation Function(Annotation expected, Annotation actual) createDiff}) {
   Set<Uri> uriSet = {};
   Set<String> actualMarkers = actualData.keys.toSet();
   Map<Uri, Map<Id, Map<String, IdValue>>> idValuePerUri = {};
   Map<Uri, Map<Id, Map<String, ActualData<T>>>> actualDataPerUri = {};
 
   void addData(String marker, Uri uri, Map<Id, IdValue> data) {
-    assert(uri != null);
+    if (uri == null) {
+      // TODO(johnniwinther): Avoid `null` URIs.
+      assert(data.isEmpty, "Non-empty data without uri: $data");
+      return;
+    }
     uriSet.add(uri);
     Map<Id, Map<String, IdValue>> idValuePerId = idValuePerUri[uri] ??= {};
     data.forEach((Id id, IdValue value) {
@@ -37,7 +42,11 @@
   actualData
       .forEach((String marker, Map<Uri, Map<Id, ActualData<T>>> dataPerUri) {
     dataPerUri.forEach((Uri uri, Map<Id, ActualData<T>> dataMap) {
-      assert(uri != null);
+      if (uri == null) {
+        // TODO(johnniwinther): Avoid `null` URIs.
+        assert(dataMap.isEmpty, "Non-empty data for `null` uri: $dataMap");
+        return;
+      }
       uriSet.add(uri);
       dataMap.forEach((Id id, ActualData<T> data) {
         Map<Id, Map<String, ActualData<T>>> actualDataPerId =
@@ -54,9 +63,12 @@
     Map<Id, Map<String, IdValue>> idValuePerId = idValuePerUri[uri] ?? {};
     Map<Id, Map<String, ActualData<T>>> actualDataPerId =
         actualDataPerUri[uri] ?? {};
-    result[uri] = _computeAnnotations(annotatedCode[uri], expectedMaps.keys,
-        actualMarkers, idValuePerId, actualDataPerId, dataInterpreter,
-        sortMarkers: false);
+    AnnotatedCode code = annotatedCode[uri];
+    assert(
+        code != null, "No annotated code for ${uri} in ${annotatedCode.keys}");
+    result[uri] = _computeAnnotations(code, expectedMaps.keys, actualMarkers,
+        idValuePerId, actualDataPerId, dataInterpreter,
+        sortMarkers: false, createDiff: createDiff);
   }
   return result;
 }
@@ -70,7 +82,8 @@
     DataInterpreter<T> dataInterpreter,
     {String defaultPrefix: '/*',
     String defaultSuffix: '*/',
-    bool sortMarkers: true}) {
+    bool sortMarkers: true,
+    Annotation Function(Annotation expected, Annotation actual) createDiff}) {
   assert(annotatedCode != null);
 
   Annotation createAnnotationFromData(
@@ -117,6 +130,7 @@
     }
 
     return new Annotation(
+        annotation?.index,
         annotation?.lineNo ?? -1,
         annotation?.columnNo ?? -1,
         offset,
@@ -136,27 +150,35 @@
     for (String marker in supportedMarkers) {
       IdValue idValue = idValuePerMarker[marker];
       ActualData<T> actualData = actualDataPerMarker[marker];
+      Annotation expectedAnnotation;
+      Annotation actualAnnotation;
       if (idValue != null && actualData != null) {
         if (dataInterpreter.isAsExpected(actualData.value, idValue.value) ==
             null) {
           // Use existing annotation.
-          newAnnotationsPerMarker[marker] = idValue.annotation;
+          expectedAnnotation = actualAnnotation = idValue.annotation;
         } else {
-          newAnnotationsPerMarker[marker] =
+          expectedAnnotation = idValue.annotation;
+          actualAnnotation =
               createAnnotationFromData(actualData, idValue.annotation);
         }
       } else if (idValue != null && !actualMarkers.contains(marker)) {
         // Use existing annotation if no actual data is provided for this
         // marker.
-        newAnnotationsPerMarker[marker] = idValue.annotation;
+        expectedAnnotation = actualAnnotation = idValue.annotation;
       } else if (actualData != null) {
         if (dataInterpreter.isAsExpected(actualData.value, null) != null) {
           // Insert annotation if the actual value is not equivalent to an
           // empty value.
-          newAnnotationsPerMarker[marker] =
-              createAnnotationFromData(actualData, null);
+          actualAnnotation = createAnnotationFromData(actualData, null);
         }
       }
+      Annotation annotation = createDiff != null
+          ? createDiff(expectedAnnotation, actualAnnotation)
+          : actualAnnotation;
+      if (annotation != null) {
+        newAnnotationsPerMarker[marker] = annotation;
+      }
     }
 
     Map<String, Map<String, Annotation>> groupedByText = {};
@@ -180,6 +202,7 @@
         }
         Annotation firstAnnotation = annotations.values.first;
         result.add(new Annotation(
+            firstAnnotation.index,
             firstAnnotation.lineNo,
             firstAnnotation.columnNo,
             firstAnnotation.offset,
diff --git a/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart b/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart
index b3e4d23..c9dbd67 100644
--- a/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart
@@ -110,6 +110,48 @@
   return '${colorizeDelimiter(start)}$text${colorizeDelimiter(end)}';
 }
 
+/// Creates an annotation that shows the difference between [expected] and
+/// [actual].
+Annotation createAnnotationsDiff(Annotation expected, Annotation actual) {
+  if (identical(expected, actual)) return null;
+  if (expected != null && actual != null) {
+    return new Annotation(
+        expected.index,
+        expected.lineNo,
+        expected.columnNo,
+        expected.offset,
+        expected.prefix,
+        '${colorizeExpected(expected.text)}'
+        '${colorizeDelimiter(' | ')}'
+        '${colorizeActual(actual.text)}',
+        expected.suffix);
+  } else if (expected != null) {
+    return new Annotation(
+        expected.index,
+        expected.lineNo,
+        expected.columnNo,
+        expected.offset,
+        expected.prefix,
+        '${colorizeExpected(expected.text)}'
+        '${colorizeDelimiter(' | ')}'
+        '${colorizeActual('---')}',
+        expected.suffix);
+  } else if (actual != null) {
+    return new Annotation(
+        actual.index,
+        actual.lineNo,
+        actual.columnNo,
+        actual.offset,
+        actual.prefix,
+        '${colorizeExpected('---')}'
+        '${colorizeDelimiter(' | ')}'
+        '${colorizeActual(actual.text)}',
+        actual.suffix);
+  } else {
+    return null;
+  }
+}
+
 /// Encapsulates the member data computed for each source file of interest.
 /// It's a glorified wrapper around a map of maps, but written this way to
 /// provide a little more information about what it's doing. [DataType] refers
@@ -442,72 +484,6 @@
   return sb.toString();
 }
 
-/// Computed and expected data for an annotated test. This is used for checking
-/// and displaying results of an annotated test.
-class IdData<T> {
-  final Map<Uri, AnnotatedCode> code;
-  final MemberAnnotations<IdValue> expectedMaps;
-  final CompiledData<T> _compiledData;
-  final MemberAnnotations<ActualData<T>> _actualMaps = new MemberAnnotations();
-
-  IdData(this.code, this.expectedMaps, this._compiledData) {
-    for (Uri uri in code.keys) {
-      _actualMaps[uri] = _compiledData.actualMaps[uri] ?? <Id, ActualData<T>>{};
-    }
-    _actualMaps.globalData.addAll(_compiledData.globalData);
-  }
-
-  Uri get mainUri => _compiledData.mainUri;
-  MemberAnnotations<ActualData<T>> get actualMaps => _actualMaps;
-
-  String actualCode(Uri uri) {
-    Map<int, List<String>> annotations = <int, List<String>>{};
-    actualMaps[uri].forEach((Id id, ActualData<T> data) {
-      annotations.putIfAbsent(data.offset, () => []).add('${data.value}');
-    });
-    return withAnnotations(code[uri].sourceCode, annotations);
-  }
-
-  String diffCode(Uri uri, DataInterpreter<T> dataValidator) {
-    Map<int, List<String>> annotations = <int, List<String>>{};
-    actualMaps[uri].forEach((Id id, ActualData<T> data) {
-      IdValue expectedValue = expectedMaps[uri][id];
-      T actualValue = data.value;
-      String unexpectedMessage =
-          dataValidator.isAsExpected(actualValue, expectedValue?.value);
-      if (unexpectedMessage != null) {
-        String expected = expectedValue?.toString() ?? '';
-        String actual = dataValidator.getText(actualValue);
-        int offset = getOffsetFromId(id, uri);
-        if (offset != null) {
-          String value1 = '${expected}';
-          String value2 = IdValue.idToString(id, '${actual}');
-          annotations
-              .putIfAbsent(offset, () => [])
-              .add(colorizeDiff(value1, ' | ', value2));
-        }
-      }
-    });
-    expectedMaps[uri].forEach((Id id, IdValue expected) {
-      if (!actualMaps[uri].containsKey(id)) {
-        int offset = getOffsetFromId(id, uri);
-        if (offset != null) {
-          String value1 = '${expected}';
-          String value2 = '---';
-          annotations
-              .putIfAbsent(offset, () => [])
-              .add(colorizeDiff(value1, ' | ', value2));
-        }
-      }
-    });
-    return withAnnotations(code[uri].sourceCode, annotations);
-  }
-
-  int getOffsetFromId(Id id, Uri uri) {
-    return _compiledData.getOffsetFromId(id, uri);
-  }
-}
-
 /// Checks [compiledData] against the expected data in [expectedMaps] derived
 /// from [code].
 Future<TestResult<T>> checkCode<T>(
@@ -521,15 +497,15 @@
     bool fatalErrors: true,
     bool succinct: false,
     void onFailure(String message)}) async {
-  IdData<T> data = new IdData<T>(code, expectedMaps, compiledData);
   bool hasFailure = false;
   Set<Uri> neededDiffs = new Set<Uri>();
 
   void checkActualMap(
       Map<Id, ActualData<T>> actualMap, Map<Id, IdValue> expectedMap,
       [Uri uri]) {
+    expectedMap ??= {};
     bool hasLocalFailure = false;
-    actualMap.forEach((Id id, ActualData<T> actualData) {
+    actualMap?.forEach((Id id, ActualData<T> actualData) {
       T actual = actualData.value;
       String actualText = dataInterpreter.getText(actual);
 
@@ -582,16 +558,17 @@
     }
   }
 
-  data.actualMaps.forEach((Uri uri, Map<Id, ActualData<T>> actualMap) {
-    checkActualMap(actualMap, data.expectedMaps[uri], uri);
+  compiledData.actualMaps.forEach((Uri uri, Map<Id, ActualData<T>> actualMap) {
+    checkActualMap(actualMap, expectedMaps[uri], uri);
   });
-  checkActualMap(data.actualMaps.globalData, data.expectedMaps.globalData);
+  checkActualMap(compiledData.globalData, expectedMaps.globalData);
 
   Set<Id> missingIds = new Set<Id>();
   void checkMissing(
       Map<Id, IdValue> expectedMap, Map<Id, ActualData<T>> actualMap,
       [Uri uri]) {
-    expectedMap.forEach((Id id, IdValue expected) {
+    actualMap ??= {};
+    expectedMap?.forEach((Id id, IdValue expected) {
       if (!actualMap.containsKey(id)) {
         missingIds.add(id);
         String message = 'MISSING $modeName DATA for ${id.descriptor}: '
@@ -610,15 +587,27 @@
     }
   }
 
-  data.expectedMaps.forEach((Uri uri, Map<Id, IdValue> expectedMap) {
-    checkMissing(expectedMap, data.actualMaps[uri], uri);
+  expectedMaps.forEach((Uri uri, Map<Id, IdValue> expectedMap) {
+    checkMissing(expectedMap, compiledData.actualMaps[uri], uri);
   });
-  checkMissing(data.expectedMaps.globalData, data.actualMaps.globalData);
+  checkMissing(expectedMaps.globalData, compiledData.globalData);
   if (!succinct) {
-    for (Uri uri in neededDiffs) {
-      print('--annotations diff [${uri.pathSegments.last}]-------------');
-      print(data.diffCode(uri, dataInterpreter));
-      print('----------------------------------------------------------');
+    if (neededDiffs.isNotEmpty) {
+      Map<Uri, List<Annotation>> annotations = computeAnnotationsPerUri(
+          code,
+          {'dummyMarker': expectedMaps},
+          compiledData.mainUri,
+          {'dummyMarker': compiledData.actualMaps},
+          dataInterpreter,
+          createDiff: createAnnotationsDiff);
+      for (Uri uri in neededDiffs) {
+        print('--annotations diff [${uri.pathSegments.last}]-------------');
+        AnnotatedCode annotatedCode = code[uri];
+        print(new AnnotatedCode(annotatedCode.annotatedCode,
+                annotatedCode.sourceCode, annotations[uri])
+            .toText());
+        print('----------------------------------------------------------');
+      }
     }
   }
   if (missingIds.isNotEmpty) {
diff --git a/pkg/_fe_analyzer_shared/test/annotated_code_helper_test.dart b/pkg/_fe_analyzer_shared/test/annotated_code_helper_test.dart
index a36a670..deafff8 100644
--- a/pkg/_fe_analyzer_shared/test/annotated_code_helper_test.dart
+++ b/pkg/_fe_analyzer_shared/test/annotated_code_helper_test.dart
@@ -8,23 +8,19 @@
 import 'package:_fe_analyzer_shared/src/testing/id_generation.dart';
 import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
 
-main() async {
-  await testDir('pkg/_fe_analyzer_shared/test/constants/data', sharedMarkers);
-  await testDir(
-      'pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data',
+main() {
+  testDir('pkg/_fe_analyzer_shared/test/constants/data', sharedMarkers);
+  testDir('pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data',
       cfeAnalyzerMarkers);
-  await testDir(
-      'pkg/_fe_analyzer_shared/test/flow_analysis/definite_assignment/data',
+  testDir('pkg/_fe_analyzer_shared/test/flow_analysis/definite_assignment/data',
       cfeAnalyzerMarkers);
-  await testDir('pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data',
+  testDir('pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data',
       cfeAnalyzerMarkers);
-  await testDir('pkg/_fe_analyzer_shared/test/flow_analysis/reachability/data',
+  testDir('pkg/_fe_analyzer_shared/test/flow_analysis/reachability/data',
       cfeAnalyzerMarkers);
-  await testDir(
-      'pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data',
+  testDir('pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data',
       cfeAnalyzerMarkers);
-  await testDir(
-      'pkg/_fe_analyzer_shared/test/inheritance/data', cfeAnalyzerMarkers);
+  testDir('pkg/_fe_analyzer_shared/test/inheritance/data', cfeAnalyzerMarkers);
 }
 
 void expectStringEquals(String value1, String value2) {
@@ -33,7 +29,7 @@
   }
 }
 
-Future<void> testDir(String dataDirPath, List<String> supportedMarkers) {
+void testDir(String dataDirPath, List<String> supportedMarkers) {
   Directory dataDir = Directory(dataDirPath);
   String relativeDir = dataDir.uri.path.replaceAll(Uri.base.path, '');
   print('Data dir: ${relativeDir}');
diff --git a/pkg/_fe_analyzer_shared/test/id_generation_test.dart b/pkg/_fe_analyzer_shared/test/id_generation_test.dart
index bc772d7..6010ed5 100644
--- a/pkg/_fe_analyzer_shared/test/id_generation_test.dart
+++ b/pkg/_fe_analyzer_shared/test/id_generation_test.dart
@@ -31,10 +31,8 @@
 some code/*test*/some more code
 ''');
   testString('/*a.test1*//*b.test2*//*c.test3*/');
-  testString('/*b.test2*//*a.test1*//*c.test3*/',
-      expectedResult: '/*a.test1*//*b.test2*//*c.test3*/');
-  testString('/*a.test1*//*c.test3*//*b.test2*/',
-      expectedResult: '/*a.test1*//*b.test2*//*c.test3*/');
+  testString('/*b.test2*//*a.test1*//*c.test3*/');
+  testString('/*a.test1*//*c.test3*//*b.test2*/');
 
   testString('some code',
       actualData: {
diff --git a/pkg/analysis_server/analysis_options.yaml b/pkg/analysis_server/analysis_options.yaml
index 3fbb3af..f20b958 100644
--- a/pkg/analysis_server/analysis_options.yaml
+++ b/pkg/analysis_server/analysis_options.yaml
@@ -12,7 +12,6 @@
     # Ignoring "style" lint rules from pedantic for now. There are pre-existing
     # violations that need to be cleaned up. Each one can be cleaned up and
     # enabled according to the value provided.
-    avoid_init_to_null: ignore
     avoid_return_types_on_setters: ignore
     empty_catches: ignore
     prefer_contains: ignore
@@ -38,9 +37,9 @@
     - camel_case_extensions
     #- omit_local_variable_types # 8650
     - prefer_adjacent_string_concatenation
-    #- prefer_collection_literals # 130
+    - prefer_collection_literals
     - prefer_conditional_assignment
-    #- prefer_final_fields # 35
+    - prefer_final_fields
     - prefer_for_elements_to_map_fromIterable
     - prefer_generic_function_type_aliases
     #- prefer_if_null_operators # 22
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index c477bdc..e20e28a 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -219,7 +219,7 @@
 
     CompoundBenchMarkResult combined = CompoundBenchMarkResult(name);
     List<String> keys =
-        (Set<String>()..addAll(results.keys)..addAll(o.results.keys)).toList();
+        (<String>{}..addAll(results.keys)..addAll(o.results.keys)).toList();
 
     for (String key in keys) {
       combined.add(key, _combine(results[key], o.results[key]));
diff --git a/pkg/analysis_server/benchmark/integration/driver.dart b/pkg/analysis_server/benchmark/integration/driver.dart
index 29366ea..6787fb8 100644
--- a/pkg/analysis_server/benchmark/integration/driver.dart
+++ b/pkg/analysis_server/benchmark/integration/driver.dart
@@ -153,7 +153,7 @@
 class Measurement {
   final String tag;
   final bool notification;
-  final List<Duration> elapsedTimes = List<Duration>();
+  final List<Duration> elapsedTimes = <Duration>[];
   int errorCount = 0;
   int unexpectedResultCount = 0;
 
@@ -223,7 +223,7 @@
  * while running the analysis server
  */
 class Results {
-  Map<String, Measurement> measurements = Map<String, Measurement>();
+  Map<String, Measurement> measurements = <String, Measurement>{};
 
   /**
    * Display results on stdout.
diff --git a/pkg/analysis_server/benchmark/integration/input_converter.dart b/pkg/analysis_server/benchmark/integration/input_converter.dart
index 60b8651..0368037 100644
--- a/pkg/analysis_server/benchmark/integration/input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/input_converter.dart
@@ -23,7 +23,7 @@
 abstract class CommonInputConverter extends Converter<String, Operation> {
   static final ERROR_PREFIX = 'Server responded with an error: ';
   final Logger logger = Logger('InstrumentationInputConverter');
-  final Set<String> eventsSeen = Set<String>();
+  final Set<String> eventsSeen = <String>{};
 
   /**
    * A mapping from request/response id to request json
@@ -256,7 +256,7 @@
       return result;
     }
     if (json is Map) {
-      Map<String, dynamic> result = Map<String, dynamic>();
+      Map<String, dynamic> result = <String, dynamic>{};
       json.forEach((origKey, value) {
         result[translateSrcPaths(origKey)] = translateSrcPaths(value);
       });
diff --git a/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart b/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
index 9dfd1eb..588b3fc 100644
--- a/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
@@ -18,14 +18,14 @@
  * into a series of operations to be sent to the analysis server.
  */
 class InstrumentationInputConverter extends CommonInputConverter {
-  final Set<String> codesSeen = Set<String>();
+  final Set<String> codesSeen = <String>{};
 
   /**
    * [readBuffer] holds the contents of the file being read from disk
    * as recorded in the instrumentation log
    * or `null` if not converting a "Read" entry.
    */
-  StringBuffer readBuffer = null;
+  StringBuffer readBuffer;
 
   InstrumentationInputConverter(String tmpSrcDirPath, PathMap srcPathMap)
       : super(tmpSrcDirPath, srcPathMap);
@@ -106,7 +106,7 @@
    * Extract fields from the given [line].
    */
   static List<String> _parseFields(String line) {
-    List<String> fields = List<String>();
+    List<String> fields = <String>[];
     int index = 0;
     StringBuffer sb = StringBuffer();
     while (index < line.length) {
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 8758c49..d01715f 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -169,7 +169,7 @@
     this.instrumentationService, {
     this.requestStatistics,
     DiagnosticServer diagnosticServer,
-    this.detachableFileSystemManager = null,
+    this.detachableFileSystemManager,
   }) : super(options, diagnosticServer, baseResourceProvider) {
     notificationManager = NotificationManager(channel, resourceProvider);
 
@@ -905,8 +905,8 @@
 
   @override
   ContextBuilder createContextBuilder(Folder folder, AnalysisOptions options) {
-    String defaultPackageFilePath = null;
-    String defaultPackagesDirectoryPath = null;
+    String defaultPackageFilePath;
+    String defaultPackagesDirectoryPath;
     String path = (analysisServer.contextManager as ContextManagerImpl)
         .normalizedPackageRoots[folder.path];
     if (path != null) {
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index dac52410..7f38fad 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -72,7 +72,7 @@
   final ServerPerformance performanceDuringStartup = ServerPerformance();
 
   /// The set of the files that are currently priority.
-  final Set<String> priorityFiles = Set<String>();
+  final Set<String> priorityFiles = <String>{};
 
   final List<String> analyzableFilePatterns = <String>[
     '**/*.${AnalysisEngine.SUFFIX_DART}',
@@ -92,7 +92,7 @@
 
   /// A list of the globs used to determine which files should be analyzed. The
   /// list is lazily created and should be accessed using [analyzedFilesGlobs].
-  List<Glob> _analyzedFilesGlobs = null;
+  List<Glob> _analyzedFilesGlobs;
 
   AbstractAnalysisServer(this.options, this.diagnosticServer,
       ResourceProvider baseResourceProvider)
diff --git a/pkg/analysis_server/lib/src/computer/computer_closingLabels.dart b/pkg/analysis_server/lib/src/computer/computer_closingLabels.dart
index a7577cd..c4b28bf 100644
--- a/pkg/analysis_server/lib/src/computer/computer_closingLabels.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_closingLabels.dart
@@ -15,8 +15,8 @@
   final LineInfo _lineInfo;
   final CompilationUnit _unit;
   final List<ClosingLabel> _closingLabels = [];
-  final Set<ClosingLabel> hasNestingSet = Set();
-  final Set<ClosingLabel> isSingleLineSet = Set();
+  final Set<ClosingLabel> hasNestingSet = {};
+  final Set<ClosingLabel> isSingleLineSet = {};
 
   DartUnitClosingLabelsComputer(this._lineInfo, this._unit);
 
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 4a1c9b1..7d04309 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -33,7 +33,7 @@
     while (token != null && token.type != TokenType.EOF) {
       Token commentToken = token.precedingComments;
       while (commentToken != null) {
-        HighlightRegionType highlightType = null;
+        HighlightRegionType highlightType;
         if (commentToken.type == TokenType.MULTI_LINE_COMMENT) {
           if (commentToken.lexeme.startsWith('/**')) {
             highlightType = HighlightRegionType.COMMENT_DOCUMENTATION;
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart b/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
index 11b1bf2..25c43d0 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
@@ -33,7 +33,7 @@
     while (token != null && token.type != TokenType.EOF) {
       Token commentToken = token.precedingComments;
       while (commentToken != null) {
-        HighlightRegionType highlightType = null;
+        HighlightRegionType highlightType;
         if (commentToken.type == TokenType.MULTI_LINE_COMMENT) {
           if (commentToken.lexeme.startsWith('/**')) {
             highlightType = HighlightRegionType.COMMENT_DOCUMENTATION;
diff --git a/pkg/analysis_server/lib/src/computer/computer_hover.dart b/pkg/analysis_server/lib/src/computer/computer_hover.dart
index 2689de5..b955006 100644
--- a/pkg/analysis_server/lib/src/computer/computer_hover.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_hover.dart
@@ -24,6 +24,10 @@
 
   DartUnitHoverComputer(this._dartdocInfo, this._unit, this._offset);
 
+  bool get _isNonNullableByDefault {
+    return _unit.declaredElement.library.isNonNullableByDefault;
+  }
+
   /**
    * Returns the computed hover, maybe `null`.
    */
@@ -63,7 +67,7 @@
           }
         }
         // description
-        hover.elementDescription = element.toString();
+        hover.elementDescription = _elementDisplayString(element);
         if (node is InstanceCreationExpression && node.keyword == null) {
           String prefix = node.isConst ? '(const) ' : '(new) ';
           hover.elementDescription = prefix + hover.elementDescription;
@@ -106,7 +110,9 @@
         hover.dartdoc = computeDocumentation(_dartdocInfo, element);
       }
       // parameter
-      hover.parameter = _safeToString(expression.staticParameterElement);
+      hover.parameter = _elementDisplayString(
+        expression.staticParameterElement,
+      );
       // types
       {
         AstNode parent = expression.parent;
@@ -120,7 +126,7 @@
             staticType = null;
           }
         }
-        hover.staticType = _safeToString(staticType);
+        hover.staticType = _typeDisplayString(staticType);
       }
       // done
       return hover;
@@ -129,6 +135,16 @@
     return null;
   }
 
+  String _elementDisplayString(Element element) {
+    return element?.getDisplayString(
+      withNullability: _isNonNullableByDefault,
+    );
+  }
+
+  String _typeDisplayString(DartType type) {
+    return type?.getDisplayString(withNullability: _isNonNullableByDefault);
+  }
+
   static String computeDocumentation(
       DartdocDirectiveInfo dartdocInfo, Element element) {
     // TODO(dantup) We're reusing this in parameter information - move it
@@ -178,6 +194,4 @@
     }
     return node.staticType;
   }
-
-  static String _safeToString(obj) => obj?.toString();
 }
diff --git a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
index f59e3de..48070f8 100644
--- a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
+++ b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
@@ -215,7 +215,7 @@
       return false;
     }
 
-    ImportDirective preferredDirective = null;
+    ImportDirective preferredDirective;
     int bestEditCount = -1;
     bool deleteHide = false;
     bool addShow = false;
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 473cf4b..1039196 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 import 'dart:collection';
-import 'dart:convert';
 import 'dart:core';
 
 import 'package:analysis_server/src/plugin/notification_manager.dart';
@@ -14,6 +13,7 @@
 import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
 import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/context/context_root.dart';
+import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/file_system/file_system.dart';
 import 'package:analyzer/src/generated/engine.dart';
@@ -28,13 +28,9 @@
 import 'package:analyzer/src/source/path_filter.dart';
 import 'package:analyzer/src/task/options.dart';
 import 'package:analyzer/src/util/glob.dart';
-import 'package:analyzer/src/util/uri.dart';
 import 'package:analyzer/src/util/yaml.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
 import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
-import 'package:package_config/packages.dart';
-import 'package:package_config/packages_file.dart' as pkgfile show parse;
-import 'package:package_config/src/packages_impl.dart' show MapPackages;
 import 'package:path/path.dart' as pathos;
 import 'package:watcher/watcher.dart';
 import 'package:yaml/yaml.dart';
@@ -120,7 +116,7 @@
    * be watched for changes.  I believe the use case for watching these files
    * is no longer relevant.
    */
-  Set<String> _dependencies = Set<String>();
+  Set<String> _dependencies = <String>{};
 
   /**
    * The analysis driver that was created for the [folder].
@@ -864,7 +860,7 @@
         _isInTopLevelDocDir(info.folder.path, folder.path)) {
       return;
     }
-    List<Resource> children = null;
+    List<Resource> children;
     try {
       children = folder.getChildren();
     } on FileSystemException {
@@ -1062,7 +1058,7 @@
       // TODO(paulberry): We shouldn't be using JavaFile here because it
       // makes the code untestable (see dartbug.com/23909).
       JavaFile packagesDirOrFile = JavaFile(packageRoot);
-      Map<String, List<Folder>> packageMap = Map<String, List<Folder>>();
+      Map<String, List<Folder>> packageMap = <String, List<Folder>>{};
       if (packagesDirOrFile.isDirectory()) {
         for (JavaFile file in packagesDirOrFile.listFiles()) {
           // Ensure symlinks in packages directory are canonicalized
@@ -1083,7 +1079,10 @@
         return PackageMapDisposition(packageMap, packageRoot: packageRoot);
       } else if (packagesDirOrFile.isFile()) {
         File packageSpecFile = resourceProvider.getFile(packageRoot);
-        Packages packages = _readPackagespec(packageSpecFile);
+        Packages packages = parseDotPackagesFile(
+          resourceProvider,
+          packageSpecFile,
+        );
         if (packages != null) {
           return PackagesFileDisposition(packages);
         }
@@ -1097,7 +1096,10 @@
     } else {
       // Try .packages first.
       if (pathContext.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
-        Packages packages = _readPackagespec(packagespecFile);
+        Packages packages = parseDotPackagesFile(
+          resourceProvider,
+          packagespecFile,
+        );
         return PackagesFileDisposition(packages);
       }
 
@@ -1118,9 +1120,13 @@
    * file for code being analyzed using the given [packages].
    */
   AnalysisOptionsProvider _createAnalysisOptionsProvider(Packages packages) {
-    Map<String, List<Folder>> packageMap =
-        ContextBuilder(resourceProvider, null, null)
-            .convertPackagesToMap(packages);
+    var packageMap = <String, List<Folder>>{};
+    if (packages != null) {
+      for (var package in packages.packages) {
+        packageMap[package.name] = [package.libFolder];
+      }
+    }
+
     List<UriResolver> resolvers = <UriResolver>[
       ResourceUriResolver(resourceProvider),
       PackageMapUriResolver(resourceProvider, packageMap),
@@ -1340,7 +1346,7 @@
   ///
   /// Returns null if there are no embedded/configured options.
   YamlMap _getEmbeddedOptions(ContextInfo info) {
-    Map embeddedOptions = null;
+    Map embeddedOptions;
     EmbedderYamlLocator locator =
         info.disposition.getEmbedderLocator(resourceProvider);
     Iterable<YamlMap> maps = locator.embedderYamls.values;
@@ -1609,18 +1615,6 @@
     return resourceProvider.getFile(path).readAsStringSync();
   }
 
-  Packages _readPackagespec(File specFile) {
-    try {
-      String contents = specFile.readAsStringSync();
-      Map<String, Uri> map =
-          pkgfile.parse(utf8.encode(contents), Uri.file(specFile.path));
-      return MapPackages(map);
-    } catch (_) {
-      //TODO(pquitslund): consider creating an error for the spec file.
-      return null;
-    }
-  }
-
   /**
    * Recompute the [FolderDisposition] for the context described by [info],
    * and update the client appropriately.
@@ -1812,13 +1806,9 @@
     if (packageMap == null) {
       packageMap = <String, List<Folder>>{};
       if (packages != null) {
-        var pathContext = resourceProvider.pathContext;
-        packages.asMap().forEach((String name, Uri uri) {
-          if (uri.scheme == 'file' || uri.scheme == '' /* unspecified */) {
-            String path = fileUriToNormalizedPath(pathContext, uri);
-            packageMap[name] = <Folder>[resourceProvider.getFolder(path)];
-          }
-        });
+        for (var package in packages.packages) {
+          packageMap[package.name] = [package.libFolder];
+        }
       }
     }
     return packageMap;
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index 645c82d..0891227 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -484,7 +484,7 @@
     // options
     var params = AnalysisUpdateOptionsParams.fromRequest(request);
     AnalysisOptions newOptions = params.options;
-    List<OptionUpdater> updaters = List<OptionUpdater>();
+    List<OptionUpdater> updaters = <OptionUpdater>[];
     if (newOptions.generateDart2jsHints != null) {
       updaters.add((engine.AnalysisOptionsImpl options) {
         options.dart2jsHint = newOptions.generateDart2jsHints;
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 547dd94..96d3a52 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -41,7 +41,7 @@
   /**
    * The completion services that the client is currently subscribed.
    */
-  final Set<CompletionService> subscriptions = Set<CompletionService>();
+  final Set<CompletionService> subscriptions = <CompletionService>{};
 
   /**
    * The next completion response id.
@@ -354,8 +354,8 @@
     Set<String> includedElementNames;
     List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
     if (subscriptions.contains(CompletionService.AVAILABLE_SUGGESTION_SETS)) {
-      includedElementKinds = Set<ElementKind>();
-      includedElementNames = Set<String>();
+      includedElementKinds = <ElementKind>{};
+      includedElementNames = <String>{};
       includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
     }
 
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
index e059c7f..8b4cd5d 100644
--- a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -299,7 +299,7 @@
 
   /// When the completion domain subscribes for changes, we start redirecting
   /// changes to this listener.
-  void Function(LibraryChange) _listener = null;
+  void Function(LibraryChange) _listener;
 
   DeclarationsTrackerData(this._tracker) {
     _tracker.changes.listen((change) {
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 9b3da69..d75faef 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -270,7 +270,7 @@
     //
     // Compute fixes associated with server-generated errors.
     //
-    List<AnalysisErrorFixes> errorFixesList = null;
+    List<AnalysisErrorFixes> errorFixesList;
     while (errorFixesList == null) {
       try {
         errorFixesList = await _computeServerErrorFixes(request, file, offset);
diff --git a/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart b/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
index 25cb287..c66bee3 100644
--- a/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
@@ -72,6 +72,7 @@
   LintFixInfo.nullClosures,
   LintFixInfo.omitLocalVariableTypes,
   LintFixInfo.preferAdjacentStringConcatenation,
+  LintFixInfo.preferCollectionLiterals,
   LintFixInfo.preferConditionalAssignment,
   LintFixInfo.preferEqualForDefaultValues,
   LintFixInfo.preferFinalFields,
@@ -80,6 +81,7 @@
   LintFixInfo.preferIfNullOperators,
   LintFixInfo.preferIsEmpty,
   LintFixInfo.preferIsNotEmpty,
+  LintFixInfo.preferIterableWhereType,
   LintFixInfo.preferSingleQuotes,
   LintFixInfo.preferSpreadCollections,
   LintFixInfo.slashForDocComments,
@@ -88,6 +90,7 @@
   LintFixInfo.unnecessaryConst,
   LintFixInfo.unnecessaryNew,
   LintFixInfo.unnecessaryThis,
+  LintFixInfo.useFunctionTypeSyntaxForParameters,
   LintFixInfo.useRethrowWhenPossible,
   //
   // Other fixes
@@ -215,13 +218,9 @@
   // avoid_types_as_parameter_names
   // camel_case_extensions
   // library_names
-  // prefer_collection_literals
   // prefer_contains
-  // prefer_iterable_whereType
   // recursive_getters
-  // unnecessary_null_in_if_null_operators
   // unrelated_type_equality_checks
-  // use_function_type_syntax_for_parameters
   // valid_regexps
 
   static final alwaysDeclareReturnTypes = LintFixInfo(
@@ -380,6 +379,12 @@
     isPedantic: true,
   );
 
+  static final preferIterableWhereType = LintFixInfo(
+      'prefer_iterable_whereType',
+      DartFixKind.CONVERT_TO_WHERE_TYPE,
+      'Add a return type where possible.',
+      isPedantic: true);
+
   static final preferSingleQuotes = LintFixInfo(
     'prefer_single_quotes',
     DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING,
@@ -439,10 +444,22 @@
     isPedantic: true,
   );
 
+  static final unnecessaryNullInIfNullOperators = LintFixInfo(
+      'unnecessary_null_in_if_null_operators',
+      DartFixKind.REMOVE_IF_NULL_OPERATOR,
+      "Remove the '??' operator.",
+      isPedantic: true);
+
   static final unnecessaryThis = LintFixInfo(
       'unnecessary_this', DartFixKind.REMOVE_THIS_EXPRESSION, 'Remove this.',
       isPedantic: true);
 
+  static final useFunctionTypeSyntaxForParameters = LintFixInfo(
+      'use_function_type_syntax_for_parameters',
+      DartFixKind.CONVERT_TO_GENERIC_FUNCTION_SYNTAX,
+      "Convert into 'Function' syntax",
+      isPedantic: true);
+
   static final useRethrowWhenPossible = LintFixInfo('use_rethrow_when_possible',
       DartFixKind.USE_RETHROW, 'Replace with rethrow.',
       isPedantic: true);
diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
index 7e10dfe..014f4c5 100644
--- a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
@@ -20,7 +20,7 @@
 import 'package:analyzer/src/lint/registry.dart';
 
 class PreferMixinFix extends FixLintTask implements FixCodeTask {
-  final classesToConvert = Set<Element>();
+  final classesToConvert = <Element>{};
 
   PreferMixinFix(DartFixListener listener) : super(listener);
 
diff --git a/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart b/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart
index fc54798..dcf9223 100644
--- a/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart
+++ b/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart
@@ -169,6 +169,11 @@
       nullableValue = "a nullable value";
     }
 
+    if (origin.kind == EdgeOriginKind.listLengthConstructor) {
+      return "List value type must be nullable because a length is specified,"
+          " and the list items are initialized as null.";
+    }
+
     CompilationUnit unit = node.thisOrAncestorOfType<CompilationUnit>();
     int lineNumber = unit.lineInfo.getLocation(node.offset).lineNumber;
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 0d38d2c..f53de11 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -109,7 +109,7 @@
 
           final key =
               _createImportedSymbolKey(elementName, declaringLibraryUri);
-          alreadyImportedSymbols.putIfAbsent(key, () => Set<String>());
+          alreadyImportedSymbols.putIfAbsent(key, () => <String>{});
           alreadyImportedSymbols[key]
               .add('${importedLibrary.librarySource.uri}');
         }
@@ -140,8 +140,8 @@
     Set<String> includedElementNames;
     List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
     if (includeSuggestionSets) {
-      includedElementKinds = Set<ElementKind>();
-      includedElementNames = Set<String>();
+      includedElementKinds = <ElementKind>{};
+      includedElementNames = <String>{};
       includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
     }
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
index 4c26bb0..eeee66f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
@@ -9,6 +9,83 @@
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 
+/// Helper for reading client dynamic registrations which may be ommitted by the
+/// client.
+class ClientDynamicRegistrations {
+  /// All dynamic registrations supported by the Dart LSP server.
+  ///
+  /// Anything listed here and supported by the client will not send a static
+  /// registration but intead dynamically register (usually only for a subset of
+  /// files such as for .dart/pubspec.yaml/etc).
+  ///
+  /// When adding new capabilities that will be registered dynamically, the
+  /// test_dynamicRegistration_XXX tests in `lsp/initialization_test.dart` should
+  /// also be updated to ensure no double-registrations.
+  static const supported = [
+    Method.textDocument_didOpen,
+    Method.textDocument_didChange,
+    Method.textDocument_didClose,
+    Method.textDocument_completion,
+    Method.textDocument_hover,
+    Method.textDocument_signatureHelp,
+    Method.textDocument_references,
+    Method.textDocument_documentHighlight,
+    Method.textDocument_formatting,
+    Method.textDocument_onTypeFormatting,
+    Method.textDocument_definition,
+    Method.textDocument_codeAction,
+    Method.textDocument_rename,
+    Method.textDocument_foldingRange,
+  ];
+  final ClientCapabilities _capabilities;
+
+  ClientDynamicRegistrations(this._capabilities);
+
+  bool get codeActions =>
+      _capabilities.textDocument?.foldingRange?.dynamicRegistration ?? false;
+
+  bool get completion =>
+      _capabilities.textDocument?.completion?.dynamicRegistration ?? false;
+
+  bool get definition =>
+      _capabilities.textDocument?.definition?.dynamicRegistration ?? false;
+
+  bool get documentHighlights =>
+      _capabilities.textDocument?.documentHighlight?.dynamicRegistration ??
+      false;
+
+  bool get documentSymbol =>
+      _capabilities.textDocument?.documentSymbol?.dynamicRegistration ?? false;
+
+  bool get folding =>
+      _capabilities.textDocument?.foldingRange?.dynamicRegistration ?? false;
+
+  bool get formatting =>
+      _capabilities.textDocument?.formatting?.dynamicRegistration ?? false;
+
+  bool get hover =>
+      _capabilities.textDocument?.hover?.dynamicRegistration ?? false;
+
+  bool get implementation =>
+      _capabilities.textDocument?.implementation?.dynamicRegistration ?? false;
+
+  bool get references =>
+      _capabilities.textDocument?.references?.dynamicRegistration ?? false;
+
+  bool get rename =>
+      _capabilities.textDocument?.rename?.dynamicRegistration ?? false;
+
+  bool get signatureHelp =>
+      _capabilities.textDocument?.signatureHelp?.dynamicRegistration ?? false;
+
+  bool get textSync =>
+      _capabilities.textDocument?.synchronization?.dynamicRegistration ?? false;
+
+  bool get typeFormatting =>
+      _capabilities.textDocument?.onTypeFormatting?.dynamicRegistration ??
+      false;
+}
+
 class InitializeMessageHandler
     extends MessageHandler<InitializeParams, InitializeResult> {
   InitializeMessageHandler(LspAnalysisServer server) : super(server);
@@ -143,80 +220,3 @@
     return success(InitializeResult(server.capabilities));
   }
 }
-
-/// Helper for reading client dynamic registrations which may be ommitted by the
-/// client.
-class ClientDynamicRegistrations {
-  ClientCapabilities _capabilities;
-  ClientDynamicRegistrations(this._capabilities);
-
-  /// All dynamic registrations supported by the Dart LSP server.
-  ///
-  /// Anything listed here and supported by the client will not send a static
-  /// registration but intead dynamically register (usually only for a subset of
-  /// files such as for .dart/pubspec.yaml/etc).
-  ///
-  /// When adding new capabilities that will be registered dynamically, the
-  /// test_dynamicRegistration_XXX tests in `lsp/initialization_test.dart` should
-  /// also be updated to ensure no double-registrations.
-  static const supported = [
-    Method.textDocument_didOpen,
-    Method.textDocument_didChange,
-    Method.textDocument_didClose,
-    Method.textDocument_completion,
-    Method.textDocument_hover,
-    Method.textDocument_signatureHelp,
-    Method.textDocument_references,
-    Method.textDocument_documentHighlight,
-    Method.textDocument_formatting,
-    Method.textDocument_onTypeFormatting,
-    Method.textDocument_definition,
-    Method.textDocument_codeAction,
-    Method.textDocument_rename,
-    Method.textDocument_foldingRange,
-  ];
-
-  bool get textSync =>
-      _capabilities.textDocument?.synchronization?.dynamicRegistration ?? false;
-
-  bool get hover =>
-      _capabilities.textDocument?.hover?.dynamicRegistration ?? false;
-
-  bool get completion =>
-      _capabilities.textDocument?.completion?.dynamicRegistration ?? false;
-
-  bool get signatureHelp =>
-      _capabilities.textDocument?.signatureHelp?.dynamicRegistration ?? false;
-
-  bool get definition =>
-      _capabilities.textDocument?.definition?.dynamicRegistration ?? false;
-
-  bool get implementation =>
-      _capabilities.textDocument?.implementation?.dynamicRegistration ?? false;
-
-  bool get references =>
-      _capabilities.textDocument?.references?.dynamicRegistration ?? false;
-
-  bool get documentHighlights =>
-      _capabilities.textDocument?.documentHighlight?.dynamicRegistration ??
-      false;
-
-  bool get documentSymbol =>
-      _capabilities.textDocument?.documentSymbol?.dynamicRegistration ?? false;
-
-  bool get formatting =>
-      _capabilities.textDocument?.formatting?.dynamicRegistration ?? false;
-
-  bool get typeFormatting =>
-      _capabilities.textDocument?.onTypeFormatting?.dynamicRegistration ??
-      false;
-
-  bool get folding =>
-      _capabilities.textDocument?.foldingRange?.dynamicRegistration ?? false;
-
-  bool get codeActions =>
-      _capabilities.textDocument?.foldingRange?.dynamicRegistration ?? false;
-
-  bool get rename =>
-      _capabilities.textDocument?.rename?.dynamicRegistration ?? false;
-}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
index b941473..4ad092f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
@@ -52,7 +52,7 @@
     // huge numbers on large projects.
     var remainingResults = 500;
 
-    final filePathsHashSet = LinkedHashSet<String>();
+    final filePathsHashSet = <String>{};
     final tracker = server.declarationsTracker;
     final declarations = search.WorkspaceSymbols(tracker).declarations(
       regex,
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index 8f0d3f7..eaa0423 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -713,8 +713,8 @@
 
   @override
   ContextBuilder createContextBuilder(Folder folder, AnalysisOptions options) {
-    String defaultPackageFilePath = null;
-    String defaultPackagesDirectoryPath = null;
+    String defaultPackageFilePath;
+    String defaultPackagesDirectoryPath;
     String path = (analysisServer.contextManager as ContextManagerImpl)
         .normalizedPackageRoots[folder.path];
     if (path != null) {
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index ad7d7b9..7b170e9 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -462,7 +462,7 @@
     bool matches(String pattern) =>
         Glob(resourceProvider.pathContext.separator, pattern).matches(filePath);
 
-    WatchEvent event = null;
+    WatchEvent event;
     List<Future<Response>> responses = <Future<Response>>[];
     for (PluginInfo plugin in _pluginMap.values) {
       PluginSession session = plugin.currentSession;
diff --git a/pkg/analysis_server/lib/src/plugin/result_merger.dart b/pkg/analysis_server/lib/src/plugin/result_merger.dart
index c03d992..e711295 100644
--- a/pkg/analysis_server/lib/src/plugin/result_merger.dart
+++ b/pkg/analysis_server/lib/src/plugin/result_merger.dart
@@ -211,7 +211,7 @@
   KytheGetKytheEntriesResult mergeKytheEntries(
       List<KytheGetKytheEntriesResult> partialResultList) {
     List<KytheEntry> mergedEntries = <KytheEntry>[];
-    Set<String> mergedFiles = Set<String>();
+    Set<String> mergedFiles = <String>{};
     for (KytheGetKytheEntriesResult partialResult in partialResultList) {
       mergedEntries.addAll(partialResult.entries);
       mergedFiles.addAll(partialResult.files);
diff --git a/pkg/analysis_server/lib/src/search/search_domain.dart b/pkg/analysis_server/lib/src/search/search_domain.dart
index 0970df2..7929337 100644
--- a/pkg/analysis_server/lib/src/search/search_domain.dart
+++ b/pkg/analysis_server/lib/src/search/search_domain.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:collection';
 
 import 'package:analysis_server/protocol/protocol_constants.dart';
 import 'package:analysis_server/src/analysis_server.dart';
@@ -188,7 +187,7 @@
     }
 
     var tracker = server.declarationsTracker;
-    var files = LinkedHashSet<String>();
+    var files = <String>{};
     int remainingMaxResults = params.maxResults;
     var declarations = search.WorkspaceSymbols(tracker).declarations(
       regExp,
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index c196526..8ed15b2 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -117,7 +117,7 @@
     // create an empty item now
     int itemId;
     {
-      String displayName = null;
+      String displayName;
       if (typeArguments != null && typeArguments.isNotEmpty) {
         displayName =
             classElement.displayName + '<' + typeArguments.join(', ') + '>';
diff --git a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
index b4feecc..264b161 100644
--- a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
+++ b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
@@ -7,7 +7,7 @@
  */
 class CompletionPerformance {
   final DateTime start = DateTime.now();
-  final Map<String, Duration> _startTimes = Map<String, Duration>();
+  final Map<String, Duration> _startTimes = <String, Duration>{};
   final Stopwatch _stopwatch = Stopwatch();
   final List<OperationPerformance> operations = <OperationPerformance>[];
 
@@ -31,7 +31,7 @@
     return '$suggestionCountFirst,  $suggestionCountLast';
   }
 
-  void complete([String tag = null]) {
+  void complete([String tag]) {
     _stopwatch.stop();
     _logDuration(tag ?? 'total time', _stopwatch.elapsed);
   }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
index c179134..c2dbb1e 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
@@ -120,7 +120,7 @@
  */
 Iterable<String> _namedArgs(DartCompletionRequest request) {
   AstNode node = request.target.containingNode;
-  List<String> namedArgs = List<String>();
+  List<String> namedArgs = <String>[];
   if (node is ArgumentList) {
     for (Expression arg in node.arguments) {
       if (arg is NamedExpression) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart
index 7c6947d..7456648 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart
@@ -169,7 +169,7 @@
     return null;
   }
 
-  final result = List<String>();
+  final result = <String>[];
   for (var size = 0;
       size < n && token != null && !token.isEof;
       token = token.previous) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
index 2eab3cb..0d6f12a 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
@@ -62,11 +62,13 @@
         // to ensure that we can return the suggestions from other providers.
         return const <CompletionSuggestion>[];
       }
+      var typeSystem = containingLibrary.typeSystem;
       LibraryScope nameScope = LibraryScope(containingLibrary);
       for (var extension in nameScope.extensions) {
         var extendedType =
             _resolveExtendedType(containingLibrary, extension, type);
-        if (extendedType != null) {
+        if (extendedType != null &&
+            typeSystem.isSubtypeOf(type, extendedType)) {
           // TODO(brianwilkerson) We might want to apply the substitution to the
           //  members of the extension for display purposes.
           _addInstanceMembers(extension);
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart
index 739ab98..4617cb5 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart
@@ -31,7 +31,7 @@
     }
 
     // Compute the list of fields already referenced in the constructor
-    List<String> referencedFields = List<String>();
+    List<String> referencedFields = <String>[];
     for (FormalParameter param in constructor.parameters.parameters) {
       if (param is DefaultFormalParameter &&
           param.parameter is FieldFormalParameter) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
index 924029c..9f939de 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
@@ -177,7 +177,7 @@
 
   @override
   visitCompilationUnit(CompilationUnit node) {
-    var previousMember = null;
+    var previousMember;
     for (var member in node.childEntities) {
       if (entity == member) {
         break;
@@ -341,13 +341,8 @@
         previous = node.findPrevious(previous);
       }
       if (previous != null && previous.type == TokenType.EQ) {
-        _addSuggestions([
-          Keyword.CONST,
-          Keyword.FALSE,
-          Keyword.NEW,
-          Keyword.NULL,
-          Keyword.TRUE
-        ]);
+        _addSuggestions(
+            [Keyword.CONST, Keyword.FALSE, Keyword.NULL, Keyword.TRUE]);
       } else {
         _addSuggestion(Keyword.IN, DART_RELEVANCE_HIGH);
       }
@@ -735,7 +730,6 @@
     _addSuggestions([
       Keyword.CONST,
       Keyword.FALSE,
-      Keyword.NEW,
       Keyword.NULL,
       Keyword.TRUE,
     ]);
@@ -829,7 +823,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SWITCH,
       Keyword.THROW,
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/language_model.dart b/pkg/analysis_server/lib/src/services/completion/dart/language_model.dart
index 8b90e92..5f59c73 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/language_model.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/language_model.dart
@@ -108,8 +108,8 @@
     // Get scores (as floats)
     final probabilities = Float32List.view(bytes.buffer);
 
-    final scores = Map<String, double>();
-    final scoresAboveThreshold = Map<String, double>();
+    final scores = <String, double>{};
+    final scoresAboveThreshold = <String, double>{};
     probabilities.asMap().forEach((k, v) {
       // x in 0, 1, ..., |V| - 1 correspond to specific members of the vocabulary.
       // x in |V|, |V| + 1, ..., |V| + 49 are pointers to reference positions along the
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
index 8edb690..290468a 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
@@ -34,7 +34,7 @@
   /**
    * The set of libraries that have been, or are currently being, visited.
    */
-  final Set<LibraryElement> visitedLibraries = Set<LibraryElement>();
+  final Set<LibraryElement> visitedLibraries = <LibraryElement>{};
 
   LibraryElementSuggestionBuilder(this.request, this.optype, [this.prefix]) {
     kind = request.target.isFunctionalArgument()
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
index ba5457b..d66685c 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
@@ -533,7 +533,7 @@
         .map((FormalParameter param) => param.identifier.name)
         .toList();
     suggestion.parameterTypes = paramList.map((FormalParameter param) {
-      TypeAnnotation type = null;
+      TypeAnnotation type;
       if (param is DefaultFormalParameter) {
         NormalFormalParameter child = param.parameter;
         if (child is SimpleFormalParameter) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 224a945..9d248a4 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -92,7 +92,7 @@
   /**
    * A set of existing completions used to prevent duplicate suggestions.
    */
-  final Set<String> _completions = Set<String>();
+  final Set<String> _completions = <String>{};
 
   /**
    * A map of element names to suggestions for synthetic getters and setters.
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart
index dc3023a..f039010 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart
@@ -67,7 +67,7 @@
         node = CompletionTarget.findFormalParameter(node, offset);
       }
 
-      String strName = null;
+      String strName;
       if (node is ExpressionStatement) {
         var expression = node.expression;
         if (expression is Identifier) {
diff --git a/pkg/analysis_server/lib/src/services/completion/postfix/postfix_completion.dart b/pkg/analysis_server/lib/src/services/completion/postfix/postfix_completion.dart
index 3163f19..184da5f 100644
--- a/pkg/analysis_server/lib/src/services/completion/postfix/postfix_completion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/postfix/postfix_completion.dart
@@ -293,7 +293,7 @@
   PostfixCompletion completion;
   SourceChange change = SourceChange('postfix-completion');
   final Map<String, LinkedEditGroup> linkedPositionGroups = {};
-  Position exitPosition = null;
+  Position exitPosition;
 
   PostfixCompletionProcessor(this.completionContext)
       : utils = CorrectionUtils(completionContext.resolveResult);
@@ -548,7 +548,7 @@
     return expr;
   }
 
-  AstNode _selectedNode({int at = null}) =>
+  AstNode _selectedNode({int at}) =>
       NodeLocator(at == null ? selectionOffset : at)
           .searchWithin(completionContext.resolveResult.unit);
 
diff --git a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
index 4332b1f..4199bc8 100644
--- a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
@@ -132,7 +132,7 @@
   List<engine.AnalysisError> errors = [];
   final Map<String, LinkedEditGroup> linkedPositionGroups =
       <String, LinkedEditGroup>{};
-  Position exitPosition = null;
+  Position exitPosition;
 
   StatementCompletionProcessor(this.statementContext)
       : utils = CorrectionUtils(statementContext.resolveResult);
@@ -268,7 +268,7 @@
   void _checkExpressions() {
     // Note: This may queue edits that have to be accounted for later.
     // See _lengthOfInsertions().
-    AstNode errorMatching(errorCode, {partialMatch = null}) {
+    AstNode errorMatching(errorCode, {partialMatch}) {
       var error = _findError(errorCode, partialMatch: partialMatch);
       if (error == null) {
         return null;
@@ -1091,7 +1091,7 @@
     return false;
   }
 
-  engine.AnalysisError _findError(ErrorCode code, {partialMatch = null}) {
+  engine.AnalysisError _findError(ErrorCode code, {partialMatch}) {
     return errors.firstWhere(
         (err) =>
             err.errorCode == code &&
@@ -1194,14 +1194,14 @@
     return Position(file, offset);
   }
 
-  void _removeError(errorCode, {partialMatch = null}) {
+  void _removeError(errorCode, {partialMatch}) {
     var error = _findError(errorCode, partialMatch: partialMatch);
     if (error != null) {
       errors.remove(error);
     }
   }
 
-  AstNode _selectedNode({int at = null}) =>
+  AstNode _selectedNode({int at}) =>
       NodeLocator(at == null ? selectionOffset : at).searchWithin(unit);
 
   void _setCompletion(StatementCompletionKind kind, [List args]) {
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index af3972a..57f0293 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -223,7 +223,7 @@
   }
 
   void _addAssistFromBuilder(DartChangeBuilder builder, AssistKind kind,
-      {List args = null}) {
+      {List args}) {
     if (builder == null) {
       return;
     }
@@ -395,7 +395,7 @@
       return;
     }
     // prepare excluded names
-    Set<String> excluded = Set<String>();
+    Set<String> excluded = <String>{};
     ScopedNameFinder scopedNameFinder = ScopedNameFinder(offset);
     expression.accept(scopedNameFinder);
     excluded.addAll(scopedNameFinder.locals.keys.toSet());
@@ -1051,8 +1051,8 @@
    */
   Future<void> _addProposal_convertToIsNotEmpty() async {
     // prepare "expr.isEmpty"
-    AstNode isEmptyAccess = null;
-    SimpleIdentifier isEmptyIdentifier = null;
+    AstNode isEmptyAccess;
+    SimpleIdentifier isEmptyIdentifier;
     if (node is SimpleIdentifier) {
       SimpleIdentifier identifier = node as SimpleIdentifier;
       AstNode parent = identifier.parent;
@@ -1357,7 +1357,7 @@
     String stateName = '_${widgetName}State';
 
     // Find fields assigned in constructors.
-    var fieldsAssignedInConstructors = Set<FieldElement>();
+    var fieldsAssignedInConstructors = <FieldElement>{};
     for (var member in widgetClass.members) {
       if (member is ConstructorDeclaration) {
         member.accept(_SimpleIdentifierRecursiveAstVisitor((node) {
@@ -1387,8 +1387,8 @@
     }
 
     // Prepare nodes to move.
-    var nodesToMove = Set<ClassMember>();
-    var elementsToMove = Set<Element>();
+    var nodesToMove = <ClassMember>{};
+    var elementsToMove = <Element>{};
     for (var member in widgetClass.members) {
       if (member is FieldDeclaration && !member.isStatic) {
         for (VariableDeclaration fieldNode in member.fields.variables) {
@@ -2065,7 +2065,7 @@
       statementPrefix = '';
     }
     // prepare excluded names
-    Set<String> excluded = Set<String>();
+    Set<String> excluded = <String>{};
     ScopedNameFinder scopedNameFinder = ScopedNameFinder(offset);
     isExpression.accept(scopedNameFinder);
     excluded.addAll(scopedNameFinder.locals.keys.toSet());
@@ -2442,7 +2442,7 @@
   }
 
   Future<void> _addProposal_replaceConditionalWithIfElse() async {
-    ConditionalExpression conditional = null;
+    ConditionalExpression conditional;
     // may be on Statement with Conditional
     Statement statement = node.thisOrAncestorOfType<Statement>();
     if (statement == null) {
@@ -2559,8 +2559,8 @@
       _coverageMarker();
       return;
     }
-    Expression thenExpression = null;
-    Expression elseExpression = null;
+    Expression thenExpression;
+    Expression elseExpression;
     bool hasReturnStatements = false;
     if (thenStatement is ReturnStatement && elseStatement is ReturnStatement) {
       hasReturnStatements = true;
diff --git a/pkg/analysis_server/lib/src/services/correction/base_processor.dart b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
index dca2f02..8d6fbad 100644
--- a/pkg/analysis_server/lib/src/services/correction/base_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
@@ -458,7 +458,7 @@
     String elementText;
     ConvertToSpreadCollectionsChange change =
         ConvertToSpreadCollectionsChange();
-    List<String> args = null;
+    List<String> args;
     if (argument is BinaryExpression &&
         argument.operator.type == TokenType.QUESTION_QUESTION) {
       Expression right = argument.rightOperand;
@@ -1584,7 +1584,7 @@
   /// A collection of the names of other simple identifiers that were found. We
   /// need to know these in order to ensure that the selected loop variable does
   /// not hide a name from an enclosing scope that is already being referenced.
-  final Set<String> otherNames = Set<String>();
+  final Set<String> otherNames = <String>{};
 
   /// Initialize a newly created finder to find references to the [parameter].
   _ParameterReferenceFinder(this.parameter) : assert(parameter != null);
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_where_type.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_where_type.dart
new file mode 100644
index 0000000..76354ec
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_where_type.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2020, 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToWhereType extends CorrectionProducer {
+  @override
+  Future<void> compute(DartChangeBuilder builder) async {
+    AstNode node = this.node;
+    if (node is! SimpleIdentifier || node.parent is! MethodInvocation) {
+      return;
+    }
+    var methodName = node as SimpleIdentifier;
+    var invocation = node.parent as MethodInvocation;
+    var arguments = invocation.argumentList.arguments;
+    if (arguments.length != 1 || arguments[0] is! FunctionExpression) {
+      return;
+    }
+    var body = (arguments[0] as FunctionExpression).body;
+    Expression returnValue;
+    if (body is ExpressionFunctionBody) {
+      returnValue = body.expression;
+    } else if (body is BlockFunctionBody) {
+      var statements = body.block.statements;
+      if (statements.length != 1 || statements[0] is! ReturnStatement) {
+        return;
+      }
+      returnValue = (statements[0] as ReturnStatement).expression;
+    } else {
+      return;
+    }
+    if (returnValue is! IsExpression) {
+      return;
+    }
+    var isExpression = returnValue as IsExpression;
+    if (isExpression.notOperator != null) {
+      return;
+    }
+    var targetType = isExpression.type;
+
+    await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addReplacement(range.startEnd(methodName, invocation), (builder) {
+        builder.write('whereType<');
+        builder.write(utils.getNodeText(targetType));
+        builder.write('>()');
+      });
+    });
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/exchange_operands.dart b/pkg/analysis_server/lib/src/services/correction/dart/exchange_operands.dart
index 6131b48..b9a9a5d 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/exchange_operands.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/exchange_operands.dart
@@ -39,7 +39,7 @@
     // maybe replace the operator
     Token operator = binaryExpression.operator;
     // prepare a new operator
-    String newOperator = null;
+    String newOperator;
     TokenType operatorType = operator.type;
     if (operatorType == TokenType.LT) {
       newOperator = '>';
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_if_null_operator.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_if_null_operator.dart
new file mode 100644
index 0000000..23ac078
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_if_null_operator.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2020, 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class RemoveIfNullOperator extends CorrectionProducer {
+  @override
+  Future<void> compute(DartChangeBuilder builder) async {
+    var expression = node.thisOrAncestorOfType<BinaryExpression>();
+    if (expression == null) {
+      return;
+    }
+    SourceRange sourceRange;
+    if (expression.leftOperand.unParenthesized is NullLiteral) {
+      sourceRange =
+          range.startStart(expression.leftOperand, expression.rightOperand);
+    } else if (expression.rightOperand.unParenthesized is NullLiteral) {
+      sourceRange =
+          range.endEnd(expression.leftOperand, expression.rightOperand);
+    } else {
+      return;
+    }
+    await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addDeletion(sourceRange);
+    });
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 97d489e..2d3348d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -220,6 +220,8 @@
       'CONVERT_TO_SINGLE_QUOTED_STRING', 50, "Convert to single quoted string");
   static const CONVERT_TO_SPREAD =
       FixKind('CONVERT_TO_SPREAD', 50, "Convert to a spread");
+  static const CONVERT_TO_WHERE_TYPE =
+      FixKind('CONVERT_TO_WHERE_TYPE', 50, "Convert to a use 'whereType'");
   static const CREATE_CLASS = FixKind('CREATE_CLASS', 50, "Create class '{0}'");
   static const CREATE_CONSTRUCTOR =
       FixKind('CREATE_CONSTRUCTOR', 50, "Create constructor '{0}'");
@@ -295,6 +297,8 @@
       FixKind('REMOVE_EMPTY_ELSE', 50, "Remove empty else clause");
   static const REMOVE_EMPTY_STATEMENT =
       FixKind('REMOVE_EMPTY_STATEMENT', 50, "Remove empty statement");
+  static const REMOVE_IF_NULL_OPERATOR =
+      FixKind('REMOVE_IF_NULL_OPERATOR', 50, "Remove the '??' operator");
   static const REMOVE_INITIALIZER =
       FixKind('REMOVE_INITIALIZER', 50, "Remove initializer");
   static const REMOVE_INTERPOLATION_BRACES = FixKind(
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart b/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
index 35a8632..d0806ae 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
@@ -148,8 +148,7 @@
   /// Add a fix whose edits were built by the [builder] that has the given
   /// [kind]. If [args] are provided, they will be used to fill in the message
   /// for the fix.
-  void _addFixFromBuilder(ChangeBuilder builder, FixKind kind,
-      {List args = null}) {
+  void _addFixFromBuilder(ChangeBuilder builder, FixKind kind, {List args}) {
     SourceChange change = builder.sourceChange;
     if (change.edits.isEmpty) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/manifest/fix_generator.dart b/pkg/analysis_server/lib/src/services/correction/fix/manifest/fix_generator.dart
index f087468..6d2bb89 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/manifest/fix_generator.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/manifest/fix_generator.dart
@@ -114,8 +114,7 @@
   /// [kind]. If [args] are provided, they will be used to fill in the message
   /// for the fix.
   // ignore: unused_element
-  void _addFixFromBuilder(ChangeBuilder builder, FixKind kind,
-      {List args = null}) {
+  void _addFixFromBuilder(ChangeBuilder builder, FixKind kind, {List args}) {
     SourceChange change = builder.sourceChange;
     if (change.edits.isEmpty) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart b/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart
index 0f4b10c..4c6bf66 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/pubspec/fix_generator.dart
@@ -72,8 +72,7 @@
   /// [kind]. If [args] are provided, they will be used to fill in the message
   /// for the fix.
   // ignore: unused_element
-  void _addFixFromBuilder(ChangeBuilder builder, FixKind kind,
-      {List args = null}) {
+  void _addFixFromBuilder(ChangeBuilder builder, FixKind kind, {List args}) {
     SourceChange change = builder.sourceChange;
     if (change.edits.isEmpty) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 22926d1..3501f4c 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -11,7 +11,12 @@
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server/src/services/correction/base_processor.dart';
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_list_literal.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_map_literal.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_null_aware.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_where_type.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_if_null_operator.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analysis_server/src/services/correction/levenshtein.dart';
@@ -54,9 +59,6 @@
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart' hide FixContributor;
 import 'package:analyzer_plugin/utilities/range_factory.dart';
 import 'package:path/path.dart';
-import 'package:analysis_server/src/services/correction/dart/convert_to_list_literal.dart';
-import 'package:analysis_server/src/services/correction/dart/convert_to_map_literal.dart';
-import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
 
 /**
  * A predicate is a one-argument function that returns a boolean value.
@@ -116,7 +118,7 @@
       final List<Fix> fixesListI = await processorI.compute();
       for (Fix f in fixesListI) {
         if (!map.containsKey(f.kind)) {
-          map[f.kind] = List<Fix>()..add(f);
+          map[f.kind] = <Fix>[]..add(f);
         } else {
           map[f.kind].add(f);
         }
@@ -125,7 +127,7 @@
 
     // For each FixKind in the HashMap, union each list together, then return
     // the set of unioned Fixes.
-    final List<Fix> result = List<Fix>();
+    final List<Fix> result = <Fix>[];
     map.forEach((FixKind kind, List<Fix> fixesListJ) {
       if (fixesListJ.first.kind.canBeAppliedTogether()) {
         Fix unionFix = _unionFixList(fixesListJ);
@@ -146,7 +148,7 @@
     final SourceChange sourceChange =
         SourceChange(fixList[0].kind.appliedTogetherMessage);
     sourceChange.edits = List.from(fixList[0].change.edits);
-    final List<SourceEdit> edits = List<SourceEdit>();
+    final List<SourceEdit> edits = <SourceEdit>[];
     edits.addAll(fixList[0].change.edits[0].edits);
     sourceChange.linkedEditGroups =
         List.from(fixList[0].change.linkedEditGroups);
@@ -646,6 +648,9 @@
       if (name == LintNames.empty_statements) {
         await _addFix_removeEmptyStatement();
       }
+      if (name == LintNames.hash_and_equals) {
+        await _addFix_addMissingHashOrEquals();
+      }
       if (name == LintNames.no_duplicate_case_values) {
         await _addFix_removeCaseStatement();
       }
@@ -747,6 +752,9 @@
       if (name == LintNames.unnecessary_this) {
         await _addFix_removeThisExpression();
       }
+      if (name == LintNames.use_function_type_syntax_for_parameters) {
+        await _addFix_convertToGenericFunctionSyntax();
+      }
       if (name == LintNames.use_rethrow_when_possible) {
         await _addFix_replaceWithRethrow();
       }
@@ -973,6 +981,42 @@
         changeBuilder, DartFixKind.ADD_MISSING_ENUM_CASE_CLAUSES);
   }
 
+  Future<void> _addFix_addMissingHashOrEquals() async {
+    final methodDecl = node.thisOrAncestorOfType<MethodDeclaration>();
+    final classDecl = node.thisOrAncestorOfType<ClassDeclaration>();
+    if (methodDecl != null && classDecl != null) {
+      final classElement = classDecl.declaredElement;
+
+      var element;
+      var memberName;
+      if (methodDecl.name.name == 'hashCode') {
+        memberName = '==';
+        element = classElement.lookUpInheritedMethod(
+            memberName, classElement.library);
+      } else {
+        memberName = 'hashCode';
+        element = classElement.lookUpInheritedConcreteGetter(
+            memberName, classElement.library);
+      }
+
+      final location =
+          utils.prepareNewClassMemberLocation(classDecl, (_) => true);
+
+      final changeBuilder = _newDartChangeBuilder();
+      await changeBuilder.addFileEdit(file, (fileBuilder) {
+        fileBuilder.addInsertion(location.offset, (builder) {
+          builder.write(location.prefix);
+          builder.writeOverride(element, invokeSuper: true);
+          builder.write(location.suffix);
+        });
+      });
+
+      changeBuilder.setSelection(Position(file, location.offset));
+      _addFixFromBuilder(changeBuilder, DartFixKind.CREATE_METHOD,
+          args: [memberName]);
+    }
+  }
+
   Future<void> _addFix_addMissingParameter() async {
     // The error is reported on ArgumentList.
     if (node is! ArgumentList) {
@@ -1002,7 +1046,7 @@
           builder.addInsertion(offset, (builder) {
             builder.write(prefix);
             builder.writeParameterMatchingArgument(
-                argument, numRequired, Set<String>());
+                argument, numRequired, <String>{});
             builder.write(suffix);
           });
         });
@@ -1076,8 +1120,8 @@
         await changeBuilder.addFileEdit(context.file, (builder) {
           builder.addInsertion(offset, (builder) {
             builder.write(prefix);
-            builder.writeParameterMatchingArgument(
-                namedExpression, 0, Set<String>());
+            builder
+                .writeParameterMatchingArgument(namedExpression, 0, <String>{});
             builder.write(suffix);
           });
         });
@@ -1582,7 +1626,7 @@
           argumentList.arguments.skip(numberOfPositionalParameters);
       for (var argument in extraArguments) {
         if (argument is! NamedExpression) {
-          ParameterElement uniqueNamedParameter = null;
+          ParameterElement uniqueNamedParameter;
           for (var namedParameter in namedParameters) {
             if (typeSystem.isSubtypeOf(
                 argument.staticType, namedParameter.type)) {
@@ -1626,8 +1670,8 @@
   }
 
   Future<void> _addFix_createClass() async {
-    Element prefixElement = null;
-    String name = null;
+    Element prefixElement;
+    String name;
     SimpleIdentifier nameNode;
     if (node is SimpleIdentifier) {
       AstNode parent = node.parent;
@@ -1841,9 +1885,9 @@
   }
 
   Future<void> _addFix_createConstructor_named() async {
-    SimpleIdentifier name = null;
-    ConstructorName constructorName = null;
-    InstanceCreationExpression instanceCreation = null;
+    SimpleIdentifier name;
+    ConstructorName constructorName;
+    InstanceCreationExpression instanceCreation;
     if (node is SimpleIdentifier) {
       // name
       name = node as SimpleIdentifier;
@@ -2530,8 +2574,8 @@
   }
 
   Future<void> _addFix_createMixin() async {
-    Element prefixElement = null;
-    String name = null;
+    Element prefixElement;
+    String name;
     SimpleIdentifier nameNode;
     if (node is SimpleIdentifier) {
       AstNode parent = node.parent;
@@ -2784,7 +2828,7 @@
   }
 
   Future<void> _addFix_importLibrary(FixKind kind, Uri library,
-      [String relativeURI = null]) async {
+      [String relativeURI]) async {
     String uriText;
     var changeBuilder = _newDartChangeBuilder();
     await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
@@ -2813,7 +2857,7 @@
     }
     // may be there is an existing import,
     // but it is with prefix and we don't use this prefix
-    var alreadyImportedWithPrefix = Set<String>();
+    var alreadyImportedWithPrefix = <String>{};
     for (ImportElement imp in unitLibraryElement.imports) {
       // prepare element
       LibraryElement libraryElement = imp.importedLibrary;
@@ -3184,7 +3228,7 @@
     }
     SimpleIdentifier memberName = node;
     AstNode parent = node.parent;
-    AstNode target = null;
+    AstNode target;
     if (parent is MethodInvocation && node == parent.methodName) {
       target = parent.target;
     } else if (parent is PropertyAccess && node == parent.propertyName) {
@@ -4051,7 +4095,7 @@
       return;
     }
     AstNode parent = node.parent;
-    AstNode target = null;
+    AstNode target;
     if (parent is MethodInvocation && node == parent.methodName) {
       target = parent.target;
     } else if (parent is PropertyAccess && node == parent.propertyName) {
@@ -4238,7 +4282,7 @@
   Future<void> _addFix_undefinedClass_useSimilar() async {
     AstNode node = this.node;
     // Prepare the optional import prefix name.
-    String prefixName = null;
+    String prefixName;
     if (node is SimpleIdentifier && node.staticElement is PrefixElement) {
       AstNode parent = node.parent;
       if (parent is PrefixedIdentifier &&
@@ -4288,7 +4332,7 @@
     AstNode node = this.node;
     if (node is SimpleIdentifier) {
       // prepare target
-      Expression target = null;
+      Expression target;
       if (node.parent is PrefixedIdentifier) {
         target = (node.parent as PrefixedIdentifier).prefix;
       } else if (node.parent is PropertyAccess) {
@@ -4401,7 +4445,7 @@
     AstNode node = this.node;
     if (node is SimpleIdentifier) {
       // Prepare the optional import prefix name.
-      String prefixName = null;
+      String prefixName;
       {
         AstNode invocation = node.parent;
         if (invocation is MethodInvocation && invocation.methodName == node) {
@@ -4517,7 +4561,7 @@
 
   Future<void> _addFix_updateSdkConstraints(String minimumVersion) async {
     Context context = resourceProvider.pathContext;
-    File pubspecFile = null;
+    File pubspecFile;
     Folder folder = resourceProvider.getFolder(context.dirname(file));
     while (folder != null) {
       pubspecFile = folder.getChildAssumingFile('pubspec.yaml');
@@ -4626,7 +4670,7 @@
   }
 
   void _addFixFromBuilder(ChangeBuilder builder, FixKind kind,
-      {List args = null, bool importsOnly = false}) {
+      {List args, bool importsOnly = false}) {
     if (builder == null) return;
     SourceChange change = builder.sourceChange;
     if (change.edits.isEmpty && !importsOnly) {
@@ -4674,11 +4718,21 @@
           ConvertToSetLiteral(),
           DartFixKind.CONVERT_TO_SET_LITERAL,
         );
+      } else if (name == LintNames.prefer_iterable_whereType) {
+        await compute(
+          ConvertToWhereType(),
+          DartFixKind.CONVERT_TO_WHERE_TYPE,
+        );
       } else if (name == LintNames.prefer_null_aware_operators) {
         await compute(
           ConvertToNullAware(),
           DartFixKind.CONVERT_TO_NULL_AWARE,
         );
+      } else if (name == LintNames.unnecessary_null_in_if_null_operators) {
+        await compute(
+          RemoveIfNullOperator(),
+          DartFixKind.REMOVE_IF_NULL_OPERATOR,
+        );
       }
     }
   }
@@ -5201,7 +5255,7 @@
   final String _targetName;
   final ElementPredicate _predicate;
 
-  Element _element = null;
+  Element _element;
   int _distance;
 
   _ClosestElementFinder(this._targetName, this._predicate, this._distance);
diff --git a/pkg/analysis_server/lib/src/services/correction/name_suggestion.dart b/pkg/analysis_server/lib/src/services/correction/name_suggestion.dart
index 5ca1d68..1f3b3ef 100644
--- a/pkg/analysis_server/lib/src/services/correction/name_suggestion.dart
+++ b/pkg/analysis_server/lib/src/services/correction/name_suggestion.dart
@@ -46,7 +46,7 @@
     }
   }
 
-  Set<String> res = Set();
+  Set<String> res = {};
   // use expression
   if (assignedExpression != null) {
     String nameFromExpression = _getBaseNameFromExpression(assignedExpression);
@@ -108,7 +108,7 @@
     text = sb.toString();
   }
   // split camel-case into separate suggested names
-  Set<String> res = Set();
+  Set<String> res = {};
   _addAll(excluded, res, getCamelWordCombinations(text));
   return List.from(res);
 }
@@ -178,7 +178,7 @@
 }
 
 String _getBaseNameFromUnwrappedExpression(Expression expression) {
-  String name = null;
+  String name;
   // analyze expressions
   if (expression is SimpleIdentifier) {
     return expression.name;
diff --git a/pkg/analysis_server/lib/src/services/correction/namespace.dart b/pkg/analysis_server/lib/src/services/correction/namespace.dart
index a57f4d1..627fc30 100644
--- a/pkg/analysis_server/lib/src/services/correction/namespace.dart
+++ b/pkg/analysis_server/lib/src/services/correction/namespace.dart
@@ -60,7 +60,7 @@
   }
   LibraryElement usedLibrary = element.library;
   // find ImportElement that imports used library with used prefix
-  List<ImportElement> candidates = null;
+  List<ImportElement> candidates;
   for (ImportElement importElement in libraryElement.imports) {
     // required library
     if (importElement.importedLibrary != usedLibrary) {
@@ -126,7 +126,7 @@
   CompilationUnit unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
   LibraryElement libraryElement = unit.declaredElement.library;
   // prepare used element
-  Element usedElement = null;
+  Element usedElement;
   if (parent is PrefixedIdentifier) {
     PrefixedIdentifier prefixed = parent;
     if (prefixed.prefix == prefixNode) {
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_directives.dart b/pkg/analysis_server/lib/src/services/correction/organize_directives.dart
index 591896c3..a4bf02d 100644
--- a/pkg/analysis_server/lib/src/services/correction/organize_directives.dart
+++ b/pkg/analysis_server/lib/src/services/correction/organize_directives.dart
@@ -108,7 +108,7 @@
     String directivesCode;
     {
       StringBuffer sb = StringBuffer();
-      _DirectivePriority currentPriority = null;
+      _DirectivePriority currentPriority;
       for (_DirectiveInfo directiveInfo in directives) {
         if (!hasUnresolvedIdentifierError) {
           UriBasedDirective directive = directiveInfo.directive;
diff --git a/pkg/analysis_server/lib/src/services/correction/sort_members.dart b/pkg/analysis_server/lib/src/services/correction/sort_members.dart
index 0065082..a7730c0 100644
--- a/pkg/analysis_server/lib/src/services/correction/sort_members.dart
+++ b/pkg/analysis_server/lib/src/services/correction/sort_members.dart
@@ -102,9 +102,9 @@
   void _sortClassMembers(ClassOrMixinDeclaration classDeclaration) {
     List<_MemberInfo> members = <_MemberInfo>[];
     for (ClassMember member in classDeclaration.members) {
-      _MemberKind kind = null;
+      _MemberKind kind;
       bool isStatic = false;
-      String name = null;
+      String name;
       if (member is ConstructorDeclaration) {
         kind = _MemberKind.CLASS_CONSTRUCTOR;
         SimpleIdentifier nameNode = member.name;
@@ -164,7 +164,7 @@
       }
       UriBasedDirective uriDirective = directive as UriBasedDirective;
       String uriContent = uriDirective.uri.stringValue;
-      _DirectivePriority kind = null;
+      _DirectivePriority kind;
       if (directive is ImportDirective) {
         if (uriContent.startsWith("dart:")) {
           kind = _DirectivePriority.IMPORT_SDK;
@@ -228,7 +228,7 @@
     {
       StringBuffer sb = StringBuffer();
       String endOfLine = this.endOfLine;
-      _DirectivePriority currentPriority = null;
+      _DirectivePriority currentPriority;
       bool firstOutputDirective = true;
       for (_DirectiveInfo directive in directives) {
         if (currentPriority != directive.priority) {
@@ -272,8 +272,8 @@
   void _sortUnitMembers() {
     List<_MemberInfo> members = [];
     for (CompilationUnitMember member in unit.declarations) {
-      _MemberKind kind = null;
-      String name = null;
+      _MemberKind kind;
+      String name;
       if (member is ClassOrMixinDeclaration) {
         kind = _MemberKind.UNIT_CLASS;
         name = member.name.name;
diff --git a/pkg/analysis_server/lib/src/services/correction/status.dart b/pkg/analysis_server/lib/src/services/correction/status.dart
index 296f924..9335d77 100644
--- a/pkg/analysis_server/lib/src/services/correction/status.dart
+++ b/pkg/analysis_server/lib/src/services/correction/status.dart
@@ -12,7 +12,7 @@
    * The current severity of this [RefactoringStatus] - the maximum of the
    * severities of its [entries].
    */
-  RefactoringProblemSeverity _severity = null;
+  RefactoringProblemSeverity _severity;
 
   /**
    * A list of [RefactoringProblem]s.
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 8b06274..df374c3 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -644,7 +644,7 @@
    * declared at [offset].
    */
   Set<String> findPossibleLocalVariableConflicts(int offset) {
-    Set<String> conflicts = Set<String>();
+    Set<String> conflicts = <String>{};
     AstNode enclosingNode = findNode(offset);
     Block enclosingBlock = enclosingNode.thisOrAncestorOfType<Block>();
     if (enclosingBlock != null) {
@@ -1070,7 +1070,7 @@
       bool shouldSkip(ClassMember existingMember)) {
     String indent = getIndent(1);
     // Find the last target member.
-    ClassMember targetMember = null;
+    ClassMember targetMember;
     List<ClassMember> members = _getMembers(declaration);
     if (members == null) {
       return null;
@@ -1417,7 +1417,7 @@
 }
 
 class _CollectReferencedUnprefixedNames extends RecursiveAstVisitor {
-  final Set<String> names = Set<String>();
+  final Set<String> names = <String>{};
 
   @override
   void visitSimpleIdentifier(SimpleIdentifier node) {
diff --git a/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart b/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart
index 35bcdd0..136f0d5 100644
--- a/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart
+++ b/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart
@@ -117,7 +117,7 @@
 
   /// The set of classes for which we are currently adding properties,
   /// used to prevent infinite recursion.
-  final Set<ClassElement> classesBeingProcessed = Set<ClassElement>();
+  final Set<ClassElement> classesBeingProcessed = <ClassElement>{};
 
   /// The resolved unit with the widget [InstanceCreationExpression].
   final ResolvedUnitResult resolvedUnit;
@@ -268,7 +268,7 @@
     var classElement = constructorElement.enclosingElement;
     if (!classesBeingProcessed.add(classElement)) return;
 
-    var existingNamed = Set<ParameterElement>();
+    var existingNamed = <ParameterElement>{};
     if (instanceCreation != null) {
       for (var argumentExpression in instanceCreation.argumentList.arguments) {
         var parameter = argumentExpression.staticParameterElement;
diff --git a/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart b/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart
index 16c228a..d0b4329 100644
--- a/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart
+++ b/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart
@@ -895,10 +895,10 @@
 
   _handleRefCallEdge(
     Element element, {
-    SyntacticEntity syntacticEntity = null,
+    SyntacticEntity syntacticEntity,
     int start = _notFound,
     int end = _notFound,
-    KytheVName enclosingTarget = null,
+    KytheVName enclosingTarget,
   }) {
     if (element is ExecutableElement &&
         _enclosingVName != _enclosingFileVName) {
@@ -932,11 +932,11 @@
   KytheVName _handleRefEdge(
     Element element,
     List<String> refEdgeTypes, {
-    SyntacticEntity syntacticEntity = null,
+    SyntacticEntity syntacticEntity,
     int start = _notFound,
     int end = _notFound,
-    KytheVName enclosingTarget = null,
-    KytheVName enclosingAnchor = null,
+    KytheVName enclosingTarget,
+    KytheVName enclosingAnchor,
   }) {
     assert(refEdgeTypes.isNotEmpty);
     element = _findNonSyntheticElement(element);
@@ -1120,7 +1120,7 @@
 /// [KytheEntry] protos.
 mixin OutputUtils {
   /// A set of [String]s which have already had a name [KytheVName] created.
-  final Set<String> nameNodes = Set<String>();
+  final Set<String> nameNodes = <String>{};
 
   String get corpus;
 
@@ -1152,13 +1152,13 @@
   /// Finally, for all anchors, a childof edge with a target of the enclosing
   /// file is written out.
   KytheVName addAnchorEdgesContainingEdge({
-    SyntacticEntity syntacticEntity = null,
+    SyntacticEntity syntacticEntity,
     int start = _notFound,
     int end = _notFound,
     List<String> edges = const [],
-    KytheVName target = null,
-    KytheVName enclosingTarget = null,
-    KytheVName enclosingAnchor = null,
+    KytheVName target,
+    KytheVName enclosingTarget,
+    KytheVName enclosingAnchor,
   }) {
     if (start == _notFound && end == _notFound) {
       if (syntacticEntity != null) {
@@ -1206,7 +1206,7 @@
   KytheEntry addEdge(KytheVName source, String edgeKind, KytheVName target,
       {int ordinalIntValue = _notFound}) {
     if (ordinalIntValue == _notFound) {
-      return addEntry(source, edgeKind, target, "/", List<int>());
+      return addEntry(source, edgeKind, target, "/", <int>[]);
     } else {
       return addEntry(source, edgeKind, target, schema.ORDINAL,
           _encodeInt(ordinalIntValue));
@@ -1240,7 +1240,7 @@
     Element functionElement,
     FormalParameterList paramNodes,
     KytheVName functionVName, {
-    AstNode returnNode = null,
+    AstNode returnNode,
   }) {
     var i = 0;
     var funcTypeVName =
@@ -1296,10 +1296,10 @@
   /// currently guarantee that the inputs to these fact kinds are valid for the
   /// associated nodeKind- if a non-null, then it will set.
   KytheVName addNodeAndFacts(String nodeKind,
-      {Element element = null,
-      KytheVName nodeVName = null,
-      String subKind = null,
-      String completeFact = null}) {
+      {Element element,
+      KytheVName nodeVName,
+      String subKind,
+      String completeFact}) {
     nodeVName ??= _vNameFromElement(element, nodeKind);
     addFact(nodeVName, schema.NODE_KIND_FACT, _encode(nodeKind));
     if (subKind != null) {
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index bc12a90..ca6dce2 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -29,6 +29,7 @@
   static const String empty_catches = 'empty_catches';
   static const String empty_constructor_bodies = 'empty_constructor_bodies';
   static const String empty_statements = 'empty_statements';
+  static const String hash_and_equals = 'hash_and_equals';
   static const String no_duplicate_case_values = 'no_duplicate_case_values';
   static const String non_constant_identifier_names =
       'non_constant_identifier_names';
@@ -51,13 +52,14 @@
       'prefer_for_elements_to_map_fromIterable';
   static const String prefer_generic_function_type_aliases =
       'prefer_generic_function_type_aliases';
-  static const String prefer_inlined_adds = 'prefer_inlined_adds';
-  static const String prefer_int_literals = 'prefer_int_literals';
   static const String prefer_if_elements_to_conditional_expressions =
       'prefer_if_elements_to_conditional_expressions';
+  static const String prefer_if_null_operators = 'prefer_if_null_operators';
+  static const String prefer_inlined_adds = 'prefer_inlined_adds';
+  static const String prefer_int_literals = 'prefer_int_literals';
   static const String prefer_is_empty = 'prefer_is_empty';
   static const String prefer_is_not_empty = 'prefer_is_not_empty';
-  static const String prefer_if_null_operators = 'prefer_if_null_operators';
+  static const String prefer_iterable_whereType = 'prefer_iterable_whereType';
   static const String prefer_null_aware_operators =
       'prefer_null_aware_operators';
   static const String prefer_relative_imports = 'prefer_relative_imports';
@@ -73,7 +75,11 @@
   static const String unnecessary_const = 'unnecessary_const';
   static const String unnecessary_lambdas = 'unnecessary_lambdas';
   static const String unnecessary_new = 'unnecessary_new';
+  static const String unnecessary_null_in_if_null_operators =
+      'unnecessary_null_in_if_null_operators';
   static const String unnecessary_overrides = 'unnecessary_overrides';
   static const String unnecessary_this = 'unnecessary_this';
+  static const String use_function_type_syntax_for_parameters =
+      'use_function_type_syntax_for_parameters';
   static const String use_rethrow_when_possible = 'use_rethrow_when_possible';
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
index 846b0b6..baed2ce 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
@@ -91,7 +91,7 @@
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
     // prepare "get" keyword
-    Token getKeyword = null;
+    Token getKeyword;
     {
       var sessionHelper = AnalysisSessionHelper(session);
       var result = await sessionHelper.getElementDeclaration(element);
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
index 4006115..2c77a76 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
@@ -56,7 +56,7 @@
   String stringLiteralPart;
   final List<SourceRange> occurrences = <SourceRange>[];
   final Map<Element, int> elementIds = <Element, int>{};
-  Set<String> excludedVariableNames = Set<String>();
+  Set<String> excludedVariableNames = <String>{};
 
   ExtractLocalRefactoringImpl(
       this.resolveResult, this.selectionOffset, this.selectionLength) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
index a13e65e..c58b0a3 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
@@ -81,7 +81,7 @@
   final int selectionLength;
   SourceRange selectionRange;
   CorrectionUtils utils;
-  final Set<Source> librariesToImport = Set<Source>();
+  final Set<Source> librariesToImport = <Source>{};
 
   @override
   String returnType = '';
@@ -112,9 +112,9 @@
   /**
    * The set of names that are referenced without any qualifier.
    */
-  final Set<String> _unqualifiedNames = Set<String>();
+  final Set<String> _unqualifiedNames = <String>{};
 
-  final Set<String> _excludedNames = Set<String>();
+  final Set<String> _excludedNames = <String>{};
   List<RefactoringMethodParameter> _parameters = <RefactoringMethodParameter>[];
   final Map<String, RefactoringMethodParameter> _parametersMap =
       <String, RefactoringMethodParameter>{};
@@ -334,7 +334,7 @@
         }
       }
       // prepare declaration source
-      String declarationSource = null;
+      String declarationSource;
       {
         String returnExpressionSource = _getMethodBodySource();
         // closure
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
index d16c157..4b42d3f 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
@@ -335,7 +335,7 @@
     }
 
     // Collect used public names.
-    var usedNames = Set<String>();
+    var usedNames = <String>{};
     for (var parameter in _parameters) {
       if (!parameter.name.startsWith('_')) {
         usedNames.add(parameter.name);
@@ -598,7 +598,7 @@
   final SourceRange expressionRange;
 
   final RefactoringStatus status = RefactoringStatus();
-  final Set<Element> uniqueElements = Set<Element>();
+  final Set<Element> uniqueElements = <Element>{};
   final List<_Parameter> parameters = [];
 
   List<ClassElement> enclosingClasses;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
index 7f45de8..e77eee7 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
@@ -57,7 +57,7 @@
   part._parameters.forEach(
       (ParameterElement parameter, List<_ParameterOccurrence> occurrences) {
     // prepare argument
-    Expression argument = null;
+    Expression argument;
     for (Expression arg in arguments) {
       if (arg.staticParameterElement == parameter) {
         argument = arg;
@@ -150,7 +150,7 @@
  * at [node].
  */
 Set<String> _getNamesConflictingAt(AstNode node) {
-  Set<String> result = Set<String>();
+  Set<String> result = <String>{};
   // local variables and functions
   {
     SourceRange localsRange = _getLocalsConflictingRange(node);
@@ -166,7 +166,7 @@
   {
     ClassElement enclosingClassElement = getEnclosingClassElement(node);
     if (enclosingClassElement != null) {
-      Set<ClassElement> elements = Set<ClassElement>();
+      Set<ClassElement> elements = <ClassElement>{};
       elements.add(enclosingClassElement);
       elements.addAll(getSuperClasses(enclosingClassElement));
       for (ClassElement classElement in elements) {
@@ -208,7 +208,7 @@
   _SourcePart _methodExpressionPart;
   _SourcePart _methodStatementsPart;
   final List<_ReferenceProcessor> _referenceProcessors = [];
-  final Set<Element> _alreadyMadeAsync = Set<Element>();
+  final Set<Element> _alreadyMadeAsync = <Element>{};
 
   InlineMethodRefactoringImpl(
       this.searchEngine, this.resolveResult, this.offset)
@@ -608,7 +608,7 @@
       // PropertyAccessorElement
       if (ref._methodElement is PropertyAccessorElement) {
         Expression usage = _node;
-        Expression target = null;
+        Expression target;
         bool cascade = false;
         if (nodeParent is PrefixedIdentifier) {
           PrefixedIdentifier propertyAccess = nodeParent;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
index 5bf14cb..11beace 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
@@ -147,7 +147,7 @@
   final bool isRename;
 
   final RefactoringStatus result = RefactoringStatus();
-  Set<Element> elements = Set<Element>();
+  Set<Element> elements = <Element>{};
   List<SearchMatch> references = <SearchMatch>[];
 
   _ClassMemberValidator.forCreate(
@@ -307,7 +307,7 @@
     if (element is ClassMemberElement) {
       elements = await getHierarchyMembers(searchEngine, element);
     } else {
-      elements = Set.from([element]);
+      elements = {element};
     }
   }
 
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
index 1e7a01a..80f05cd 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
@@ -56,7 +56,7 @@
     // update declaration
     {
       PrefixElement prefix = element.prefix;
-      SourceEdit edit = null;
+      SourceEdit edit;
       if (newName.isEmpty) {
         ImportDirective node = _findNode();
         int uriEnd = node.uri.end;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
index 0d06dbd..2d3c138 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
@@ -112,7 +112,7 @@
   final String newName;
   final LocalElement target;
   final Map<Element, SourceRange> visibleRangeMap;
-  final Set<Element> conflictingLocals = Set<Element>();
+  final Set<Element> conflictingLocals = <Element>{};
 
   _ConflictValidatorVisitor(
     this.result,
diff --git a/pkg/analysis_server/lib/src/services/search/hierarchy.dart b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
index 0931c5a..5deff3b 100644
--- a/pkg/analysis_server/lib/src/services/search/hierarchy.dart
+++ b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
@@ -184,7 +184,7 @@
 Set<ClassElement> getSuperClasses(ClassElement seed) {
   Set<ClassElement> result = HashSet<ClassElement>();
   // prepare queue
-  List<ClassElement> queue = List<ClassElement>();
+  List<ClassElement> queue = <ClassElement>[];
   queue.add(seed);
   // process queue
   while (queue.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart b/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart
index 3390b97..74af165 100644
--- a/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart
+++ b/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart
@@ -27,8 +27,8 @@
 
     String libraryUriStr = type.librarySource.uri.toString();
     bool hasSubtypes = false;
-    Set<String> visitedIds = Set<String>();
-    Set<String> members = Set<String>();
+    Set<String> visitedIds = <String>{};
+    Set<String> members = <String>{};
 
     Future<void> addMembers(ClassElement type, SubtypeResult subtype) async {
       // TODO(brianwilkerson) Determine whether this await is necessary.
@@ -61,7 +61,7 @@
   Future<Set<ClassElement>> searchAllSubtypes(ClassElement type) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
-    Set<ClassElement> allSubtypes = Set<ClassElement>();
+    Set<ClassElement> allSubtypes = <ClassElement>{};
 
     Future<void> addSubtypes(ClassElement type) async {
       // TODO(brianwilkerson) Determine whether this await is necessary.
@@ -134,7 +134,7 @@
   Future<List<SearchMatch>> searchTopLevelDeclarations(String pattern) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
-    Set<Element> allElements = Set<Element>();
+    Set<Element> allElements = <Element>{};
     RegExp regExp = RegExp(pattern);
     List<AnalysisDriver> drivers = _drivers.toList();
     for (AnalysisDriver driver in drivers) {
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index a879b47..e52b7b0 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -20,6 +20,7 @@
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:linter/src/rules.dart';
+import 'package:meta/meta.dart';
 
 import 'src/utilities/mock_packages.dart';
 
@@ -27,7 +28,7 @@
  * Finds an [Element] with the given [name].
  */
 Element findChildElement(Element root, String name, [ElementKind kind]) {
-  Element result = null;
+  Element result;
   root.accept(_ElementVisitorFunctionWrapper((Element element) {
     if (element.name != name) {
       return;
@@ -182,6 +183,7 @@
     return resolveResult.unit;
   }
 
+  @mustCallSuper
   void setUp() {
     registerLintRules();
 
diff --git a/pkg/analysis_server/test/analysis/get_hover_test.dart b/pkg/analysis_server/test/analysis/get_hover_test.dart
index c13e5bf..d4fee97 100644
--- a/pkg/analysis_server/test/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/analysis/get_hover_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
 
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_generated.dart';
@@ -637,6 +638,30 @@
     expect(hover, isNull);
   }
 
+  test_nonNullable() async {
+    createAnalysisOptionsFile(experiments: ['non-nullable']);
+    addTestFile('''
+int? f(double? a) => null;
+
+main() {
+  f(null);
+}
+''');
+    var hover = await prepareHover('f(null)');
+    _assertJsonText(hover, r'''
+{
+  "offset": 39,
+  "length": 1,
+  "containingLibraryPath": "/project/bin/test.dart",
+  "containingLibraryName": "bin/test.dart",
+  "elementDescription": "int? f(double? a)",
+  "elementKind": "function",
+  "isDeprecated": false,
+  "staticType": "int? Function(double?)"
+}
+''');
+  }
+
   test_parameter_declaration_fieldFormal() async {
     addTestFile('''
 class A {
@@ -702,4 +727,15 @@
     expect(hover.elementKind, 'parameter');
     expect(hover.staticType, 'int');
   }
+
+  void _assertJsonText(Object object, String expected) {
+    expected = expected.trimRight();
+    var actual = JsonEncoder.withIndent('  ').convert(object);
+    if (actual != expected) {
+      print('-----');
+      print(actual);
+      print('-----');
+    }
+    expect(actual, expected);
+  }
 }
diff --git a/pkg/analysis_server/test/analysis_server_test.dart b/pkg/analysis_server/test/analysis_server_test.dart
index c4e462d..56bba4d 100644
--- a/pkg/analysis_server/test/analysis_server_test.dart
+++ b/pkg/analysis_server/test/analysis_server_test.dart
@@ -36,7 +36,7 @@
    */
   Future do_not_test_no_duplicate_notifications() async {
     // Subscribe to STATUS so we'll know when analysis is done.
-    server.serverServices = [ServerService.STATUS].toSet();
+    server.serverServices = {ServerService.STATUS};
     newFolder('/foo');
     newFolder('/bar');
     newFile('/foo/foo.dart', content: 'import "../bar/bar.dart";');
@@ -45,7 +45,7 @@
     Map<AnalysisService, Set<String>> subscriptions =
         <AnalysisService, Set<String>>{};
     for (AnalysisService service in AnalysisService.VALUES) {
-      subscriptions[service] = <String>[bar.path].toSet();
+      subscriptions[service] = <String>{bar.path};
     }
     // The following line causes the isolate to continue running even though the
     // test completes.
@@ -58,7 +58,7 @@
     await server.onAnalysisComplete;
     expect(server.statusAnalyzing, isFalse);
     expect(channel.notificationsReceived, isNotEmpty);
-    Set<String> notificationTypesReceived = Set<String>();
+    Set<String> notificationTypesReceived = <String>{};
     for (Notification notification in channel.notificationsReceived) {
       String notificationType = notification.event;
       switch (notificationType) {
@@ -139,7 +139,7 @@
 ''');
     server.setAnalysisRoots('0', [convertPath('/project')], [], {});
     server.setAnalysisSubscriptions(<AnalysisService, Set<String>>{
-      AnalysisService.NAVIGATION: Set<String>.from([path])
+      AnalysisService.NAVIGATION: <String>{path}
     });
 
     // We respect subscriptions, even for excluded files.
@@ -159,7 +159,7 @@
 ''');
     server.setAnalysisRoots('0', [convertPath('/project')], [], {});
     server.setAnalysisSubscriptions(<AnalysisService, Set<String>>{
-      AnalysisService.NAVIGATION: Set<String>.from([path])
+      AnalysisService.NAVIGATION: <String>{path}
     });
 
     // We respect subscriptions, even for excluded files.
diff --git a/pkg/analysis_server/test/channel/byte_stream_channel_test.dart b/pkg/analysis_server/test/channel/byte_stream_channel_test.dart
index b49a2fd..9050f25 100644
--- a/pkg/analysis_server/test/channel/byte_stream_channel_test.dart
+++ b/pkg/analysis_server/test/channel/byte_stream_channel_test.dart
@@ -265,7 +265,7 @@
   Encoding encoding;
 
   @override
-  Future done = null;
+  Future done;
 
   @override
   void add(List<int> data) {}
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index afcc4cb..895a345 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -1645,7 +1645,7 @@
 
   TestContextManagerCallbacks callbacks;
 
-  String projPath = null;
+  String projPath;
 
   AnalysisError missing_return =
       AnalysisError(null, 0, 1, HintCode.MISSING_RETURN, [
diff --git a/pkg/analysis_server/test/integration/analysis/get_hover_test.dart b/pkg/analysis_server/test/integration/analysis/get_hover_test.dart
index 5fd6acf..f665ff1 100644
--- a/pkg/analysis_server/test/integration/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/get_hover_test.dart
@@ -69,9 +69,9 @@
     List<String> staticTypeRegexps, {
     bool isLocal = false,
     bool isCore = false,
-    String docRegexp = null,
+    String docRegexp,
     bool isLiteral = false,
-    List<String> parameterRegexps = null,
+    List<String> parameterRegexps,
   }) {
     int offset = text.indexOf(target);
     return sendAnalysisGetHover(pathname, offset).then((result) async {
diff --git a/pkg/analysis_server/test/integration/analysis/highlights2_test.dart b/pkg/analysis_server/test/integration/analysis/highlights2_test.dart
index dbfdde6..9967f27 100644
--- a/pkg/analysis_server/test/integration/analysis/highlights2_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/highlights2_test.dart
@@ -42,7 +42,7 @@
         String highlightedText = text.substring(startIndex, endIndex);
         HighlightRegionType type = region.type;
         if (!highlights.containsKey(type)) {
-          highlights[type] = Set<String>();
+          highlights[type] = <String>{};
         }
         highlights[type].add(highlightedText);
       }
diff --git a/pkg/analysis_server/test/integration/analysis/highlights_test.dart b/pkg/analysis_server/test/integration/analysis/highlights_test.dart
index 511c27b..375cf79 100644
--- a/pkg/analysis_server/test/integration/analysis/highlights_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/highlights_test.dart
@@ -40,7 +40,7 @@
         String highlightedText = text.substring(startIndex, endIndex);
         HighlightRegionType type = region.type;
         if (!highlights.containsKey(type)) {
-          highlights[type] = Set<String>();
+          highlights[type] = <String>{};
         }
         highlights[type].add(highlightedText);
       }
diff --git a/pkg/analysis_server/test/integration/analysis/overrides_test.dart b/pkg/analysis_server/test/integration/analysis/overrides_test.dart
index c21b798..9505e02b 100644
--- a/pkg/analysis_server/test/integration/analysis/overrides_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/overrides_test.dart
@@ -95,7 +95,7 @@
         List<OverriddenMember> interfaceMembers = override.interfaceMembers;
         if (expectedOverridesInterfaces.isNotEmpty) {
           expect(interfaceMembers, isNotNull);
-          Set<String> actualOverridesInterfaces = Set<String>();
+          Set<String> actualOverridesInterfaces = <String>{};
           for (OverriddenMember overriddenMember in interfaceMembers) {
             expect(overriddenMember.element.name, equals(methodName));
             String className = overriddenMember.className;
diff --git a/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart b/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart
index bff7062..d597d84 100644
--- a/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart
@@ -22,7 +22,7 @@
     // To avoid races, set up listeners for the notifications before we initialise
     // and track which event came first to ensure they arrived in the expected
     // order.
-    bool firstNotificationWasAnalyzing = null;
+    bool firstNotificationWasAnalyzing;
     final startNotification = waitForAnalysisStart()
         .then((_) => firstNotificationWasAnalyzing ??= true);
     final completeNotification = waitForAnalysisComplete()
diff --git a/pkg/analysis_server/test/integration/support/integration_tests.dart b/pkg/analysis_server/test/integration/support/integration_tests.dart
index a7b0193..c5beb46 100644
--- a/pkg/analysis_server/test/integration/support/integration_tests.dart
+++ b/pkg/analysis_server/test/integration/support/integration_tests.dart
@@ -488,7 +488,7 @@
   /**
    * Stopwatch that we use to generate timing information for debug output.
    */
-  Stopwatch _time = Stopwatch();
+  final Stopwatch _time = Stopwatch();
 
   /**
    * The [currentElapseTime] at which the last communication was received from the server
diff --git a/pkg/analysis_server/test/lsp/analyzer_status_test.dart b/pkg/analysis_server/test/lsp/analyzer_status_test.dart
index 1f5859c..4ddd08f 100644
--- a/pkg/analysis_server/test/lsp/analyzer_status_test.dart
+++ b/pkg/analysis_server/test/lsp/analyzer_status_test.dart
@@ -22,7 +22,7 @@
     // To avoid races, set up listeners for the notifications before we initialise
     // and track which event came first to ensure they arrived in the expected
     // order.
-    bool firstNotificationWasAnalyzing = null;
+    bool firstNotificationWasAnalyzing;
     final startNotification = waitForAnalysisStart()
         .then((_) => firstNotificationWasAnalyzing ??= true);
     final completeNotification = waitForAnalysisComplete()
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart
index c30d692..8ff865f 100644
--- a/pkg/analysis_server/test/lsp/diagnostic_test.dart
+++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -87,7 +87,7 @@
 
     newFile(dotFolderFilePath, content: 'String a = 1;');
 
-    List<Diagnostic> diagnostics = null;
+    List<Diagnostic> diagnostics;
     waitForDiagnostics(dotFolderFileUri).then((d) => diagnostics = d);
 
     // Send a request for a hover.
diff --git a/pkg/analysis_server/test/mocks.dart b/pkg/analysis_server/test/mocks.dart
index f029b61..348afa7 100644
--- a/pkg/analysis_server/test/mocks.dart
+++ b/pkg/analysis_server/test/mocks.dart
@@ -203,34 +203,34 @@
 
 class MockSource extends StringTypedMock implements Source {
   @override
-  TimestampedData<String> contents = null;
+  TimestampedData<String> contents;
 
   @override
-  String encoding = null;
+  String encoding;
 
   @override
-  String fullName = null;
+  String fullName;
 
   @override
-  bool isInSystemLibrary = null;
+  bool isInSystemLibrary;
 
   @override
-  Source librarySource = null;
+  Source librarySource;
 
   @override
-  int modificationStamp = null;
+  int modificationStamp;
 
   @override
-  String shortName = null;
+  String shortName;
 
   @override
-  Source source = null;
+  Source source;
 
   @override
-  Uri uri = null;
+  Uri uri;
 
   @override
-  UriKind uriKind = null;
+  UriKind uriKind;
 
   MockSource([String name = 'mocked.dart']) : super(name);
 
diff --git a/pkg/analysis_server/test/protocol_server_test.dart b/pkg/analysis_server/test/protocol_server_test.dart
index b855259..627a3d3 100644
--- a/pkg/analysis_server/test/protocol_server_test.dart
+++ b/pkg/analysis_server/test/protocol_server_test.dart
@@ -302,7 +302,7 @@
   String message;
 
   @override
-  String correction = null;
+  String correction;
 
   @override
   int length;
diff --git a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
index b8ebe4e..d1e5ff2 100644
--- a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
@@ -80,7 +80,7 @@
       List<int> requiredParamIndices = const <int>[],
       bool includeColon = true,
       bool includeComma = false}) {
-    List<CompletionSuggestion> expected = List<CompletionSuggestion>();
+    List<CompletionSuggestion> expected = <CompletionSuggestion>[];
     int paramIndex = 0;
     namedArgumentsWithTypes.forEach((String name, String type) {
       String completion = includeColon ? '$name: ' : name;
@@ -106,7 +106,7 @@
    * Assert that the specified suggestions are the only suggestions.
    */
   void assertSuggestions(List<String> suggestions) {
-    List<CompletionSuggestion> expected = List<CompletionSuggestion>();
+    List<CompletionSuggestion> expected = <CompletionSuggestion>[];
     for (String suggestion in suggestions) {
       // Selection offset should be before any trailing commas.
       int selectionOffset =
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
index a42d116..25ba5d3 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
@@ -8,9 +8,10 @@
 import 'package:analysis_server/src/services/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart'
-    show DartCompletionRequestImpl;
+    show DartCompletionManager, DartCompletionRequestImpl;
 import 'package:analyzer/src/generated/parser.dart' as analyzer;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:meta/meta.dart';
 import 'package:test/test.dart';
 
 import '../../../abstract_context.dart';
@@ -21,16 +22,58 @@
   return c1.compareTo(c2);
 }
 
-abstract class DartCompletionContributorTest extends AbstractContextTest {
+/// Base class for tests that validate individual [DartCompletionContributor]
+/// suggestions.
+abstract class DartCompletionContributorTest
+    extends _BaseDartCompletionContributorTest {
+  DartCompletionContributor contributor;
+
+  @nonVirtual
+  @override
+  Future<List<CompletionSuggestion>> computeContributedSuggestions(
+      DartCompletionRequest request) async {
+    return contributor.computeSuggestions(request);
+  }
+
+  DartCompletionContributor createContributor();
+
+  @override
+  void setUp() {
+    super.setUp();
+    contributor = createContributor();
+  }
+}
+
+/// Base class for tests that validate [DartCompletionManager] suggestions.
+class DartCompletionManagerTest extends _BaseDartCompletionContributorTest {
+  DartCompletionManager completionManager;
+
+  @nonVirtual
+  @override
+  Future<List<CompletionSuggestion>> computeContributedSuggestions(
+      DartCompletionRequest request) async {
+    final baseRequest = CompletionRequestImpl(
+        request.result, completionOffset, CompletionPerformance());
+    return completionManager.computeSuggestions(baseRequest);
+  }
+
+  @override
+  setUp() {
+    super.setUp();
+    completionManager = DartCompletionManager();
+  }
+}
+
+abstract class _BaseDartCompletionContributorTest extends AbstractContextTest {
   static const String _UNCHECKED = '__UNCHECKED__';
   String testFile;
   int completionOffset;
   int replacementOffset;
   int replacementLength;
-  DartCompletionContributor contributor;
-  DartCompletionRequest request;
-  List<CompletionSuggestion> suggestions;
 
+  DartCompletionRequest request;
+
+  List<CompletionSuggestion> suggestions;
   /**
    * If `true` and `null` is specified as the suggestion's expected returnType
    * then the actual suggestion is expected to have a `dynamic` returnType.
@@ -75,7 +118,7 @@
     expect(suggestion.hasNamedParameters, isNotNull);
   }
 
-  void assertNoSuggestions({CompletionSuggestionKind kind = null}) {
+  void assertNoSuggestions({CompletionSuggestionKind kind}) {
     if (kind == null) {
       if (suggestions.isNotEmpty) {
         failedCompletion('Expected no suggestions', suggestions);
@@ -102,7 +145,7 @@
   CompletionSuggestion assertSuggest(String completion,
       {CompletionSuggestionKind csKind = CompletionSuggestionKind.INVOCATION,
       int relevance = DART_RELEVANCE_DEFAULT,
-      ElementKind elemKind = null,
+      ElementKind elemKind,
       bool isDeprecated = false,
       bool isPotential = false,
       String elemFile,
@@ -461,6 +504,9 @@
     return cs;
   }
 
+  Future<List<CompletionSuggestion>> computeContributedSuggestions(
+      DartCompletionRequest request);
+
   Future computeSuggestions({int times = 200}) async {
     var resolveResult = await session.getResolvedUnit(testFile);
     CompletionRequestImpl baseRequest = CompletionRequestImpl(
@@ -474,12 +520,10 @@
     replacementLength = range.length;
 
     // Request completions
-    suggestions = await contributor.computeSuggestions(request);
+    suggestions = await computeContributedSuggestions(request);
     expect(suggestions, isNotNull, reason: 'expected suggestions');
   }
 
-  DartCompletionContributor createContributor();
-
   void failedCompletion(String message,
       [Iterable<CompletionSuggestion> completions]) {
     StringBuffer sb = StringBuffer(message);
@@ -495,9 +539,9 @@
   }
 
   CompletionSuggestion getSuggest(
-      {String completion = null,
-      CompletionSuggestionKind csKind = null,
-      ElementKind elemKind = null}) {
+      {String completion,
+      CompletionSuggestionKind csKind,
+      ElementKind elemKind}) {
     CompletionSuggestion cs;
     if (suggestions != null) {
       suggestions.forEach((CompletionSuggestion s) {
@@ -546,6 +590,5 @@
   void setUp() {
     super.setUp();
     testFile = convertPath('/home/test/lib/test.dart');
-    contributor = createContributor();
   }
 }
diff --git a/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart
index c4d1a54..71142b0 100644
--- a/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart
@@ -144,6 +144,24 @@
 
   test_literal_doesNotMatch() async {
     addTestSource('''
+extension E on String {
+  bool a(int b, int c) {}
+  int get b => 0;
+  set c(int d) {}
+}
+
+void f() {
+  0.^
+}
+''');
+    await computeSuggestions();
+    assertNotSuggested('a');
+    assertNotSuggested('b');
+    assertNotSuggested('c');
+  }
+
+  test_literal_doesNotMatch_generic() async {
+    addTestSource('''
 extension E<T extends num> on List<T> {
   bool a(int b, int c) {}
   int get b => 0;
diff --git a/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart
index c06ca93..9a53e8d 100644
--- a/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart
@@ -28,7 +28,6 @@
     Keyword.FALSE,
     Keyword.FOR,
     Keyword.IF,
-    Keyword.NEW,
     Keyword.NULL,
     Keyword.TRUE,
   ];
@@ -38,7 +37,6 @@
   static const List<Keyword> EXPRESSION_START_INSTANCE = [
     Keyword.CONST,
     Keyword.FALSE,
-    Keyword.NEW,
     Keyword.NULL,
     Keyword.SUPER,
     Keyword.THIS,
@@ -48,7 +46,6 @@
   static const List<Keyword> EXPRESSION_START_NO_INSTANCE = [
     Keyword.CONST,
     Keyword.FALSE,
-    Keyword.NEW,
     Keyword.NULL,
     Keyword.TRUE,
   ];
@@ -174,7 +171,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SUPER,
       Keyword.SWITCH,
@@ -201,7 +197,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SUPER,
       Keyword.SWITCH,
@@ -228,7 +223,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SWITCH,
       Keyword.THROW,
@@ -252,7 +246,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SUPER,
       Keyword.THIS,
@@ -278,7 +271,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SWITCH,
       Keyword.THROW,
@@ -304,7 +296,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SUPER,
       Keyword.SWITCH,
@@ -332,7 +323,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SWITCH,
       Keyword.THROW,
@@ -355,7 +345,6 @@
       Keyword.FINAL,
       Keyword.FOR,
       Keyword.IF,
-      Keyword.NEW,
       Keyword.RETURN,
       Keyword.SWITCH,
       Keyword.THROW,
@@ -381,9 +370,9 @@
   void assertSuggestKeywords(Iterable<Keyword> expectedKeywords,
       {List<String> pseudoKeywords = NO_PSEUDO_KEYWORDS,
       int relevance = DART_RELEVANCE_KEYWORD}) {
-    Set<String> expectedCompletions = Set<String>();
+    Set<String> expectedCompletions = <String>{};
     Map<String, int> expectedOffsets = <String, int>{};
-    Set<String> actualCompletions = Set<String>();
+    Set<String> actualCompletions = <String>{};
     expectedCompletions.addAll(expectedKeywords.map((keyword) {
       String text = keyword.lexeme;
       if (['import', 'export', 'part'].contains(text)) {
diff --git a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
index 93380d8..6cdf195 100644
--- a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
@@ -37,6 +37,45 @@
     _assertNoOverrideContaining('bar');
   }
 
+  test_customOperator() async {
+    addTestSource('''
+class A {
+  void operator &(A other) { }
+}
+class B extends A {
+  other^
+}
+''');
+    await computeSuggestions();
+    _assertOverride('''
+@override
+  void operator &(A other) {
+    // TODO: implement &
+    super & other;
+  }''',
+        displayText: '&(A other) { … }',
+        selectionOffset: 68,
+        selectionLength: 14);
+  }
+
+  test_equalsOperator() async {
+    addTestSource('''
+class A {
+  other^
+}
+''');
+    await computeSuggestions();
+    _assertOverride('''
+@override
+  bool operator ==(Object other) {
+    // TODO: implement ==
+    return super == other;
+  }''',
+        displayText: '==(Object other) { … }',
+        selectionOffset: 75,
+        selectionLength: 22);
+  }
+
   test_fromMultipleSuperclasses() async {
     addTestSource(r'''
 class A {
diff --git a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
index d8c4fd5..3f50c49 100644
--- a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
+++ b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
@@ -25,7 +25,7 @@
   var res = sortedNodes as String;
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('as String', (node) => node is AsExpression);
     expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
         unorderedEquals(['sortedNodes', 'nodes']));
@@ -38,7 +38,7 @@
   TreeNode node = null;
 }
 ''');
-    Set<String> excluded = Set<String>.from([]);
+    Set<String> excluded = <String>{};
     DartType expectedType = findLocalVariable('node').type;
     Expression assignedExpression =
         findNodeAtString('null;', (node) => node is NullLiteral);
@@ -58,12 +58,12 @@
     // first choice for "double" is "d"
     expect(
         getVariableNameSuggestionsForExpression(
-            expectedType, assignedExpression, Set.from([])),
+            expectedType, assignedExpression, {}),
         unorderedEquals(['d']));
     // if "d" is used, try "e", "f", etc
     expect(
         getVariableNameSuggestionsForExpression(
-            expectedType, assignedExpression, Set.from(['d', 'e'])),
+            expectedType, assignedExpression, {'d', 'e'}),
         unorderedEquals(['f']));
   }
 
@@ -78,12 +78,12 @@
     // first choice for "int" is "i"
     expect(
         getVariableNameSuggestionsForExpression(
-            expectedType, assignedExpression, Set.from([])),
+            expectedType, assignedExpression, {}),
         unorderedEquals(['i']));
     // if "i" is used, try "j", "k", etc
     expect(
         getVariableNameSuggestionsForExpression(
-            expectedType, assignedExpression, Set.from(['i', 'j'])),
+            expectedType, assignedExpression, {'i', 'j'}),
         unorderedEquals(['k']));
   }
 
@@ -98,7 +98,7 @@
     // first choice for "String" is "s"
     expect(
         getVariableNameSuggestionsForExpression(
-            expectedType, assignedExpression, Set.from([])),
+            expectedType, assignedExpression, {}),
         unorderedEquals(['s']));
   }
 
@@ -110,7 +110,7 @@
   }
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('new List');
     expect(
         getVariableNameSuggestionsForExpression(null, expr, excluded,
@@ -129,7 +129,7 @@
   print(topNodes[0]);
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('topNodes[0]').parent;
     var names = getVariableNameSuggestionsForExpression(null, expr, excluded);
     expect(names, unorderedEquals(['topNode', 'node', 'object']));
@@ -145,7 +145,7 @@
   new NoSuchClass.named();
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     expect(
         getVariableNameSuggestionsForExpression(
             null, findNodeAtString('new NoSuchClass()'), excluded),
@@ -171,7 +171,7 @@
   foo(a: 111, c: 333, b: 222);
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     {
       var expr = findNodeAtString('111');
       expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
@@ -196,7 +196,7 @@
   foo(111, 222, 333);
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     {
       var expr = findNodeAtString('111');
       expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
@@ -221,7 +221,7 @@
   foo(111, 222);
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     {
       var expr = findNodeAtString('111');
       expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
@@ -240,7 +240,7 @@
   var res = p.getSortedNodes();
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('p.get', (node) => node is MethodInvocation);
     expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
         unorderedEquals(['sortedNodes', 'nodes']));
@@ -252,7 +252,7 @@
   var res = p.sortedNodes();
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('p.sorted', (node) => node is MethodInvocation);
     expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
         unorderedEquals(['sortedNodes', 'nodes']));
@@ -264,7 +264,7 @@
   var res = p.get();
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('p.get', (node) => node is MethodInvocation);
     expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
         unorderedEquals([]));
@@ -276,7 +276,7 @@
   var res = p.sortedNodes;
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     expect(
         getVariableNameSuggestionsForExpression(
             null,
@@ -292,7 +292,7 @@
   p._computeSuffix();
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     expect(
         getVariableNameSuggestionsForExpression(
             null,
@@ -313,7 +313,7 @@
   var res = p.q.sortedNodes;
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     PropertyAccess expression =
         findNodeAtString('p.q.sorted', (node) => node is PropertyAccess);
     expect(getVariableNameSuggestionsForExpression(null, expression, excluded),
@@ -327,7 +327,7 @@
   var res = sortedNodes;
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     var expr = findNodeAtString('sortedNodes;');
     expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
         unorderedEquals(['sortedNodes', 'nodes']));
@@ -340,7 +340,7 @@
   var res = getSortedNodes();
 }
 ''');
-    var excluded = Set<String>.from([]);
+    var excluded = <String>{};
     expect(
         getVariableNameSuggestionsForExpression(
             null,
@@ -352,14 +352,14 @@
 
   void test_forText() {
     {
-      Set<String> excluded = Set<String>.from([]);
+      Set<String> excluded = <String>{};
       List<String> suggestions =
           getVariableNameSuggestionsForText('Goodbye, cruel world!', excluded);
       expect(suggestions,
           unorderedEquals(['goodbyeCruelWorld', 'cruelWorld', 'world']));
     }
     {
-      Set<String> excluded = Set<String>.from(['world']);
+      Set<String> excluded = <String>{'world'};
       List<String> suggestions =
           getVariableNameSuggestionsForText('Goodbye, cruel world!', excluded);
       expect(suggestions,
diff --git a/pkg/analysis_server/test/services/refactoring/abstract_rename.dart b/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
index 1a16fef..61b96c0 100644
--- a/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
+++ b/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
@@ -23,7 +23,7 @@
    * of the given [searches].
    */
   void assertPotentialEdits(List<String> searches) {
-    Set<int> expectedOffsets = Set<int>();
+    Set<int> expectedOffsets = <int>{};
     for (String search in searches) {
       int offset = findOffset(search);
       expectedOffsets.add(offset);
diff --git a/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart b/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart
index 297fdf6..5515b02 100644
--- a/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart
+++ b/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart
@@ -10,8 +10,8 @@
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
-import 'nnbd_migration_test_base.dart';
 import '../../../analysis_abstract.dart';
+import 'nnbd_migration_test_base.dart';
 
 main() {
   defineReflectiveSuite(() {
@@ -598,6 +598,45 @@
     assertDetail(detail: regions[2].details[0], offset: 90, length: 1);
   }
 
+  test_listConstructor_length() async {
+    UnitInfo unit = await buildInfoForSingleTestFile('''
+void f() {
+  List<int> list = List<int>(10);
+}
+''', migratedContent: '''
+void f() {
+  List<int?> list = List<int?>(10);
+}
+''');
+    List<RegionInfo> regions = unit.regions;
+    expect(regions, hasLength(2));
+    // regions[0] is `num? a`.
+    assertRegion(region: regions[1], offset: 39, details: [
+      "List value type must be nullable because a length is specified,"
+          " and the list items are initialized as null."
+    ]);
+  }
+
+  @FailingTest(issue: "https://github.com/dart-lang/sdk/issues/40064")
+  test_listConstructor_length_implicitType() async {
+    UnitInfo unit = await buildInfoForSingleTestFile('''
+void f() {
+  List<int> list = List(10);
+}
+''', migratedContent: '''
+void f() {
+  List<int?> list = List(10);
+}
+''');
+    List<RegionInfo> regions = unit.regions;
+    expect(regions, hasLength(2));
+    // regions[0] is `num? a`.
+    assertRegion(region: regions[1], offset: 40, details: [
+      "List value type must be nullable because a length is specified,"
+          " and the list items are initialized as null."
+    ]);
+  }
+
   test_listLiteralTypeArgument_collectionIf() async {
     UnitInfo unit = await buildInfoForSingleTestFile('''
 void f() {
diff --git a/pkg/analysis_server/test/src/plugin/notification_manager_test.dart b/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
index bee200f..57035e8 100644
--- a/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
+++ b/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
@@ -62,7 +62,7 @@
 
   void test_handlePluginNotification_folding() {
     manager.setSubscriptions({
-      server.AnalysisService.FOLDING: Set.from([fileA, fileB])
+      server.AnalysisService.FOLDING: {fileA, fileB}
     });
     FoldingRegion region1 = foldingRegion(10, 3);
     FoldingRegion region2 = foldingRegion(20, 6);
@@ -74,7 +74,7 @@
 
   void test_handlePluginNotification_highlights() {
     manager.setSubscriptions({
-      server.AnalysisService.HIGHLIGHTS: Set.from([fileA, fileB])
+      server.AnalysisService.HIGHLIGHTS: {fileA, fileB}
     });
     HighlightRegion region1 = highlightRegion(10, 3);
     HighlightRegion region2 = highlightRegion(20, 6);
@@ -86,7 +86,7 @@
 
   void test_handlePluginNotification_naviation() {
     manager.setSubscriptions({
-      server.AnalysisService.NAVIGATION: Set.from([fileA, fileB])
+      server.AnalysisService.NAVIGATION: {fileA, fileB}
     });
     plugin.AnalysisNavigationParams pluginParams =
         pluginNavigationParams(0, 0, file: fileA);
@@ -99,7 +99,7 @@
 
   void test_handlePluginNotification_occurences() {
     manager.setSubscriptions({
-      server.AnalysisService.OCCURRENCES: Set.from([fileA, fileB])
+      server.AnalysisService.OCCURRENCES: {fileA, fileB}
     });
     Occurrences occurrences1 = occurrences(0, 0);
     Occurrences occurrences2 = occurrences(5, 7);
@@ -112,7 +112,7 @@
 
   void test_handlePluginNotification_outline() {
     manager.setSubscriptions({
-      server.AnalysisService.OUTLINE: Set.from([fileA, fileB])
+      server.AnalysisService.OUTLINE: {fileA, fileB}
     });
     Outline outline1 = outline(0, 0);
     plugin.AnalysisOutlineParams params =
@@ -177,7 +177,7 @@
 
   void test_recordFoldingRegions_withSubscription() {
     manager.setSubscriptions({
-      server.AnalysisService.FOLDING: Set.from([fileA, fileB])
+      server.AnalysisService.FOLDING: {fileA, fileB}
     });
     //
     // Regions should be reported when they are recorded.
@@ -216,7 +216,7 @@
 
   void test_recordHighlightRegions_withSubscription() {
     manager.setSubscriptions({
-      server.AnalysisService.HIGHLIGHTS: Set.from([fileA, fileB])
+      server.AnalysisService.HIGHLIGHTS: {fileA, fileB}
     });
     //
     // Regions should be reported when they are recorded.
@@ -256,7 +256,7 @@
 
   void test_recordNavigationParams_withSubscription() {
     manager.setSubscriptions({
-      server.AnalysisService.NAVIGATION: Set.from([fileA, fileB])
+      server.AnalysisService.NAVIGATION: {fileA, fileB}
     });
     //
     // Parameters should be reported when they are recorded.
@@ -324,7 +324,7 @@
 
   void test_recordOccurrences_withSubscription() {
     manager.setSubscriptions({
-      server.AnalysisService.OCCURRENCES: Set.from([fileA, fileB])
+      server.AnalysisService.OCCURRENCES: {fileA, fileB}
     });
     //
     // Occurrences should be reported when they are recorded.
@@ -367,7 +367,7 @@
     // TODO(brianwilkerson) Figure out outlines. What should we do when merge
     // cannot produce a single outline?
     manager.setSubscriptions({
-      server.AnalysisService.OUTLINE: Set.from([fileA, fileB])
+      server.AnalysisService.OUTLINE: {fileA, fileB}
     });
     //
     // Outlines should be reported when they are recorded.
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_generic_function_syntax_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_generic_function_syntax_test.dart
index 13a2b32..8c615c2 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_generic_function_syntax_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_generic_function_syntax_test.dart
@@ -11,12 +11,13 @@
 
 main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(ConvertToGenericFunctionSyntaxTest);
+    defineReflectiveTests(PreferGenericFunctionTypeAliasesTest);
+    defineReflectiveTests(UseFunctionTypeSyntaxForParametersTest);
   });
 }
 
 @reflectiveTest
-class ConvertToGenericFunctionSyntaxTest extends FixProcessorLintTest {
+class PreferGenericFunctionTypeAliasesTest extends FixProcessorLintTest {
   @override
   FixKind get kind => DartFixKind.CONVERT_TO_GENERIC_FUNCTION_SYNTAX;
 
@@ -65,6 +66,15 @@
 typedef F<P, R> = R Function(P x);
 ''');
   }
+}
+
+@reflectiveTest
+class UseFunctionTypeSyntaxForParametersTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.CONVERT_TO_GENERIC_FUNCTION_SYNTAX;
+
+  @override
+  String get lintCode => LintNames.use_function_type_syntax_for_parameters;
 
   test_functionTypedParameter_noParameterTypes() async {
     await resolveTestUnit('''
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_set_literal_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_set_literal_test.dart
index b50cf3a..dd47239 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_set_literal_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_set_literal_test.dart
@@ -59,6 +59,21 @@
 ''');
   }
 
+  @failingTest
+  test_default_typeArg_linkedHashSet() async {
+    // LinkedHashSet isn't converted even though the lint reports that case.
+    await resolveTestUnit('''
+import 'dart:collection';
+
+var s = /*LINT*/LinkedHashSet<int>();
+''');
+    await assertHasFix('''
+import 'dart:collection';
+
+var s = /*LINT*/<int>{};
+''');
+  }
+
   test_from_empty() async {
     await resolveTestUnit('''
 var s = /*LINT*/Set.from([]);
@@ -68,6 +83,20 @@
 ''');
   }
 
+  @failingTest
+  test_from_inferred() async {
+    // _setWouldBeInferred does not check for inference based on the parameter
+    // type.
+    await resolveTestUnit('''
+void f(Set<int> s) {}
+var s = f(/*LINT*/Set.from([]));
+''');
+    await assertHasFix('''
+void f(Set<int> s) {}
+var s = f(/*LINT*/{});
+''');
+  }
+
   test_from_newKeyword() async {
     await resolveTestUnit('''
 var s = /*LINT*/new Set.from([2, 3]);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart
new file mode 100644
index 0000000..035720c
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToWhereTypeTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToWhereTypeTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.CONVERT_TO_WHERE_TYPE;
+
+  @override
+  String get lintCode => LintNames.prefer_iterable_whereType;
+
+  test_default_declaredType() async {
+    await resolveTestUnit('''
+Iterable<C> f(List<Object> list) {
+  return list./*LINT*/where((e) => e is C);
+}
+class C {}
+''');
+    await assertHasFix('''
+Iterable<C> f(List<Object> list) {
+  return list./*LINT*/whereType<C>();
+}
+class C {}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
index 819327f..6b98754 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -14,10 +15,61 @@
     defineReflectiveTests(CreateMethodTest);
     defineReflectiveTests(CreateMethodMixinTest);
     defineReflectiveTests(CreateMethodWithExtensionMethodsTest);
+    defineReflectiveTests(AddMissingHashOrEqualsTest);
   });
 }
 
 @reflectiveTest
+class AddMissingHashOrEqualsTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.CREATE_METHOD;
+
+  @override
+  String get lintCode => LintNames.hash_and_equals;
+
+  test_equals() async {
+    await resolveTestUnit('''
+class C {
+  @override
+  int get /*LINT*/hashCode => 13;
+}
+''');
+    await assertHasFix('''
+class C {
+  @override
+  int get /*LINT*/hashCode => 13;
+
+  @override
+  bool operator ==(Object other) {
+    // TODO: implement ==
+    return super == other;
+  }
+}
+''');
+  }
+
+  test_hashCode() async {
+    await resolveTestUnit('''
+class C {
+  @override
+  bool operator /*LINT*/==(Object other) => false;
+}
+''');
+    await assertHasFix('''
+class C {
+  @override
+  bool operator /*LINT*/==(Object other) => false;
+
+  @override
+  // TODO: implement hashCode
+  int get hashCode => super.hashCode;
+
+}
+''');
+  }
+}
+
+@reflectiveTest
 class CreateMethodMixinTest extends FixProcessorTest {
   @override
   FixKind get kind => DartFixKind.CREATE_METHOD;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index b5e5f46..3721622 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -189,7 +189,7 @@
     }
 
     // Assert that none of the fixes are a fix-all fix.
-    Fix foundFix = null;
+    Fix foundFix;
     for (Fix fix in fixes) {
       if (fix.isFixAllFix()) {
         fail('A fix-all fix was found for the error: $error '
@@ -216,7 +216,7 @@
     List<Fix> fixes = await _computeFixes(error);
 
     // Assert that there exists such a fix in the list.
-    Fix foundFix = null;
+    Fix foundFix;
     for (Fix fix in fixes) {
       if (fix.kind == kind && fix.isFixAllFix()) {
         foundFix = fix;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
new file mode 100644
index 0000000..0fd0c22
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2020, 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RemoveIfNullOperatorTest);
+  });
+}
+
+@reflectiveTest
+class RemoveIfNullOperatorTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_IF_NULL_OPERATOR;
+
+  @override
+  String get lintCode => LintNames.unnecessary_null_in_if_null_operators;
+
+  test_left() async {
+    await resolveTestUnit('''
+var a = '';
+var b = /*LINT*/null ?? a;
+''');
+    await assertHasFix('''
+var a = '';
+var b = /*LINT*/a;
+''');
+  }
+
+  test_right() async {
+    await resolveTestUnit('''
+var a = '';
+var b = /*LINT*/a ?? null;
+''');
+    await assertHasFix('''
+var a = '';
+var b = /*LINT*/a;
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 5f4a631..3a8d4a1 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -57,6 +57,7 @@
 import 'convert_to_single_quoted_string_test.dart'
     as convert_to_single_quoted_string;
 import 'convert_to_spread_test.dart' as convert_to_spread;
+import 'convert_to_where_type_test.dart' as convert_to_where_type;
 import 'create_class_test.dart' as create_class;
 import 'create_constructor_for_final_fields_test.dart'
     as create_constructor_for_final_field;
@@ -97,6 +98,7 @@
     as remove_empty_constructor_body;
 import 'remove_empty_else_test.dart' as remove_empty_else;
 import 'remove_empty_statement_test.dart' as remove_empty_statement;
+import 'remove_if_null_operator_test.dart' as remove_if_null_operator;
 import 'remove_initializer_test.dart' as remove_initializer;
 import 'remove_interpolation_braces_test.dart' as remove_interpolation_braces;
 import 'remove_method_declaration_test.dart' as remove_method_declaration;
@@ -192,6 +194,7 @@
     convert_to_set_literal.main();
     convert_to_single_quoted_string.main();
     convert_to_spread.main();
+    convert_to_where_type.main();
     create_class.main();
     create_constructor_for_final_field.main();
     create_constructor_super.main();
@@ -230,6 +233,7 @@
     remove_empty_constructor_body.main();
     remove_empty_else.main();
     remove_empty_statement.main();
+    remove_if_null_operator.main();
     remove_initializer.main();
     remove_interpolation_braces.main();
     remove_method_declaration.main();
diff --git a/pkg/analysis_server/test/stress/replay/replay.dart b/pkg/analysis_server/test/stress/replay/replay.dart
index a8be67d..4dfef6a 100644
--- a/pkg/analysis_server/test/stress/replay/replay.dart
+++ b/pkg/analysis_server/test/stress/replay/replay.dart
@@ -370,7 +370,7 @@
       // Iterate over the history, applying changes.
       //
       bool firstCheckout = true;
-      ErrorMap expectedErrors = null;
+      ErrorMap expectedErrors;
       Iterable<String> changedPubspecs;
       while (iterator.moveNext()) {
         //
@@ -506,7 +506,7 @@
   /**
    * Display usage information, preceded by the [errorMessage] if one is given.
    */
-  void _showUsage(ArgParser parser, [String errorMessage = null]) {
+  void _showUsage(ArgParser parser, [String errorMessage]) {
     if (errorMessage != null) {
       stderr.writeln(errorMessage);
       stderr.writeln();
@@ -607,7 +607,7 @@
     addUpdateContent(AddContentOverlay(content));
     for (List<SourceEdit> editList in editLists.reversed) {
       for (SourceEdit edit in editList.reversed) {
-        var overlay = null;
+        var overlay;
         if (overlayStyle == OverlayStyle.change) {
           overlay = ChangeContentOverlay([edit]);
         } else if (overlayStyle == OverlayStyle.multipleAdd) {
diff --git a/pkg/analysis_server/test/stress/utilities/git.dart b/pkg/analysis_server/test/stress/utilities/git.dart
index 6d9532d..d0a827b 100644
--- a/pkg/analysis_server/test/stress/utilities/git.dart
+++ b/pkg/analysis_server/test/stress/utilities/git.dart
@@ -219,7 +219,7 @@
     String srcPath = _makeAbsolute(input.substring(startIndex, endIndex));
     startIndex = endIndex + 1;
     // Parse field 14
-    String dstPath = null;
+    String dstPath;
     if (status.startsWith('C') || status.startsWith('R')) {
       endIndex = _findEnd(input, startIndex);
       dstPath = _makeAbsolute(input.substring(startIndex, endIndex));
@@ -406,7 +406,7 @@
    *
    * If a [commandSink] is provided, any calls to git will be written to it.
    */
-  GitRepository(this.path, {this.logger = null});
+  GitRepository(this.path, {this.logger});
 
   /**
    * Checkout the given [commit] from the repository. This is done by running
diff --git a/pkg/analysis_server/test/stress/utilities/logger.dart b/pkg/analysis_server/test/stress/utilities/logger.dart
index bbdd406..56f68e5 100644
--- a/pkg/analysis_server/test/stress/utilities/logger.dart
+++ b/pkg/analysis_server/test/stress/utilities/logger.dart
@@ -33,7 +33,7 @@
    * the [content] contains the actual information. If a list of [arguments] is
    * provided, then they will be written after the content.
    */
-  void log(String label, String content, {List<String> arguments = null}) {
+  void log(String label, String content, {List<String> arguments}) {
     for (int i = _labelWidth - label.length; i > 0; i--) {
       sink.write(' ');
     }
diff --git a/pkg/analysis_server/test/stress/utilities/server.dart b/pkg/analysis_server/test/stress/utilities/server.dart
index ef98023..cf93f1d 100644
--- a/pkg/analysis_server/test/stress/utilities/server.dart
+++ b/pkg/analysis_server/test/stress/utilities/server.dart
@@ -80,7 +80,7 @@
    * The time at which the response was received, or `null` if no response has
    * been received.
    */
-  int responseTime = null;
+  int responseTime;
 
   /**
    * The response that was received.
@@ -162,7 +162,7 @@
    * The process in which the server is running, or `null` if the server hasn't
    * been started yet.
    */
-  Process _process = null;
+  Process _process;
 
   /**
    * Number that should be used to compute the 'id' to send in the next command
@@ -220,7 +220,7 @@
    * If a [logger] is provided, the communications between the client (this
    * test) and the server will be written to it.
    */
-  Server({this.logger = null});
+  Server({this.logger});
 
   /**
    * Return a future that will complete when a 'server.status' notification is
diff --git a/pkg/analysis_server/test/timing/timing_framework.dart b/pkg/analysis_server/test/timing/timing_framework.dart
index c53e1f6..3e90901 100644
--- a/pkg/analysis_server/test/timing/timing_framework.dart
+++ b/pkg/analysis_server/test/timing/timing_framework.dart
@@ -108,7 +108,7 @@
    */
   List<int> toMilliseconds(List<int> times) {
     int count = times.length;
-    List<int> convertedValues = List<int>();
+    List<int> convertedValues = <int>[];
     for (int i = 0; i < count; i++) {
       convertedValues.add(times[i] ~/ NANOSECONDS_PER_MILLISECOND);
     }
@@ -224,7 +224,7 @@
    * number of times.
    */
   Future<TimingResult> run() async {
-    List<int> times = List<int>();
+    List<int> times = <int>[];
     await oneTimeSetUp();
     await _repeat(warmupCount, null);
     await _repeat(timingCount, times);
diff --git a/pkg/analysis_server/tool/instrumentation/log/log.dart b/pkg/analysis_server/tool/instrumentation/log/log.dart
index d627fbd..6048c5f 100644
--- a/pkg/analysis_server/tool/instrumentation/log/log.dart
+++ b/pkg/analysis_server/tool/instrumentation/log/log.dart
@@ -372,9 +372,9 @@
     }
     logEntries = <LogEntry>[];
     analysisRanges = <EntryRange>[];
-    NotificationEntry analysisStartEntry = null;
+    NotificationEntry analysisStartEntry;
     int analysisStartIndex = -1;
-    NotificationEntry pubStartEntry = null;
+    NotificationEntry pubStartEntry;
     for (String line in logContent) {
       LogEntry entry = LogEntry.from(logEntries.length, line);
       if (entry != null) {
@@ -434,7 +434,7 @@
             String id = entry.param('id');
             if (id != null) {
               _completionMap
-                  .putIfAbsent(id, () => List<NotificationEntry>())
+                  .putIfAbsent(id, () => <NotificationEntry>[])
                   .add(entry);
             }
           }
@@ -626,7 +626,7 @@
    * A list containing the descriptions of problems that were found while
    * processing the log file, or `null` if no problems were found.
    */
-  List<String> _problems = null;
+  List<String> _problems;
 
   /**
    * Initialize a newly created log entry with the given [timeStamp].
@@ -1104,12 +1104,12 @@
   /**
    * The name of the class implementing the task.
    */
-  String _taskName = null;
+  String _taskName;
 
   /**
    * The description of the target of the task.
    */
-  String _target = null;
+  String _target;
 
   /**
    * Initialize a newly created entry with the given [index] and [timeStamp] to
diff --git a/pkg/analysis_server/tool/instrumentation/page/log_page.dart b/pkg/analysis_server/tool/instrumentation/page/log_page.dart
index f872f7f..9150d38 100644
--- a/pkg/analysis_server/tool/instrumentation/page/log_page.dart
+++ b/pkg/analysis_server/tool/instrumentation/page/log_page.dart
@@ -37,7 +37,7 @@
    * The number of entries to be written, or `null` if all of the entries should
    * be written.
    */
-  int pageLength = null;
+  int pageLength;
 
   /**
    * The number of digits in the event stamps that are the same for every entry.
@@ -133,7 +133,7 @@
    * Write the given log [entry] to the given [sink].
    */
   void _writeEntry(StringSink sink, LogEntry entry) {
-    String id = null;
+    String id;
     String clickHandler = 'clearHighlight()';
     String icon = '';
     String description = entry.kindName;
diff --git a/pkg/analysis_server/tool/instrumentation/page/stats_page.dart b/pkg/analysis_server/tool/instrumentation/page/stats_page.dart
index 36b9434..4025fd0 100644
--- a/pkg/analysis_server/tool/instrumentation/page/stats_page.dart
+++ b/pkg/analysis_server/tool/instrumentation/page/stats_page.dart
@@ -104,7 +104,7 @@
       } else if (entry is RequestEntry) {
         String method = entry.method;
         int latency = entry.timeStamp - entry.clientRequestTime;
-        latencyData.putIfAbsent(method, () => List<int>()).add(latency);
+        latencyData.putIfAbsent(method, () => <int>[]).add(latency);
         if (method == 'completion.getSuggestions') {
           ResponseEntry response = log.responseFor(entry);
           if (response != null) {
@@ -128,9 +128,7 @@
         int responseTime = response.timeStamp - entry.timeStamp;
         var pluginData = pluginResponseData.putIfAbsent(
             entry.pluginId, () => <String, List<int>>{});
-        pluginData
-            .putIfAbsent(entry.method, () => List<int>())
-            .add(responseTime);
+        pluginData.putIfAbsent(entry.method, () => <int>[]).add(responseTime);
       }
     }
   }
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 82571278..4771a00 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -43,7 +43,7 @@
     // Keep track of our base classes so they can look up their super classes
     // later in their fromJson() to deserialise into the most specific type.
     interface.baseTypes.forEach((base) {
-      final subTypes = _subtypes[base.dartType] ??= List<String>();
+      final subTypes = _subtypes[base.dartType] ??= <String>[];
       subTypes.add(interface.name);
     });
   });
diff --git a/pkg/analysis_server/tool/spec/codegen_java_types.dart b/pkg/analysis_server/tool/spec/codegen_java_types.dart
index a8a7063..9eedfa7 100644
--- a/pkg/analysis_server/tool/spec/codegen_java_types.dart
+++ b/pkg/analysis_server/tool/spec/codegen_java_types.dart
@@ -56,7 +56,7 @@
     GeneratedDirectory(pathToGenTypes, (String pkgPath) {
   Api api = readApi(pkgPath);
   Map<String, ImpliedType> impliedTypes = computeImpliedTypes(api);
-  Map<String, FileContentsComputer> map = Map<String, FileContentsComputer>();
+  Map<String, FileContentsComputer> map = <String, FileContentsComputer>{};
   for (ImpliedType impliedType in impliedTypes.values) {
     String typeNameInSpec = capitalize(impliedType.camelName);
     bool isRefactoringFeedback = impliedType.kind == 'refactoringFeedback';
@@ -73,7 +73,7 @@
           typeNameInJava = _typeRenames[typeNameInSpec];
         }
         map['$typeNameInJava.java'] = (String pkgPath) async {
-          String superclassName = null;
+          String superclassName;
           if (isRefactoringFeedback) {
             superclassName = 'RefactoringFeedback';
           }
@@ -366,7 +366,7 @@
         }));
         write('public $className(');
         // write out parameters to constructor
-        List<String> parameters = List();
+        List<String> parameters = [];
         if (className == 'Outline') {
           parameters.add('Outline parent');
         }
@@ -515,7 +515,7 @@
               writeln(';');
             }
             write('return new $className(');
-            List<String> parameters = List();
+            List<String> parameters = [];
             for (TypeObjectField field in fields) {
               if (!_isTypeFieldInUpdateContentUnionType(
                   className, field.name)) {
@@ -631,7 +631,7 @@
             writeln('$className other = ($className) obj;');
             writeln('return');
             indent(() {
-              List<String> equalsForField = List<String>();
+              List<String> equalsForField = <String>[];
               for (TypeObjectField field in fields) {
                 equalsForField.add(_getEqualsLogicForField(field, 'other'));
               }
diff --git a/pkg/analysis_server/tool/spec/from_html.dart b/pkg/analysis_server/tool/spec/from_html.dart
index 64c525d..c125d20 100644
--- a/pkg/analysis_server/tool/spec/from_html.dart
+++ b/pkg/analysis_server/tool/spec/from_html.dart
@@ -86,8 +86,8 @@
     Api api;
     List<String> versions = <String>[];
     List<Domain> domains = <Domain>[];
-    Types types = null;
-    Refactorings refactorings = null;
+    Types types;
+    Refactorings refactorings;
     recurse(html, 'api', {
       'domain': (dom.Element element) {
         domains.add(domainFromHtml(element));
@@ -120,7 +120,7 @@
   void checkAttributes(
       dom.Element element, List<String> requiredAttributes, String context,
       {List<String> optionalAttributes = const []}) {
-    Set<String> attributesFound = Set<String>();
+    Set<String> attributesFound = <String>{};
     element.attributes.forEach((name, value) {
       if (!requiredAttributes.contains(name) &&
           !optionalAttributes.contains(name)) {
diff --git a/pkg/analysis_server/tool/spec/to_html.dart b/pkg/analysis_server/tool/spec/to_html.dart
index f684954..332556f 100644
--- a/pkg/analysis_server/tool/spec/to_html.dart
+++ b/pkg/analysis_server/tool/spec/to_html.dart
@@ -220,7 +220,7 @@
   /**
    * Set of types defined in the API.
    */
-  Set<String> definedTypes = Set<String>();
+  Set<String> definedTypes = <String>{};
 
   /**
    * Mappings from HTML elements to API nodes.
@@ -418,7 +418,7 @@
    * boldface.
    */
   void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) {
-    Set<String> fieldsToBold = Set<String>();
+    Set<String> fieldsToBold = <String>{};
     if (typeForBolding != null) {
       for (TypeObjectField field in typeForBolding.fields) {
         fieldsToBold.add(field.name);
diff --git a/pkg/analysis_server_client/analysis_options.yaml b/pkg/analysis_server_client/analysis_options.yaml
index 3368902..38ce34f 100644
--- a/pkg/analysis_server_client/analysis_options.yaml
+++ b/pkg/analysis_server_client/analysis_options.yaml
@@ -33,7 +33,7 @@
     - prefer_conditional_assignment
     - prefer_contains
     - prefer_equal_for_default_values
-    #- prefer_final_fields # 2
+    - prefer_final_fields
     - prefer_for_elements_to_map_fromIterable
     - prefer_generic_function_type_aliases
     - prefer_if_null_operators
diff --git a/pkg/analysis_server_client/lib/handler/connection_handler.dart b/pkg/analysis_server_client/lib/handler/connection_handler.dart
index 1360506..aada7f4 100644
--- a/pkg/analysis_server_client/lib/handler/connection_handler.dart
+++ b/pkg/analysis_server_client/lib/handler/connection_handler.dart
@@ -21,7 +21,7 @@
 ///
 /// Clients may mix-in this class, but may not extend or implement it.
 mixin ConnectionHandler implements NotificationHandler {
-  Completer<bool> _connected = Completer();
+  final Completer<bool> _connected = Completer();
 
   /// Clients should implement this method to return the server being managed.
   /// This mixin will stop the server process if a connection cannot be
diff --git a/pkg/analysis_server_client/lib/server.dart b/pkg/analysis_server_client/lib/server.dart
index 6ee864f..f30db66 100644
--- a/pkg/analysis_server_client/lib/server.dart
+++ b/pkg/analysis_server_client/lib/server.dart
@@ -20,7 +20,7 @@
 class Server {
   /// If not `null`, [_listener] will be sent information
   /// about interactions with the server.
-  ServerListener _listener;
+  final ServerListener _listener;
 
   /// Server process object, or `null` if server hasn't been started yet
   /// or if the server has already been stopped.
diff --git a/pkg/analyzer/lib/src/context/builder.dart b/pkg/analyzer/lib/src/context/builder.dart
index fd8d3dd..b12d22e 100644
--- a/pkg/analyzer/lib/src/context/builder.dart
+++ b/pkg/analyzer/lib/src/context/builder.dart
@@ -16,6 +16,7 @@
         bazelAnalysisOptionsPath,
         flutterAnalysisOptionsPath;
 import 'package:analyzer/src/context/context_root.dart';
+import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/context_root.dart' as api;
 import 'package:analyzer/src/dart/analysis/driver.dart'
@@ -40,9 +41,7 @@
 import 'package:analyzer/src/workspace/pub.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
 import 'package:args/args.dart';
-import 'package:package_config/packages.dart';
-import 'package:package_config/packages_file.dart';
-import 'package:package_config/src/packages_impl.dart';
+import 'package:package_config/packages.dart' as package_config;
 import 'package:path/src/context.dart';
 import 'package:yaml/yaml.dart';
 
@@ -182,16 +181,15 @@
     return driver;
   }
 
-  Map<String, List<Folder>> convertPackagesToMap(Packages packages) {
-    Map<String, List<Folder>> folderMap = HashMap<String, List<Folder>>();
-    if (packages != null && packages != Packages.noPackages) {
-      var pathContext = resourceProvider.pathContext;
-      packages.asMap().forEach((String packageName, Uri uri) {
-        String path = fileUriToNormalizedPath(pathContext, uri);
-        folderMap[packageName] = [resourceProvider.getFolder(path)];
-      });
+  /**
+   * Return an analysis options object containing the default option values.
+   */
+  AnalysisOptions createDefaultOptions() {
+    AnalysisOptions defaultOptions = builderOptions.defaultOptions;
+    if (defaultOptions == null) {
+      return AnalysisOptionsImpl();
     }
-    return folderMap;
+    return AnalysisOptionsImpl.from(defaultOptions);
   }
 
 //  void _processAnalysisOptions(
@@ -217,25 +215,11 @@
 //    }
 //  }
 
-  /**
-   * Return an analysis options object containing the default option values.
-   */
-  AnalysisOptions createDefaultOptions() {
-    AnalysisOptions defaultOptions = builderOptions.defaultOptions;
-    if (defaultOptions == null) {
-      return AnalysisOptionsImpl();
-    }
-    return AnalysisOptionsImpl.from(defaultOptions);
-  }
-
   Packages createPackageMap(String rootDirectoryPath) {
     String filePath = builderOptions.defaultPackageFilePath;
     if (filePath != null) {
       File configFile = resourceProvider.getFile(filePath);
-      List<int> bytes = configFile.readAsBytesSync();
-      Map<String, Uri> map = parse(bytes, configFile.toUri());
-      resolveSymbolicLinks(map);
-      return MapPackages(map);
+      return parseDotPackagesFile(resourceProvider, configFile);
     }
     String directoryPath = builderOptions.defaultPackagesDirectoryPath;
     if (directoryPath != null) {
@@ -276,27 +260,20 @@
    * that is not found, it instead checks for the presence of a `packages/`
    * directory in the same place. If that also fails, it starts checking parent
    * directories for a `.packages` file, and stops if it finds it. Otherwise it
-   * gives up and returns [Packages.noPackages].
+   * gives up and returns [Packages.empty].
    */
   Packages findPackagesFromFile(String path) {
     Resource location = _findPackagesLocation(path);
     if (location is File) {
-      List<int> fileBytes = location.readAsBytesSync();
-      Map<String, Uri> map;
       try {
-        map =
-            parse(fileBytes, resourceProvider.pathContext.toUri(location.path));
-      } catch (exception) {
-        // If we cannot read the file, then we respond as if the file did not
-        // exist.
-        return Packages.noPackages;
+        return parseDotPackagesFile(resourceProvider, location);
+      } catch (_) {
+        return Packages.empty;
       }
-      resolveSymbolicLinks(map);
-      return MapPackages(map);
     } else if (location is Folder) {
       return getPackagesFromFolder(location);
     }
-    return Packages.noPackages;
+    return Packages.empty;
   }
 
   /**
@@ -380,11 +357,7 @@
       }
     } else {
       // Search for the default analysis options.
-      // TODO(danrubel) determine if bazel or gn project depends upon flutter
-      Source source;
-      if (workspace.hasFlutterDependency) {
-        source = sourceFactory.forUri(flutterAnalysisOptionsPath);
-      }
+      Source source = sourceFactory.forUri(flutterAnalysisOptionsPath);
       if (source == null || !source.exists()) {
         source = sourceFactory.forUri(bazelAnalysisOptionsPath);
       }
@@ -461,20 +434,29 @@
    *
    * Package names are resolved as relative to sub-directories of the package
    * directory.
+   *
+   * TODO(scheglov) Remove this feature
    */
   Packages getPackagesFromFolder(Folder folder) {
     Context pathContext = resourceProvider.pathContext;
-    Map<String, Uri> map = HashMap<String, Uri>();
+    var map = <String, Package>{};
     for (Resource child in folder.getChildren()) {
       if (child is Folder) {
         // Inline resolveSymbolicLinks for performance reasons.
         String packageName = pathContext.basename(child.path);
-        String folderPath = resolveSymbolicLink(child);
-        String uriPath = pathContext.join(folderPath, '.');
-        map[packageName] = pathContext.toUri(uriPath);
+        String packagePath = resolveSymbolicLink(child);
+        var rootFolder = resourceProvider.getFolder(packagePath);
+        var libFolder = rootFolder.getChildAssumingFolder('lib');
+        var package = Package(
+          name: packageName,
+          rootFolder: rootFolder,
+          libFolder: libFolder,
+          languageVersion: null,
+        );
+        map[packageName] = package;
       }
     }
-    return MapPackages(map);
+    return Packages(map);
   }
 
   /**
@@ -520,37 +502,20 @@
    * if neither is found.
    */
   Resource _findPackagesLocation(String path) {
-    Folder folder = resourceProvider.getFolder(path);
-    if (!folder.exists) {
-      return null;
-    }
+    var resource = resourceProvider.getResource(path);
+    while (resource != null) {
+      if (resource is Folder) {
+        var dotPackagesFile = resource.getChildAssumingFile('.packages');
+        if (dotPackagesFile.exists) {
+          return dotPackagesFile;
+        }
 
-    File checkForConfigFile(Folder folder) {
-      File file = folder.getChildAssumingFile('.packages');
-      if (file.exists) {
-        return file;
+        var packagesDirectory = resource.getChildAssumingFolder('packages');
+        if (packagesDirectory.exists) {
+          return packagesDirectory;
+        }
       }
-      return null;
-    }
-
-    // Check for $cwd/.packages
-    File packagesCfgFile = checkForConfigFile(folder);
-    if (packagesCfgFile != null) {
-      return packagesCfgFile;
-    }
-    // Check for $cwd/packages/
-    Folder packagesDir = folder.getChildAssumingFolder("packages");
-    if (packagesDir.exists) {
-      return packagesDir;
-    }
-    // Check for cwd(/..)+/.packages
-    Folder parentDir = folder.parent;
-    while (parentDir != null) {
-      packagesCfgFile = checkForConfigFile(parentDir);
-      if (packagesCfgFile != null) {
-        return packagesCfgFile;
-      }
-      parentDir = parentDir.parent;
+      resource = resource.parent;
     }
     return null;
   }
@@ -573,24 +538,45 @@
     return null;
   }
 
+  static Map<String, List<Folder>> convertPackagesToMap(
+    ResourceProvider resourceProvider,
+    package_config.Packages packages,
+  ) {
+    Map<String, List<Folder>> folderMap = HashMap<String, List<Folder>>();
+    if (packages != null && packages != package_config.Packages.noPackages) {
+      var pathContext = resourceProvider.pathContext;
+      packages.asMap().forEach((String packageName, Uri uri) {
+        String path = fileUriToNormalizedPath(pathContext, uri);
+        folderMap[packageName] = [resourceProvider.getFolder(path)];
+      });
+    }
+    return folderMap;
+  }
+
   static Workspace createWorkspace(ResourceProvider resourceProvider,
       String rootPath, ContextBuilder contextBuilder) {
+    var packages = contextBuilder.createPackageMap(rootPath);
+    var packageMap = <String, List<Folder>>{};
+    for (var package in packages.packages) {
+      packageMap[package.name] = [package.libFolder];
+    }
+
     if (_hasPackageFileInPath(resourceProvider, rootPath)) {
       // A Bazel or Gn workspace that includes a '.packages' file is treated
       // like a normal (non-Bazel/Gn) directory. But may still use
       // package:build or Pub.
       return PackageBuildWorkspace.find(
-              resourceProvider, rootPath, contextBuilder) ??
-          PubWorkspace.find(resourceProvider, rootPath, contextBuilder) ??
-          BasicWorkspace.find(resourceProvider, rootPath, contextBuilder);
+              resourceProvider, packageMap, rootPath) ??
+          PubWorkspace.find(resourceProvider, packageMap, rootPath) ??
+          BasicWorkspace.find(resourceProvider, packageMap, rootPath);
     }
     Workspace workspace = BazelWorkspace.find(resourceProvider, rootPath);
     workspace ??= GnWorkspace.find(resourceProvider, rootPath);
     workspace ??=
-        PackageBuildWorkspace.find(resourceProvider, rootPath, contextBuilder);
-    workspace ??= PubWorkspace.find(resourceProvider, rootPath, contextBuilder);
-    return workspace ??
-        BasicWorkspace.find(resourceProvider, rootPath, contextBuilder);
+        PackageBuildWorkspace.find(resourceProvider, packageMap, rootPath);
+    workspace ??= PubWorkspace.find(resourceProvider, packageMap, rootPath);
+    workspace ??= BasicWorkspace.find(resourceProvider, packageMap, rootPath);
+    return workspace;
   }
 
   /**
diff --git a/pkg/analyzer/lib/src/context/packages.dart b/pkg/analyzer/lib/src/context/packages.dart
index 51c57ce..2552f33 100644
--- a/pkg/analyzer/lib/src/context/packages.dart
+++ b/pkg/analyzer/lib/src/context/packages.dart
@@ -96,6 +96,8 @@
 }
 
 class Packages {
+  static final empty = Packages({});
+
   final Map<String, Package> _map;
 
   Packages(Map<String, Package> map) : _map = map;
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 808919f..a257211 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -4646,8 +4646,6 @@
     _body = _becomeParentOf(body);
   }
 
-  ForStatementImpl._();
-
   @override
   Statement get body => _body;
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
new file mode 100644
index 0000000..061b865
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -0,0 +1,338 @@
+// Copyright (c) 2020, 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/dart/element/type_provider.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
+import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
+import 'package:analyzer/src/dart/resolver/resolution_result.dart';
+import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/element_type_provider.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/type_promotion_manager.dart';
+import 'package:analyzer/src/generated/type_system.dart';
+import 'package:analyzer/src/task/strong/checker.dart';
+import 'package:meta/meta.dart';
+
+/// Helper for resolving [BinaryExpression]s.
+class BinaryExpressionResolver {
+  final ResolverVisitor _resolver;
+  final TypePromotionManager _promoteManager;
+  final FlowAnalysisHelper _flowAnalysis;
+  final ElementTypeProvider _elementTypeProvider;
+  final TypePropertyResolver _typePropertyResolver;
+  final InvocationInferenceHelper _inferenceHelper;
+
+  BinaryExpressionResolver({
+    @required ResolverVisitor resolver,
+    @required TypePromotionManager promoteManager,
+    @required FlowAnalysisHelper flowAnalysis,
+    @required ElementTypeProvider elementTypeProvider,
+  })  : _resolver = resolver,
+        _promoteManager = promoteManager,
+        _flowAnalysis = flowAnalysis,
+        _elementTypeProvider = elementTypeProvider,
+        _typePropertyResolver = resolver.typePropertyResolver,
+        _inferenceHelper = resolver.inferenceHelper;
+
+  ErrorReporter get _errorReporter => _resolver.errorReporter;
+
+  bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault;
+
+  TypeProvider get _typeProvider => _resolver.typeProvider;
+
+  TypeSystemImpl get _typeSystem => _resolver.typeSystem;
+
+  void resolve(BinaryExpressionImpl node) {
+    TokenType operator = node.operator.type;
+    Expression left = node.leftOperand;
+    Expression right = node.rightOperand;
+    var flow = _flowAnalysis?.flow;
+
+    if (operator == TokenType.AMPERSAND_AMPERSAND) {
+      InferenceContext.setType(left, _typeProvider.boolType);
+      InferenceContext.setType(right, _typeProvider.boolType);
+
+      // TODO(scheglov) Do we need these checks for null?
+      left?.accept(_resolver);
+
+      if (_flowAnalysis != null) {
+        flow?.logicalBinaryOp_rightBegin(left, isAnd: true);
+        _flowAnalysis.checkUnreachableNode(right);
+        right.accept(_resolver);
+        flow?.logicalBinaryOp_end(node, right, isAnd: true);
+      } else {
+        _promoteManager.visitBinaryExpression_and_rhs(
+          left,
+          right,
+          () {
+            right.accept(_resolver);
+          },
+        );
+      }
+
+      _resolve1(node);
+    } else if (operator == TokenType.BAR_BAR) {
+      InferenceContext.setType(left, _typeProvider.boolType);
+      InferenceContext.setType(right, _typeProvider.boolType);
+
+      // TODO(scheglov) Do we need these checks for null?
+      left?.accept(_resolver);
+
+      flow?.logicalBinaryOp_rightBegin(left, isAnd: false);
+      _flowAnalysis?.checkUnreachableNode(right);
+      right.accept(_resolver);
+      flow?.logicalBinaryOp_end(node, right, isAnd: false);
+
+      _resolve1(node);
+    } else if (operator == TokenType.BANG_EQ || operator == TokenType.EQ_EQ) {
+      left.accept(_resolver);
+      _flowAnalysis?.flow?.equalityOp_rightBegin(left);
+      right.accept(_resolver);
+      _resolve1(node);
+      _flowAnalysis?.flow?.equalityOp_end(node, right,
+          notEqual: operator == TokenType.BANG_EQ);
+    } else {
+      if (operator == TokenType.QUESTION_QUESTION) {
+        var leftContextType = InferenceContext.getContext(node);
+        if (leftContextType != null && _isNonNullableByDefault) {
+          leftContextType = _typeSystem.makeNullable(leftContextType);
+        }
+        InferenceContext.setType(left, leftContextType);
+      }
+      // TODO(scheglov) Do we need these checks for null?
+      left?.accept(_resolver);
+
+      // Call ElementResolver.visitBinaryExpression to resolve the user-defined
+      // operator method, if applicable.
+      _resolve1(node);
+
+      if (operator == TokenType.QUESTION_QUESTION) {
+        // Set the right side, either from the context, or using the information
+        // from the left side if it is more precise.
+        DartType contextType = InferenceContext.getContext(node);
+        DartType leftType = left?.staticType;
+        if (contextType == null || contextType.isDynamic) {
+          contextType = leftType;
+        }
+        InferenceContext.setType(right, contextType);
+      } else {
+        var invokeType = node.staticInvokeType;
+        if (invokeType != null && invokeType.parameters.isNotEmpty) {
+          // If this is a user-defined operator, set the right operand context
+          // using the operator method's parameter type.
+          var rightParam = invokeType.parameters[0];
+          InferenceContext.setType(
+              right, _elementTypeProvider.getVariableType(rightParam));
+        }
+      }
+
+      if (operator == TokenType.QUESTION_QUESTION) {
+        flow?.ifNullExpression_rightBegin(node.leftOperand);
+        right.accept(_resolver);
+        flow?.ifNullExpression_end();
+      } else {
+        // TODO(scheglov) Do we need these checks for null?
+        right?.accept(_resolver);
+      }
+    }
+    _resolve2(node);
+  }
+
+  /// Set the static type of [node] to be the least upper bound of the static
+  /// types of subexpressions [expr1] and [expr2].
+  ///
+  /// TODO(scheglov) this is duplicate
+  void _analyzeLeastUpperBound(
+      Expression node, Expression expr1, Expression expr2,
+      {bool read = false}) {
+    DartType staticType1 = _getExpressionType(expr1, read: read);
+    DartType staticType2 = _getExpressionType(expr2, read: read);
+
+    _analyzeLeastUpperBoundTypes(node, staticType1, staticType2);
+  }
+
+  /// Set the static type of [node] to be the least upper bound of the static
+  /// types [staticType1] and [staticType2].
+  ///
+  /// TODO(scheglov) this is duplicate
+  void _analyzeLeastUpperBoundTypes(
+      Expression node, DartType staticType1, DartType staticType2) {
+    if (staticType1 == null) {
+      // TODO(brianwilkerson) Determine whether this can still happen.
+      staticType1 = DynamicTypeImpl.instance;
+    }
+
+    if (staticType2 == null) {
+      // TODO(brianwilkerson) Determine whether this can still happen.
+      staticType2 = DynamicTypeImpl.instance;
+    }
+
+    DartType staticType =
+        _typeSystem.getLeastUpperBound(staticType1, staticType2) ??
+            DynamicTypeImpl.instance;
+
+    staticType = _resolver.toLegacyTypeIfOptOut(staticType);
+
+    _inferenceHelper.recordStaticType(node, staticType);
+  }
+
+  /// Gets the definite type of expression, which can be used in cases where
+  /// the most precise type is desired, for example computing the least upper
+  /// bound.
+  ///
+  /// See [getExpressionType] for more information. Without strong mode, this is
+  /// equivalent to [_getStaticType].
+  ///
+  /// TODO(scheglov) this is duplicate
+  DartType _getExpressionType(Expression expr, {bool read = false}) =>
+      getExpressionType(expr, _typeSystem, _typeProvider,
+          read: read, elementTypeProvider: _elementTypeProvider);
+
+  /// Return the static type of the given [expression] that is to be used for
+  /// type analysis.
+  ///
+  /// TODO(scheglov) this is duplicate
+  DartType _getStaticType(Expression expression, {bool read = false}) {
+    if (expression is NullLiteral) {
+      return _typeProvider.nullType;
+    }
+    DartType type = read
+        ? getReadType(expression, elementTypeProvider: _elementTypeProvider)
+        : expression.staticType;
+    return _resolveTypeParameter(type);
+  }
+
+  void _resolve1(BinaryExpressionImpl node) {
+    Token operator = node.operator;
+    if (operator.isUserDefinableOperator) {
+      _resolveBinaryExpression(node, operator.lexeme);
+    } else if (operator.type == TokenType.BANG_EQ) {
+      _resolveBinaryExpression(node, TokenType.EQ_EQ.lexeme);
+    }
+  }
+
+  void _resolve2(BinaryExpressionImpl node) {
+    if (node.operator.type == TokenType.QUESTION_QUESTION) {
+      if (_isNonNullableByDefault) {
+        // The static type of a compound assignment using ??= with NNBD is the
+        // least upper bound of the static types of the LHS and RHS after
+        // promoting the LHS/ to non-null (as we know its value will not be used
+        // if null)
+        _analyzeLeastUpperBoundTypes(
+            node,
+            _typeSystem.promoteToNonNull(
+                _getExpressionType(node.leftOperand, read: true)),
+            _getExpressionType(node.rightOperand, read: true));
+      } else {
+        // Without NNBD, evaluation of an if-null expression e of the form
+        // e1 ?? e2 is equivalent to the evaluation of the expression
+        // ((x) => x == null ? e2 : x)(e1).  The static type of e is the least
+        // upper bound of the static type of e1 and the static type of e2.
+        _analyzeLeastUpperBound(node, node.leftOperand, node.rightOperand);
+      }
+      return;
+    }
+
+    if (identical(node.leftOperand.staticType, NeverTypeImpl.instance)) {
+      _inferenceHelper.recordStaticType(node, NeverTypeImpl.instance);
+      return;
+    }
+
+    DartType staticType =
+        node.staticInvokeType?.returnType ?? DynamicTypeImpl.instance;
+    if (node.leftOperand is! ExtensionOverride) {
+      staticType = _typeSystem.refineBinaryExpressionType(
+        _getStaticType(node.leftOperand),
+        node.operator.type,
+        node.rightOperand.staticType,
+        staticType,
+      );
+    }
+    _inferenceHelper.recordStaticType(node, staticType);
+  }
+
+  void _resolveBinaryExpression(BinaryExpression node, String methodName) {
+    Expression leftOperand = node.leftOperand;
+
+    if (leftOperand is ExtensionOverride) {
+      ExtensionElement extension = leftOperand.extensionName.staticElement;
+      MethodElement member = extension.getMethod(methodName);
+      if (member == null) {
+        _errorReporter.reportErrorForToken(
+          CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR,
+          node.operator,
+          [methodName, extension.name],
+        );
+      }
+      node.staticElement = member;
+      return;
+    }
+
+    var leftType = _getStaticType(leftOperand);
+
+    if (identical(leftType, NeverTypeImpl.instance)) {
+      _resolver.errorReporter.reportErrorForNode(
+        StaticWarningCode.INVALID_USE_OF_NEVER_VALUE,
+        leftOperand,
+      );
+      return;
+    }
+
+    ResolutionResult result = _typePropertyResolver.resolve(
+      receiver: leftOperand,
+      receiverType: leftType,
+      name: methodName,
+      receiverErrorNode: leftOperand,
+      nameErrorNode: node,
+    );
+
+    node.staticElement = result.getter;
+    node.staticInvokeType =
+        _elementTypeProvider.safeExecutableType(result.getter);
+    if (_shouldReportInvalidMember(leftType, result)) {
+      if (leftOperand is SuperExpression) {
+        _errorReporter.reportErrorForToken(
+          StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
+          node.operator,
+          [methodName, leftType],
+        );
+      } else {
+        _errorReporter.reportErrorForToken(
+          StaticTypeWarningCode.UNDEFINED_OPERATOR,
+          node.operator,
+          [methodName, leftType],
+        );
+      }
+    }
+  }
+
+  /// If the given [type] is a type parameter, resolve it to the type that should
+  /// be used when looking up members. Otherwise, return the original type.
+  ///
+  /// TODO(scheglov) this is duplicate
+  DartType _resolveTypeParameter(DartType type) =>
+      type?.resolveToBound(_typeProvider.objectType);
+
+  /// Return `true` if we should report an error for the lookup [result] on
+  /// the [type].
+  ///
+  /// TODO(scheglov) this is duplicate
+  bool _shouldReportInvalidMember(DartType type, ResolutionResult result) {
+    if (result.isNone && type != null && !type.isDynamic) {
+      if (_isNonNullableByDefault && _typeSystem.isPotentiallyNullable(type)) {
+        return false;
+      }
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
index 1b410fc..6acef20 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
@@ -14,7 +14,6 @@
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/element_type_provider.dart';
 import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/type_system.dart';
 import 'package:meta/meta.dart';
 
 /// Helper for resolving [FunctionExpressionInvocation]s.
@@ -36,8 +35,6 @@
 
   ExtensionMemberResolver get _extensionResolver => _resolver.extensionResolver;
 
-  TypeSystemImpl get _typeSystem => _resolver.typeSystem;
-
   void resolve(FunctionExpressionInvocationImpl node) {
     var rawType = _resolveCallElement(node);
 
@@ -49,22 +46,10 @@
       return;
     }
 
-    var invokeType = _instantiateInvokeType(node, rawType);
-
-    node.staticInvokeType = invokeType;
-
-    var argumentList = node.argumentList;
-    var parameters = ResolverVisitor.resolveArgumentsToParameters(
-      argumentList,
-      invokeType.parameters,
-      _errorReporter.reportErrorForNode,
+    _inferenceHelper.resolveFunctionExpressionInvocation(
+      node: node,
+      rawType: rawType,
     );
-    argumentList.correspondingStaticParameters = parameters;
-
-    _inferenceHelper.inferArgumentTypesForInvocation(node, rawType);
-    _resolveArguments(node);
-
-    _inferenceHelper.inferGenericInvocationExpression(node, rawType);
 
     var returnType = _inferenceHelper.computeInvokeReturnType(
       node.staticInvokeType,
@@ -73,35 +58,6 @@
     _inferenceHelper.recordStaticType(node, returnType);
   }
 
-  FunctionType _instantiateInvokeType(
-    FunctionExpressionInvocation node,
-    FunctionType rawType,
-  ) {
-    var typeParameters = rawType.typeFormals;
-
-    var arguments = node.typeArguments?.arguments;
-    if (arguments != null && arguments.length != typeParameters.length) {
-      // TODO(scheglov) The error is suboptimal for this node type.
-      _errorReporter.reportErrorForNode(
-        StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
-        node,
-        [rawType, typeParameters.length, arguments.length],
-      );
-      // Wrong number of type arguments. Ignore them.
-      arguments = null;
-    }
-
-    if (typeParameters.isEmpty) {
-      return rawType;
-    }
-
-    if (arguments == null) {
-      return _typeSystem.instantiateToBounds(rawType);
-    } else {
-      return rawType.instantiate(arguments.map((n) => n.type).toList());
-    }
-  }
-
   void _resolveArguments(FunctionExpressionInvocationImpl node) {
     node.argumentList.accept(_resolver);
   }
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index db2da5f..4275f25 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -12,26 +12,31 @@
 import 'package:analyzer/src/dart/element/type_algebra.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/element_type_provider.dart';
 import 'package:analyzer/src/generated/migration.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:meta/meta.dart';
 
 class InvocationInferenceHelper {
-  final LibraryElementImpl _definingLibrary;
+  final ResolverVisitor _resolver;
   final ElementTypeProvider _elementTypeProvider;
   final ErrorReporter _errorReporter;
   final FlowAnalysisHelper _flowAnalysis;
   final TypeSystemImpl _typeSystem;
   final TypeProviderImpl _typeProvider;
 
+  List<DartType> _typeArgumentTypes;
+  FunctionType _invokeType;
+
   InvocationInferenceHelper({
+    @required ResolverVisitor resolver,
     @required LibraryElementImpl definingLibrary,
     @required ElementTypeProvider elementTypeProvider,
     @required ErrorReporter errorReporter,
     @required FlowAnalysisHelper flowAnalysis,
     @required TypeSystemImpl typeSystem,
-  })  : _definingLibrary = definingLibrary,
+  })  : _resolver = resolver,
         _elementTypeProvider = elementTypeProvider,
         _errorReporter = errorReporter,
         _typeSystem = typeSystem,
@@ -40,16 +45,10 @@
 
   /// Compute the return type of the method or function represented by the given
   /// type that is being invoked.
-  DartType /*!*/ computeInvokeReturnType(DartType type,
+  DartType computeInvokeReturnType(DartType type,
       {@required bool isNullAware}) {
-    TypeImpl /*!*/ returnType;
-    if (type is InterfaceType) {
-      MethodElement callMethod = type.lookUpMethod2(
-          FunctionElement.CALL_METHOD_NAME, _definingLibrary);
-      returnType =
-          _elementTypeProvider.safeExecutableType(callMethod)?.returnType ??
-              DynamicTypeImpl.instance;
-    } else if (type is FunctionType) {
+    TypeImpl returnType;
+    if (type is FunctionType) {
       returnType = type.returnType ?? DynamicTypeImpl.instance;
     } else {
       returnType = DynamicTypeImpl.instance;
@@ -137,27 +136,11 @@
         fnType is FunctionType &&
         fnType.typeFormals.isNotEmpty) {
       // Get the parameters that correspond to the uninstantiated generic.
-      List<ParameterElement> rawParameters =
-          ResolverVisitor.resolveArgumentsToParameters(
-              argumentList, fnType.parameters, null);
-
-      List<ParameterElement> params = <ParameterElement>[];
-      List<DartType> argTypes = <DartType>[];
-      for (int i = 0, length = rawParameters.length; i < length; i++) {
-        ParameterElement parameter = rawParameters[i];
-        if (parameter != null) {
-          params.add(parameter);
-          argTypes.add(argumentList.arguments[i].staticType);
-        }
-      }
-      var typeArgs = _typeSystem.inferGenericFunctionOrType(
-        typeParameters: fnType.typeFormals,
-        parameters: params,
-        declaredReturnType: fnType.returnType,
-        argumentTypes: argTypes,
-        contextReturnType: InferenceContext.getContext(node),
+      List<DartType> typeArgs = _inferUpwards(
+        rawType: fnType,
+        argumentList: argumentList,
+        contextType: InferenceContext.getContext(node),
         isConst: isConst,
-        errorReporter: _errorReporter,
         errorNode: errorNode,
       );
       if (node is InvocationExpressionImpl) {
@@ -249,6 +232,230 @@
     }
   }
 
+  /// Finish resolution of the [FunctionExpressionInvocation].
+  ///
+  /// We have already found the invoked [ExecutableElement], and the [rawType]
+  /// is its not yet instantiated type. Here we perform downwards inference,
+  /// resolution of arguments, and upwards inference.
+  void resolveFunctionExpressionInvocation({
+    @required FunctionExpressionInvocationImpl node,
+    @required FunctionType rawType,
+  }) {
+    _resolveInvocation(
+      rawType: rawType,
+      typeArgumentList: node.typeArguments,
+      argumentList: node.argumentList,
+      contextType: InferenceContext.getContext(node),
+      isConst: false,
+      errorNode: node.function,
+    );
+
+    node.typeArgumentTypes = _typeArgumentTypes;
+    node.staticInvokeType = _invokeType;
+  }
+
+  /// Finish resolution of the [MethodInvocation].
+  ///
+  /// We have already found the invoked [ExecutableElement], and the [rawType]
+  /// is its not yet instantiated type. Here we perform downwards inference,
+  /// resolution of arguments, and upwards inference.
+  void resolveMethodInvocation({
+    @required MethodInvocationImpl node,
+    @required FunctionType rawType,
+  }) {
+    _resolveInvocation(
+      rawType: rawType,
+      typeArgumentList: node.typeArguments,
+      argumentList: node.argumentList,
+      contextType: InferenceContext.getContext(node),
+      isConst: false,
+      errorNode: node.function,
+    );
+
+    node.typeArgumentTypes = _typeArgumentTypes;
+    node.staticInvokeType = _invokeType;
+
+    var returnType = computeInvokeReturnType(
+      _invokeType,
+      isNullAware: node.isNullAware,
+    );
+    recordStaticType(node, returnType);
+  }
+
+  List<DartType> _inferDownwards({
+    @required FunctionType rawType,
+    @required DartType contextType,
+    @required bool isConst,
+    @required AstNode errorNode,
+  }) {
+    return _typeSystem.inferGenericFunctionOrType(
+      typeParameters: rawType.typeFormals,
+      parameters: const <ParameterElement>[],
+      declaredReturnType: rawType.returnType,
+      argumentTypes: const <DartType>[],
+      contextReturnType: contextType,
+      downwards: true,
+      isConst: isConst,
+      errorReporter: _errorReporter,
+      errorNode: errorNode,
+    );
+  }
+
+  /// TODO(scheglov) Instead of [isConst] sanitize [contextType] before calling.
+  List<DartType> _inferUpwards({
+    @required FunctionType rawType,
+    @required DartType contextType,
+    @required ArgumentList argumentList,
+    @required bool isConst,
+    @required AstNode errorNode,
+  }) {
+    // Get the parameters that correspond to the uninstantiated generic.
+    List<ParameterElement> rawParameters =
+        ResolverVisitor.resolveArgumentsToParameters(
+            argumentList, rawType.parameters, null);
+
+    List<ParameterElement> params = <ParameterElement>[];
+    List<DartType> argTypes = <DartType>[];
+    for (int i = 0, length = rawParameters.length; i < length; i++) {
+      ParameterElement parameter = rawParameters[i];
+      if (parameter != null) {
+        params.add(parameter);
+        argTypes.add(argumentList.arguments[i].staticType);
+      }
+    }
+    var typeArgs = _typeSystem.inferGenericFunctionOrType(
+      typeParameters: rawType.typeFormals,
+      parameters: params,
+      declaredReturnType: rawType.returnType,
+      argumentTypes: argTypes,
+      contextReturnType: contextType,
+      isConst: isConst,
+      errorReporter: _errorReporter,
+      errorNode: errorNode,
+    );
+    return typeArgs;
+  }
+
+  void _resolveArguments(ArgumentList argumentList) {
+    argumentList.accept(_resolver);
+  }
+
+  void _resolveInvocation({
+    @required FunctionType rawType,
+    @required DartType contextType,
+    @required TypeArgumentList typeArgumentList,
+    @required ArgumentList argumentList,
+    @required bool isConst,
+    @required AstNode errorNode,
+  }) {
+    if (typeArgumentList != null) {
+      _resolveInvocationWithTypeArguments(
+        rawType: rawType,
+        typeArgumentList: typeArgumentList,
+        argumentList: argumentList,
+      );
+    } else {
+      _resolveInvocationWithoutTypeArguments(
+        rawType: rawType,
+        contextType: contextType,
+        argumentList: argumentList,
+        isConst: isConst,
+        errorNode: errorNode,
+      );
+    }
+    _setCorrespondingParameters(argumentList, _invokeType);
+  }
+
+  void _resolveInvocationWithoutTypeArguments({
+    @required FunctionType rawType,
+    @required DartType contextType,
+    @required ArgumentList argumentList,
+    @required bool isConst,
+    @required AstNode errorNode,
+  }) {
+    var typeParameters = rawType.typeFormals;
+
+    if (typeParameters.isEmpty) {
+      InferenceContext.setType(argumentList, rawType);
+      _resolveArguments(argumentList);
+
+      _typeArgumentTypes = const <DartType>[];
+      _invokeType = rawType;
+    } else {
+      rawType = _getFreshType(rawType);
+
+      List<DartType> downwardsTypeArguments = _inferDownwards(
+        rawType: rawType,
+        contextType: contextType,
+        isConst: isConst,
+        errorNode: errorNode,
+      );
+
+      var downwardsInvokeType = rawType.instantiate(downwardsTypeArguments);
+      InferenceContext.setType(argumentList, downwardsInvokeType);
+
+      _resolveArguments(argumentList);
+
+      _typeArgumentTypes = _inferUpwards(
+        rawType: rawType,
+        argumentList: argumentList,
+        contextType: contextType,
+        isConst: isConst,
+        errorNode: errorNode,
+      );
+      _invokeType = rawType.instantiate(_typeArgumentTypes);
+    }
+  }
+
+  void _resolveInvocationWithTypeArguments({
+    FunctionType rawType,
+    TypeArgumentList typeArgumentList,
+    ArgumentList argumentList,
+  }) {
+    var typeParameters = rawType.typeFormals;
+
+    List<DartType> typeArguments;
+    if (typeArgumentList.arguments.length != typeParameters.length) {
+      _errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
+        typeArgumentList,
+        [
+          rawType,
+          typeParameters.length,
+          typeArgumentList.arguments.length,
+        ],
+      );
+      typeArguments = List.filled(
+        typeParameters.length,
+        DynamicTypeImpl.instance,
+      );
+    } else {
+      typeArguments = typeArgumentList.arguments
+          .map((typeArgument) => typeArgument.type)
+          .toList(growable: true);
+    }
+
+    var invokeType = rawType.instantiate(typeArguments);
+    InferenceContext.setType(argumentList, invokeType);
+
+    _resolveArguments(argumentList);
+
+    _typeArgumentTypes = typeArguments;
+    _invokeType = invokeType;
+  }
+
+  void _setCorrespondingParameters(
+    ArgumentList argumentList,
+    FunctionType invokeType,
+  ) {
+    var parameters = ResolverVisitor.resolveArgumentsToParameters(
+      argumentList,
+      invokeType.parameters,
+      _errorReporter.reportErrorForNode,
+    );
+    argumentList.correspondingStaticParameters = parameters;
+  }
+
   static DartType _getFreshType(DartType type) {
     if (type is FunctionType) {
       var parameters = getFreshTypeParameters(type.typeFormals);
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index a5ec37b..a80d2d0 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -12,7 +12,6 @@
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
 import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
-import 'package:analyzer/src/dart/resolver/resolution_result.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/element_type_provider.dart';
@@ -175,45 +174,40 @@
     }
   }
 
-  /// Check for a generic type, and apply type arguments.
-  FunctionType _instantiateFunctionType(
-      FunctionType invokeType, TypeArgumentList typeArguments, AstNode node) {
-    var typeFormals = invokeType.typeFormals;
-    var arguments = typeArguments?.arguments;
-    if (arguments != null && arguments.length != typeFormals.length) {
-      _resolver.errorReporter.reportErrorForNode(
-          StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
-          node,
-          [invokeType, typeFormals.length, arguments?.length ?? 0]);
-      arguments = null;
-    }
-
-    if (typeFormals.isNotEmpty) {
-      if (arguments == null) {
-        var typeArguments =
-            _typeSystem.instantiateTypeFormalsToBounds(typeFormals);
-        _invocation.typeArgumentTypes = typeArguments;
-        return invokeType.instantiate(typeArguments);
-      } else {
-        var typeArguments = arguments.map((n) => n.type).toList();
-        _invocation.typeArgumentTypes = typeArguments;
-        return invokeType.instantiate(typeArguments);
-      }
-    } else {
-      _invocation.typeArgumentTypes = const <DartType>[];
-    }
-
-    return invokeType;
-  }
-
   bool _isCoreFunction(DartType type) {
     // TODO(scheglov) Can we optimize this?
     return type is InterfaceType && type.isDartCoreFunction;
   }
 
-  ExecutableElement _lookUpClassMember(ClassElement element, String name) {
-    // TODO(scheglov) Use class hierarchy.
-    return element.lookUpMethod(name, _definingLibrary);
+  void _reportInstanceAccessToStaticMember(
+    SimpleIdentifier nameNode,
+    ExecutableElement element,
+    bool nullReceiver,
+  ) {
+    if (_resolver.enclosingExtension != null) {
+      _resolver.errorReporter.reportErrorForNode(
+        CompileTimeErrorCode
+            .UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
+        nameNode,
+        [element.enclosingElement.displayName],
+      );
+    } else if (nullReceiver) {
+      _resolver.errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
+        nameNode,
+        [element.enclosingElement.displayName],
+      );
+    } else {
+      _resolver.errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
+        nameNode,
+        [
+          nameNode.name,
+          element.kind.displayName,
+          element.enclosingElement.displayName,
+        ],
+      );
+    }
   }
 
   void _reportInvocationOfNonFunction(MethodInvocation node) {
@@ -310,17 +304,6 @@
     }
   }
 
-  /// Given an [argumentList] and the [parameters] related to the element that
-  /// will be invoked using those arguments, compute the list of parameters that
-  /// correspond to the list of arguments. An error will be reported if any of
-  /// the arguments cannot be matched to a parameter. Return the parameters that
-  /// correspond to the arguments.
-  List<ParameterElement> _resolveArgumentsToParameters(
-      ArgumentList argumentList, List<ParameterElement> parameters) {
-    return ResolverVisitor.resolveArgumentsToParameters(
-        argumentList, parameters, _resolver.errorReporter.reportErrorForNode);
-  }
-
   /// Given that we are accessing a property of the given [classElement] with the
   /// given [propertyName], return the element that represents the property.
   Element _resolveElement(
@@ -343,50 +326,6 @@
     return null;
   }
 
-  /// If there is an extension matching the [receiverType] and defining a
-  /// member with the given [name], resolve to the corresponding extension
-  /// method. Return a result indicating whether the [node] was resolved and if
-  /// not why.
-  ResolutionResult _resolveExtension(
-    MethodInvocation node,
-    Expression receiver,
-    DartType receiverType,
-    SimpleIdentifier nameNode,
-    String name,
-  ) {
-    var result = _extensionResolver.findExtension(
-      receiverType,
-      name,
-      nameNode,
-    );
-
-    if (!result.isSingle) {
-      _setDynamicResolution(node);
-      return result;
-    }
-
-    var member = _resolver.toLegacyElement(result.getter);
-    nameNode.staticElement = member;
-
-    if (member.isStatic) {
-      _setDynamicResolution(node);
-      _resolver.errorReporter.reportErrorForNode(
-          StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
-          nameNode,
-          [name, member.kind.displayName, member.enclosingElement.name]);
-      return result;
-    }
-
-    if (member is PropertyAccessorElement) {
-      _rewriteAsFunctionExpressionInvocation(
-          node, _elementTypeProvider.getExecutableReturnType(member));
-      return result;
-    }
-
-    _setResolution(node, _elementTypeProvider.getExecutableType(member));
-    return result;
-  }
-
   void _resolveExtensionMember(MethodInvocation node, Identifier receiver,
       ExtensionElement extension, SimpleIdentifier nameNode, String name) {
     var getter = extension.getGetter(name);
@@ -469,36 +408,13 @@
       return;
     }
 
-    ResolutionResult result =
-        _extensionResolver.findExtension(receiverType, name, nameNode);
-    if (result.isSingle) {
-      var member = _resolver.toLegacyElement(result.getter);
-      nameNode.staticElement = member;
-      if (member is PropertyAccessorElement) {
-        return _rewriteAsFunctionExpressionInvocation(
-            node, _elementTypeProvider.getExecutableReturnType(member));
-      }
-      return _setResolution(
-          node, _elementTypeProvider.getExecutableType(member));
-    } else if (result.isAmbiguous) {
-      return;
-    }
-
-    // We can invoke Object methods on Function.
-    var member = _inheritance.getMember(
-      _resolver.typeProvider.functionType,
-      Name(null, name),
-    );
-    if (member != null) {
-      nameNode.staticElement = member;
-      return _setResolution(
-          node, _elementTypeProvider.getExecutableType(member));
-    }
-
-    _reportUndefinedMethod(
-      node,
-      name,
-      _resolver.typeProvider.functionType.element,
+    _resolveReceiverType(
+      node: node,
+      receiver: receiver,
+      receiverType: receiverType,
+      nameNode: nameNode,
+      name: name,
+      receiverErrorNode: nameNode,
     );
   }
 
@@ -512,53 +428,14 @@
       return;
     }
 
-    var target = _inheritance.getMember(receiverType, _currentName);
-    if (target != null) {
-      _resolver.nullableDereferenceVerifier
-          .methodInvocation(receiver, receiverType, name);
-      target = _resolver.toLegacyElement(target);
-      nameNode.staticElement = target;
-      if (target is PropertyAccessorElement) {
-        return _rewriteAsFunctionExpressionInvocation(
-            node, _elementTypeProvider.getExecutableReturnType(target));
-      }
-      return _setResolution(
-          node, _elementTypeProvider.getExecutableType(target));
-    }
-
-    // Look for an applicable extension.
-    var result =
-        _resolveExtension(node, receiver, receiverType, nameNode, name);
-    if (result.isSingle) {
-      return;
-    }
-
-    // The interface of the receiver does not have an instance member.
-    // Try to recover and find a member in the class.
-    var targetElement = _lookUpClassMember(receiverType.element, name);
-    if (targetElement != null && targetElement.isStatic) {
-      nameNode.staticElement = targetElement;
-      _setDynamicResolution(node);
-      _resolver.errorReporter.reportErrorForNode(
-        StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
-        nameNode,
-        [
-          name,
-          targetElement.kind.displayName,
-          targetElement.enclosingElement.displayName,
-        ],
-      );
-      return;
-    }
-
-    _setDynamicResolution(node);
-    if (result.isNone) {
-      _resolver.errorReporter.reportErrorForNode(
-        StaticTypeWarningCode.UNDEFINED_METHOD,
-        nameNode,
-        [name, receiverType.element.displayName],
-      );
-    }
+    _resolveReceiverType(
+      node: node,
+      receiver: receiver,
+      receiverType: receiverType,
+      nameNode: nameNode,
+      name: name,
+      receiverErrorNode: receiver,
+    );
   }
 
   void _resolveReceiverNever(
@@ -643,73 +520,23 @@
       return _reportInvocationOfNonFunction(node);
     }
 
-    InterfaceType receiverType;
-    ClassElement enclosingClass = _resolver.enclosingClass;
-    if (enclosingClass == null) {
-      if (_resolver.enclosingExtension == null) {
-        return _reportUndefinedFunction(node, node.methodName);
-      }
-      var extendedType =
-          _resolveTypeParameter(_resolver.enclosingExtension.extendedType);
-      _resolver.nullableDereferenceVerifier.implicitThis(
-        nameNode,
-        extendedType,
-      );
-      if (extendedType is InterfaceType) {
-        receiverType = extendedType;
-      } else if (extendedType is FunctionType) {
-        receiverType = _resolver.typeProvider.functionType;
-      } else {
-        return _reportUndefinedFunction(node, node.methodName);
-      }
-      enclosingClass = receiverType.element;
+    DartType receiverType;
+    if (_resolver.enclosingClass != null) {
+      receiverType = _resolver.enclosingClass.thisType;
+    } else if (_resolver.enclosingExtension != null) {
+      receiverType = _resolver.enclosingExtension.extendedType;
     } else {
-      receiverType = enclosingClass.thisType;
-    }
-    var target = _inheritance.getMember(receiverType, _currentName);
-
-    if (target != null) {
-      target = _resolver.toLegacyElement(target);
-      nameNode.staticElement = target;
-      if (target is PropertyAccessorElement) {
-        return _rewriteAsFunctionExpressionInvocation(
-            node, _elementTypeProvider.getExecutableReturnType(target));
-      }
-      return _setResolution(
-          node, _elementTypeProvider.getExecutableType(target));
+      return _reportUndefinedFunction(node, node.methodName);
     }
 
-    var targetElement = _lookUpClassMember(enclosingClass, name);
-    if (targetElement != null && targetElement.isStatic) {
-      nameNode.staticElement = targetElement;
-      _setDynamicResolution(node);
-      if (_resolver.enclosingExtension != null) {
-        _resolver.errorReporter.reportErrorForNode(
-            CompileTimeErrorCode
-                .UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
-            nameNode,
-            [targetElement.enclosingElement.displayName]);
-      } else {
-        _resolver.errorReporter.reportErrorForNode(
-            StaticTypeWarningCode
-                .UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
-            nameNode,
-            [targetElement.enclosingElement.displayName]);
-      }
-      return;
-    }
-
-    var result = _extensionResolver.findExtension(receiverType, name, nameNode);
-    if (result.isSingle) {
-      var target = _resolver.toLegacyElement(result.getter);
-      if (target != null) {
-        nameNode.staticElement = target;
-        _setResolution(node, _elementTypeProvider.getExecutableType(target));
-        return;
-      }
-    }
-
-    return _reportUndefinedMethod(node, name, enclosingClass);
+    _resolveReceiverType(
+      node: node,
+      receiver: null,
+      receiverType: receiverType,
+      nameNode: nameNode,
+      name: name,
+      receiverErrorNode: nameNode,
+    );
   }
 
   void _resolveReceiverPrefix(MethodInvocation node, SimpleIdentifier receiver,
@@ -803,6 +630,62 @@
         [name, enclosingClass.displayName]);
   }
 
+  void _resolveReceiverType({
+    @required MethodInvocation node,
+    @required Expression receiver,
+    @required DartType receiverType,
+    @required SimpleIdentifier nameNode,
+    @required String name,
+    @required Expression receiverErrorNode,
+  }) {
+    var result = _resolver.typePropertyResolver.resolve(
+      receiver: receiver,
+      receiverType: receiverType,
+      name: name,
+      receiverErrorNode: receiverErrorNode,
+      nameErrorNode: nameNode,
+    );
+
+    if (result.isAmbiguous) {
+      _setDynamicResolution(node);
+      return;
+    }
+
+    var target = result.getter;
+    if (target != null) {
+      nameNode.staticElement = target;
+
+      if (target.isStatic) {
+        _reportInstanceAccessToStaticMember(
+          nameNode,
+          target,
+          receiver == null,
+        );
+      }
+
+      if (target is PropertyAccessorElement) {
+        return _rewriteAsFunctionExpressionInvocation(
+            node, _elementTypeProvider.getExecutableReturnType(target));
+      }
+      return _setResolution(
+          node, _elementTypeProvider.getExecutableType(target));
+    }
+
+    _setDynamicResolution(node);
+
+    String receiverClassName = '<unknown>';
+    if (receiverType is InterfaceType) {
+      receiverClassName = receiverType.element.name;
+    } else if (receiverType is FunctionType) {
+      receiverClassName = 'Function';
+    }
+    _resolver.errorReporter.reportErrorForNode(
+      StaticTypeWarningCode.UNDEFINED_METHOD,
+      nameNode,
+      [name, receiverClassName],
+    );
+  }
+
   void _resolveReceiverTypeLiteral(MethodInvocation node, ClassElement receiver,
       SimpleIdentifier nameNode, String name) {
     if (node.isCascaded) {
@@ -917,23 +800,7 @@
     }
 
     if (type is FunctionType) {
-      // TODO(scheglov) Extract this when receiver is already FunctionType?
-      var instantiatedType = _instantiateFunctionType(
-        type,
-        node.typeArguments,
-        node.methodName,
-      );
-      instantiatedType = _toSyntheticFunctionType(instantiatedType);
-      node.staticInvokeType = instantiatedType;
-      node.staticType = instantiatedType.returnType;
-      // TODO(scheglov) too much magic
-      node.argumentList.correspondingStaticParameters =
-          _resolveArgumentsToParameters(
-        node.argumentList,
-        instantiatedType.parameters,
-      );
-
-      _resolveArguments_finishInference(node);
+      _inferenceHelper.resolveMethodInvocation(node: node, rawType: type);
       return;
     }
 
@@ -964,30 +831,4 @@
     }
     return null;
   }
-
-  /// As an experiment for using synthetic [FunctionType]s, we replace some
-  /// function types with the equivalent synthetic function type instance.
-  /// The assumption that we try to prove is that only the set of parameters,
-  /// with their names, types and kinds is important, but the element that
-  /// encloses them is not (`null` for synthetic function types).
-  static FunctionType _toSyntheticFunctionType(FunctionType type) {
-//    if (type.element is GenericFunctionTypeElement) {
-//      var synthetic = FunctionTypeImpl.synthetic(
-//        type.returnType,
-//        type.typeFormals.map((e) {
-//          return TypeParameterElementImpl.synthetic(e.name)..bound = e.bound;
-//        }).toList(),
-//        type.parameters.map((p) {
-//          return ParameterElementImpl.synthetic(
-//            p.name,
-//            p.type,
-//            // ignore: deprecated_member_use_from_same_package
-//            p.parameterKind,
-//          );
-//        }).toList(),
-//      );
-//      return synthetic;
-//    }
-    return type;
-  }
 }
diff --git a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
new file mode 100644
index 0000000..4416b89
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
@@ -0,0 +1,229 @@
+// Copyright (c) 2020, 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
+import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
+import 'package:analyzer/src/dart/resolver/resolution_result.dart';
+import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/element_type_provider.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/type_system.dart';
+import 'package:analyzer/src/task/strong/checker.dart';
+import 'package:meta/meta.dart';
+
+/// Helper for resolving [PostfixExpression]s.
+class PostfixExpressionResolver {
+  final ResolverVisitor _resolver;
+  final FlowAnalysisHelper _flowAnalysis;
+  final ElementTypeProvider _elementTypeProvider;
+  final TypePropertyResolver _typePropertyResolver;
+  final InvocationInferenceHelper _inferenceHelper;
+
+  PostfixExpressionResolver({
+    @required ResolverVisitor resolver,
+    @required FlowAnalysisHelper flowAnalysis,
+    @required ElementTypeProvider elementTypeProvider,
+  })  : _resolver = resolver,
+        _flowAnalysis = flowAnalysis,
+        _elementTypeProvider = elementTypeProvider,
+        _typePropertyResolver = resolver.typePropertyResolver,
+        _inferenceHelper = resolver.inferenceHelper;
+
+  ErrorReporter get _errorReporter => _resolver.errorReporter;
+
+  bool get _isNonNullableByDefault => _typeSystem.isNonNullableByDefault;
+
+  TypeSystemImpl get _typeSystem => _resolver.typeSystem;
+
+  void resolve(PostfixExpressionImpl node) {
+    if (node.operator.type == TokenType.BANG) {
+      _resolveNullCheck(node);
+      return;
+    }
+
+    node.operand.accept(_resolver);
+
+    var receiverType = getReadType(
+      node.operand,
+      elementTypeProvider: _elementTypeProvider,
+    );
+
+    _resolve1(node, receiverType);
+    _resolve2(node, receiverType);
+  }
+
+  /// Check that the result [type] of a prefix or postfix `++` or `--`
+  /// expression is assignable to the write type of the [operand].
+  ///
+  /// TODO(scheglov) this is duplicate
+  void _checkForInvalidAssignmentIncDec(
+      AstNode node, Expression operand, DartType type) {
+    var operandWriteType = _getWriteType(operand);
+    if (!_typeSystem.isAssignableTo(type, operandWriteType)) {
+      _resolver.errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.INVALID_ASSIGNMENT,
+        node,
+        [type, operandWriteType],
+      );
+    }
+  }
+
+  /// Compute the static return type of the method or function represented by the given element.
+  ///
+  /// @param element the element representing the method or function invoked by the given node
+  /// @return the static return type that was computed
+  ///
+  /// TODO(scheglov) this is duplicate
+  DartType _computeStaticReturnType(Element element) {
+    if (element is PropertyAccessorElement) {
+      //
+      // This is a function invocation expression disguised as something else.
+      // We are invoking a getter and then invoking the returned function.
+      //
+      FunctionType propertyType =
+          _elementTypeProvider.getExecutableType(element);
+      if (propertyType != null) {
+        return _resolver.inferenceHelper.computeInvokeReturnType(
+            propertyType.returnType,
+            isNullAware: false);
+      }
+    } else if (element is ExecutableElement) {
+      return _resolver.inferenceHelper.computeInvokeReturnType(
+          _elementTypeProvider.getExecutableType(element),
+          isNullAware: false);
+    }
+    return DynamicTypeImpl.instance;
+  }
+
+  /// Return the name of the method invoked by the given postfix [expression].
+  String _getPostfixOperator(PostfixExpression expression) {
+    if (expression.operator.type == TokenType.PLUS_PLUS) {
+      return TokenType.PLUS.lexeme;
+    } else if (expression.operator.type == TokenType.MINUS_MINUS) {
+      return TokenType.MINUS.lexeme;
+    } else {
+      throw UnsupportedError(
+          'Unsupported postfix operator ${expression.operator.lexeme}');
+    }
+  }
+
+  DartType _getWriteType(Expression node) {
+    if (node is SimpleIdentifier) {
+      var element = node.staticElement;
+      if (element is PromotableElement) {
+        return _elementTypeProvider.getVariableType(element);
+      }
+    }
+    return node.staticType;
+  }
+
+  void _resolve1(PostfixExpression node, DartType receiverType) {
+    Expression operand = node.operand;
+
+    if (identical(receiverType, NeverTypeImpl.instance)) {
+      _resolver.errorReporter.reportErrorForNode(
+        StaticWarningCode.INVALID_USE_OF_NEVER_VALUE,
+        operand,
+      );
+      return;
+    }
+
+    String methodName = _getPostfixOperator(node);
+    var result = _typePropertyResolver.resolve(
+      receiver: operand,
+      receiverType: receiverType,
+      name: methodName,
+      receiverErrorNode: operand,
+      nameErrorNode: operand,
+    );
+    node.staticElement = result.getter;
+    if (_shouldReportInvalidMember(receiverType, result)) {
+      if (operand is SuperExpression) {
+        _errorReporter.reportErrorForToken(
+          StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
+          node.operator,
+          [methodName, receiverType],
+        );
+      } else {
+        _errorReporter.reportErrorForToken(
+          StaticTypeWarningCode.UNDEFINED_OPERATOR,
+          node.operator,
+          [methodName, receiverType],
+        );
+      }
+    }
+  }
+
+  void _resolve2(PostfixExpression node, DartType receiverType) {
+    Expression operand = node.operand;
+
+    if (identical(receiverType, NeverTypeImpl.instance)) {
+      _inferenceHelper.recordStaticType(node, NeverTypeImpl.instance);
+    } else {
+      DartType operatorReturnType;
+      if (receiverType.isDartCoreInt) {
+        // No need to check for `intVar++`, the result is `int`.
+        operatorReturnType = receiverType;
+      } else {
+        var operatorElement = node.staticElement;
+        operatorReturnType = _computeStaticReturnType(operatorElement);
+        _checkForInvalidAssignmentIncDec(node, operand, operatorReturnType);
+      }
+      if (operand is SimpleIdentifier) {
+        var element = operand.staticElement;
+        if (element is PromotableElement) {
+          _flowAnalysis?.flow?.write(element, operatorReturnType);
+        }
+      }
+    }
+
+    _inferenceHelper.recordStaticType(node, receiverType);
+  }
+
+  void _resolveNullCheck(PostfixExpressionImpl node) {
+    var operand = node.operand;
+
+    var contextType = InferenceContext.getContext(node);
+    if (contextType != null) {
+      if (_isNonNullableByDefault) {
+        contextType = _typeSystem.makeNullable(contextType);
+      }
+      InferenceContext.setType(operand, contextType);
+    }
+
+    operand.accept(_resolver);
+
+    var operandType = getReadType(
+      operand,
+      elementTypeProvider: _elementTypeProvider,
+    );
+
+    var type = _typeSystem.promoteToNonNull(operandType);
+    _inferenceHelper.recordStaticType(node, type);
+
+    _flowAnalysis?.flow?.nonNullAssert_end(operand);
+  }
+
+  /// Return `true` if we should report an error for the lookup [result] on
+  /// the [type].
+  ///
+  /// TODO(scheglov) this is duplicate
+  bool _shouldReportInvalidMember(DartType type, ResolutionResult result) {
+    if (result.isNone && type != null && !type.isDynamic) {
+      if (_isNonNullableByDefault && _typeSystem.isPotentiallyNullable(type)) {
+        return false;
+      }
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
index 6e4700a..1ad1850 100644
--- a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
@@ -52,16 +52,14 @@
     var operand = node.operand;
 
     if (operator == TokenType.BANG) {
-      InferenceContext.setType(operand, _typeProvider.boolType);
+      _resolveNegation(node, operand);
+      return;
     }
+
     operand.accept(_resolver);
 
     _resolve1(node);
     _resolve2(node);
-
-    if (operator == TokenType.BANG) {
-      _flowAnalysis?.flow?.logicalNot_end(node, operand);
-    }
   }
 
   /// Check that the result [type] of a prefix or postfix `++` or `--`
@@ -226,9 +224,7 @@
 
   void _resolve2(PrefixExpressionImpl node) {
     TokenType operator = node.operator.type;
-    if (operator == TokenType.BANG) {
-      _recordStaticType(node, _nonNullable(_typeProvider.boolType));
-    } else if (identical(node.operand.staticType, NeverTypeImpl.instance)) {
+    if (identical(node.operand.staticType, NeverTypeImpl.instance)) {
       _recordStaticType(node, NeverTypeImpl.instance);
     } else {
       // The other cases are equivalent to invoking a method.
@@ -255,6 +251,16 @@
     }
   }
 
+  void _resolveNegation(PrefixExpressionImpl node, Expression operand) {
+    InferenceContext.setType(operand, _typeProvider.boolType);
+
+    operand.accept(_resolver);
+
+    _recordStaticType(node, _nonNullable(_typeProvider.boolType));
+
+    _flowAnalysis?.flow?.logicalNot_end(node, operand);
+  }
+
   /// If the given [type] is a type parameter, resolve it to the type that should
   /// be used when looking up members. Otherwise, return the original type.
   ///
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 7cd588e..885602f 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -206,16 +206,6 @@
   }
 
   @override
-  void visitBinaryExpression(BinaryExpression node) {
-    Token operator = node.operator;
-    if (operator.isUserDefinableOperator) {
-      _resolveBinaryExpression(node, operator.lexeme);
-    } else if (operator.type == TokenType.BANG_EQ) {
-      _resolveBinaryExpression(node, TokenType.EQ_EQ.lexeme);
-    }
-  }
-
-  @override
   void visitBreakStatement(BreakStatement node) {
     node.target = _lookupBreakOrContinueTarget(node, node.label, false);
   }
@@ -549,50 +539,6 @@
   }
 
   @override
-  void visitPostfixExpression(PostfixExpression node) {
-    Expression operand = node.operand;
-    if (node.operator.type == TokenType.BANG) {
-      // Null-assertion operator (`!`).  There's nothing to do, since this is a
-      // built-in operation (there's no associated operator declaration).
-      return;
-    }
-    DartType staticType = _getStaticType(operand);
-
-    if (identical(staticType, NeverTypeImpl.instance)) {
-      _resolver.errorReporter.reportErrorForNode(
-        StaticWarningCode.INVALID_USE_OF_NEVER_VALUE,
-        operand,
-      );
-      return;
-    }
-
-    String methodName = _getPostfixOperator(node);
-    var result = _typePropertyResolver.resolve(
-      receiver: operand,
-      receiverType: staticType,
-      name: methodName,
-      receiverErrorNode: operand,
-      nameErrorNode: operand,
-    );
-    node.staticElement = result.getter;
-    if (_shouldReportInvalidMember(staticType, result)) {
-      if (operand is SuperExpression) {
-        _errorReporter.reportErrorForToken(
-          StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
-          node.operator,
-          [methodName, staticType],
-        );
-      } else {
-        _errorReporter.reportErrorForToken(
-          StaticTypeWarningCode.UNDEFINED_OPERATOR,
-          node.operator,
-          [methodName, staticType],
-        );
-      }
-    }
-  }
-
-  @override
   void visitPrefixedIdentifier(PrefixedIdentifier node) {
     SimpleIdentifier prefix = node.prefix;
     SimpleIdentifier identifier = node.identifier;
@@ -1046,20 +992,6 @@
   }
 
   /**
-   * Return the name of the method invoked by the given postfix [expression].
-   */
-  String _getPostfixOperator(PostfixExpression expression) {
-    if (expression.operator.type == TokenType.PLUS_PLUS) {
-      return TokenType.PLUS.lexeme;
-    } else if (expression.operator.type == TokenType.MINUS_MINUS) {
-      return TokenType.MINUS.lexeme;
-    } else {
-      throw UnsupportedError(
-          'Unsupported postfix operator ${expression.operator.lexeme}');
-    }
-  }
-
-  /**
    * Return the static type of the given [expression] that is to be used for
    * type analysis.
    */
@@ -1340,61 +1272,6 @@
         argumentList, parameters, _errorReporter.reportErrorForNode);
   }
 
-  void _resolveBinaryExpression(BinaryExpression node, String methodName) {
-    Expression leftOperand = node.leftOperand;
-
-    if (leftOperand is ExtensionOverride) {
-      ExtensionElement extension = leftOperand.extensionName.staticElement;
-      MethodElement member = extension.getMethod(methodName);
-      if (member == null) {
-        _errorReporter.reportErrorForToken(
-          CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR,
-          node.operator,
-          [methodName, extension.name],
-        );
-      }
-      node.staticElement = member;
-      return;
-    }
-
-    DartType leftType = _getStaticType(leftOperand);
-
-    if (identical(leftType, NeverTypeImpl.instance)) {
-      _resolver.errorReporter.reportErrorForNode(
-        StaticWarningCode.INVALID_USE_OF_NEVER_VALUE,
-        leftOperand,
-      );
-      return;
-    }
-
-    ResolutionResult result = _typePropertyResolver.resolve(
-      receiver: leftOperand,
-      receiverType: leftType,
-      name: methodName,
-      receiverErrorNode: leftOperand,
-      nameErrorNode: node,
-    );
-
-    node.staticElement = result.getter;
-    node.staticInvokeType =
-        _elementTypeProvider.safeExecutableType(result.getter);
-    if (_shouldReportInvalidMember(leftType, result)) {
-      if (leftOperand is SuperExpression) {
-        _errorReporter.reportErrorForToken(
-          StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
-          node.operator,
-          [methodName, leftType],
-        );
-      } else {
-        _errorReporter.reportErrorForToken(
-          StaticTypeWarningCode.UNDEFINED_OPERATOR,
-          node.operator,
-          [methodName, leftType],
-        );
-      }
-    }
-  }
-
   /**
    * Resolve the names in the given [combinators] in the scope of the given
    * [library].
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index a1c4b3a..50f345c 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -23,11 +23,13 @@
 import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
+import 'package:analyzer/src/dart/resolver/binary_expression_resolver.dart';
 import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/dart/resolver/function_expression_invocation_resolver.dart';
 import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
 import 'package:analyzer/src/dart/resolver/method_invocation_resolver.dart';
+import 'package:analyzer/src/dart/resolver/postfix_expression_resolver.dart';
 import 'package:analyzer/src/dart/resolver/prefix_expression_resolver.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
@@ -207,7 +209,9 @@
   /// Helper for resolving [ListLiteral] and [SetOrMapLiteral].
   TypedLiteralResolver _typedLiteralResolver;
 
+  BinaryExpressionResolver _binaryExpressionResolver;
   FunctionExpressionInvocationResolver _functionExpressionInvocationResolver;
+  PostfixExpressionResolver _postfixExpressionResolver;
   PrefixExpressionResolver _prefixExpressionResolver;
 
   InvocationInferenceHelper inferenceHelper;
@@ -329,17 +333,29 @@
     this.extensionResolver = ExtensionMemberResolver(this);
     this.typePropertyResolver = TypePropertyResolver(this);
     this.inferenceHelper = InvocationInferenceHelper(
+      resolver: this,
       definingLibrary: definingLibrary,
       elementTypeProvider: _elementTypeProvider,
       flowAnalysis: _flowAnalysis,
       errorReporter: errorReporter,
       typeSystem: typeSystem,
     );
+    this._binaryExpressionResolver = BinaryExpressionResolver(
+      resolver: this,
+      promoteManager: _promoteManager,
+      flowAnalysis: _flowAnalysis,
+      elementTypeProvider: _elementTypeProvider,
+    );
     this._functionExpressionInvocationResolver =
         FunctionExpressionInvocationResolver(
       resolver: this,
       elementTypeProvider: _elementTypeProvider,
     );
+    this._postfixExpressionResolver = PostfixExpressionResolver(
+      resolver: this,
+      flowAnalysis: _flowAnalysis,
+      elementTypeProvider: _elementTypeProvider,
+    );
     this._prefixExpressionResolver = PrefixExpressionResolver(
       resolver: this,
       flowAnalysis: _flowAnalysis,
@@ -641,92 +657,7 @@
 
   @override
   void visitBinaryExpression(BinaryExpression node) {
-    TokenType operator = node.operator.type;
-    Expression left = node.leftOperand;
-    Expression right = node.rightOperand;
-    var flow = _flowAnalysis?.flow;
-
-    if (operator == TokenType.AMPERSAND_AMPERSAND) {
-      InferenceContext.setType(left, typeProvider.boolType);
-      InferenceContext.setType(right, typeProvider.boolType);
-
-      // TODO(scheglov) Do we need these checks for null?
-      left?.accept(this);
-
-      if (_flowAnalysis != null) {
-        flow?.logicalBinaryOp_rightBegin(left, isAnd: true);
-        _flowAnalysis.checkUnreachableNode(right);
-        right.accept(this);
-        flow?.logicalBinaryOp_end(node, right, isAnd: true);
-      } else {
-        _promoteManager.visitBinaryExpression_and_rhs(
-          left,
-          right,
-          () {
-            right.accept(this);
-          },
-        );
-      }
-
-      node.accept(elementResolver);
-    } else if (operator == TokenType.BAR_BAR) {
-      InferenceContext.setType(left, typeProvider.boolType);
-      InferenceContext.setType(right, typeProvider.boolType);
-
-      left?.accept(this);
-
-      flow?.logicalBinaryOp_rightBegin(left, isAnd: false);
-      _flowAnalysis?.checkUnreachableNode(right);
-      right.accept(this);
-      flow?.logicalBinaryOp_end(node, right, isAnd: false);
-
-      node.accept(elementResolver);
-    } else if (operator == TokenType.BANG_EQ || operator == TokenType.EQ_EQ) {
-      left.accept(this);
-      _flowAnalysis?.flow?.equalityOp_rightBegin(left);
-      right.accept(this);
-      node.accept(elementResolver);
-      _flowAnalysis?.flow?.equalityOp_end(node, right,
-          notEqual: operator == TokenType.BANG_EQ);
-    } else {
-      if (operator == TokenType.QUESTION_QUESTION) {
-        InferenceContext.setTypeFromNode(left, node);
-      }
-      left?.accept(this);
-
-      // Call ElementResolver.visitBinaryExpression to resolve the user-defined
-      // operator method, if applicable.
-      node.accept(elementResolver);
-
-      if (operator == TokenType.QUESTION_QUESTION) {
-        // Set the right side, either from the context, or using the information
-        // from the left side if it is more precise.
-        DartType contextType = InferenceContext.getContext(node);
-        DartType leftType = left?.staticType;
-        if (contextType == null || contextType.isDynamic) {
-          contextType = leftType;
-        }
-        InferenceContext.setType(right, contextType);
-      } else {
-        var invokeType = node.staticInvokeType;
-        if (invokeType != null && invokeType.parameters.isNotEmpty) {
-          // If this is a user-defined operator, set the right operand context
-          // using the operator method's parameter type.
-          var rightParam = invokeType.parameters[0];
-          InferenceContext.setType(
-              right, _elementTypeProvider.getVariableType(rightParam));
-        }
-      }
-
-      if (operator == TokenType.QUESTION_QUESTION) {
-        flow?.ifNullExpression_rightBegin(node.leftOperand);
-        right.accept(this);
-        flow?.ifNullExpression_end();
-      } else {
-        right?.accept(this);
-      }
-    }
-    node.accept(typeAnalyzer);
+    _binaryExpressionResolver.resolve(node);
   }
 
   @override
@@ -1583,12 +1514,7 @@
 
   @override
   void visitPostfixExpression(PostfixExpression node) {
-    super.visitPostfixExpression(node);
-
-    var operator = node.operator.type;
-    if (operator == TokenType.BANG) {
-      _flowAnalysis?.flow?.nonNullAssert_end(node.operand);
-    }
+    _postfixExpressionResolver.resolve(node);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 2bad223..7114129 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -340,83 +340,6 @@
   }
 
   /**
-   * The Dart Language Specification, 12.20: <blockquote>The static type of a logical boolean
-   * expression is `bool`.</blockquote>
-   *
-   * The Dart Language Specification, 12.21:<blockquote>A bitwise expression of the form
-   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A bitwise expression of the form <i>super op
-   * e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
-   *
-   * The Dart Language Specification, 12.22: <blockquote>The static type of an equality expression
-   * is `bool`.</blockquote>
-   *
-   * The Dart Language Specification, 12.23: <blockquote>A relational expression of the form
-   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A relational expression of the form <i>super op
-   * e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
-   *
-   * The Dart Language Specification, 12.24: <blockquote>A shift expression of the form
-   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A shift expression of the form <i>super op
-   * e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
-   *
-   * The Dart Language Specification, 12.25: <blockquote>An additive expression of the form
-   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. An additive expression of the form <i>super op
-   * e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
-   *
-   * The Dart Language Specification, 12.26: <blockquote>A multiplicative expression of the form
-   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A multiplicative expression of the form <i>super op
-   * e<sub>2</sub></i> is equivalent to the method invocation
-   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
-   */
-  @override
-  void visitBinaryExpression(BinaryExpression node) {
-    if (node.operator.type == TokenType.QUESTION_QUESTION) {
-      if (_isNonNullableByDefault) {
-        // The static type of a compound assignment using ??= with NNBD is the
-        // least upper bound of the static types of the LHS and RHS after
-        // promoting the LHS/ to non-null (as we know its value will not be used
-        // if null)
-        _analyzeLeastUpperBoundTypes(
-            node,
-            _typeSystem.promoteToNonNull(
-                _getExpressionType(node.leftOperand, read: true)),
-            _getExpressionType(node.rightOperand, read: true));
-      } else {
-        // Without NNBD, evaluation of an if-null expression e of the form
-        // e1 ?? e2 is equivalent to the evaluation of the expression
-        // ((x) => x == null ? e2 : x)(e1).  The static type of e is the least
-        // upper bound of the static type of e1 and the static type of e2.
-        _analyzeLeastUpperBound(node, node.leftOperand, node.rightOperand);
-      }
-      return;
-    }
-
-    if (identical(node.leftOperand.staticType, NeverTypeImpl.instance)) {
-      _recordStaticType(node, NeverTypeImpl.instance);
-      return;
-    }
-
-    DartType staticType = node.staticInvokeType?.returnType ?? _dynamicType;
-    if (node.leftOperand is! ExtensionOverride) {
-      staticType = _typeSystem.refineBinaryExpressionType(
-        node.leftOperand.staticType,
-        node.operator.type,
-        node.rightOperand.staticType,
-        staticType,
-      );
-    }
-    _recordStaticType(node, staticType);
-  }
-
-  /**
    * The Dart Language Specification, 12.4: <blockquote>The static type of a boolean literal is
    * bool.</blockquote>
    */
@@ -636,62 +559,6 @@
   }
 
   /**
-   * The Dart Language Specification, 12.28: <blockquote>A postfix expression of the form
-   * <i>v++</i>, where <i>v</i> is an identifier, is equivalent to <i>(){var r = v; v = r + 1;
-   * return r}()</i>.
-   *
-   * A postfix expression of the form <i>C.v++</i> is equivalent to <i>(){var r = C.v; C.v = r + 1;
-   * return r}()</i>.
-   *
-   * A postfix expression of the form <i>e1.v++</i> is equivalent to <i>(x){var r = x.v; x.v = r +
-   * 1; return r}(e1)</i>.
-   *
-   * A postfix expression of the form <i>e1[e2]++</i> is equivalent to <i>(a, i){var r = a[i]; a[i]
-   * = r + 1; return r}(e1, e2)</i>
-   *
-   * A postfix expression of the form <i>v--</i>, where <i>v</i> is an identifier, is equivalent to
-   * <i>(){var r = v; v = r - 1; return r}()</i>.
-   *
-   * A postfix expression of the form <i>C.v--</i> is equivalent to <i>(){var r = C.v; C.v = r - 1;
-   * return r}()</i>.
-   *
-   * A postfix expression of the form <i>e1.v--</i> is equivalent to <i>(x){var r = x.v; x.v = r -
-   * 1; return r}(e1)</i>.
-   *
-   * A postfix expression of the form <i>e1[e2]--</i> is equivalent to <i>(a, i){var r = a[i]; a[i]
-   * = r - 1; return r}(e1, e2)</i></blockquote>
-   */
-  @override
-  void visitPostfixExpression(PostfixExpression node) {
-    Expression operand = node.operand;
-    TypeImpl staticType = _getStaticType(operand, read: true);
-
-    if (node.operator.type == TokenType.BANG) {
-      staticType = _typeSystem.promoteToNonNull(staticType);
-    } else if (identical(staticType, NeverTypeImpl.instance)) {
-      _recordStaticType(node, NeverTypeImpl.instance);
-    } else {
-      DartType operatorReturnType;
-      if (staticType.isDartCoreInt) {
-        // No need to check for `intVar++`, the result is `int`.
-        operatorReturnType = staticType;
-      } else {
-        var operatorElement = node.staticElement;
-        operatorReturnType = _computeStaticReturnType(operatorElement);
-        _checkForInvalidAssignmentIncDec(node, operand, operatorReturnType);
-      }
-      if (operand is SimpleIdentifier) {
-        var element = operand.staticElement;
-        if (element is PromotableElement) {
-          _flowAnalysis?.flow?.write(element, operatorReturnType);
-        }
-      }
-    }
-
-    _recordStaticType(node, staticType);
-  }
-
-  /**
    * See [visitSimpleIdentifier].
    */
   @override
@@ -1004,20 +871,6 @@
     _recordStaticType(node, staticType);
   }
 
-  /// Check that the result [type] of a prefix or postfix `++` or `--`
-  /// expression is assignable to the write type of the [operand].
-  void _checkForInvalidAssignmentIncDec(
-      AstNode node, Expression operand, DartType type) {
-    var operandWriteType = _getStaticType(operand);
-    if (!_typeSystem.isAssignableTo(type, operandWriteType)) {
-      _resolver.errorReporter.reportErrorForNode(
-        StaticTypeWarningCode.INVALID_ASSIGNMENT,
-        node,
-        [type, operandWriteType],
-      );
-    }
-  }
-
   /**
    * Given a function body and its return type, compute the return type of
    * the entire function, taking into account whether the function body
@@ -1044,33 +897,6 @@
   }
 
   /**
-   * Compute the static return type of the method or function represented by the given element.
-   *
-   * @param element the element representing the method or function invoked by the given node
-   * @return the static return type that was computed
-   */
-  DartType _computeStaticReturnType(Element element) {
-    if (element is PropertyAccessorElement) {
-      //
-      // This is a function invocation expression disguised as something else.
-      // We are invoking a getter and then invoking the returned function.
-      //
-      FunctionType propertyType =
-          _elementTypeProvider.getExecutableType(element);
-      if (propertyType != null) {
-        return _resolver.inferenceHelper.computeInvokeReturnType(
-            propertyType.returnType,
-            isNullAware: false);
-      }
-    } else if (element is ExecutableElement) {
-      return _resolver.inferenceHelper.computeInvokeReturnType(
-          _elementTypeProvider.getExecutableType(element),
-          isNullAware: false);
-    }
-    return _dynamicType;
-  }
-
-  /**
    * Given a function declaration, compute the return static type of the function. The return type
    * of functions with a block body is `dynamicType`, with an expression body it is the type
    * of the expression.
diff --git a/pkg/analyzer/lib/src/lint/analysis.dart b/pkg/analyzer/lib/src/lint/analysis.dart
index e85f5b6..53ea208 100644
--- a/pkg/analyzer/lib/src/lint/analysis.dart
+++ b/pkg/analyzer/lib/src/lint/analysis.dart
@@ -141,8 +141,11 @@
     if (options.packageRootPath != null) {
       builder.builderOptions.defaultPackagesDirectoryPath =
           options.packageRootPath;
-      Map<String, List<Folder>> packageMap =
-          builder.convertPackagesToMap(builder.createPackageMap(null));
+      var packages = builder.createPackageMap(null);
+      var packageMap = <String, List<Folder>>{};
+      for (var package in packages.packages) {
+        packageMap[package.name] = [package.libFolder];
+      }
       resolvers.add(PackageMapUriResolver(resourceProvider, packageMap));
     }
 
diff --git a/pkg/analyzer/lib/src/workspace/basic.dart b/pkg/analyzer/lib/src/workspace/basic.dart
index 0225481..13455c9 100644
--- a/pkg/analyzer/lib/src/workspace/basic.dart
+++ b/pkg/analyzer/lib/src/workspace/basic.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/workspace/simple.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
@@ -23,8 +22,10 @@
   BasicWorkspacePackage _theOnlyPackage;
 
   BasicWorkspace._(
-      ResourceProvider provider, String root, ContextBuilder builder)
-      : super(provider, root, builder);
+    ResourceProvider provider,
+    Map<String, List<Folder>> packageMap,
+    String root,
+  ) : super(provider, packageMap, root);
 
   @override
   WorkspacePackage findPackageFor(String filePath) {
@@ -45,12 +46,15 @@
    * (or [path]'s parent if [path] points to a file).
    */
   static BasicWorkspace find(
-      ResourceProvider provider, String path, ContextBuilder builder) {
+    ResourceProvider provider,
+    Map<String, List<Folder>> packageMap,
+    String path,
+  ) {
     Resource resource = provider.getResource(path);
     if (resource is File) {
       path = resource.parent.path;
     }
-    return BasicWorkspace._(provider, path, builder);
+    return BasicWorkspace._(provider, packageMap, path);
   }
 }
 
diff --git a/pkg/analyzer/lib/src/workspace/bazel.dart b/pkg/analyzer/lib/src/workspace/bazel.dart
index 0b2c7c9..9e42c63 100644
--- a/pkg/analyzer/lib/src/workspace/bazel.dart
+++ b/pkg/analyzer/lib/src/workspace/bazel.dart
@@ -200,9 +200,6 @@
   bool get isBazel => true;
 
   @override
-  Map<String, List<Folder>> get packageMap => null;
-
-  @override
   UriResolver get packageUriResolver => BazelPackageUriResolver(this);
 
   @override
diff --git a/pkg/analyzer/lib/src/workspace/gn.dart b/pkg/analyzer/lib/src/workspace/gn.dart
index 0f91f05..e0aae5f 100644
--- a/pkg/analyzer/lib/src/workspace/gn.dart
+++ b/pkg/analyzer/lib/src/workspace/gn.dart
@@ -2,21 +2,18 @@
 // 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 'dart:collection';
 import 'dart:core';
 
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/file_system/file_system.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/source_io.dart';
 import 'package:analyzer/src/source/package_map_resolver.dart';
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
-import 'package:analyzer/src/util/uri.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
-import 'package:package_config/packages.dart';
-import 'package:package_config/packages_file.dart';
-import 'package:package_config/src/packages_impl.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 
 /**
@@ -48,35 +45,18 @@
   final String root;
 
   /**
-   * The paths to the .packages files.
-   */
-  final List<String> _packagesFilePaths;
-
-  /**
-   * The map of package locations indexed by package name.
-   *
-   * This is a cached field.
+   * The map from a package name to the list of its `lib/` folders.
    */
   Map<String, List<Folder>> _packageMap;
 
-  /**
-   * The package location strategy.
-   *
-   * This is a cached field.
-   */
-  Packages _packages;
+  GnWorkspace._(this.provider, this.root, this._packageMap);
 
-  GnWorkspace._(this.provider, this.root, this._packagesFilePaths);
-
-  @override
-  Map<String, List<Folder>> get packageMap =>
-      _packageMap ??= _convertPackagesToMap(packages);
-
-  Packages get packages => _packages ??= _createPackages();
+  @visibleForTesting
+  Map<String, List<Folder>> get packageMap => _packageMap;
 
   @override
   UriResolver get packageUriResolver =>
-      PackageMapUriResolver(provider, packageMap);
+      PackageMapUriResolver(provider, _packageMap);
 
   @override
   SourceFactory createSourceFactory(DartSdk sdk, SummaryDataStore summaryData) {
@@ -133,65 +113,6 @@
   }
 
   /**
-   * Creates an alternate representation for available packages.
-   */
-  Map<String, List<Folder>> _convertPackagesToMap(Packages packages) {
-    Map<String, List<Folder>> folderMap = HashMap<String, List<Folder>>();
-    if (packages != null && packages != Packages.noPackages) {
-      var pathContext = provider.pathContext;
-      packages.asMap().forEach((String packageName, Uri uri) {
-        String filePath = fileUriToNormalizedPath(pathContext, uri);
-        folderMap[packageName] = [provider.getFolder(filePath)];
-      });
-    }
-    return folderMap;
-  }
-
-  /**
-   * Loads the packages from the .packages file.
-   */
-  Packages _createPackages() {
-    Map<String, Uri> map = _packagesFilePaths.map((String filePath) {
-      File configFile = provider.getFile(filePath);
-      List<int> bytes = configFile.readAsBytesSync();
-      return parse(bytes, configFile.toUri());
-    }).reduce((mapOne, mapTwo) {
-      mapOne.addAll(mapTwo);
-      return mapOne;
-    });
-    _resolveSymbolicLinks(map);
-    return MapPackages(map);
-  }
-
-  /**
-   * Resolve any symbolic links encoded in the path to the given [folder].
-   */
-  String _resolveSymbolicLink(Folder folder) {
-    try {
-      return folder.resolveSymbolicLinksSync().path;
-    } on FileSystemException {
-      return folder.path;
-    }
-  }
-
-  /**
-   * Resolve any symbolic links encoded in the URI's in the given [map] by
-   * replacing the values in the map.
-   */
-  void _resolveSymbolicLinks(Map<String, Uri> map) {
-    path.Context pathContext = provider.pathContext;
-    for (String packageName in map.keys) {
-      String filePath = fileUriToNormalizedPath(pathContext, map[packageName]);
-      Folder folder = provider.getFolder(filePath);
-      String folderPath = _resolveSymbolicLink(folder);
-      // Add a '.' so that the URI is suitable for resolving relative URI's
-      // against it.
-      String uriPath = pathContext.join(folderPath, '.');
-      map[packageName] = pathContext.toUri(uriPath);
-    }
-  }
-
-  /**
    * Find the GN workspace that contains the given [filePath].
    *
    * Return `null` if a workspace could not be found. For a workspace to be
@@ -213,12 +134,21 @@
       if (folder.getChildAssumingFolder(_jiriRootName).exists) {
         // Found the .jiri_root file, must be a non-git workspace.
         String root = folder.path;
-        List<String> packagesFiles =
-            _findPackagesFile(provider, root, filePath);
+
+        var packagesFiles = _findPackagesFile(provider, root, filePath);
         if (packagesFiles.isEmpty) {
           return null;
         }
-        return GnWorkspace._(provider, root, packagesFiles);
+
+        var packageMap = <String, List<Folder>>{};
+        for (var packagesFile in packagesFiles) {
+          var packages = parseDotPackagesFile(provider, packagesFile);
+          for (var package in packages.packages) {
+            packageMap[package.name] = [package.libFolder];
+          }
+        }
+
+        return GnWorkspace._(provider, root, packageMap);
       }
 
       // Go up a folder.
@@ -235,7 +165,7 @@
    * target. For a complete view of the package, all of these files need to be
    * taken into account.
    */
-  static List<String> _findPackagesFile(
+  static List<File> _findPackagesFile(
     ResourceProvider provider,
     String root,
     String filePath,
@@ -244,18 +174,17 @@
     String sourceDirectory = pathContext.relative(filePath, from: root);
     Folder outDirectory = _getOutDirectory(root, provider);
     if (outDirectory == null) {
-      return const <String>[];
+      return const <File>[];
     }
     Folder genDir = outDirectory.getChildAssumingFolder(
         pathContext.join('dartlang', 'gen', sourceDirectory));
     if (!genDir.exists) {
-      return const <String>[];
+      return const <File>[];
     }
     return genDir
         .getChildren()
         .whereType<File>()
         .where((File file) => pathContext.extension(file.path) == '.packages')
-        .map((File file) => file.path)
         .toList();
   }
 
diff --git a/pkg/analyzer/lib/src/workspace/package_build.dart b/pkg/analyzer/lib/src/workspace/package_build.dart
index 875eb3c..92aa30e 100644
--- a/pkg/analyzer/lib/src/workspace/package_build.dart
+++ b/pkg/analyzer/lib/src/workspace/package_build.dart
@@ -5,7 +5,6 @@
 import 'dart:core';
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/file_system/file_system.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -153,6 +152,11 @@
   final ResourceProvider provider;
 
   /**
+   * The map from a package name to the list of its `lib/` folders.
+   */
+  final Map<String, List<Folder>> _packageMap;
+
+  /**
    * The absolute workspace root path (the directory containing the `.dart_tool`
    * directory).
    */
@@ -165,22 +169,6 @@
    */
   final String projectPackageName;
 
-  final ContextBuilder _builder;
-
-  /**
-   * The map of package locations indexed by package name.
-   *
-   * This is a cached field.
-   */
-  Map<String, List<Folder>> _packageMap;
-
-  /**
-   * The package location strategy.
-   *
-   * This is a cached field.
-   */
-  Packages _packages;
-
   /**
    * The singular package in this workspace.
    *
@@ -189,22 +177,11 @@
   PackageBuildWorkspacePackage _theOnlyPackage;
 
   PackageBuildWorkspace._(
-      this.provider, this.root, this.projectPackageName, this._builder);
-
-  @override
-  Map<String, List<Folder>> get packageMap {
-    _packageMap ??= _builder.convertPackagesToMap(packages);
-    return _packageMap;
-  }
-
-  Packages get packages {
-    _packages ??= _builder.createPackageMap(root);
-    return _packages;
-  }
+      this.provider, this._packageMap, this.root, this.projectPackageName);
 
   @override
   UriResolver get packageUriResolver => PackageBuildPackageUriResolver(
-      this, PackageMapUriResolver(provider, packageMap));
+      this, PackageMapUriResolver(provider, _packageMap));
 
   /**
    * For some package file, which may or may not be a package source (it could
@@ -216,7 +193,7 @@
    * to the project root.
    */
   File builtFile(String builtPath, String packageName) {
-    if (!packageMap.containsKey(packageName)) {
+    if (!_packageMap.containsKey(packageName)) {
       return null;
     }
     path.Context context = provider.pathContext;
@@ -295,8 +272,8 @@
    *
    * Return `null` if the filePath is not in a package:build workspace.
    */
-  static PackageBuildWorkspace find(
-      ResourceProvider provider, String filePath, ContextBuilder builder) {
+  static PackageBuildWorkspace find(ResourceProvider provider,
+      Map<String, List<Folder>> packageMap, String filePath) {
     Folder folder = provider.getFolder(filePath);
     while (true) {
       Folder parent = folder.parent;
@@ -316,7 +293,7 @@
         try {
           final yaml = loadYaml(pubspec.readAsStringSync());
           return PackageBuildWorkspace._(
-              provider, folder.path, yaml['name'], builder);
+              provider, packageMap, folder.path, yaml['name']);
         } catch (_) {}
       }
 
diff --git a/pkg/analyzer/lib/src/workspace/pub.dart b/pkg/analyzer/lib/src/workspace/pub.dart
index cbec8ab..6cd27ee 100644
--- a/pkg/analyzer/lib/src/workspace/pub.dart
+++ b/pkg/analyzer/lib/src/workspace/pub.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/workspace/simple.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
@@ -19,8 +18,11 @@
   /// Each Pub workspace is itself one package.
   PubWorkspacePackage _theOnlyPackage;
 
-  PubWorkspace._(ResourceProvider provider, String root, ContextBuilder builder)
-      : super(provider, root, builder);
+  PubWorkspace._(
+    ResourceProvider provider,
+    Map<String, List<Folder>> packageMap,
+    String root,
+  ) : super(provider, packageMap, root);
 
   @override
   WorkspacePackage findPackageFor(String filePath) {
@@ -35,7 +37,10 @@
 
   /// Find the pub workspace that contains the given [path].
   static PubWorkspace find(
-      ResourceProvider provider, String filePath, ContextBuilder builder) {
+    ResourceProvider provider,
+    Map<String, List<Folder>> packageMap,
+    String filePath,
+  ) {
     Resource resource = provider.getResource(filePath);
     if (resource is File) {
       filePath = resource.parent.path;
@@ -50,7 +55,7 @@
       if (folder.getChildAssumingFile(_pubspecName).exists) {
         // Found the pubspec.yaml file; this is our root.
         String root = folder.path;
-        return PubWorkspace._(provider, root, builder);
+        return PubWorkspace._(provider, packageMap, root);
       }
 
       // Go up a folder.
diff --git a/pkg/analyzer/lib/src/workspace/simple.dart b/pkg/analyzer/lib/src/workspace/simple.dart
index bddf28b..ba316fb 100644
--- a/pkg/analyzer/lib/src/workspace/simple.dart
+++ b/pkg/analyzer/lib/src/workspace/simple.dart
@@ -9,7 +9,6 @@
 import 'package:analyzer/src/source/package_map_resolver.dart';
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
-import 'package:package_config/packages.dart';
 
 /// An abstract class for simple workspaces which do not feature any build
 /// artifacts or generated files.
@@ -20,28 +19,14 @@
   /// The [ResourceProvider] by which paths are converted into [Resource]s.
   final ResourceProvider provider;
 
+  @override
+  Map<String, List<Folder>> packageMap;
+
   /// The absolute workspace root path.
   @override
   final String root;
 
-  final ContextBuilder _builder;
-
-  Map<String, List<Folder>> _packageMap;
-
-  Packages _packages;
-
-  SimpleWorkspace(this.provider, this.root, this._builder);
-
-  @override
-  Map<String, List<Folder>> get packageMap {
-    _packageMap ??= _builder.convertPackagesToMap(packages);
-    return _packageMap;
-  }
-
-  Packages get packages {
-    _packages ??= _builder.createPackageMap(root);
-    return _packages;
-  }
+  SimpleWorkspace(this.provider, this.packageMap, this.root);
 
   @override
   UriResolver get packageUriResolver =>
diff --git a/pkg/analyzer/lib/src/workspace/workspace.dart b/pkg/analyzer/lib/src/workspace/workspace.dart
index 9b70903..1d6de14 100644
--- a/pkg/analyzer/lib/src/workspace/workspace.dart
+++ b/pkg/analyzer/lib/src/workspace/workspace.dart
@@ -2,7 +2,6 @@
 // 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/file_system/file_system.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
@@ -14,22 +13,11 @@
  */
 abstract class Workspace {
   /**
-   * Return `true` if this workspace defines a single "project" and that
-   * "project" depends upon flutter.
-   */
-  bool get hasFlutterDependency => packageMap?.containsKey('flutter') ?? false;
-
-  /**
    * Return true iff this [Workspace] is a [BazelWorkspace].
    */
   bool get isBazel => false;
 
   /**
-   * Return a (possibly null) map of package sources.
-   */
-  Map<String, List<Folder>> get packageMap;
-
-  /**
    * The [UriResolver] that can resolve `package` URIs.
    */
   UriResolver get packageUriResolver;
diff --git a/pkg/analyzer/test/generated/element_resolver_test.dart b/pkg/analyzer/test/generated/element_resolver_test.dart
index 5d1965d..8cf3d18 100644
--- a/pkg/analyzer/test/generated/element_resolver_test.dart
+++ b/pkg/analyzer/test/generated/element_resolver_test.dart
@@ -13,7 +13,6 @@
 import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
-import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/element_resolver.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -442,57 +441,6 @@
     _listener.assertNoErrors();
   }
 
-  test_visitBinaryExpression_bangEq() async {
-    // String i;
-    // var j;
-    // i == j
-    InterfaceType stringType = _typeProvider.stringType;
-    SimpleIdentifier left = AstTestFactory.identifier3("i");
-    left.staticType = stringType;
-    BinaryExpression expression = AstTestFactory.binaryExpression(
-        left, TokenType.BANG_EQ, AstTestFactory.identifier3("j"));
-    _resolveNode(expression);
-    var stringElement = stringType.element;
-    expect(expression.staticElement, isNotNull);
-    expect(
-        expression.staticElement,
-        stringElement.lookUpMethod(
-            TokenType.EQ_EQ.lexeme, stringElement.library));
-    _listener.assertNoErrors();
-  }
-
-  test_visitBinaryExpression_eq() async {
-    // String i;
-    // var j;
-    // i == j
-    InterfaceType stringType = _typeProvider.stringType;
-    SimpleIdentifier left = AstTestFactory.identifier3("i");
-    left.staticType = stringType;
-    BinaryExpression expression = AstTestFactory.binaryExpression(
-        left, TokenType.EQ_EQ, AstTestFactory.identifier3("j"));
-    _resolveNode(expression);
-    var stringElement = stringType.element;
-    expect(
-        expression.staticElement,
-        stringElement.lookUpMethod(
-            TokenType.EQ_EQ.lexeme, stringElement.library));
-    _listener.assertNoErrors();
-  }
-
-  test_visitBinaryExpression_plus() async {
-    // num i;
-    // var j;
-    // i + j
-    InterfaceType numType = _typeProvider.numType;
-    SimpleIdentifier left = AstTestFactory.identifier3("i");
-    left.staticType = numType;
-    BinaryExpression expression = AstTestFactory.binaryExpression(
-        left, TokenType.PLUS, AstTestFactory.identifier3("j"));
-    _resolveNode(expression);
-    expect(expression.staticElement, numType.getMethod('+'));
-    _listener.assertNoErrors();
-  }
-
   test_visitBreakStatement_withLabel() async {
     // loop: while (true) {
     //   break loop;
@@ -799,45 +747,6 @@
     _listener.assertNoErrors();
   }
 
-  test_visitPostfixExpression() async {
-    InterfaceType numType = _typeProvider.numType;
-    SimpleIdentifier operand = AstTestFactory.identifier3("i");
-    operand.staticType = numType;
-    PostfixExpression expression =
-        AstTestFactory.postfixExpression(operand, TokenType.PLUS_PLUS);
-    _resolveNode(expression);
-    expect(expression.staticElement, numType.getMethod('+'));
-    _listener.assertNoErrors();
-  }
-
-  @failingTest
-  test_visitPostfixExpression_bang() async {
-    InterfaceType numType = _typeProvider.numType;
-    SimpleIdentifier operand = AstTestFactory.identifier3("i");
-    operand.staticType = numType;
-    PostfixExpression expression =
-        AstTestFactory.postfixExpression(operand, TokenType.BANG);
-    // TODO(danrubel): fails with Unsupported operation
-    _resolveNode(expression);
-    _listener.assertErrorsWithCodes([StaticTypeWarningCode.UNDEFINED_OPERATOR]);
-  }
-
-  @failingTest
-  test_visitPostfixExpression_bang_NNBD() async {
-    // TODO(danrubel): enable NNBD
-    InterfaceType numType = _typeProvider.numType;
-    SimpleIdentifier operand = AstTestFactory.identifier3("i");
-    operand.staticType = numType;
-    PostfixExpression expression =
-        AstTestFactory.postfixExpression(operand, TokenType.BANG);
-    _resolveNode(expression);
-    // TODO(danrubel): fails with Unsupported operation
-    expect(expression.staticElement, numType.getMethod('!'));
-    _listener.assertNoErrors();
-    // TODO(scheglov) This is wrong: `expr!` should not be resolved to `num.!`.
-    fail('Expectations are wrong.');
-  }
-
   test_visitPrefixedIdentifier_dynamic() async {
     DartType dynamicType = _typeProvider.dynamicType;
     SimpleIdentifier target = AstTestFactory.identifier3("a");
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 14efa23..d1b571d 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -4286,7 +4286,11 @@
   }
 
   void test_parseTopLevelVariable_late() {
-    var unit = parseCompilationUnit('late a;', featureSet: nonNullable);
+    var unit = parseCompilationUnit('late a;',
+        featureSet: nonNullable,
+        errors: [
+          expectedError(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, 5, 1)
+        ]);
     var declaration = unit.declarations[0] as TopLevelVariableDeclaration;
     var declarationList = declaration.variables;
     expect(declarationList.keyword, isNull);
@@ -4304,7 +4308,11 @@
   }
 
   void test_parseTopLevelVariable_late_init() {
-    var unit = parseCompilationUnit('late a = 0;', featureSet: nonNullable);
+    var unit = parseCompilationUnit('late a = 0;',
+        featureSet: nonNullable,
+        errors: [
+          expectedError(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, 5, 1)
+        ]);
     var declaration = unit.declarations[0] as TopLevelVariableDeclaration;
     var declarationList = declaration.variables;
     expect(declarationList.keyword, isNull);
diff --git a/pkg/analyzer/test/generated/static_type_analyzer_test.dart b/pkg/analyzer/test/generated/static_type_analyzer_test.dart
index 6273223..90ae072 100644
--- a/pkg/analyzer/test/generated/static_type_analyzer_test.dart
+++ b/pkg/analyzer/test/generated/static_type_analyzer_test.dart
@@ -448,110 +448,6 @@
     _listener.assertNoErrors();
   }
 
-  void test_visitBinaryExpression_equals() {
-    // 2 == 3
-    Expression node = AstTestFactory.binaryExpression(
-        _resolvedInteger(2), TokenType.EQ_EQ, _resolvedInteger(3));
-    expect(_analyze(node), same(_typeProvider.boolType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_ifNull() {
-    // 1 ?? 1.5
-    Expression node = AstTestFactory.binaryExpression(
-        _resolvedInteger(1), TokenType.QUESTION_QUESTION, _resolvedDouble(1.5));
-    expect(_analyze(node), _typeProvider.numType);
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_logicalAnd() {
-    // false && true
-    Expression node = AstTestFactory.binaryExpression(
-        AstTestFactory.booleanLiteral(false),
-        TokenType.AMPERSAND_AMPERSAND,
-        AstTestFactory.booleanLiteral(true));
-    expect(_analyze(node), same(_typeProvider.boolType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_logicalOr() {
-    // false || true
-    Expression node = AstTestFactory.binaryExpression(
-        AstTestFactory.booleanLiteral(false),
-        TokenType.BAR_BAR,
-        AstTestFactory.booleanLiteral(true));
-    expect(_analyze(node), same(_typeProvider.boolType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_notEquals() {
-    // 2 != 3
-    Expression node = AstTestFactory.binaryExpression(
-        _resolvedInteger(2), TokenType.BANG_EQ, _resolvedInteger(3));
-    expect(_analyze(node), same(_typeProvider.boolType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_plusID() {
-    // 1 + 2.0
-    BinaryExpression node = AstTestFactory.binaryExpression(
-        _resolvedInteger(1), TokenType.PLUS, _resolvedDouble(2.0));
-    node.staticElement = _typeProvider.numType.getMethod('+');
-    expect(_analyze(node), same(_typeProvider.doubleType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_plusII() {
-    // 1 + 2
-    BinaryExpression node = AstTestFactory.binaryExpression(
-        _resolvedInteger(1), TokenType.PLUS, _resolvedInteger(2));
-    node.staticElement = _typeProvider.numType.getMethod('+');
-    expect(_analyze(node), same(_typeProvider.intType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_slash() {
-    // 2 / 2
-    BinaryExpressionImpl node = AstTestFactory.binaryExpression(
-        _resolvedInteger(2), TokenType.SLASH, _resolvedInteger(2));
-    node.staticElement = _typeProvider.numType.getMethod('/');
-    node.staticInvokeType = node.staticElement.type;
-    expect(_analyze(node), _typeProvider.doubleType);
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_star_notSpecial() {
-    // class A {
-    //   A operator *(double value);
-    // }
-    // (a as A) * 2.0
-    ClassElementImpl classA = ElementFactory.classElement2("A");
-    InterfaceType typeA = interfaceTypeStar(classA);
-    MethodElement operator =
-        ElementFactory.methodElement("*", typeA, [_typeProvider.doubleType]);
-    classA.methods = <MethodElement>[operator];
-
-    var asExpression = AstTestFactory.asExpression(
-        AstTestFactory.identifier3("a"), AstTestFactory.typeName(classA));
-    asExpression.staticType = typeA;
-
-    BinaryExpressionImpl node = AstTestFactory.binaryExpression(
-        asExpression, TokenType.PLUS, _resolvedDouble(2.0));
-    node.staticElement = operator;
-    node.staticInvokeType = node.staticElement.type;
-    expect(_analyze(node), same(typeA));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitBinaryExpression_starID() {
-    // 1 * 2.0
-    BinaryExpression node = AstTestFactory.binaryExpression(
-        _resolvedInteger(1), TokenType.PLUS, _resolvedDouble(2.0));
-    node.staticElement = _typeProvider.numType.getMethod('*');
-    expect(_analyze(node), same(_typeProvider.doubleType));
-    _listener.assertNoErrors();
-  }
-
   void test_visitBooleanLiteral_false() {
     // false
     Expression node = AstTestFactory.booleanLiteral(false);
@@ -982,22 +878,6 @@
     _listener.assertNoErrors();
   }
 
-  void test_visitPostfixExpression_minusMinus() {
-    // 0--
-    PostfixExpression node = AstTestFactory.postfixExpression(
-        _resolvedInteger(0), TokenType.MINUS_MINUS);
-    expect(_analyze(node), same(_typeProvider.intType));
-    _listener.assertNoErrors();
-  }
-
-  void test_visitPostfixExpression_plusPlus() {
-    // 0++
-    PostfixExpression node = AstTestFactory.postfixExpression(
-        _resolvedInteger(0), TokenType.PLUS_PLUS);
-    expect(_analyze(node), same(_typeProvider.intType));
-    _listener.assertNoErrors();
-  }
-
   void test_visitPrefixedIdentifier_getter() {
     DartType boolType = _typeProvider.boolType;
     PropertyAccessorElementImpl getter =
diff --git a/pkg/analyzer/test/src/context/builder_test.dart b/pkg/analyzer/test/src/context/builder_test.dart
index 78958c0..ff0feb7 100644
--- a/pkg/analyzer/test/src/context/builder_test.dart
+++ b/pkg/analyzer/test/src/context/builder_test.dart
@@ -6,6 +6,7 @@
 import 'package:analyzer/src/command_line/arguments.dart';
 import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/context/context_root.dart';
+import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/context/source.dart';
 import 'package:analyzer/src/file_system/file_system.dart';
 import 'package:analyzer/src/generated/engine.dart';
@@ -24,8 +25,6 @@
 import 'package:analyzer/src/workspace/pub.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
 import 'package:args/args.dart';
-import 'package:package_config/packages.dart';
-import 'package:package_config/src/packages_impl.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -230,32 +229,6 @@
 //    _expectEqualOptions(options, expected);
   }
 
-  void test_convertPackagesToMap_noPackages() {
-    expect(builder.convertPackagesToMap(Packages.noPackages), isEmpty);
-  }
-
-  void test_convertPackagesToMap_null() {
-    expect(builder.convertPackagesToMap(null), isEmpty);
-  }
-
-  void test_convertPackagesToMap_packages() {
-    String fooName = 'foo';
-    String fooPath = convertPath('/pkg/foo');
-    Uri fooUri = resourceProvider.pathContext.toUri(fooPath);
-    String barName = 'bar';
-    String barPath = convertPath('/pkg/bar');
-    Uri barUri = resourceProvider.pathContext.toUri(barPath);
-
-    MapPackages packages = MapPackages({fooName: fooUri, barName: barUri});
-    Map<String, List<Folder>> result = builder.convertPackagesToMap(packages);
-    expect(result, isNotNull);
-    expect(result, hasLength(2));
-    expect(result[fooName], hasLength(1));
-    expect(result[fooName][0].path, fooPath);
-    expect(result[barName], hasLength(1));
-    expect(result[barName][0].path, barPath);
-  }
-
   void test_createDefaultOptions_default() {
     // Invert a subset of the options to ensure that the default options are
     // being returned.
@@ -289,11 +262,13 @@
     builderOptions.defaultPackagesDirectoryPath = packageDirPath;
 
     Packages packages = builder.createPackageMap(projectPath);
-    expect(packages, isNotNull);
-    Map<String, Uri> map = packages.asMap();
-    expect(map, hasLength(2));
-    expect(map[fooName], convertedDirectoryUri(fooPath));
-    expect(map[barName], convertedDirectoryUri(barPath));
+    _assertPackages(
+      packages,
+      {
+        'foo': convertPath('/root/packages/foo/lib'),
+        'bar': convertPath('/root/packages/bar/lib'),
+      },
+    );
   }
 
   void test_createPackageMap_fromPackageDirectory_inRoot() {
@@ -308,11 +283,13 @@
     newFolder(barPath);
 
     Packages packages = builder.createPackageMap(projectPath);
-    expect(packages, isNotNull);
-    Map<String, Uri> map = packages.asMap();
-    expect(map, hasLength(2));
-    expect(map[fooName], convertedDirectoryUri(fooPath));
-    expect(map[barName], convertedDirectoryUri(barPath));
+    _assertPackages(
+      packages,
+      {
+        'foo': convertPath('/root/project/packages/foo/lib'),
+        'bar': convertPath('/root/project/packages/bar/lib'),
+      },
+    );
   }
 
   void test_createPackageMap_fromPackageFile_explicit() {
@@ -321,20 +298,20 @@
     String projectPath = join(rootPath, 'project');
     String packageFilePath = join(rootPath, 'child', '.packages');
     newFolder(projectPath);
-    Uri fooUri = convertedDirectoryUri('/pkg/foo');
-    Uri barUri = convertedDirectoryUri('/pkg/bar');
     newFile(packageFilePath, content: '''
-foo:$fooUri
-bar:$barUri
+foo:${toUriStr('/pkg/foo')}
+bar:${toUriStr('/pkg/bar')}
 ''');
 
     builderOptions.defaultPackageFilePath = packageFilePath;
     Packages packages = builder.createPackageMap(projectPath);
-    expect(packages, isNotNull);
-    Map<String, Uri> map = packages.asMap();
-    expect(map, hasLength(2));
-    expect(map['foo'], fooUri);
-    expect(map['bar'], barUri);
+    _assertPackages(
+      packages,
+      {
+        'foo': convertPath('/pkg/foo'),
+        'bar': convertPath('/pkg/bar'),
+      },
+    );
   }
 
   void test_createPackageMap_fromPackageFile_inParentOfRoot() {
@@ -343,19 +320,19 @@
     String projectPath = join(rootPath, 'project');
     String packageFilePath = join(rootPath, '.packages');
     newFolder(projectPath);
-    Uri fooUri = convertedDirectoryUri('/pkg/foo');
-    Uri barUri = convertedDirectoryUri('/pkg/bar');
     newFile(packageFilePath, content: '''
-foo:$fooUri
-bar:$barUri
+foo:${toUriStr('/pkg/foo')}
+bar:${toUriStr('/pkg/bar')}
 ''');
 
     Packages packages = builder.createPackageMap(projectPath);
-    expect(packages, isNotNull);
-    Map<String, Uri> map = packages.asMap();
-    expect(map, hasLength(2));
-    expect(map['foo'], fooUri);
-    expect(map['bar'], barUri);
+    _assertPackages(
+      packages,
+      {
+        'foo': convertPath('/pkg/foo'),
+        'bar': convertPath('/pkg/bar'),
+      },
+    );
   }
 
   void test_createPackageMap_fromPackageFile_inRoot() {
@@ -364,32 +341,32 @@
     String projectPath = join(rootPath, 'project');
     String packageFilePath = join(projectPath, '.packages');
     newFolder(projectPath);
-    Uri fooUri = convertedDirectoryUri('/pkg/foo');
-    Uri barUri = convertedDirectoryUri('/pkg/bar');
     newFile(packageFilePath, content: '''
-foo:$fooUri
-bar:$barUri
+foo:${toUriStr('/pkg/foo')}
+bar:${toUriStr('/pkg/bar')}
 ''');
 
     Packages packages = builder.createPackageMap(projectPath);
-    expect(packages, isNotNull);
-    Map<String, Uri> map = packages.asMap();
-    expect(map, hasLength(2));
-    expect(map['foo'], fooUri);
-    expect(map['bar'], barUri);
+    _assertPackages(
+      packages,
+      {
+        'foo': convertPath('/pkg/foo'),
+        'bar': convertPath('/pkg/bar'),
+      },
+    );
   }
 
   void test_createPackageMap_none() {
     String rootPath = convertPath('/root');
     newFolder(rootPath);
     Packages packages = builder.createPackageMap(rootPath);
-    expect(packages, same(Packages.noPackages));
+    expect(packages.packages, isEmpty);
   }
 
   void test_createPackageMap_rootDoesNotExist() {
     String rootPath = convertPath('/root');
     Packages packages = builder.createPackageMap(rootPath);
-    expect(packages, same(Packages.noPackages));
+    expect(packages.packages, isEmpty);
   }
 
   void test_createSourceFactory_bazelWorkspace_fileProvider() {
@@ -848,6 +825,16 @@
     expect(result.path, filePath);
   }
 
+  void _assertPackages(Packages packages, Map<String, String> nameToPath) {
+    expect(packages, isNotNull);
+    expect(packages.packages, hasLength(nameToPath.length));
+    for (var name in nameToPath.keys) {
+      var expectedPath = nameToPath[name];
+      var path = packages[name].libFolder.path;
+      expect(path, expectedPath, reason: 'package $name');
+    }
+  }
+
   _defineMockLintRules() {
     _mockLintRule = _MockLintRule('mock_lint_rule');
     Registry.ruleRegistry.register(_mockLintRule);
diff --git a/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart b/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart
new file mode 100644
index 0000000..083f98c
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart
@@ -0,0 +1,220 @@
+// Copyright (c) 2020, 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/analysis/experiments.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(BinaryExpressionResolutionTest);
+    defineReflectiveTests(BinaryExpressionResolutionWithNnbdTest);
+  });
+}
+
+@reflectiveTest
+class BinaryExpressionResolutionTest extends DriverResolutionTest {
+  test_bangEq() async {
+    await assertNoErrorsInCode(r'''
+f(int a, int b) {
+  a != b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a != b'),
+      element: numElement.getMethod('=='),
+      type: 'bool',
+    );
+  }
+
+  test_eqEq() async {
+    await assertNoErrorsInCode(r'''
+f(int a, int b) {
+  a == b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a == b'),
+      element: numElement.getMethod('=='),
+      type: 'bool',
+    );
+  }
+
+  test_ifNull() async {
+    await assertNoErrorsInCode(r'''
+f(int a, double b) {
+  a ?? b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a ?? b'),
+      element: null,
+      type: 'num',
+    );
+  }
+
+  test_logicalAnd() async {
+    await assertNoErrorsInCode(r'''
+f(bool a, bool b) {
+  a && b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a && b'),
+      element: boolElement.getMethod('&&'),
+      type: 'bool',
+    );
+  }
+
+  test_logicalOr() async {
+    await assertNoErrorsInCode(r'''
+f(bool a, bool b) {
+  a || b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a || b'),
+      element: boolElement.getMethod('||'),
+      type: 'bool',
+    );
+  }
+
+  test_plus_int_double() async {
+    await assertNoErrorsInCode(r'''
+f(int a, double b) {
+  a + b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a + b'),
+      element: numElement.getMethod('+'),
+      type: 'double',
+    );
+  }
+
+  test_plus_int_int() async {
+    await assertNoErrorsInCode(r'''
+f(int a, int b) {
+  a + b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a + b'),
+      element: numElement.getMethod('+'),
+      type: 'int',
+    );
+  }
+
+  test_receiverTypeParameter_bound_dynamic() async {
+    await assertNoErrorsInCode(r'''
+f<T extends dynamic>(T a) {
+  a + 0;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a + 0'),
+      element: null,
+      type: 'dynamic',
+    );
+  }
+
+  test_receiverTypeParameter_bound_num() async {
+    await assertNoErrorsInCode(r'''
+f<T extends num>(T a) {
+  a + 0;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a + 0'),
+      element: numElement.getMethod('+'),
+      type: 'num',
+    );
+  }
+
+  test_slash() async {
+    await assertNoErrorsInCode(r'''
+f(int a, int b) {
+  a / b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a / b'),
+      element: numElement.getMethod('/'),
+      type: 'double',
+    );
+  }
+
+  test_star_int_double() async {
+    await assertNoErrorsInCode(r'''
+f(int a, double b) {
+  a * b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a * b'),
+      element: numElement.getMethod('*'),
+      type: 'double',
+    );
+  }
+
+  test_star_int_int() async {
+    await assertNoErrorsInCode(r'''
+f(int a, int b) {
+  a * b;
+}
+''');
+
+    assertBinaryExpression(
+      findNode.binary('a * b'),
+      element: numElement.getMethod('*'),
+      type: 'int',
+    );
+  }
+}
+
+@reflectiveTest
+class BinaryExpressionResolutionWithNnbdTest extends DriverResolutionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [EnableString.non_nullable]
+    ..implicitCasts = false;
+
+  @override
+  bool get typeToStringWithNullability => true;
+
+  test_ifNull_left_nullable() async {
+    await assertNoErrorsInCode(r'''
+T f<T>(T t) => t;
+
+int g() => f(null) ?? 0;
+''');
+
+    assertMethodInvocation2(
+      findNode.methodInvocation('f(null)'),
+      element: findElement.topFunction('f'),
+      typeArgumentTypes: ['int?'],
+      invokeType: 'int? Function(int?)',
+      type: 'int?',
+    );
+
+    assertBinaryExpression(
+      findNode.binary('?? 0'),
+      element: null,
+      type: 'int',
+    );
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index 3143261..0420fdf 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -224,10 +224,12 @@
 ''', [
       error(StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 57, 3),
     ]);
-    _assertInvalidInvocation(
-      'a.foo(0)',
-      findElement.method('foo'),
-      expectedNameType: '(int) → void',
+    assertMethodInvocation2(
+      findNode.methodInvocation('a.foo(0)'),
+      element: findElement.method('foo'),
+      typeArgumentTypes: [],
+      invokeType: 'void Function(int)',
+      type: 'void',
     );
   }
 
@@ -783,12 +785,15 @@
               .UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
           71,
           3),
+      error(CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS, 74, 3),
     ]);
 
-    _assertInvalidInvocation(
-      'foo(0)',
-      findElement.method('foo'),
-      expectedNameType: '(int) → void',
+    assertMethodInvocation2(
+      findNode.methodInvocation('foo(0)'),
+      element: findElement.method('foo'),
+      typeArgumentTypes: [],
+      invokeType: 'void Function()',
+      type: 'void',
     );
   }
 
@@ -969,7 +974,7 @@
   foo<int>();
 }
 ''', [
-      error(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, 26, 3),
+      error(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, 29, 5),
     ]);
     assertMethodInvocation(
       findNode.methodInvocation('foo<int>()'),
@@ -987,13 +992,13 @@
   foo<int>();
 }
 ''', [
-      error(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, 55, 3),
+      error(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, 58, 5),
     ]);
     assertMethodInvocation(
       findNode.methodInvocation('foo<int>()'),
       findElement.topFunction('foo'),
-      'Map<num, dynamic> Function()',
-      expectedTypeArguments: ['num', 'dynamic'],
+      'Map<dynamic, dynamic> Function()',
+      expectedTypeArguments: ['dynamic', 'dynamic'],
     );
     assertTypeName(findNode.typeName('int>'), intElement, 'int');
   }
@@ -1582,19 +1587,6 @@
     assertType(foo, 'void Function(int)');
   }
 
-  test_noReceiver_parameter_call_nullAware() async {
-    await assertNoErrorsInCode(r'''
-double Function(int) foo;
-
-main() {
-  foo?.call(1);
-}
-    ''');
-
-    var invocation = findNode.methodInvocation('call(1)');
-    assertTypeLegacy(invocation.target);
-  }
-
   test_noReceiver_method_superClass() async {
     await assertNoErrorsInCode(r'''
 class A {
@@ -1635,6 +1627,19 @@
     );
   }
 
+  test_noReceiver_parameter_call_nullAware() async {
+    await assertNoErrorsInCode(r'''
+double Function(int) foo;
+
+main() {
+  foo?.call(1);
+}
+    ''');
+
+    var invocation = findNode.methodInvocation('call(1)');
+    assertTypeLegacy(invocation.target);
+  }
+
   test_noReceiver_topFunction() async {
     await assertNoErrorsInCode(r'''
 void foo(int _) {}
@@ -1768,7 +1773,8 @@
   foo<int, double>();
 }
 ''', [
-      error(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, 29, 3),
+      error(
+          StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, 32, 13),
     ]);
     var invocation = findNode.methodInvocation('foo<int, double>();');
     assertTypeArgumentTypes(invocation, ['dynamic']);
diff --git a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
index 78ed3b2..1f63ff2 100644
--- a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
@@ -274,45 +274,6 @@
     assertType(findNode.typeName('A {} // 2'), 'A');
   }
 
-  test_nonNullPromotion_typeParameter() async {
-    await assertErrorsInCode(r'''
-class C<T> {
-  void foo(T? t) {
-    T temp = t!;
-  }
-  T bar(T? t) {
-    return t!;
-  }
-}
-''', [
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 38, 4),
-    ]);
-  }
-
-  test_null_assertion_operator_changes_null_to_never() async {
-    await assertErrorsInCode('''
-main() {
-  Null x = null;
-  x!;
-}
-''', [
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 16, 1),
-    ]);
-    assertType(findNode.postfix('x!'), 'Never');
-  }
-
-  test_null_assertion_operator_removes_nullability() async {
-    await assertErrorsInCode('''
-main() {
-  Object? x = null;
-  x!;
-}
-''', [
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 19, 1),
-    ]);
-    assertType(findNode.postfix('x!'), 'Object');
-  }
-
   test_parameter_functionTyped() async {
     await assertNoErrorsInCode('''
 void f1(void p1()) {}
diff --git a/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart b/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart
new file mode 100644
index 0000000..ea38101
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart
@@ -0,0 +1,167 @@
+// Copyright (c) 2020, 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/analysis/experiments.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(PostfixExpressionResolutionTest);
+    defineReflectiveTests(PostfixExpressionResolutionWithNnbdTest);
+  });
+}
+
+@reflectiveTest
+class PostfixExpressionResolutionTest extends DriverResolutionTest {
+  test_dec_localVariable() async {
+    await assertNoErrorsInCode(r'''
+f(int x) {
+  x--;
+}
+''');
+
+    assertPostfixExpression(
+      findNode.postfix('x--'),
+      element: numElement.getMethod('-'),
+      type: 'int',
+    );
+  }
+
+  test_inc_localVariable() async {
+    await assertNoErrorsInCode(r'''
+f(int x) {
+  x++;
+}
+''');
+
+    assertPostfixExpression(
+      findNode.postfix('x++'),
+      element: numElement.getMethod('+'),
+      type: 'int',
+    );
+  }
+
+  test_inc_property_differentTypes() async {
+    await assertNoErrorsInCode(r'''
+dynamic get x => 0;
+
+set x(Object _) {}
+
+f() {
+  x++;
+}
+''');
+
+    assertSimpleIdentifier(
+      findNode.simple('x++'),
+      element: findElement.topSet('x'),
+      type: 'Object',
+    );
+
+    assertPostfixExpression(
+      findNode.postfix('x++'),
+      element: null,
+      type: 'dynamic',
+    );
+  }
+}
+
+@reflectiveTest
+class PostfixExpressionResolutionWithNnbdTest
+    extends PostfixExpressionResolutionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions =>
+      AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
+
+  @override
+  bool get typeToStringWithNullability => true;
+
+  test_inc_localVariable_depromote() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  Object operator +(int _) => this;
+}
+
+f(Object x) {
+  if (x is A) {
+    x++;
+    x; // ref
+  }
+}
+''');
+
+    assertType(findNode.simple('x++;'), 'A');
+
+    assertPostfixExpression(
+      findNode.postfix('x++'),
+      element: findElement.method('+'),
+      type: 'A',
+    );
+
+    assertType(findNode.simple('x; // ref'), 'Object');
+  }
+
+  test_nullCheck() async {
+    await assertNoErrorsInCode(r'''
+f(int? x) {
+  x!;
+}
+''');
+
+    assertPostfixExpression(
+      findNode.postfix('x!'),
+      element: null,
+      type: 'int',
+    );
+  }
+
+  test_nullCheck_null() async {
+    await assertNoErrorsInCode('''
+main(Null x) {
+  x!;
+}
+''');
+
+    assertType(findNode.postfix('x!'), 'Never');
+  }
+
+  test_nullCheck_nullableContext() async {
+    await assertNoErrorsInCode(r'''
+T f<T>(T t) => t;
+
+int g() => f(null)!;
+''');
+
+    assertMethodInvocation2(
+      findNode.methodInvocation('f(null)'),
+      element: findElement.topFunction('f'),
+      typeArgumentTypes: ['int?'],
+      invokeType: 'int? Function(int?)',
+      type: 'int?',
+    );
+
+    assertPostfixExpression(
+      findNode.postfix('f(null)!'),
+      element: null,
+      type: 'int',
+    );
+  }
+
+  test_nullCheck_typeParameter() async {
+    await assertNoErrorsInCode(r'''
+f<T>(T? x) {
+  x!;
+}
+''');
+
+    assertPostfixExpression(
+      findNode.postfix('x!'),
+      element: null,
+      type: 'T',
+    );
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index 92a29b2..ba65a7c 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -6,6 +6,7 @@
 
 import 'assignment_test.dart' as assignment;
 import 'ast_rewrite_test.dart' as ast_rewrite;
+import 'binary_expression_test.dart' as binary_expression;
 import 'class_alias_test.dart' as class_alias;
 import 'class_test.dart' as class_resolution;
 import 'comment_test.dart' as comment;
@@ -39,6 +40,7 @@
 import 'namespace_test.dart' as namespace;
 import 'non_nullable_test.dart' as non_nullable;
 import 'optional_const_test.dart' as optional_const;
+import 'postfix_expression_test.dart' as postfix_expression;
 import 'prefix_expression_test.dart' as prefix_expression;
 import 'prefixed_identifier_test.dart' as prefixed_identifier;
 import 'property_access_test.dart' as property_access;
@@ -50,6 +52,7 @@
   defineReflectiveSuite(() {
     assignment.main();
     ast_rewrite.main();
+    binary_expression.main();
     class_alias.main();
     class_resolution.main();
     comment.main();
@@ -80,6 +83,7 @@
     namespace.main();
     non_nullable.main();
     optional_const.main();
+    postfix_expression.main();
     prefix_expression.main();
     prefixed_identifier.main();
     property_access.main();
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
index 3054091..48fd70f 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
@@ -144,7 +144,7 @@
   test_instanceVariableInitializer_inDeclaration_late() async {
     await assertNoErrorsInCode(r'''
 class A {
-  late f = this;
+  late var f = this;
 }
 ''');
   }
@@ -152,16 +152,16 @@
   test_mixinVariableInitializer_inDeclaration_late() async {
     await assertNoErrorsInCode(r'''
 mixin A {
-  late f = this;
+  late var f = this;
 }
 ''');
   }
 
   test_variableInitializer_late() async {
     await assertErrorsInCode('''
-late x = this;
+late var x = this;
 ''', [
-      error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 9, 4),
+      error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 13, 4),
     ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
index 7662cff..26be039 100644
--- a/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
@@ -782,6 +782,7 @@
   x.foo();
 }
 ''', [
+      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 54, 1),
       error(StaticTypeWarningCode.UNDEFINED_METHOD, 56, 3),
     ]);
   }
diff --git a/pkg/analyzer/test/src/diagnostics/use_of_void_result_test.dart b/pkg/analyzer/test/src/diagnostics/use_of_void_result_test.dart
index 1632029..ecf7c3f 100644
--- a/pkg/analyzer/test/src/diagnostics/use_of_void_result_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/use_of_void_result_test.dart
@@ -705,22 +705,14 @@
   AnalysisOptionsImpl get analysisOptions =>
       AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
 
-  test_bang_nonVoid() async {
-    await assertNoErrorsInCode(r'''
-int? f() => 1;
-g() {
-  f()!;
-}
-''');
-  }
-
-  test_bang_void() async {
+  test_nullCheck() async {
     await assertErrorsInCode(r'''
-void f() => 1;
-g() {
-  f()!;
+f(void x) {
+  x!;
 }
-''', [ExpectedError(StaticWarningCode.USE_OF_VOID_RESULT, 23, 4)]);
+''', [ExpectedError(StaticWarningCode.USE_OF_VOID_RESULT, 14, 2)]);
+
+    assertType(findNode.postfix('x!'), 'void');
   }
 }
 
diff --git a/pkg/analyzer/test/src/task/strong/inferred_type_test.dart b/pkg/analyzer/test/src/task/strong/inferred_type_test.dart
index a4af38c..75b9376 100644
--- a/pkg/analyzer/test/src/task/strong/inferred_type_test.dart
+++ b/pkg/analyzer/test/src/task/strong/inferred_type_test.dart
@@ -1903,7 +1903,7 @@
 /*error:INVALID_OVERRIDE*/m(x) => x;
 }
 main() {
-  int y = new D()./*error:WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD*/m<int>(42);
+  int y = new D().m/*error:WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD*/<int>(42);
   print(y);
 }
 ''');
diff --git a/pkg/analyzer/test/src/workspace/basic_test.dart b/pkg/analyzer/test/src/workspace/basic_test.dart
index 48f754f..f338eba 100644
--- a/pkg/analyzer/test/src/workspace/basic_test.dart
+++ b/pkg/analyzer/test/src/workspace/basic_test.dart
@@ -2,11 +2,8 @@
 // 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/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer/src/workspace/basic.dart';
-import 'package:package_config/packages.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -24,15 +21,9 @@
   BasicWorkspace workspace;
 
   setUp() {
-    final contextBuilder = MockContextBuilder();
-    final packages = MockPackages();
-    final packageMap = <String, List<Folder>>{'project': []};
-    contextBuilder.packagesMapMap[convertPath('/workspace')] = packages;
-    contextBuilder.packagesToMapMap[packages] = packageMap;
-
     newFolder('/workspace');
-    workspace = BasicWorkspace.find(
-        resourceProvider, convertPath('/workspace'), contextBuilder);
+    workspace =
+        BasicWorkspace.find(resourceProvider, {}, convertPath('/workspace'));
     expect(workspace.isBazel, isFalse);
   }
 
@@ -92,45 +83,23 @@
   }
 
   void test_find_directory() {
-    BasicWorkspace workspace = BasicWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+    BasicWorkspace workspace =
+        BasicWorkspace.find(resourceProvider, {}, convertPath('/workspace'));
     expect(workspace.root, convertPath('/workspace'));
     expect(workspace.isBazel, isFalse);
   }
 
   void test_find_fail_notAbsolute() {
     expect(
-        () => BasicWorkspace.find(resourceProvider, convertPath('not_absolute'),
-            MockContextBuilder()),
+        () => BasicWorkspace.find(
+            resourceProvider, {}, convertPath('not_absolute')),
         throwsA(TypeMatcher<ArgumentError>()));
   }
 
   void test_find_file() {
-    BasicWorkspace workspace = BasicWorkspace.find(resourceProvider,
-        convertPath('/workspace/project/lib/lib1.dart'), MockContextBuilder());
+    BasicWorkspace workspace = BasicWorkspace.find(
+        resourceProvider, {}, convertPath('/workspace/project/lib/lib1.dart'));
     expect(workspace.root, convertPath('/workspace/project/lib'));
     expect(workspace.isBazel, isFalse);
   }
 }
-
-class MockContextBuilder implements ContextBuilder {
-  Map<String, Packages> packagesMapMap = <String, Packages>{};
-  Map<Packages, Map<String, List<Folder>>> packagesToMapMap =
-      <Packages, Map<String, List<Folder>>>{};
-
-  @override
-  Map<String, List<Folder>> convertPackagesToMap(Packages packages) =>
-      packagesToMapMap[packages];
-
-  @override
-  Packages createPackageMap(String rootDirectoryPath) =>
-      packagesMapMap[rootDirectoryPath];
-
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
-
-class MockPackages implements Packages {
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
diff --git a/pkg/analyzer/test/src/workspace/package_build_test.dart b/pkg/analyzer/test/src/workspace/package_build_test.dart
index e71b398..630bf0c 100644
--- a/pkg/analyzer/test/src/workspace/package_build_test.dart
+++ b/pkg/analyzer/test/src/workspace/package_build_test.dart
@@ -3,12 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer/src/workspace/package_build.dart';
-import 'package:package_config/packages.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -23,28 +21,6 @@
   });
 }
 
-class MockContextBuilder implements ContextBuilder {
-  Map<String, Packages> packagesMapMap = <String, Packages>{};
-  Map<Packages, Map<String, List<Folder>>> packagesToMapMap =
-      <Packages, Map<String, List<Folder>>>{};
-
-  @override
-  Map<String, List<Folder>> convertPackagesToMap(Packages packages) =>
-      packagesToMapMap[packages];
-
-  @override
-  Packages createPackageMap(String rootDirectoryPath) =>
-      packagesMapMap[rootDirectoryPath];
-
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
-
-class MockPackages implements Packages {
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
-
 class MockUriResolver implements UriResolver {
   Map<Uri, File> uriToFile = {};
   Map<String, Uri> pathToUri = {};
@@ -74,12 +50,14 @@
   void setUp() {
     newFolder('/workspace/.dart_tool/build/generated/project/lib');
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
-    final MockContextBuilder contextBuilder = MockContextBuilder();
-    final Packages packages = MockPackages();
-    contextBuilder.packagesMapMap[convertPath('/workspace')] = packages;
-    contextBuilder.packagesToMapMap[packages] = {'project': []};
+
     workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), contextBuilder);
+      resourceProvider,
+      {
+        'project': [getFolder('/workspace')]
+      },
+      convertPath('/workspace'),
+    );
     resolver = PackageBuildFileUriResolver(workspace);
     newFile('/workspace/test.dart');
     newFile('/workspace/.dart_tool/build/generated/project/gen.dart');
@@ -221,12 +199,13 @@
         newFile(path);
       }
     }
-    final contextBuilder = MockContextBuilder();
-    final packages = MockPackages();
-    contextBuilder.packagesMapMap[convertPath(workspacePath)] = packages;
-    contextBuilder.packagesToMapMap[packages] = {'project': []};
     workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath(workspacePath), contextBuilder);
+      resourceProvider,
+      {
+        'project': [getFolder('/workspace')]
+      },
+      convertPath(workspacePath),
+    );
     packageUriResolver = MockUriResolver();
     resolver = PackageBuildPackageUriResolver(workspace, packageUriResolver);
   }
@@ -329,25 +308,15 @@
   }
 
   PackageBuildWorkspace _createPackageBuildWorkspace() {
-    final contextBuilder = MockContextBuilder();
-    final packagesForWorkspace = MockPackages();
-    contextBuilder.packagesMapMap[convertPath('/workspace')] =
-        packagesForWorkspace;
-    contextBuilder.packagesToMapMap[packagesForWorkspace] = {
-      'project': <Folder>[getFolder('/workspace')]
-    };
-
-    final packagesForWorkspace2 = MockPackages();
-    contextBuilder.packagesMapMap[convertPath('/workspace2')] =
-        packagesForWorkspace2;
-    contextBuilder.packagesToMapMap[packagesForWorkspace2] = {
-      'project2': <Folder>[]
-    };
-
     newFolder('/workspace/.dart_tool/build');
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), contextBuilder);
+      resourceProvider,
+      {
+        'project': [getFolder('/workspace')]
+      },
+      convertPath('/workspace'),
+    );
     packageUriResolver = MockUriResolver();
     PackageBuildPackageUriResolver(workspace, packageUriResolver);
     return workspace;
@@ -396,16 +365,25 @@
 
   void test_find_fail_notAbsolute() {
     expect(
-        () => PackageBuildWorkspace.find(resourceProvider,
-            convertPath('not_absolute'), MockContextBuilder()),
-        throwsA(const TypeMatcher<ArgumentError>()));
+      () {
+        return PackageBuildWorkspace.find(
+          resourceProvider,
+          {},
+          convertPath('not_absolute'),
+        );
+      },
+      throwsArgumentError,
+    );
   }
 
   void test_find_hasDartToolAndPubspec() {
     newFolder('/workspace/.dart_tool/build/generated/project/lib');
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace'),
+    );
     expect(workspace.root, convertPath('/workspace'));
     expect(workspace.projectPackageName, 'project');
   }
@@ -417,9 +395,10 @@
         'name: subproject'.codeUnits);
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider,
-        convertPath('/workspace/opened/up/a/child/dir'),
-        MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace/opened/up/a/child/dir'),
+    );
     expect(workspace.root, convertPath('/workspace/opened/up/a/child/dir'));
     expect(workspace.projectPackageName, 'subproject');
   }
@@ -432,9 +411,10 @@
         'not: yaml: here!!! 111'.codeUnits);
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider,
-        convertPath('/workspace/opened/up/a/child/dir'),
-        MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace/opened/up/a/child/dir'),
+    );
     expect(workspace.root, convertPath('/workspace'));
     expect(workspace.projectPackageName, 'project');
   }
@@ -445,9 +425,10 @@
     newFolder('/workspace/opened/up/a/child/dir/.dart_tool/build');
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider,
-        convertPath('/workspace/opened/up/a/child/dir'),
-        MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace/opened/up/a/child/dir'),
+    );
     expect(workspace.root, convertPath('/workspace'));
     expect(workspace.projectPackageName, 'project');
   }
@@ -459,9 +440,10 @@
         'name: subproject'.codeUnits);
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider,
-        convertPath('/workspace/opened/up/a/child/dir'),
-        MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace/opened/up/a/child/dir'),
+    );
     expect(workspace.root, convertPath('/workspace'));
     expect(workspace.projectPackageName, 'project');
   }
@@ -471,14 +453,20 @@
     newFolder('/workspace/.dart_tool');
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace'),
+    );
     expect(workspace, isNull);
   }
 
   void test_find_hasDartToolNoPubspec() {
     newFolder('/workspace/.dart_tool/build/generated/project/lib');
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace'),
+    );
     expect(workspace, isNull);
   }
 
@@ -487,7 +475,10 @@
     newFolder('/workspace/.dart_tool/pub');
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace'),
+    );
     expect(workspace, isNull);
   }
 
@@ -496,14 +487,20 @@
     newFileWithBytes(
         '/workspace/pubspec.yaml', 'not: yaml: here! 1111'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace'),
+    );
     expect(workspace, isNull);
   }
 
   void test_find_hasPubspecNoDartTool() {
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
     PackageBuildWorkspace workspace = PackageBuildWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+      resourceProvider,
+      {},
+      convertPath('/workspace'),
+    );
     expect(workspace, isNull);
   }
 
@@ -588,24 +585,17 @@
         workspace.findFile(convertPath('/workspace/web/file.dart')), webFile);
   }
 
-  void test_supports_flutter() {
-    newFolder('/workspace/.dart_tool/build');
-    newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
-    PackageBuildWorkspace workspace =
-        _createWorkspace('/workspace', ['project', 'flutter']);
-
-    expect(workspace.hasFlutterDependency, true);
-  }
-
   PackageBuildWorkspace _createWorkspace(
       String root, List<String> packageNames) {
-    final contextBuilder = MockContextBuilder();
-    final packages = MockPackages();
-    final packageMap = Map<String, List<Folder>>.fromIterable(packageNames,
-        value: ((_) => []));
-    contextBuilder.packagesMapMap[convertPath(root)] = packages;
-    contextBuilder.packagesToMapMap[packages] = packageMap;
     return PackageBuildWorkspace.find(
-        resourceProvider, convertPath(root), contextBuilder);
+      resourceProvider,
+      Map.fromIterables(
+        packageNames,
+        packageNames.map(
+          (name) => [getFolder('/packages/$name/lib')],
+        ),
+      ),
+      convertPath(root),
+    );
   }
 }
diff --git a/pkg/analyzer/test/src/workspace/pub_test.dart b/pkg/analyzer/test/src/workspace/pub_test.dart
index ef2dcd0..c5d5c3c 100644
--- a/pkg/analyzer/test/src/workspace/pub_test.dart
+++ b/pkg/analyzer/test/src/workspace/pub_test.dart
@@ -2,11 +2,8 @@
 // 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/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer/src/workspace/pub.dart';
-import 'package:package_config/packages.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -19,42 +16,14 @@
   });
 }
 
-class MockContextBuilder implements ContextBuilder {
-  Map<String, Packages> packagesMapMap = <String, Packages>{};
-  Map<Packages, Map<String, List<Folder>>> packagesToMapMap =
-      <Packages, Map<String, List<Folder>>>{};
-
-  @override
-  Map<String, List<Folder>> convertPackagesToMap(Packages packages) =>
-      packagesToMapMap[packages];
-
-  @override
-  Packages createPackageMap(String rootDirectoryPath) =>
-      packagesMapMap[rootDirectoryPath];
-
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
-
-class MockPackages implements Packages {
-  @override
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
-
 @reflectiveTest
 class PubWorkspacePackageTest with ResourceProviderMixin {
   PubWorkspace workspace;
 
   setUp() {
-    final contextBuilder = MockContextBuilder();
-    final packages = MockPackages();
-    final packageMap = <String, List<Folder>>{'project': []};
-    contextBuilder.packagesMapMap[convertPath('/workspace')] = packages;
-    contextBuilder.packagesToMapMap[packages] = packageMap;
-
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
-    workspace = PubWorkspace.find(
-        resourceProvider, convertPath('/workspace'), contextBuilder);
+    workspace =
+        PubWorkspace.find(resourceProvider, {}, convertPath('/workspace'));
     expect(workspace.isBazel, isFalse);
   }
 
@@ -111,29 +80,29 @@
 class PubWorkspaceTest with ResourceProviderMixin {
   void test_find_directory() {
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
-    PubWorkspace workspace = PubWorkspace.find(
-        resourceProvider, convertPath('/workspace'), MockContextBuilder());
+    PubWorkspace workspace =
+        PubWorkspace.find(resourceProvider, {}, convertPath('/workspace'));
     expect(workspace.isBazel, isFalse);
     expect(workspace.root, convertPath('/workspace'));
   }
 
   void test_find_fail_notAbsolute() {
     expect(
-        () => PubWorkspace.find(resourceProvider, convertPath('not_absolute'),
-            MockContextBuilder()),
+        () => PubWorkspace.find(
+            resourceProvider, {}, convertPath('not_absolute')),
         throwsA(TypeMatcher<ArgumentError>()));
   }
 
   void test_find_file() {
     newFileWithBytes('/workspace/pubspec.yaml', 'name: project'.codeUnits);
-    PubWorkspace workspace = PubWorkspace.find(resourceProvider,
-        convertPath('/workspace/lib/lib1.dart'), MockContextBuilder());
+    PubWorkspace workspace = PubWorkspace.find(
+        resourceProvider, {}, convertPath('/workspace/lib/lib1.dart'));
     expect(workspace.root, convertPath('/workspace'));
   }
 
   void test_find_missingPubspec() {
-    PubWorkspace workspace = PubWorkspace.find(resourceProvider,
-        convertPath('/workspace/lib/lib1.dart'), MockContextBuilder());
+    PubWorkspace workspace = PubWorkspace.find(
+        resourceProvider, {}, convertPath('/workspace/lib/lib1.dart'));
     expect(workspace, isNull);
   }
 }
diff --git a/pkg/analyzer_cli/analysis_options.yaml b/pkg/analyzer_cli/analysis_options.yaml
index 250a903..b4f4357 100644
--- a/pkg/analyzer_cli/analysis_options.yaml
+++ b/pkg/analyzer_cli/analysis_options.yaml
@@ -33,7 +33,7 @@
     - null_closures
     #- omit_local_variable_types # 273
     - prefer_adjacent_string_concatenation
-    #- prefer_collection_literals # 15
+    - prefer_collection_literals
     - prefer_conditional_assignment
     - prefer_contains
     - prefer_equal_for_default_values
diff --git a/pkg/analyzer_cli/lib/src/analyzer_impl.dart b/pkg/analyzer_cli/lib/src/analyzer_impl.dart
index dbd70cc..54d6d76 100644
--- a/pkg/analyzer_cli/lib/src/analyzer_impl.dart
+++ b/pkg/analyzer_cli/lib/src/analyzer_impl.dart
@@ -43,7 +43,7 @@
   final FileState libraryFile;
 
   /// All files references by the analyzed library.
-  final Set<String> files = Set<String>();
+  final Set<String> files = <String>{};
 
   /// All [AnalysisErrorInfo]s in the analyzed library.
   final List<ErrorsResult> errorsResults = [];
@@ -136,8 +136,8 @@
 
   /// Fills [files].
   void prepareSources(LibraryElement library) {
-    var units = Set<CompilationUnitElement>();
-    var libraries = Set<LibraryElement>();
+    var units = <CompilationUnitElement>{};
+    var libraries = <LibraryElement>{};
     addLibrarySources(library, libraries, units);
   }
 
diff --git a/pkg/analyzer_cli/lib/src/batch_mode.dart b/pkg/analyzer_cli/lib/src/batch_mode.dart
index 6f958ef..e0c8731 100644
--- a/pkg/analyzer_cli/lib/src/batch_mode.dart
+++ b/pkg/analyzer_cli/lib/src/batch_mode.dart
@@ -43,7 +43,7 @@
       }
       // Prepare arguments.
       var lineArgs = line.split(RegExp('\\s+'));
-      var args = List<String>();
+      var args = <String>[];
       args.addAll(sharedArgs);
       args.addAll(lineArgs);
       args.remove('-b');
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index 65c6d32..1ef5c57 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -564,7 +564,7 @@
  * Tracks paths to dependencies, really just a thin api around a Set<String>.
  */
 class DependencyTracker {
-  final _dependencies = Set<String>();
+  final _dependencies = <String>{};
 
   /// The path to the file to create once tracking is done.
   final String outputPath;
diff --git a/pkg/analyzer_cli/lib/src/driver.dart b/pkg/analyzer_cli/lib/src/driver.dart
index 10b5e01..1c968c8 100644
--- a/pkg/analyzer_cli/lib/src/driver.dart
+++ b/pkg/analyzer_cli/lib/src/driver.dart
@@ -214,9 +214,9 @@
     }
 
     // These are used to do part file analysis across sources.
-    Set<String> dartFiles = Set<String>();
-    Set<FileState> libraryFiles = Set<FileState>();
-    Set<FileState> danglingParts = Set<FileState>();
+    Set<String> dartFiles = <String>{};
+    Set<FileState> libraryFiles = <FileState>{};
+    Set<FileState> danglingParts = <FileState>{};
 
     // Note: This references analysisDriver via closure, so it will change over
     // time during the following analysis.
@@ -263,7 +263,7 @@
       // Add all the files to be analyzed en masse to the context. Skip any
       // files that were added earlier (whether explicitly or implicitly) to
       // avoid causing those files to be unnecessarily re-read.
-      Set<String> filesToAnalyze = Set<String>();
+      Set<String> filesToAnalyze = <String>{};
 
       // Collect files for analysis.
       // Note that these files will all be analyzed in the same context.
@@ -640,7 +640,7 @@
       return null;
     }
 
-    Map<String, List<Folder>> folderMap = Map<String, List<Folder>>();
+    Map<String, List<Folder>> folderMap = <String, List<Folder>>{};
     var pathContext = resourceProvider.pathContext;
     packages.asMap().forEach((String packagePath, Uri uri) {
       String path = fileUriToNormalizedPath(pathContext, uri);
@@ -775,7 +775,7 @@
           'Package root directory ($packageRootPath) does not exist.');
     }
     var packages = packageRoot.listSync(followLinks: false);
-    var result = Map<String, List<Folder>>();
+    var result = <String, List<Folder>>{};
     for (var package in packages) {
       var packageName = path.basename(package.path);
       var realPath = package.resolveSymbolicLinksSync();
diff --git a/pkg/analyzer_cli/lib/src/error_formatter.dart b/pkg/analyzer_cli/lib/src/error_formatter.dart
index e20098e..daa9baf 100644
--- a/pkg/analyzer_cli/lib/src/error_formatter.dart
+++ b/pkg/analyzer_cli/lib/src/error_formatter.dart
@@ -196,7 +196,7 @@
   void formatErrors(List<ErrorsResult> results) {
     stats.unfilteredCount += results.length;
 
-    List<AnalysisError> errors = List<AnalysisError>();
+    List<AnalysisError> errors = <AnalysisError>[];
     Map<AnalysisError, ErrorsResult> errorToLine = {};
     for (ErrorsResult result in results) {
       for (AnalysisError error in result.errors) {
@@ -222,7 +222,7 @@
   AnsiLogger ansi;
 
   // This is a Set in order to de-dup CLI errors.
-  final Set<CLIError> batchedErrors = Set();
+  final Set<CLIError> batchedErrors = {};
 
   HumanErrorFormatter(
       StringSink out, CommandLineOptions options, AnalysisStats stats,
diff --git a/pkg/analyzer_cli/test/driver_test.dart b/pkg/analyzer_cli/test/driver_test.dart
index d22f546..2f44d95 100644
--- a/pkg/analyzer_cli/test/driver_test.dart
+++ b/pkg/analyzer_cli/test/driver_test.dart
@@ -69,7 +69,7 @@
   /// Try to find a appropriate directory to pass to "--dart-sdk" that will
   /// allow summaries to be found.
   String _findSdkDirForSummaries() {
-    Set<String> triedDirectories = Set<String>();
+    Set<String> triedDirectories = <String>{};
     bool isSuitable(String sdkDir) {
       triedDirectories.add(sdkDir);
       return File(path.join(sdkDir, 'lib', '_internal', 'strong.sum'))
diff --git a/pkg/analyzer_cli/tool/perf.dart b/pkg/analyzer_cli/tool/perf.dart
index 9437c2d..5e6e272 100644
--- a/pkg/analyzer_cli/tool/perf.dart
+++ b/pkg/analyzer_cli/tool/perf.dart
@@ -146,7 +146,7 @@
 /// Load and scans all files we need to process: files reachable from the
 /// entrypoint and all core libraries automatically included by the VM.
 Set<Source> scanReachableFiles(Uri entryUri) {
-  var files = Set<Source>();
+  var files = <Source>{};
   var loadTimer = Stopwatch()..start();
   collectSources(sources.forUri2(entryUri), files);
 
@@ -182,8 +182,8 @@
 /// sources.
 Future setup(Uri entryUri) async {
   var provider = PhysicalResourceProvider.INSTANCE;
-  var packageMap = ContextBuilder(provider, null, null)
-      .convertPackagesToMap(await findPackages(entryUri));
+  var packageMap = ContextBuilder.convertPackagesToMap(
+      provider, await findPackages(entryUri));
   sources = SourceFactory([
     ResourceUriResolver(provider),
     PackageMapUriResolver(provider, packageMap),
diff --git a/pkg/analyzer_plugin/analysis_options.yaml b/pkg/analyzer_plugin/analysis_options.yaml
index 53f1319..2000d43 100644
--- a/pkg/analyzer_plugin/analysis_options.yaml
+++ b/pkg/analyzer_plugin/analysis_options.yaml
@@ -12,7 +12,7 @@
     - always_require_non_null_named_parameters
     - annotate_overrides
     - avoid_empty_else
-    #- avoid_init_to_null # 19
+    - avoid_init_to_null
     - avoid_null_checks_in_equality_operators
     - avoid_relative_lib_imports
     - avoid_return_types_on_setters
@@ -28,11 +28,11 @@
     - null_closures
     #- omit_local_variable_types # 1759
     - prefer_adjacent_string_concatenation
-    #- prefer_collection_literals # 9
+    - prefer_collection_literals
     - prefer_conditional_assignment
     - prefer_contains
     - prefer_equal_for_default_values
-    #- prefer_final_fields # 5
+    - prefer_final_fields
     - prefer_for_elements_to_map_fromIterable
     - prefer_generic_function_type_aliases
     #- prefer_if_null_operators # 12
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart
index 556a34c..00529de 100644
--- a/pkg/analyzer_plugin/lib/plugin/plugin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart
@@ -635,7 +635,7 @@
   Future<Response> _getResponse(Request request, int requestTime) async {
     // TODO(brianwilkerson) Determine whether this await is necessary.
     await null;
-    ResponseResult result = null;
+    ResponseResult result;
     switch (request.method) {
       case ANALYSIS_REQUEST_GET_NAVIGATION:
         var params = AnalysisGetNavigationParams.fromRequest(request);
diff --git a/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart b/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
index f727885..afeb695 100644
--- a/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
+++ b/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
@@ -89,7 +89,7 @@
   /**
    * The port used to send notifications and responses to the server.
    */
-  SendPort _sendPort;
+  final SendPort _sendPort;
 
   /**
    * The port used to receive requests from the server.
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index a185399..d4571c5 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -18,7 +18,7 @@
    * The end-of-line marker used in the file being edited, or `null` if the
    * default marker should be used.
    */
-  String eol = null;
+  String eol;
 
   /**
    * A table mapping group ids to the associated linked edit groups.
@@ -174,7 +174,7 @@
    * The end-of-line marker used in the file being edited, or `null` if the
    * default marker should be used.
    */
-  String _eol = null;
+  String _eol;
 
   /**
    * The buffer in which the content of the edit is being composed.
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 87e0a2b..26e57b4 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -94,7 +94,7 @@
  * An [EditBuilder] used to build edits in Dart files.
  */
 class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
-  List<String> _KNOWN_METHOD_NAME_PREFIXES = ['get', 'is', 'to'];
+  final List<String> _KNOWN_METHOD_NAME_PREFIXES = ['get', 'is', 'to'];
 
   /**
    * Whether [_enclosingClass] and [_enclosingExecutable] have been initialized.
@@ -521,16 +521,8 @@
           writeln();
           write(prefix2);
           selectAll(() {
-            write('super.');
-            write(memberName);
-            write('(');
-            for (int i = 0; i < parameters.length; i++) {
-              if (i > 0) {
-                write(', ');
-              }
-              write(parameters[i].name);
-            }
-            write(');');
+            write('super');
+            _writeSuperMemberInvocation(element, memberName, parameters);
           });
           writeln();
         } else {
@@ -542,16 +534,8 @@
         write(prefix2);
         if (invokeSuper) {
           selectAll(() {
-            write('return super.');
-            write(memberName);
-            write('(');
-            for (int i = 0; i < parameters.length; i++) {
-              if (i > 0) {
-                write(', ');
-              }
-              write(parameters[i].name);
-            }
-            write(');');
+            write('return super');
+            _writeSuperMemberInvocation(element, memberName, parameters);
           });
         } else {
           selectAll(() {
@@ -654,7 +638,7 @@
   void writeParametersMatchingArguments(ArgumentList argumentList) {
     // TODO(brianwilkerson) Handle the case when there are required parameters
     // after named parameters.
-    Set<String> usedNames = Set<String>();
+    Set<String> usedNames = <String>{};
     List<Expression> arguments = argumentList.arguments;
     bool hasNamedParameters = false;
     for (int i = 0; i < arguments.length; i++) {
@@ -728,7 +712,7 @@
         addLinkedEdit(groupName, (LinkedEditBuilder builder) {
           wroteType = _writeType(type, methodBeingCopied: methodBeingCopied);
           if (wroteType && addSupertypeProposals) {
-            _addSuperTypeProposals(builder, type, Set<DartType>());
+            _addSuperTypeProposals(builder, type, <DartType>{});
           }
         });
       } else {
@@ -869,7 +853,7 @@
   }
 
   String _getBaseNameFromUnwrappedExpression(Expression expression) {
-    String name = null;
+    String name;
     // analyze expressions
     if (expression is SimpleIdentifier) {
       return expression.name;
@@ -963,7 +947,7 @@
    */
   List<String> _getVariableNameSuggestionsForExpression(DartType expectedType,
       Expression assignedExpression, Set<String> excluded) {
-    Set<String> res = Set();
+    Set<String> res = {};
     // use expression
     if (assignedExpression != null) {
       String nameFromExpression =
@@ -1076,6 +1060,21 @@
     }
   }
 
+  void _writeSuperMemberInvocation(ExecutableElement element, String memberName,
+      List<ParameterElement> parameters) {
+    final isOperator = element.isOperator;
+    write(isOperator ? ' ' : '.');
+    write(memberName);
+    write(isOperator ? ' ' : '(');
+    for (int i = 0; i < parameters.length; i++) {
+      if (i > 0) {
+        write(', ');
+      }
+      write(parameters[i].name);
+    }
+    write(isOperator ? ';' : ');');
+  }
+
   /**
    * Write the code to reference [type] in this compilation unit.
    *
@@ -1306,7 +1305,7 @@
     return ImportLibraryElementResultImpl(null);
   }
 
-  String importLibraryWithRelativeUri(String uriText, [String prefix = null]) {
+  String importLibraryWithRelativeUri(String uriText, [String prefix]) {
     return _importLibraryWithRelativeUri(uriText, prefix).uriText;
   }
 
@@ -1566,7 +1565,7 @@
    * [uriText].
    */
   _LibraryToImport _importLibraryWithRelativeUri(String uriText,
-      [String prefix = null]) {
+      [String prefix]) {
     var import = librariesToRelativelyImport[uriText];
     if (import == null) {
       import = _LibraryToImport(uriText, prefix);
@@ -1624,7 +1623,7 @@
 
   @override
   void addSuperTypesAsSuggestions(DartType type) {
-    _addSuperTypesAsSuggestions(type, Set<DartType>());
+    _addSuperTypesAsSuggestions(type, <DartType>{});
   }
 
   /**
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/element_suggestion_builder.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/element_suggestion_builder.dart
index 87daf76..5878519 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/element_suggestion_builder.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/element_suggestion_builder.dart
@@ -23,7 +23,7 @@
   /**
    * A set of existing completions used to prevent duplicate suggestions.
    */
-  final Set<String> _completions = Set<String>();
+  final Set<String> _completions = <String>{};
 
   /**
    * A map of element names to suggestions for synthetic getters and setters.
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index 6baa843..86403e9 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -132,7 +132,7 @@
    * The type that is required by the context in which the completion was
    * activated, or `null` if there is no such type, or it cannot be determined.
    */
-  DartType _requiredType = null;
+  DartType _requiredType;
 
   /**
    * Determine the suggestions that should be made based upon the given
diff --git a/pkg/analyzer_plugin/lib/src/utilities/visitors/local_declaration_visitor.dart b/pkg/analyzer_plugin/lib/src/utilities/visitors/local_declaration_visitor.dart
index e92ac3b..5bdf50e 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/visitors/local_declaration_visitor.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/visitors/local_declaration_visitor.dart
@@ -249,7 +249,7 @@
         } else if (param is NormalFormalParameter) {
           normalParam = param;
         }
-        TypeAnnotation type = null;
+        TypeAnnotation type;
         if (normalParam is FieldFormalParameter) {
           type = normalParam.type;
         } else if (normalParam is FunctionTypedFormalParameter) {
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart b/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
index 088ab48..57abe69 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
@@ -353,12 +353,12 @@
    * Note: the enumerated values stored in this map are intended to be bitwise
    * compared.
    */
-  Map<String, int> _completionTypesGenerated = HashMap<String, int>();
+  final Map<String, int> _completionTypesGenerated = HashMap<String, int>();
 
   /**
    * Map from completion identifier to completion suggestion
    */
-  Map<String, CompletionSuggestion> _suggestionMap =
+  final Map<String, CompletionSuggestion> _suggestionMap =
       <String, CompletionSuggestion>{};
 
   /**
diff --git a/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart b/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart
index 6e07db2..f3a4206 100644
--- a/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart
+++ b/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart
@@ -152,7 +152,7 @@
    * [appliedTogetherMessage].
    */
   const FixKind(this.name, this.priority, this.message,
-      {this.appliedTogetherMessage = null});
+      {this.appliedTogetherMessage});
 
   @override
   int get hashCode => name.hashCode;
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 59ee846..72ea138 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -5,7 +5,7 @@
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_plugin
 
 environment:
-  sdk: '>=2.0.0 <3.0.0'
+  sdk: '>=2.3.0 <3.0.0'
 
 dependencies:
   analyzer: '>=0.35.3 <0.39.0'
diff --git a/pkg/analyzer_plugin/test/integration/support/integration_tests.dart b/pkg/analyzer_plugin/test/integration/support/integration_tests.dart
index 4cf9c48..a77238c 100644
--- a/pkg/analyzer_plugin/test/integration/support/integration_tests.dart
+++ b/pkg/analyzer_plugin/test/integration/support/integration_tests.dart
@@ -421,7 +421,7 @@
   /**
    * Server process object, or null if server hasn't been started yet.
    */
-  Process _process = null;
+  Process _process;
 
   /**
    * Commands that have been sent to the server but not yet acknowledged, and
@@ -457,7 +457,7 @@
   /**
    * Stopwatch that we use to generate timing information for debug output.
    */
-  Stopwatch _time = Stopwatch();
+  final Stopwatch _time = Stopwatch();
 
   /**
    * The [currentElapseTime] at which the last communication was received from the server
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
index 1b7da81..880ac38 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
@@ -916,7 +916,7 @@
     await builder.addFileEdit(path, (FileEditBuilder builder) {
       builder.addInsertion(2, (EditBuilder builder) {
         (builder as DartEditBuilder)
-            .writeParameterMatchingArgument(argument, 0, Set<String>());
+            .writeParameterMatchingArgument(argument, 0, <String>{});
       });
     });
     SourceEdit edit = getEdit(builder);
diff --git a/pkg/analyzer_plugin/test/src/utilities/completion/completion_target_test.dart b/pkg/analyzer_plugin/test/src/utilities/completion/completion_target_test.dart
index d9f7b95..cf6fd01 100644
--- a/pkg/analyzer_plugin/test/src/utilities/completion/completion_target_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/completion/completion_target_test.dart
@@ -911,7 +911,7 @@
   void assertTarget(
     String entityText,
     String nodeText, {
-    int argIndex = null,
+    int argIndex,
     String droppedToken,
     bool isFunctionalArgument = false,
     String expectedExecutable,
diff --git a/pkg/analyzer_plugin/test/support/abstract_context.dart b/pkg/analyzer_plugin/test/support/abstract_context.dart
index 0bee02c..eee4c1f 100644
--- a/pkg/analyzer_plugin/test/support/abstract_context.dart
+++ b/pkg/analyzer_plugin/test/support/abstract_context.dart
@@ -22,7 +22,7 @@
  * Finds an [Element] with the given [name].
  */
 Element findChildElement(Element root, String name, [ElementKind kind]) {
-  Element result = null;
+  Element result;
   root.accept(_ElementVisitorFunctionWrapper((Element element) {
     if (element.name != name) {
       return;
diff --git a/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart b/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart
index 3ff5e0b..7f31ef9 100644
--- a/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart
+++ b/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart
@@ -68,7 +68,7 @@
     expect(suggestion.hasNamedParameters, isNotNull);
   }
 
-  void assertNoSuggestions({CompletionSuggestionKind kind = null}) {
+  void assertNoSuggestions({CompletionSuggestionKind kind}) {
     if (kind == null) {
       if (suggestions.isNotEmpty) {
         failedCompletion('Expected no suggestions', suggestions);
@@ -95,7 +95,7 @@
   CompletionSuggestion assertSuggest(String completion,
       {CompletionSuggestionKind csKind = CompletionSuggestionKind.INVOCATION,
       int relevance = DART_RELEVANCE_DEFAULT,
-      ElementKind elemKind = null,
+      ElementKind elemKind,
       bool isDeprecated = false,
       bool isPotential = false,
       String elemFile,
@@ -456,9 +456,9 @@
   }
 
   CompletionSuggestion getSuggest(
-      {String completion = null,
-      CompletionSuggestionKind csKind = null,
-      ElementKind elemKind = null}) {
+      {String completion,
+      CompletionSuggestionKind csKind,
+      ElementKind elemKind}) {
     CompletionSuggestion cs;
     if (suggestions != null) {
       suggestions.forEach((CompletionSuggestion s) {
diff --git a/pkg/analyzer_plugin/tool/spec/from_html.dart b/pkg/analyzer_plugin/tool/spec/from_html.dart
index 3a51897..1299a78 100644
--- a/pkg/analyzer_plugin/tool/spec/from_html.dart
+++ b/pkg/analyzer_plugin/tool/spec/from_html.dart
@@ -86,8 +86,8 @@
     Api api;
     List<String> versions = <String>[];
     List<Domain> domains = <Domain>[];
-    Types types = null;
-    Refactorings refactorings = null;
+    Types types;
+    Refactorings refactorings;
     recurse(html, 'api', {
       'domain': (dom.Element element) {
         domains.add(domainFromHtml(element));
@@ -120,7 +120,7 @@
   void checkAttributes(
       dom.Element element, List<String> requiredAttributes, String context,
       {List<String> optionalAttributes = const []}) {
-    Set<String> attributesFound = Set<String>();
+    Set<String> attributesFound = <String>{};
     element.attributes.forEach((name, value) {
       if (!requiredAttributes.contains(name) &&
           !optionalAttributes.contains(name)) {
diff --git a/pkg/analyzer_plugin/tool/spec/to_html.dart b/pkg/analyzer_plugin/tool/spec/to_html.dart
index a192908..d032e31 100644
--- a/pkg/analyzer_plugin/tool/spec/to_html.dart
+++ b/pkg/analyzer_plugin/tool/spec/to_html.dart
@@ -220,7 +220,7 @@
   /**
    * Set of types defined in the API.
    */
-  Set<String> definedTypes = Set<String>();
+  Set<String> definedTypes = <String>{};
 
   /**
    * Mappings from HTML elements to API nodes.
@@ -418,7 +418,7 @@
    * boldface.
    */
   void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) {
-    Set<String> fieldsToBold = Set<String>();
+    Set<String> fieldsToBold = <String>{};
     if (typeForBolding != null) {
       for (TypeObjectField field in typeForBolding.fields) {
         fieldsToBold.add(field.name);
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index e97afa01..6412b51 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -339,9 +339,8 @@
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = new Selector(SelectorKind.CALL, constructor.memberName,
         _elementMap.getCallStructure(node.arguments));
-    AbstractValue mask = _memberData.typeOfSend(node);
     handleConstructorInvoke(
-        node, node.arguments, selector, mask, constructor, arguments);
+        node, node.arguments, selector, constructor, arguments);
 
     _inferrer.analyze(constructor);
     if (_inferrer.checkIfExposesThis(constructor)) {
@@ -356,9 +355,8 @@
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = new Selector(SelectorKind.CALL, constructor.memberName,
         _elementMap.getCallStructure(node.arguments));
-    AbstractValue mask = _memberData.typeOfSend(node);
     handleConstructorInvoke(
-        node, node.arguments, selector, mask, constructor, arguments);
+        node, node.arguments, selector, constructor, arguments);
 
     _inferrer.analyze(constructor);
     if (_inferrer.checkIfExposesThis(constructor)) {
@@ -851,10 +849,32 @@
     return new ArgumentsTypes(positional, named);
   }
 
+  AbstractValue _typeOfReceiver(ir.TreeNode node, ir.TreeNode receiver) {
+    KernelGlobalTypeInferenceElementData data = _memberData;
+    AbstractValue mask = data.typeOfReceiver(node);
+    if (mask != null) return mask;
+    // TODO(sigmund): ensure that this is only called once per node.
+    DartType staticType = _getStaticType(receiver);
+    // TODO(sigmund): this needs to be adjusted when we enable non-nullable
+    // types to handle legacy and nullable wrappers.
+    if (staticType is InterfaceType) {
+      ClassEntity cls = staticType.element;
+      if (receiver is ir.ThisExpression && !_closedWorld.isUsedAsMixin(cls)) {
+        mask = _closedWorld.abstractValueDomain.createNonNullSubclass(cls);
+      } else {
+        mask = _closedWorld.abstractValueDomain.createNullableSubtype(cls);
+      }
+      data.setReceiverTypeMask(node, mask);
+      return mask;
+    }
+    // TODO(sigmund): consider also extracting the bound of type parameters.
+    return null;
+  }
+
   @override
   TypeInformation visitMethodInvocation(ir.MethodInvocation node) {
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
+    AbstractValue mask = _typeOfReceiver(node, node.receiver);
 
     ir.TreeNode receiver = node.receiver;
     if (receiver is ir.VariableGet &&
@@ -868,7 +888,7 @@
       }
 
       TypeInformation type =
-          handleStaticInvoke(node, selector, mask, info.callMethod, arguments);
+          handleStaticInvoke(node, selector, info.callMethod, arguments);
       FunctionType functionType =
           _elementMap.elementEnvironment.getFunctionType(info.callMethod);
       if (functionType.returnType.containsFreeTypeVariables) {
@@ -1029,8 +1049,8 @@
           _closedWorld.commonElements.streamIteratorConstructor;
 
       /// Synthesize a call to the [StreamIterator] constructor.
-      iteratorType = handleStaticInvoke(node, null, null, constructor,
-          new ArgumentsTypes([expressionType], null));
+      iteratorType = handleStaticInvoke(
+          node, null, constructor, new ArgumentsTypes([expressionType], null));
     } else {
       TypeInformation expressionType = visit(node.iterable);
       Selector iteratorSelector = Selectors.iterator;
@@ -1119,9 +1139,8 @@
     ConstructorEntity constructor = _elementMap.getConstructor(node.target);
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
     return handleConstructorInvoke(
-        node, node.arguments, selector, mask, constructor, arguments);
+        node, node.arguments, selector, constructor, arguments);
   }
 
   /// Try to find the length given to a fixed array constructor call.
@@ -1177,11 +1196,10 @@
       ir.Node node,
       ir.Arguments arguments,
       Selector selector,
-      AbstractValue mask,
       ConstructorEntity constructor,
       ArgumentsTypes argumentsTypes) {
     TypeInformation returnType =
-        handleStaticInvoke(node, selector, mask, constructor, argumentsTypes);
+        handleStaticInvoke(node, selector, constructor, argumentsTypes);
     if (_elementMap.commonElements.isUnnamedListConstructor(constructor)) {
       // We have `new List(...)`.
       if (arguments.positional.isEmpty && arguments.named.isEmpty) {
@@ -1228,17 +1246,16 @@
   }
 
   TypeInformation handleStaticInvoke(ir.Node node, Selector selector,
-      AbstractValue mask, MemberEntity element, ArgumentsTypes arguments) {
-    return _inferrer.registerCalledMember(node, selector, mask, _analyzedMember,
+      MemberEntity element, ArgumentsTypes arguments) {
+    return _inferrer.registerCalledMember(node, selector, _analyzedMember,
         element, arguments, _sideEffectsBuilder, inLoop);
   }
 
   TypeInformation handleClosureCall(ir.Node node, Selector selector,
-      AbstractValue mask, MemberEntity member, ArgumentsTypes arguments) {
+      MemberEntity member, ArgumentsTypes arguments) {
     return _inferrer.registerCalledClosure(
         node,
         selector,
-        mask,
         _inferrer.typeOfMember(member),
         _analyzedMember,
         arguments,
@@ -1246,14 +1263,10 @@
         inLoop: inLoop);
   }
 
-  TypeInformation handleForeignInvoke(
-      ir.StaticInvocation node,
-      FunctionEntity function,
-      ArgumentsTypes arguments,
-      Selector selector,
-      AbstractValue mask) {
+  TypeInformation handleForeignInvoke(ir.StaticInvocation node,
+      FunctionEntity function, ArgumentsTypes arguments, Selector selector) {
     String name = function.name;
-    handleStaticInvoke(node, selector, mask, function, arguments);
+    handleStaticInvoke(node, selector, function, arguments);
     if (name == Identifiers.JS) {
       NativeBehavior nativeBehavior =
           _elementMap.getNativeBehaviorForJsCall(node);
@@ -1282,16 +1295,15 @@
     MemberEntity member = _elementMap.getMember(node.target);
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
     if (_closedWorld.commonElements.isForeign(member)) {
-      return handleForeignInvoke(node, member, arguments, selector, mask);
+      return handleForeignInvoke(node, member, arguments, selector);
     } else if (member.isConstructor) {
       return handleConstructorInvoke(
-          node, node.arguments, selector, mask, member, arguments);
+          node, node.arguments, selector, member, arguments);
     } else {
       assert(member.isFunction, "Unexpected static invocation target: $member");
       TypeInformation type =
-          handleStaticInvoke(node, selector, mask, member, arguments);
+          handleStaticInvoke(node, selector, member, arguments);
       FunctionType functionType =
           _elementMap.elementEnvironment.getFunctionType(member);
       if (functionType.returnType.containsFreeTypeVariables) {
@@ -1311,16 +1323,14 @@
 
   @override
   TypeInformation visitStaticGet(ir.StaticGet node) {
-    AbstractValue mask = _memberData.typeOfSend(node);
-    assert(mask == null);
-    return createStaticGetTypeInformation(node, node.target, mask: mask);
+    return createStaticGetTypeInformation(node, node.target);
   }
 
-  TypeInformation createStaticGetTypeInformation(ir.Node node, ir.Member target,
-      {AbstractValue mask}) {
+  TypeInformation createStaticGetTypeInformation(
+      ir.Node node, ir.Member target) {
     MemberEntity member = _elementMap.getMember(target);
     return handleStaticInvoke(
-        node, new Selector.getter(member.memberName), mask, member, null);
+        node, new Selector.getter(member.memberName), member, null);
   }
 
   @override
@@ -1330,17 +1340,16 @@
       _state.markThisAsExposed();
     }
     MemberEntity member = _elementMap.getMember(node.target);
-    AbstractValue mask = _memberData.typeOfSend(node);
-    handleStaticInvoke(node, new Selector.setter(member.memberName), mask,
-        member, new ArgumentsTypes([rhsType], null));
+    handleStaticInvoke(node, new Selector.setter(member.memberName), member,
+        new ArgumentsTypes([rhsType], null));
     return rhsType;
   }
 
-  TypeInformation handlePropertyGet(
-      ir.TreeNode node, TypeInformation receiverType, ir.Member interfaceTarget,
+  TypeInformation handlePropertyGet(ir.TreeNode node, ir.TreeNode receiver,
+      TypeInformation receiverType, ir.Member interfaceTarget,
       {bool isThis}) {
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
+    AbstractValue mask = _typeOfReceiver(node, receiver);
     if (isThis) {
       _checkIfExposesThis(
           selector, _types.newTypedSelector(receiverType, mask));
@@ -1363,21 +1372,23 @@
   @override
   TypeInformation visitPropertyGet(ir.PropertyGet node) {
     TypeInformation receiverType = visit(node.receiver);
-    return handlePropertyGet(node, receiverType, node.interfaceTarget,
+    return handlePropertyGet(
+        node, node.receiver, receiverType, node.interfaceTarget,
         isThis: node.receiver is ir.ThisExpression);
   }
 
   @override
   TypeInformation visitDirectPropertyGet(ir.DirectPropertyGet node) {
     TypeInformation receiverType = thisType;
-    return handlePropertyGet(node, receiverType, node.target, isThis: true);
+    return handlePropertyGet(node, node.receiver, receiverType, node.target,
+        isThis: true);
   }
 
   @override
   TypeInformation visitPropertySet(ir.PropertySet node) {
     TypeInformation receiverType = visit(node.receiver);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
+    AbstractValue mask = _typeOfReceiver(node, node.receiver);
 
     TypeInformation rhsType = visit(node.value);
     if (node.value is ir.ThisExpression) {
@@ -1730,13 +1741,13 @@
     return _types.nonNullEmpty();
   }
 
-  TypeInformation handleSuperNoSuchMethod(ir.Node node, Selector selector,
-      AbstractValue mask, ArgumentsTypes arguments) {
+  TypeInformation handleSuperNoSuchMethod(
+      ir.Node node, Selector selector, ArgumentsTypes arguments) {
     // Ensure we create a node, to make explicit the call to the
     // `noSuchMethod` handler.
     FunctionEntity noSuchMethod =
         _elementMap.getSuperNoSuchMethod(_analyzedMember.enclosingClass);
-    return handleStaticInvoke(node, selector, mask, noSuchMethod, arguments);
+    return handleStaticInvoke(node, selector, noSuchMethod, arguments);
   }
 
   @override
@@ -1748,10 +1759,8 @@
     MemberEntity member =
         _elementMap.getSuperMember(_analyzedMember, node.name);
     assert(member != null, "No member found for super property get: $node");
-    AbstractValue mask = _memberData.typeOfSend(node);
     Selector selector = new Selector.getter(_elementMap.getName(node.name));
-    TypeInformation type =
-        handleStaticInvoke(node, selector, mask, member, null);
+    TypeInformation type = handleStaticInvoke(node, selector, member, null);
     if (member.isGetter) {
       FunctionType functionType =
           _elementMap.elementEnvironment.getFunctionType(member);
@@ -1781,10 +1790,9 @@
     MemberEntity member =
         _elementMap.getSuperMember(_analyzedMember, node.name, setter: true);
     assert(member != null, "No member found for super property set: $node");
-    AbstractValue mask = _memberData.typeOfSend(node);
     Selector selector = new Selector.setter(_elementMap.getName(node.name));
     ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
-    handleStaticInvoke(node, selector, mask, member, arguments);
+    handleStaticInvoke(node, selector, member, arguments);
     return rhsType;
   }
 
@@ -1798,17 +1806,16 @@
         _elementMap.getSuperMember(_analyzedMember, node.name);
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
     if (member == null) {
       // TODO(johnniwinther): This shouldn't be necessary.
-      return handleSuperNoSuchMethod(node, selector, mask, arguments);
+      return handleSuperNoSuchMethod(node, selector, arguments);
     } else {
       assert(member.isFunction, "Unexpected super invocation target: $member");
       if (isIncompatibleInvoke(member, arguments)) {
-        return handleSuperNoSuchMethod(node, selector, mask, arguments);
+        return handleSuperNoSuchMethod(node, selector, arguments);
       } else {
         TypeInformation type =
-            handleStaticInvoke(node, selector, mask, member, arguments);
+            handleStaticInvoke(node, selector, member, arguments);
         FunctionType functionType =
             _elementMap.elementEnvironment.getFunctionType(member);
         if (functionType.returnType.containsFreeTypeVariables) {
diff --git a/pkg/compiler/lib/src/inferrer/closure_tracer.dart b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
index 3dd6f3f..0a01c9b 100644
--- a/pkg/compiler/lib/src/inferrer/closure_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
@@ -7,7 +7,6 @@
 import '../common/names.dart' show Identifiers, Names;
 import '../elements/entities.dart';
 import '../universe/selector.dart' show Selector;
-import 'abstract_value_domain.dart';
 import 'debug.dart' as debug;
 import 'inferrer_engine.dart';
 import 'node_tracer.dart';
@@ -56,14 +55,13 @@
 
   void _analyzeCall(CallSiteTypeInformation info) {
     Selector selector = info.selector;
-    AbstractValue mask = info.mask;
     tracedElements.forEach((FunctionEntity functionElement) {
       if (!selector.callStructure
           .signatureApplies(functionElement.parameterStructure)) {
         return;
       }
       inferrer.updateParameterInputs(
-          info, functionElement, info.arguments, selector, mask,
+          info, functionElement, info.arguments, selector,
           remove: false, addToQueue: false);
     });
   }
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 729e9bc..a510d20 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -208,7 +208,7 @@
     assert(validCallType(callType, node, selector));
     switch (callType) {
       case CallType.access:
-        data.setTypeMask(node, mask);
+        data.setReceiverTypeMask(node, mask);
         break;
       case CallType.indirectAccess:
         // indirect access is not diretly recorded in the result data.
@@ -675,7 +675,8 @@
       if (info is StaticCallSiteTypeInformation) {
         MemberEntity member = info.calledElement;
         inferredDataBuilder.addFunctionCalledInLoop(member);
-      } else if (info.mask != null &&
+      } else if (info is DynamicCallSiteTypeInformation &&
+          info.mask != null &&
           abstractValueDomain.containsAll(info.mask).isDefinitelyFalse) {
         // For instance methods, we only register a selector called in a
         // loop if it is a typed selector, to avoid marking too many
@@ -728,7 +729,7 @@
   /// inputs must be added or removed. If [init] is false, parameters are
   /// added to the work queue.
   void updateParameterInputs(TypeInformation caller, MemberEntity callee,
-      ArgumentsTypes arguments, Selector selector, AbstractValue mask,
+      ArgumentsTypes arguments, Selector selector,
       {bool remove, bool addToQueue: true}) {
     if (callee.name == Identifiers.noSuchMethod_) return;
     if (callee.isField) {
@@ -898,7 +899,6 @@
   TypeInformation registerCalledMember(
       Object node,
       Selector selector,
-      AbstractValue mask,
       MemberEntity caller,
       MemberEntity callee,
       ArgumentsTypes arguments,
@@ -911,7 +911,6 @@
         caller,
         callee,
         selector,
-        mask,
         arguments,
         inLoop);
     // If this class has a 'call' method then we have essentially created a
@@ -953,52 +952,31 @@
       {bool inLoop,
       bool isConditional}) {
     if (selector.isClosureCall) {
-      return registerCalledClosure(node, selector, mask, receiverType, caller,
-          arguments, sideEffectsBuilder,
+      return registerCalledClosure(
+          node, selector, receiverType, caller, arguments, sideEffectsBuilder,
           inLoop: inLoop);
     }
 
     if (closedWorld.includesClosureCall(selector, mask)) {
       sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
     }
+
     closedWorld.locateMembers(selector, mask).forEach((callee) {
       _updateSideEffects(sideEffectsBuilder, selector, callee);
     });
 
-    CallSiteTypeInformation info;
-
-    // We force using indirection for `==` because it is a very common dynamic
-    // call site on many apps.
-    // TODO(sigmund): it would be even better if we could automatically detect
-    // when dynamic calls are growing too big and add the indirection at that
-    // point.
-    if (selector.name == '==') {
-      info = new IndirectDynamicCallSiteTypeInformation(
-          abstractValueDomain,
-          types.currentMember,
-          node,
-          _typeOfSharedDynamicCall(selector, CallStructure.ONE_ARG),
-          caller,
-          selector,
-          mask,
-          receiverType,
-          arguments,
-          inLoop,
-          isConditional);
-    } else {
-      info = new DynamicCallSiteTypeInformation(
-          abstractValueDomain,
-          types.currentMember,
-          callType,
-          node,
-          caller,
-          selector,
-          mask,
-          receiverType,
-          arguments,
-          inLoop,
-          isConditional);
-    }
+    CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
+        abstractValueDomain,
+        types.currentMember,
+        callType,
+        node,
+        caller,
+        selector,
+        mask,
+        receiverType,
+        arguments,
+        inLoop,
+        isConditional);
     info.addToGraph(this);
     types.allocatedCalls.add(info);
     return info;
@@ -1033,7 +1011,6 @@
   TypeInformation registerCalledClosure(
       ir.Node node,
       Selector selector,
-      AbstractValue mask,
       TypeInformation closure,
       MemberEntity caller,
       ArgumentsTypes arguments,
@@ -1046,7 +1023,6 @@
         node,
         caller,
         selector,
-        mask,
         closure,
         arguments,
         inLoop);
@@ -1142,6 +1118,8 @@
   /// and call sites, we may have a quadratic number of edges in the graph, so
   /// we add a level of indirection to merge the information and keep the graph
   /// smaller.
+  // TODO(sigmund): start using or delete indirection logic.
+  // ignore: unused_element
   DynamicCallSiteTypeInformation _typeOfSharedDynamicCall(
       Selector selector, CallStructure structure) {
     DynamicCallSiteTypeInformation info = _sharedCalls[selector];
@@ -1324,8 +1302,7 @@
   /// objects in a debugging data stream.
   static const String tag = 'global-type-inference-element-data';
 
-  // TODO(johnniwinther): Rename this together with [typeOfSend].
-  Map<ir.TreeNode, AbstractValue> _sendMap;
+  Map<ir.TreeNode, AbstractValue> _receiverMap;
 
   Map<ir.ForInStatement, AbstractValue> _iteratorMap;
   Map<ir.ForInStatement, AbstractValue> _currentMap;
@@ -1333,8 +1310,8 @@
 
   KernelGlobalTypeInferenceElementData();
 
-  KernelGlobalTypeInferenceElementData.internal(
-      this._sendMap, this._iteratorMap, this._currentMap, this._moveNextMap);
+  KernelGlobalTypeInferenceElementData.internal(this._receiverMap,
+      this._iteratorMap, this._currentMap, this._moveNextMap);
 
   /// Deserializes a [GlobalTypeInferenceElementData] object from [source].
   factory KernelGlobalTypeInferenceElementData.readFromDataSource(
@@ -1370,7 +1347,7 @@
     sink.inMemberContext(context, () {
       sink.begin(tag);
       sink.writeTreeNodeMapInContext(
-          _sendMap,
+          _receiverMap,
           (AbstractValue value) =>
               abstractValueDomain.writeAbstractValueToDataSink(sink, value),
           allowNull: true);
@@ -1395,10 +1372,10 @@
 
   @override
   GlobalTypeInferenceElementData compress() {
-    if (_sendMap != null) {
-      _sendMap.removeWhere(_mapsToNull);
-      if (_sendMap.isEmpty) {
-        _sendMap = null;
+    if (_receiverMap != null) {
+      _receiverMap.removeWhere(_mapsToNull);
+      if (_receiverMap.isEmpty) {
+        _receiverMap = null;
       }
     }
     if (_iteratorMap != null) {
@@ -1419,7 +1396,7 @@
         _moveNextMap = null;
       }
     }
-    if (_sendMap == null &&
+    if (_receiverMap == null &&
         _iteratorMap == null &&
         _currentMap == null &&
         _moveNextMap == null) {
@@ -1429,9 +1406,9 @@
   }
 
   @override
-  AbstractValue typeOfSend(ir.TreeNode node) {
-    if (_sendMap == null) return null;
-    return _sendMap[node];
+  AbstractValue typeOfReceiver(ir.TreeNode node) {
+    if (_receiverMap == null) return null;
+    return _receiverMap[node];
   }
 
   void setCurrentTypeMask(ir.ForInStatement node, AbstractValue mask) {
@@ -1467,15 +1444,9 @@
     return _iteratorMap[node];
   }
 
-  void setTypeMask(ir.TreeNode node, AbstractValue mask) {
-    _sendMap ??= <ir.TreeNode, AbstractValue>{};
-    _sendMap[node] = mask;
-  }
-
-  @override
-  AbstractValue typeOfGetter(ir.TreeNode node) {
-    if (_sendMap == null) return null;
-    return _sendMap[node];
+  void setReceiverTypeMask(ir.TreeNode node, AbstractValue mask) {
+    _receiverMap ??= <ir.TreeNode, AbstractValue>{};
+    _receiverMap[node] = mask;
   }
 }
 
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index 3091168..0e3fd2d 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -926,7 +926,6 @@
   final Object _call;
   final MemberEntity caller;
   final Selector selector;
-  final AbstractValue mask;
   final ArgumentsTypes arguments;
   final bool inLoop;
 
@@ -936,7 +935,6 @@
       this._call,
       this.caller,
       this.selector,
-      this.mask,
       this.arguments,
       this.inLoop)
       : super.noInputs(abstractValueDomain.emptyType, context) {
@@ -965,10 +963,9 @@
       MemberEntity enclosing,
       this.calledElement,
       Selector selector,
-      AbstractValue mask,
       ArgumentsTypes arguments,
       bool inLoop)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop);
 
   MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) {
@@ -983,8 +980,7 @@
     if (arguments != null) {
       arguments.forEach((info) => info.addUser(this));
     }
-    inferrer.updateParameterInputs(
-        this, calledElement, arguments, selector, mask,
+    inferrer.updateParameterInputs(this, calledElement, arguments, selector,
         remove: false, addToQueue: false);
   }
 
@@ -1052,6 +1048,7 @@
   final DynamicCallSiteTypeInformation dynamicCall;
   final bool isConditional;
   final TypeInformation receiver;
+  final AbstractValue mask;
 
   IndirectDynamicCallSiteTypeInformation(
       AbstractValueDomain abstractValueDomain,
@@ -1060,12 +1057,12 @@
       this.dynamicCall,
       MemberEntity enclosing,
       Selector selector,
-      AbstractValue mask,
+      this.mask,
       this.receiver,
       ArgumentsTypes arguments,
       bool inLoop,
       this.isConditional)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop);
 
   @override
@@ -1143,6 +1140,7 @@
 class DynamicCallSiteTypeInformation<T> extends CallSiteTypeInformation {
   final CallType _callType;
   final TypeInformation receiver;
+  final AbstractValue mask;
   final bool isConditional;
   bool _hasClosureCallTargets;
 
@@ -1156,12 +1154,12 @@
       T call,
       MemberEntity enclosing,
       Selector selector,
-      AbstractValue mask,
+      this.mask,
       this.receiver,
       ArgumentsTypes arguments,
       bool inLoop,
       this.isConditional)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop) {
     assert(validCallType(_callType, _call, selector));
   }
@@ -1196,8 +1194,7 @@
           inferrer.types.getInferredTypeOfMember(element);
       _addCall(callee);
       callee.addUser(this);
-      inferrer.updateParameterInputs(
-          this, element, arguments, selector, typeMask,
+      inferrer.updateParameterInputs(this, element, arguments, selector,
           remove: false, addToQueue: false);
     }
   }
@@ -1360,8 +1357,7 @@
             inferrer.types.getInferredTypeOfMember(element);
         _addCall(callee);
         callee.addUser(this);
-        inferrer.updateParameterInputs(
-            this, element, arguments, selector, typeMask,
+        inferrer.updateParameterInputs(this, element, arguments, selector,
             remove: false, addToQueue: true);
       });
 
@@ -1373,8 +1369,7 @@
             inferrer.types.getInferredTypeOfMember(element);
         _removeCall(callee);
         callee.removeUser(this);
-        inferrer.updateParameterInputs(
-            this, element, arguments, selector, typeMask,
+        inferrer.updateParameterInputs(this, element, arguments, selector,
             remove: true, addToQueue: true);
       });
     }
@@ -1448,8 +1443,7 @@
           MemberTypeInformation callee =
               inferrer.types.getInferredTypeOfMember(element);
           callee.addCall(caller, _call);
-          inferrer.updateParameterInputs(
-              this, element, arguments, selector, mask,
+          inferrer.updateParameterInputs(this, element, arguments, selector,
               remove: false, addToQueue: true);
         }
       }
@@ -1497,11 +1491,10 @@
       Object call,
       MemberEntity enclosing,
       Selector selector,
-      AbstractValue mask,
       this.closure,
       ArgumentsTypes arguments,
       bool inLoop)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop);
 
   @override
diff --git a/pkg/compiler/lib/src/inferrer/types.dart b/pkg/compiler/lib/src/inferrer/types.dart
index 374f01a..5c5c4f1 100644
--- a/pkg/compiler/lib/src/inferrer/types.dart
+++ b/pkg/compiler/lib/src/inferrer/types.dart
@@ -52,13 +52,9 @@
   /// The inferred return type when this result belongs to a function element.
   AbstractValue get returnType;
 
-  /// Returns the type of a send [node].
-  // TODO(johnniwinther): Rename this.
-  AbstractValue typeOfSend(ir.TreeNode node);
-
-  /// Returns the type of the getter in a complex send-set [node], for example,
-  /// the type of the `a.f` getter in `a.f += b`.
-  AbstractValue typeOfGetter(ir.TreeNode node);
+  /// Returns the receiver type of a node that is a property get, set, or method
+  /// invocation.
+  AbstractValue typeOfReceiver(ir.TreeNode node);
 
   /// Returns the type of the iterator in a [loop].
   AbstractValue typeOfIterator(ir.TreeNode node);
@@ -88,8 +84,7 @@
   GlobalTypeInferenceElementData compress();
 
   // TODO(johnniwinther): Remove this. Maybe split by access/invoke.
-  AbstractValue typeOfSend(ir.TreeNode node);
-  AbstractValue typeOfGetter(ir.TreeNode node);
+  AbstractValue typeOfReceiver(ir.TreeNode node);
 
   AbstractValue typeOfIterator(ir.TreeNode node);
 
@@ -443,9 +438,7 @@
   }
 
   @override
-  AbstractValue typeOfSend(ir.Node node) => _data?.typeOfSend(node);
-  @override
-  AbstractValue typeOfGetter(ir.Node node) => _data?.typeOfGetter(node);
+  AbstractValue typeOfReceiver(ir.Node node) => _data?.typeOfReceiver(node);
   @override
   AbstractValue typeOfIterator(ir.Node node) => _data?.typeOfIterator(node);
   @override
@@ -524,10 +517,7 @@
   AbstractValue typeOfIterator(ir.Node node) => null;
 
   @override
-  AbstractValue typeOfGetter(ir.Node node) => null;
-
-  @override
-  AbstractValue typeOfSend(ir.Node node) => null;
+  AbstractValue typeOfReceiver(ir.Node node) => null;
 
   @override
   bool get isCalledOnce => false;
@@ -568,10 +558,7 @@
   AbstractValue typeOfIterator(ir.Node node) => null;
 
   @override
-  AbstractValue typeOfGetter(ir.Node node) => null;
-
-  @override
-  AbstractValue typeOfSend(ir.Node node) => null;
+  AbstractValue typeOfReceiver(ir.Node node) => null;
 
   @override
   bool get isCalledOnce => false;
@@ -612,10 +599,7 @@
   AbstractValue typeOfIterator(ir.Node node) => null;
 
   @override
-  AbstractValue typeOfGetter(ir.Node node) => null;
-
-  @override
-  AbstractValue typeOfSend(ir.Node node) => null;
+  AbstractValue typeOfReceiver(ir.Node node) => null;
 
   @override
   bool get isCalledOnce => false;
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 9eab8f8..3e31aa5e 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -539,23 +539,23 @@
   @override
   AbstractValue receiverTypeOfInvocation(
       ir.MethodInvocation node, AbstractValueDomain abstractValueDomain) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
   AbstractValue receiverTypeOfGet(ir.PropertyGet node) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
   AbstractValue receiverTypeOfDirectGet(ir.DirectPropertyGet node) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
   AbstractValue receiverTypeOfSet(
       ir.PropertySet node, AbstractValueDomain abstractValueDomain) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
index a7e3505..2306954 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
@@ -76,10 +76,6 @@
   /// This should only set `true` by our REPL compiler.
   bool replCompile;
 
-  /// Mapping from absolute file paths to bazel short path to substitute in
-  /// source maps.
-  final Map<String, String> bazelMapping;
-
   final Map<String, String> summaryModules;
 
   final List<ModuleFormat> moduleFormats;
@@ -103,7 +99,6 @@
       this.emitMetadata = false,
       this.enableAsserts = true,
       this.replCompile = false,
-      this.bazelMapping = const {},
       this.summaryModules = const {},
       this.moduleFormats = const [],
       this.experiments = const {},
@@ -119,8 +114,6 @@
             enableAsserts: args['enable-asserts'] as bool,
             experiments: parseExperimentalArguments(
                 args['enable-experiment'] as List<String>),
-            bazelMapping:
-                _parseBazelMappings(args['bazel-mapping'] as List<String>),
             summaryModules: _parseCustomSummaryModules(
                 args['summary'] as List<String>, moduleRoot, summaryExtension),
             moduleFormats: parseModuleFormatOption(args),
@@ -151,12 +144,6 @@
       ..addOption('module-name',
           help: 'The output module name, used in some JS module formats.\n'
               'Defaults to the output file name (without .js).')
-      // TODO(jmesserly): rename this, it has nothing to do with bazel.
-      ..addMultiOption('bazel-mapping',
-          help: '--bazel-mapping=gen/to/library.dart,to/library.dart\n'
-              'adjusts the path in source maps.',
-          splitCommas: false,
-          hide: hide)
       ..addOption('library-root',
           help: '(deprecated) used to name libraries inside the module, '
               'ignored with -k.',
@@ -235,17 +222,6 @@
   return pathToModule;
 }
 
-Map<String, String> _parseBazelMappings(List<String> argument) {
-  var mappings = <String, String>{};
-  for (var mapping in argument) {
-    var splitMapping = mapping.split(',');
-    if (splitMapping.length >= 2) {
-      mappings[p.absolute(splitMapping[0])] = splitMapping[1];
-    }
-  }
-  return mappings;
-}
-
 /// Taken from analyzer to implement `--ignore-unrecognized-flags`
 List<String> filterUnknownArguments(List<String> args, ArgParser parser) {
   if (!args.contains('--ignore-unrecognized-flags')) return args;
@@ -335,8 +311,7 @@
 /// matching [multiRootScheme] are adjusted to be relative to
 /// [multiRootOutputPath].
 // TODO(jmesserly): find a new home for this.
-Map placeSourceMap(Map sourceMap, String sourceMapPath,
-    Map<String, String> bazelMappings, String multiRootScheme,
+Map placeSourceMap(Map sourceMap, String sourceMapPath, String multiRootScheme,
     {String multiRootOutputPath}) {
   var map = Map.from(sourceMap);
   // Convert to a local file path if it's not.
@@ -366,10 +341,6 @@
     // Convert to a local file path if it's not.
     sourcePath = sourcePathToUri(p.absolute(p.fromUri(uri))).path;
 
-    // Allow bazel mappings to override.
-    var match = bazelMappings != null ? bazelMappings[sourcePath] : null;
-    if (match != null) return match;
-
     // Fall back to a relative path against the source map itself.
     sourcePath = p.url.relative(sourcePath, from: sourceMapDir);
 
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index f2666eb..8315891 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -407,7 +407,6 @@
         inlineSourceMap: options.inlineSourceMap,
         jsUrl: p.toUri(output).toString(),
         mapUrl: p.toUri(output + '.map').toString(),
-        bazelMapping: options.bazelMapping,
         customScheme: multiRootScheme,
         multiRootOutputPath: multiRootOutputPath);
 
@@ -507,7 +506,6 @@
         inlineSourceMap: options.inlineSourceMap,
         jsUrl: p.toUri(output).toString(),
         mapUrl: p.toUri(output + '.map').toString(),
-        bazelMapping: options.bazelMapping,
         customScheme: multiRootScheme,
         multiRootOutputPath: multiRootOutputPath);
 
@@ -543,7 +541,6 @@
     bool inlineSourceMap = false,
     String jsUrl,
     String mapUrl,
-    Map<String, String> bazelMapping,
     String customScheme,
     String multiRootOutputPath}) {
   var opts = js_ast.JavaScriptPrintingOptions(
@@ -564,8 +561,7 @@
 
   Map builtMap;
   if (buildSourceMap && sourceMap != null) {
-    builtMap = placeSourceMap(
-        sourceMap.build(jsUrl), mapUrl, bazelMapping, customScheme,
+    builtMap = placeSourceMap(sourceMap.build(jsUrl), mapUrl, customScheme,
         multiRootOutputPath: multiRootOutputPath);
     var jsDir = p.dirname(p.fromUri(jsUrl));
     var relative = p.relative(p.fromUri(mapUrl), from: jsDir);
diff --git a/pkg/dev_compiler/tool/nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/nnbd_sdk_error_golden.txt
index d9fd291..8b13789 100644
--- a/pkg/dev_compiler/tool/nnbd_sdk_error_golden.txt
+++ b/pkg/dev_compiler/tool/nnbd_sdk_error_golden.txt
@@ -1,2 +1 @@
-ERROR|STATIC_TYPE_WARNING|INVALID_ASSIGNMENT|lib/async/async.dart|5554|39|14|A value of type '_PendingEvents<T>?' can't be assigned to a variable of type '_PendingEvents<T>'.
-ERROR|STATIC_TYPE_WARNING|RETURN_OF_INVALID_TYPE|lib/async/async.dart|5390|14|8|A value of type 'Object?' can't be returned from function '_pendingEvents' because it has a return type of '_PendingEvents<T>?'.
+
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 210998e..132c992 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -3826,18 +3826,39 @@
               forest.argumentsPositional(arguments).length)
           .withLocation(uri, arguments.fileOffset, noLength);
     }
-    List<Object> named = forest.argumentsNamed(arguments);
+    List<NamedExpression> named = forest.argumentsNamed(arguments);
     if (named.isNotEmpty) {
-      Set<String> names =
+      Set<String> parameterNames =
           new Set.from(function.namedParameters.map((a) => a.name));
       for (NamedExpression argument in named) {
-        if (!names.contains(argument.name)) {
+        if (!parameterNames.contains(argument.name)) {
           return fasta.templateNoSuchNamedParameter
               .withArguments(argument.name)
               .withLocation(uri, argument.fileOffset, argument.name.length);
         }
       }
     }
+    if (function.namedParameters.isNotEmpty) {
+      if (libraryBuilder.isNonNullableByDefault &&
+          libraryBuilder.loader.performNnbdChecks) {
+        Set<String> argumentNames = new Set.from(named.map((a) => a.name));
+        for (VariableDeclaration parameter in function.namedParameters) {
+          if (parameter.isRequired && !argumentNames.contains(parameter.name)) {
+            if (libraryBuilder.loader.nnbdStrongMode) {
+              return fasta.templateValueForRequiredParameterNotProvidedError
+                  .withArguments(parameter.name)
+                  .withLocation(uri, arguments.fileOffset, fasta.noLength);
+            } else {
+              addProblem(
+                  fasta.templateValueForRequiredParameterNotProvidedWarning
+                      .withArguments(parameter.name),
+                  arguments.fileOffset,
+                  fasta.noLength);
+            }
+          }
+        }
+      }
+    }
 
     List<DartType> types = forest.argumentsTypeArguments(arguments);
     if (typeParameters.length != types.length) {
@@ -3873,7 +3894,7 @@
               forest.argumentsPositional(arguments).length)
           .withLocation(uri, arguments.fileOffset, noLength);
     }
-    List<Object> named = forest.argumentsNamed(arguments);
+    List<NamedExpression> named = forest.argumentsNamed(arguments);
     if (named.isNotEmpty) {
       Set<String> names =
           new Set.from(function.namedParameters.map((a) => a.name));
@@ -3885,6 +3906,27 @@
         }
       }
     }
+    if (function.namedParameters.isNotEmpty) {
+      if (libraryBuilder.isNonNullableByDefault &&
+          libraryBuilder.loader.performNnbdChecks) {
+        Set<String> argumentNames = new Set.from(named.map((a) => a.name));
+        for (NamedType parameter in function.namedParameters) {
+          if (parameter.isRequired && !argumentNames.contains(parameter.name)) {
+            if (libraryBuilder.loader.nnbdStrongMode) {
+              return fasta.templateValueForRequiredParameterNotProvidedError
+                  .withArguments(parameter.name)
+                  .withLocation(uri, arguments.fileOffset, fasta.noLength);
+            } else {
+              addProblem(
+                  fasta.templateValueForRequiredParameterNotProvidedWarning
+                      .withArguments(parameter.name),
+                  arguments.fileOffset,
+                  fasta.noLength);
+            }
+          }
+        }
+      }
+    }
     List<Object> types = forest.argumentsTypeArguments(arguments);
     List<TypeParameter> typeParameters = function.typeParameters;
     if (typeParameters.length != types.length && types.length != 0) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index 52d77ba..1febb17 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -2491,6 +2491,10 @@
 
           String uri = '${classBuilder.library.uri}';
           if (uri == 'dart:js' &&
+                  classBuilder.fileUri.pathSegments.last == 'js.dart' ||
+              // Remove 'js_dart2js.dart' once the file is renamed
+              // to 'js.dart' everywhere.
+              uri == 'dart:js' &&
                   classBuilder.fileUri.pathSegments.last == 'js_dart2js.dart' ||
               uri == 'dart:_interceptors' &&
                   classBuilder.fileUri.pathSegments.last == 'js_number.dart') {
diff --git a/pkg/front_end/lib/src/testing/id_testing_helper.dart b/pkg/front_end/lib/src/testing/id_testing_helper.dart
index 69b9af4..e88e95a 100644
--- a/pkg/front_end/lib/src/testing/id_testing_helper.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_helper.dart
@@ -419,7 +419,7 @@
   }
 
   CfeCompiledData<T> compiledData = new CfeCompiledData<T>(
-      compilerResult, testData.testFileUri, actualMaps, globalData);
+      compilerResult, testData.entryPoint, actualMaps, globalData);
   return checkCode(config.name, testData.testFileUri, testData.code,
       memberAnnotations, compiledData, dataComputer.dataValidator,
       fatalErrors: fatalErrors, succinct: succinct, onFailure: onFailure);
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 770426b..694f491 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -595,6 +595,10 @@
 UnterminatedToken/analyzerCode: Fail
 UnterminatedToken/example: Fail
 UntranslatableUri/part_wrapped_script: Fail # Importing file in the (now) part.
+ValueForRequiredParameterNotProvidedError/analyzerCode: Fail
+ValueForRequiredParameterNotProvidedError/example: Fail
+ValueForRequiredParameterNotProvidedWarning/analyzerCode: Fail
+ValueForRequiredParameterNotProvidedWarning/example: Fail
 VarAsTypeName/part_wrapped_script1: Fail
 VarAsTypeName/script1: Fail # Too many problems
 WebLiteralCannotBeRepresentedExactly/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 1e72804..035c8e2 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3761,3 +3761,10 @@
 RequiredNamedParameterHasDefaultValueWarning:
   template: "Required named parameter '#name' has a default value."
   severity: WARNING
+
+ValueForRequiredParameterNotProvidedError:
+  template: "Required named parameter '#name' must be provided."
+
+ValueForRequiredParameterNotProvidedWarning:
+  template: "Missing required named parameter '#name'."
+  severity: WARNING
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart
new file mode 100644
index 0000000..127e1fe
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart
@@ -0,0 +1,10 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+  Function? f1 = foo;
+  f1!(42);
+
+  Function f2 = bar;
+  f2!<int>(42);
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.expect
new file mode 100644
index 0000000..7b185d5e
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.expect
@@ -0,0 +1,130 @@
+beginCompilationUnit(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(?)
+      handleType(Object, ?)
+      handleIdentifier(foo, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(int)
+        endMetadataStar(0)
+        beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(i)
+          handleType(int, null)
+          handleIdentifier(i, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, i, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginLiteralString("42")
+      endLiteralString(0, ;)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(;, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(?)
+      handleType(Object, ?)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      beginTypeVariables(<)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        handleIdentifier(T, typeVariableDeclaration)
+        beginTypeVariable(T)
+          handleTypeVariablesDefined(T, 1)
+          handleNoType(T)
+        endTypeVariable(>, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        beginFormalParameter(T, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T, typeReference)
+          handleNoTypeArguments(t)
+          handleType(T, null)
+          handleIdentifier(t, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, t, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(main)
+  beginMetadataStar(main)
+  endMetadataStar(0)
+  beginTopLevelMember(main)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(main, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(?)
+        handleType(Function, ?)
+        beginVariablesDeclaration(f1, null, null)
+          handleIdentifier(f1, localVariableDeclaration)
+          beginInitializedIdentifier(f1)
+            beginVariableInitializer(=)
+              handleIdentifier(foo, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(foo, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f1)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f1, expression)
+        handleNoTypeArguments(!)
+        handleNoArguments(!)
+        handleSend(f1, !)
+        handleNonNullAssertExpression(!)
+        handleNoTypeArguments(()
+        beginArguments(()
+          handleLiteralInt(42)
+        endArguments(1, (, ))
+        handleSend((, ))
+        handleExpressionStatement(;)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(f2)
+        handleType(Function, null)
+        beginVariablesDeclaration(f2, null, null)
+          handleIdentifier(f2, localVariableDeclaration)
+          beginInitializedIdentifier(f2)
+            beginVariableInitializer(=)
+              handleIdentifier(bar, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(bar, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f2)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f2, expression)
+        handleNonNullAssertExpression(!)
+        beginTypeArguments(<)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(>)
+          handleType(int, null)
+        endTypeArguments(1, <, >)
+        beginArguments(()
+          handleLiteralInt(42)
+        endArguments(1, (, ))
+        handleSend(f2, ;)
+        handleExpressionStatement(;)
+      endBlockFunctionBody(4, {, })
+    endTopLevelMethod(main, null, })
+  endTopLevelDeclaration()
+endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.intertwined.expect
new file mode 100644
index 0000000..3f3083a
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.intertwined.expect
@@ -0,0 +1,285 @@
+parseUnit(Object)
+  skipErrorTokens(Object)
+  listener: beginCompilationUnit(Object)
+  syntheticPreviousToken(Object)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(, null, , Instance of 'SimpleNullableType', null, foo)
+        listener: beginTopLevelMethod(, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(?)
+        listener: handleType(Object, ?)
+        ensureIdentifier(?, topLevelFunctionDeclaration)
+          listener: handleIdentifier(foo, topLevelFunctionDeclaration)
+        parseMethodTypeVar(foo)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(foo, foo, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(foo, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(int)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(int, typeReference)
+                listener: handleNoTypeArguments(i)
+                listener: handleType(int, null)
+                ensureIdentifier(int, formalParameterDeclaration)
+                  listener: handleIdentifier(i, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, i, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralString(=>)
+                      parseSingleLiteralString(=>)
+                        listener: beginLiteralString("42")
+                        listener: endLiteralString(0, ;)
+            ensureSemicolon("42")
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(Object)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(;, null, ;, Instance of 'SimpleNullableType', null, bar)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(?)
+        listener: handleType(Object, ?)
+        ensureIdentifier(?, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          listener: beginTypeVariables(<)
+          listener: beginMetadataStar(T)
+          listener: endMetadataStar(0)
+          listener: handleIdentifier(T, typeVariableDeclaration)
+          listener: beginTypeVariable(T)
+          listener: handleTypeVariablesDefined(T, 1)
+          listener: handleNoType(T)
+          listener: endTypeVariable(>, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(T)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T, typeReference)
+                listener: handleNoTypeArguments(t)
+                listener: handleType(T, null)
+                ensureIdentifier(T, formalParameterDeclaration)
+                  listener: handleIdentifier(t, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, t, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(main)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(main)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(main)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, main)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(main, topLevelFunctionDeclaration)
+        parseMethodTypeVar(main)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(main, main, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(main, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, Function)
+          parseStatement({)
+            parseStatementX({)
+              parseExpressionStatementOrDeclaration({, false)
+                parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, false)
+                  looksLikeLocalFunction(f1)
+                  parseExpressionWithoutCascade(=)
+                    parsePrecedenceExpression(=, 1, false)
+                      parseUnaryExpression(=, false)
+                        parsePrimary(=, expression)
+                          parseSendOrFunctionLiteral(=, expression)
+                            parseSend(=, expression)
+                              ensureIdentifier(=, expression)
+                              parseArgumentsOpt(foo)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(?)
+                  listener: handleType(Function, ?)
+                  listener: beginVariablesDeclaration(f1, null, null)
+                  parseVariablesDeclarationRest(?, true)
+                    parseOptionallyInitializedIdentifier(?)
+                      ensureIdentifier(?, localVariableDeclaration)
+                        listener: handleIdentifier(f1, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f1)
+                      parseVariableInitializerOpt(f1)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(foo, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(foo)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(foo, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f1)
+                    ensureSemicolon(foo)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f1)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f1)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f1, expression)
+                              listener: handleNoTypeArguments(!)
+                              parseArgumentsOpt(f1)
+                                listener: handleNoArguments(!)
+                              listener: handleSend(f1, !)
+                      listener: handleNonNullAssertExpression(!)
+                      parseArgumentOrIndexStar(!, Instance of 'NoTypeParamOrArg')
+                        listener: handleNoTypeArguments(()
+                        parseArguments(!)
+                          parseArgumentsRest(()
+                            listener: beginArguments(()
+                            parseExpression(()
+                              parsePrecedenceExpression((, 1, true)
+                                parseUnaryExpression((, true)
+                                  parsePrimary((, expression)
+                                    parseLiteralInt(()
+                                      listener: handleLiteralInt(42)
+                            listener: endArguments(1, (, ))
+                        listener: handleSend((, ))
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, Function)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclaration(;, false)
+                parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                  looksLikeLocalFunction(f2)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(f2)
+                  listener: handleType(Function, null)
+                  listener: beginVariablesDeclaration(f2, null, null)
+                  parseVariablesDeclarationRest(Function, true)
+                    parseOptionallyInitializedIdentifier(Function)
+                      ensureIdentifier(Function, localVariableDeclaration)
+                        listener: handleIdentifier(f2, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f2)
+                      parseVariableInitializerOpt(f2)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(bar, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(bar)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(bar, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f2)
+                    ensureSemicolon(bar)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f2)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f2)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f2, expression)
+                              listener: handleNonNullAssertExpression(!)
+                              listener: beginTypeArguments(<)
+                              listener: handleIdentifier(int, typeReference)
+                              listener: handleNoTypeArguments(>)
+                              listener: handleType(int, null)
+                              listener: endTypeArguments(1, <, >)
+                              parseArgumentsOpt(>)
+                                parseArguments(>)
+                                  parseArgumentsRest(()
+                                    listener: beginArguments(()
+                                    parseExpression(()
+                                      parsePrecedenceExpression((, 1, true)
+                                        parseUnaryExpression((, true)
+                                          parsePrimary((, expression)
+                                            parseLiteralInt(()
+                                              listener: handleLiteralInt(42)
+                                    listener: endArguments(1, (, ))
+                              listener: handleSend(f2, ;)
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(4, {, })
+        listener: endTopLevelMethod(main, null, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(Object)
+  listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.parser.expect
new file mode 100644
index 0000000..85afc92
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.parser.expect
@@ -0,0 +1,23 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+Function? f1 = foo;
+f1!(42);
+
+Function f2 = bar;
+f2!<int>(42);
+}
+
+
+Object[StringToken]?[SimpleToken] foo[StringToken]([BeginToken]int[StringToken] i[StringToken])[SimpleToken] =>[SimpleToken] "42"[StringToken];[SimpleToken]
+Object[StringToken]?[SimpleToken] bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken]T[StringToken] t[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken]?[SimpleToken] f1[StringToken] =[SimpleToken] foo[StringToken];[SimpleToken]
+f1[StringToken]![SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]![SimpleToken]<[BeginToken]int[StringToken]>[SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.scanner.expect
new file mode 100644
index 0000000..85afc92
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776.dart.scanner.expect
@@ -0,0 +1,23 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+Function? f1 = foo;
+f1!(42);
+
+Function f2 = bar;
+f2!<int>(42);
+}
+
+
+Object[StringToken]?[SimpleToken] foo[StringToken]([BeginToken]int[StringToken] i[StringToken])[SimpleToken] =>[SimpleToken] "42"[StringToken];[SimpleToken]
+Object[StringToken]?[SimpleToken] bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken]T[StringToken] t[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken]?[SimpleToken] f1[StringToken] =[SimpleToken] foo[StringToken];[SimpleToken]
+f1[StringToken]![SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]![SimpleToken]<[BeginToken]int[StringToken]>[SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart
new file mode 100644
index 0000000..e8a92a4
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart
@@ -0,0 +1,10 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+  Function? f1 = foo;
+  f1!(42);
+
+  Function f2 = bar;
+  f2<int>(42);
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.expect
new file mode 100644
index 0000000..721e3a4
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.expect
@@ -0,0 +1,129 @@
+beginCompilationUnit(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(?)
+      handleType(Object, ?)
+      handleIdentifier(foo, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(int)
+        endMetadataStar(0)
+        beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(i)
+          handleType(int, null)
+          handleIdentifier(i, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, i, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginLiteralString("42")
+      endLiteralString(0, ;)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(;, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(?)
+      handleType(Object, ?)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      beginTypeVariables(<)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        handleIdentifier(T, typeVariableDeclaration)
+        beginTypeVariable(T)
+          handleTypeVariablesDefined(T, 1)
+          handleNoType(T)
+        endTypeVariable(>, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        beginFormalParameter(T, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T, typeReference)
+          handleNoTypeArguments(t)
+          handleType(T, null)
+          handleIdentifier(t, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, t, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(main)
+  beginMetadataStar(main)
+  endMetadataStar(0)
+  beginTopLevelMember(main)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(main, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(?)
+        handleType(Function, ?)
+        beginVariablesDeclaration(f1, null, null)
+          handleIdentifier(f1, localVariableDeclaration)
+          beginInitializedIdentifier(f1)
+            beginVariableInitializer(=)
+              handleIdentifier(foo, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(foo, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f1)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f1, expression)
+        handleNoTypeArguments(!)
+        handleNoArguments(!)
+        handleSend(f1, !)
+        handleNonNullAssertExpression(!)
+        handleNoTypeArguments(()
+        beginArguments(()
+          handleLiteralInt(42)
+        endArguments(1, (, ))
+        handleSend((, ))
+        handleExpressionStatement(;)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(f2)
+        handleType(Function, null)
+        beginVariablesDeclaration(f2, null, null)
+          handleIdentifier(f2, localVariableDeclaration)
+          beginInitializedIdentifier(f2)
+            beginVariableInitializer(=)
+              handleIdentifier(bar, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(bar, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f2)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f2, expression)
+        beginTypeArguments(<)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(>)
+          handleType(int, null)
+        endTypeArguments(1, <, >)
+        beginArguments(()
+          handleLiteralInt(42)
+        endArguments(1, (, ))
+        handleSend(f2, ;)
+        handleExpressionStatement(;)
+      endBlockFunctionBody(4, {, })
+    endTopLevelMethod(main, null, })
+  endTopLevelDeclaration()
+endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.intertwined.expect
new file mode 100644
index 0000000..2fb237b
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.intertwined.expect
@@ -0,0 +1,285 @@
+parseUnit(Object)
+  skipErrorTokens(Object)
+  listener: beginCompilationUnit(Object)
+  syntheticPreviousToken(Object)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(, null, , Instance of 'SimpleNullableType', null, foo)
+        listener: beginTopLevelMethod(, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(?)
+        listener: handleType(Object, ?)
+        ensureIdentifier(?, topLevelFunctionDeclaration)
+          listener: handleIdentifier(foo, topLevelFunctionDeclaration)
+        parseMethodTypeVar(foo)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(foo, foo, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(foo, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(int)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(int, typeReference)
+                listener: handleNoTypeArguments(i)
+                listener: handleType(int, null)
+                ensureIdentifier(int, formalParameterDeclaration)
+                  listener: handleIdentifier(i, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, i, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralString(=>)
+                      parseSingleLiteralString(=>)
+                        listener: beginLiteralString("42")
+                        listener: endLiteralString(0, ;)
+            ensureSemicolon("42")
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(Object)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(;, null, ;, Instance of 'SimpleNullableType', null, bar)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(?)
+        listener: handleType(Object, ?)
+        ensureIdentifier(?, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          listener: beginTypeVariables(<)
+          listener: beginMetadataStar(T)
+          listener: endMetadataStar(0)
+          listener: handleIdentifier(T, typeVariableDeclaration)
+          listener: beginTypeVariable(T)
+          listener: handleTypeVariablesDefined(T, 1)
+          listener: handleNoType(T)
+          listener: endTypeVariable(>, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(T)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T, typeReference)
+                listener: handleNoTypeArguments(t)
+                listener: handleType(T, null)
+                ensureIdentifier(T, formalParameterDeclaration)
+                  listener: handleIdentifier(t, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, t, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(main)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(main)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(main)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, main)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(main, topLevelFunctionDeclaration)
+        parseMethodTypeVar(main)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(main, main, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(main, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, Function)
+          parseStatement({)
+            parseStatementX({)
+              parseExpressionStatementOrDeclaration({, false)
+                parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, false)
+                  looksLikeLocalFunction(f1)
+                  parseExpressionWithoutCascade(=)
+                    parsePrecedenceExpression(=, 1, false)
+                      parseUnaryExpression(=, false)
+                        parsePrimary(=, expression)
+                          parseSendOrFunctionLiteral(=, expression)
+                            parseSend(=, expression)
+                              ensureIdentifier(=, expression)
+                              parseArgumentsOpt(foo)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(?)
+                  listener: handleType(Function, ?)
+                  listener: beginVariablesDeclaration(f1, null, null)
+                  parseVariablesDeclarationRest(?, true)
+                    parseOptionallyInitializedIdentifier(?)
+                      ensureIdentifier(?, localVariableDeclaration)
+                        listener: handleIdentifier(f1, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f1)
+                      parseVariableInitializerOpt(f1)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(foo, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(foo)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(foo, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f1)
+                    ensureSemicolon(foo)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f1)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f1)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f1, expression)
+                              listener: handleNoTypeArguments(!)
+                              parseArgumentsOpt(f1)
+                                listener: handleNoArguments(!)
+                              listener: handleSend(f1, !)
+                      listener: handleNonNullAssertExpression(!)
+                      parseArgumentOrIndexStar(!, Instance of 'NoTypeParamOrArg')
+                        listener: handleNoTypeArguments(()
+                        parseArguments(!)
+                          parseArgumentsRest(()
+                            listener: beginArguments(()
+                            parseExpression(()
+                              parsePrecedenceExpression((, 1, true)
+                                parseUnaryExpression((, true)
+                                  parsePrimary((, expression)
+                                    parseLiteralInt(()
+                                      listener: handleLiteralInt(42)
+                            listener: endArguments(1, (, ))
+                        listener: handleSend((, ))
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, Function)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclaration(;, false)
+                parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                  looksLikeLocalFunction(f2)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(f2)
+                  listener: handleType(Function, null)
+                  listener: beginVariablesDeclaration(f2, null, null)
+                  parseVariablesDeclarationRest(Function, true)
+                    parseOptionallyInitializedIdentifier(Function)
+                      ensureIdentifier(Function, localVariableDeclaration)
+                        listener: handleIdentifier(f2, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f2)
+                      parseVariableInitializerOpt(f2)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(bar, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(bar)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(bar, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f2)
+                    ensureSemicolon(bar)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f2)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f2)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            looksLikeFunctionBody(;)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f2, expression)
+                              listener: beginTypeArguments(<)
+                              listener: handleIdentifier(int, typeReference)
+                              listener: handleNoTypeArguments(>)
+                              listener: handleType(int, null)
+                              listener: endTypeArguments(1, <, >)
+                              parseArgumentsOpt(>)
+                                parseArguments(>)
+                                  parseArgumentsRest(()
+                                    listener: beginArguments(()
+                                    parseExpression(()
+                                      parsePrecedenceExpression((, 1, true)
+                                        parseUnaryExpression((, true)
+                                          parsePrimary((, expression)
+                                            parseLiteralInt(()
+                                              listener: handleLiteralInt(42)
+                                    listener: endArguments(1, (, ))
+                              listener: handleSend(f2, ;)
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(4, {, })
+        listener: endTopLevelMethod(main, null, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(Object)
+  listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.parser.expect
new file mode 100644
index 0000000..7d382f2
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.parser.expect
@@ -0,0 +1,23 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+Function? f1 = foo;
+f1!(42);
+
+Function f2 = bar;
+f2<int>(42);
+}
+
+
+Object[StringToken]?[SimpleToken] foo[StringToken]([BeginToken]int[StringToken] i[StringToken])[SimpleToken] =>[SimpleToken] "42"[StringToken];[SimpleToken]
+Object[StringToken]?[SimpleToken] bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken]T[StringToken] t[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken]?[SimpleToken] f1[StringToken] =[SimpleToken] foo[StringToken];[SimpleToken]
+f1[StringToken]![SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]<[BeginToken]int[StringToken]>[SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.scanner.expect
new file mode 100644
index 0000000..7d382f2
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime1.dart.scanner.expect
@@ -0,0 +1,23 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+Function? f1 = foo;
+f1!(42);
+
+Function f2 = bar;
+f2<int>(42);
+}
+
+
+Object[StringToken]?[SimpleToken] foo[StringToken]([BeginToken]int[StringToken] i[StringToken])[SimpleToken] =>[SimpleToken] "42"[StringToken];[SimpleToken]
+Object[StringToken]?[SimpleToken] bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken]T[StringToken] t[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken]?[SimpleToken] f1[StringToken] =[SimpleToken] foo[StringToken];[SimpleToken]
+f1[StringToken]![SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]<[BeginToken]int[StringToken]>[SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart
new file mode 100644
index 0000000..1dc19a5
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart
@@ -0,0 +1,10 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+  Function? f1 = foo;
+  f1!(42);
+
+  Function f2 = bar;
+  (f2!)<int>(42);
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.expect
new file mode 100644
index 0000000..76b42cb
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.expect
@@ -0,0 +1,134 @@
+beginCompilationUnit(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(?)
+      handleType(Object, ?)
+      handleIdentifier(foo, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(int)
+        endMetadataStar(0)
+        beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(i)
+          handleType(int, null)
+          handleIdentifier(i, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, i, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginLiteralString("42")
+      endLiteralString(0, ;)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(;, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(?)
+      handleType(Object, ?)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      beginTypeVariables(<)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        handleIdentifier(T, typeVariableDeclaration)
+        beginTypeVariable(T)
+          handleTypeVariablesDefined(T, 1)
+          handleNoType(T)
+        endTypeVariable(>, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        beginFormalParameter(T, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T, typeReference)
+          handleNoTypeArguments(t)
+          handleType(T, null)
+          handleIdentifier(t, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, t, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(main)
+  beginMetadataStar(main)
+  endMetadataStar(0)
+  beginTopLevelMember(main)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(main, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(?)
+        handleType(Function, ?)
+        beginVariablesDeclaration(f1, null, null)
+          handleIdentifier(f1, localVariableDeclaration)
+          beginInitializedIdentifier(f1)
+            beginVariableInitializer(=)
+              handleIdentifier(foo, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(foo, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f1)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f1, expression)
+        handleNoTypeArguments(!)
+        handleNoArguments(!)
+        handleSend(f1, !)
+        handleNonNullAssertExpression(!)
+        handleNoTypeArguments(()
+        beginArguments(()
+          handleLiteralInt(42)
+        endArguments(1, (, ))
+        handleSend((, ))
+        handleExpressionStatement(;)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(f2)
+        handleType(Function, null)
+        beginVariablesDeclaration(f2, null, null)
+          handleIdentifier(f2, localVariableDeclaration)
+          beginInitializedIdentifier(f2)
+            beginVariableInitializer(=)
+              handleIdentifier(bar, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(bar, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f2)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f2, expression)
+        handleNoTypeArguments(!)
+        handleNoArguments(!)
+        handleSend(f2, !)
+        handleNonNullAssertExpression(!)
+        handleParenthesizedExpression(()
+        beginTypeArguments(<)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(>)
+          handleType(int, null)
+        endTypeArguments(1, <, >)
+        beginArguments(()
+          handleLiteralInt(42)
+        endArguments(1, (, ))
+        handleSend((, ))
+        handleExpressionStatement(;)
+      endBlockFunctionBody(4, {, })
+    endTopLevelMethod(main, null, })
+  endTopLevelDeclaration()
+endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.intertwined.expect
new file mode 100644
index 0000000..1b497087
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.intertwined.expect
@@ -0,0 +1,300 @@
+parseUnit(Object)
+  skipErrorTokens(Object)
+  listener: beginCompilationUnit(Object)
+  syntheticPreviousToken(Object)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(, null, , Instance of 'SimpleNullableType', null, foo)
+        listener: beginTopLevelMethod(, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(?)
+        listener: handleType(Object, ?)
+        ensureIdentifier(?, topLevelFunctionDeclaration)
+          listener: handleIdentifier(foo, topLevelFunctionDeclaration)
+        parseMethodTypeVar(foo)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(foo, foo, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(foo, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(int)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(int, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(int, typeReference)
+                listener: handleNoTypeArguments(i)
+                listener: handleType(int, null)
+                ensureIdentifier(int, formalParameterDeclaration)
+                  listener: handleIdentifier(i, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, i, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralString(=>)
+                      parseSingleLiteralString(=>)
+                        listener: beginLiteralString("42")
+                        listener: endLiteralString(0, ;)
+            ensureSemicolon("42")
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(Object)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(;, null, ;, Instance of 'SimpleNullableType', null, bar)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(?)
+        listener: handleType(Object, ?)
+        ensureIdentifier(?, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          listener: beginTypeVariables(<)
+          listener: beginMetadataStar(T)
+          listener: endMetadataStar(0)
+          listener: handleIdentifier(T, typeVariableDeclaration)
+          listener: beginTypeVariable(T)
+          listener: handleTypeVariablesDefined(T, 1)
+          listener: handleNoType(T)
+          listener: endTypeVariable(>, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(T)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T, typeReference)
+                listener: handleNoTypeArguments(t)
+                listener: handleType(T, null)
+                ensureIdentifier(T, formalParameterDeclaration)
+                  listener: handleIdentifier(t, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, t, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(main)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(main)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(main)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, main)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(main, topLevelFunctionDeclaration)
+        parseMethodTypeVar(main)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(main, main, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(main, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, Function)
+          parseStatement({)
+            parseStatementX({)
+              parseExpressionStatementOrDeclaration({, false)
+                parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, false)
+                  looksLikeLocalFunction(f1)
+                  parseExpressionWithoutCascade(=)
+                    parsePrecedenceExpression(=, 1, false)
+                      parseUnaryExpression(=, false)
+                        parsePrimary(=, expression)
+                          parseSendOrFunctionLiteral(=, expression)
+                            parseSend(=, expression)
+                              ensureIdentifier(=, expression)
+                              parseArgumentsOpt(foo)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(?)
+                  listener: handleType(Function, ?)
+                  listener: beginVariablesDeclaration(f1, null, null)
+                  parseVariablesDeclarationRest(?, true)
+                    parseOptionallyInitializedIdentifier(?)
+                      ensureIdentifier(?, localVariableDeclaration)
+                        listener: handleIdentifier(f1, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f1)
+                      parseVariableInitializerOpt(f1)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(foo, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(foo)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(foo, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f1)
+                    ensureSemicolon(foo)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f1)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f1)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f1, expression)
+                              listener: handleNoTypeArguments(!)
+                              parseArgumentsOpt(f1)
+                                listener: handleNoArguments(!)
+                              listener: handleSend(f1, !)
+                      listener: handleNonNullAssertExpression(!)
+                      parseArgumentOrIndexStar(!, Instance of 'NoTypeParamOrArg')
+                        listener: handleNoTypeArguments(()
+                        parseArguments(!)
+                          parseArgumentsRest(()
+                            listener: beginArguments(()
+                            parseExpression(()
+                              parsePrecedenceExpression((, 1, true)
+                                parseUnaryExpression((, true)
+                                  parsePrimary((, expression)
+                                    parseLiteralInt(()
+                                      listener: handleLiteralInt(42)
+                            listener: endArguments(1, (, ))
+                        listener: handleSend((, ))
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, Function)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclaration(;, false)
+                parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                  looksLikeLocalFunction(f2)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(f2)
+                  listener: handleType(Function, null)
+                  listener: beginVariablesDeclaration(f2, null, null)
+                  parseVariablesDeclarationRest(Function, true)
+                    parseOptionallyInitializedIdentifier(Function)
+                      ensureIdentifier(Function, localVariableDeclaration)
+                        listener: handleIdentifier(f2, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f2)
+                      parseVariableInitializerOpt(f2)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(bar, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(bar)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(bar, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f2)
+                    ensureSemicolon(bar)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, ()
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclaration(;, false)
+                parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                  looksLikeLocalFunction(()
+                  parseExpressionStatement(;)
+                    parseExpression(;)
+                      parsePrecedenceExpression(;, 1, true)
+                        parseUnaryExpression(;, true)
+                          parsePrimary(;, expression)
+                            parseParenthesizedExpressionOrFunctionLiteral(;)
+                              parseParenthesizedExpression(;)
+                                parseExpressionInParenthesis(;)
+                                  parseExpressionInParenthesisRest(()
+                                    parseExpression(()
+                                      parsePrecedenceExpression((, 1, true)
+                                        parseUnaryExpression((, true)
+                                          parsePrimary((, expression)
+                                            parseSendOrFunctionLiteral((, expression)
+                                              parseSend((, expression)
+                                                ensureIdentifier((, expression)
+                                                  listener: handleIdentifier(f2, expression)
+                                                listener: handleNoTypeArguments(!)
+                                                parseArgumentsOpt(f2)
+                                                  listener: handleNoArguments(!)
+                                                listener: handleSend(f2, !)
+                                        listener: handleNonNullAssertExpression(!)
+                                    ensureCloseParen(!, ()
+                                listener: handleParenthesizedExpression(()
+                        listener: beginTypeArguments(<)
+                        listener: handleIdentifier(int, typeReference)
+                        listener: handleNoTypeArguments(>)
+                        listener: handleType(int, null)
+                        listener: endTypeArguments(1, <, >)
+                        parseArgumentOrIndexStar(>, Instance of 'SimpleTypeArgument1')
+                          parseArguments(>)
+                            parseArgumentsRest(()
+                              listener: beginArguments(()
+                              parseExpression(()
+                                parsePrecedenceExpression((, 1, true)
+                                  parseUnaryExpression((, true)
+                                    parsePrimary((, expression)
+                                      parseLiteralInt(()
+                                        listener: handleLiteralInt(42)
+                              listener: endArguments(1, (, ))
+                          listener: handleSend((, ))
+                    ensureSemicolon())
+                    listener: handleExpressionStatement(;)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(4, {, })
+        listener: endTopLevelMethod(main, null, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(Object)
+  listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.parser.expect
new file mode 100644
index 0000000..ed2d418
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.parser.expect
@@ -0,0 +1,23 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+Function? f1 = foo;
+f1!(42);
+
+Function f2 = bar;
+(f2!)<int>(42);
+}
+
+
+Object[StringToken]?[SimpleToken] foo[StringToken]([BeginToken]int[StringToken] i[StringToken])[SimpleToken] =>[SimpleToken] "42"[StringToken];[SimpleToken]
+Object[StringToken]?[SimpleToken] bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken]T[StringToken] t[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken]?[SimpleToken] f1[StringToken] =[SimpleToken] foo[StringToken];[SimpleToken]
+f1[StringToken]![SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+([BeginToken]f2[StringToken]![SimpleToken])[SimpleToken]<[BeginToken]int[StringToken]>[SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.scanner.expect
new file mode 100644
index 0000000..ed2d418
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime2.dart.scanner.expect
@@ -0,0 +1,23 @@
+Object? foo(int i) => "42";
+Object? bar<T>(T t) => 42;
+
+main() {
+Function? f1 = foo;
+f1!(42);
+
+Function f2 = bar;
+(f2!)<int>(42);
+}
+
+
+Object[StringToken]?[SimpleToken] foo[StringToken]([BeginToken]int[StringToken] i[StringToken])[SimpleToken] =>[SimpleToken] "42"[StringToken];[SimpleToken]
+Object[StringToken]?[SimpleToken] bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken]T[StringToken] t[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken]?[SimpleToken] f1[StringToken] =[SimpleToken] foo[StringToken];[SimpleToken]
+f1[StringToken]![SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+([BeginToken]f2[StringToken]![SimpleToken])[SimpleToken]<[BeginToken]int[StringToken]>[SimpleToken]([BeginToken]42[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart
new file mode 100644
index 0000000..a607c8d
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart
@@ -0,0 +1,6 @@
+Object bar<T1, T2>(T1 t1, T2 t2) => 42;
+
+main() {
+  Function f2 = bar;
+  f2!<int, String>(42, "42");
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.expect
new file mode 100644
index 0000000..5a45979
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.expect
@@ -0,0 +1,97 @@
+beginCompilationUnit(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(bar)
+      handleType(Object, null)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      beginTypeVariables(<)
+        beginMetadataStar(T1)
+        endMetadataStar(0)
+        handleIdentifier(T1, typeVariableDeclaration)
+        beginTypeVariable(T1)
+          beginMetadataStar(T2)
+          endMetadataStar(0)
+          handleIdentifier(T2, typeVariableDeclaration)
+          beginTypeVariable(T2)
+            handleTypeVariablesDefined(T2, 2)
+            handleNoType(T2)
+          endTypeVariable(>, 1, null, null)
+          handleNoType(T1)
+        endTypeVariable(,, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(T1)
+        endMetadataStar(0)
+        beginFormalParameter(T1, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T1, typeReference)
+          handleNoTypeArguments(t1)
+          handleType(T1, null)
+          handleIdentifier(t1, formalParameterDeclaration)
+          handleFormalParameterWithoutValue(,)
+        endFormalParameter(null, null, t1, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+        beginMetadataStar(T2)
+        endMetadataStar(0)
+        beginFormalParameter(T2, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T2, typeReference)
+          handleNoTypeArguments(t2)
+          handleType(T2, null)
+          handleIdentifier(t2, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, t2, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(2, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(main)
+  beginMetadataStar(main)
+  endMetadataStar(0)
+  beginTopLevelMember(main)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(main, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(f2)
+        handleType(Function, null)
+        beginVariablesDeclaration(f2, null, null)
+          handleIdentifier(f2, localVariableDeclaration)
+          beginInitializedIdentifier(f2)
+            beginVariableInitializer(=)
+              handleIdentifier(bar, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(bar, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f2)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f2, expression)
+        handleNonNullAssertExpression(!)
+        beginTypeArguments(<)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(,)
+          handleType(int, null)
+          handleIdentifier(String, typeReference)
+          handleNoTypeArguments(>)
+          handleType(String, null)
+        endTypeArguments(2, <, >)
+        beginArguments(()
+          handleLiteralInt(42)
+          beginLiteralString("42")
+          endLiteralString(0, ))
+        endArguments(2, (, ))
+        handleSend(f2, ;)
+        handleExpressionStatement(;)
+      endBlockFunctionBody(2, {, })
+    endTopLevelMethod(main, null, })
+  endTopLevelDeclaration()
+endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.intertwined.expect
new file mode 100644
index 0000000..a565c35
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.intertwined.expect
@@ -0,0 +1,191 @@
+parseUnit(Object)
+  skipErrorTokens(Object)
+  listener: beginCompilationUnit(Object)
+  syntheticPreviousToken(Object)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(, null, , Instance of 'SimpleType', null, bar)
+        listener: beginTopLevelMethod(, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(bar)
+        listener: handleType(Object, null)
+        ensureIdentifier(Object, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          listener: beginTypeVariables(<)
+          parseMetadataStar(<)
+            listener: beginMetadataStar(T1)
+            listener: endMetadataStar(0)
+          ensureIdentifier(<, typeVariableDeclaration)
+            listener: handleIdentifier(T1, typeVariableDeclaration)
+          listener: beginTypeVariable(T1)
+          parseMetadataStar(,)
+            listener: beginMetadataStar(T2)
+            listener: endMetadataStar(0)
+          ensureIdentifier(,, typeVariableDeclaration)
+            listener: handleIdentifier(T2, typeVariableDeclaration)
+          listener: beginTypeVariable(T2)
+          listener: handleTypeVariablesDefined(T2, 2)
+          listener: handleNoType(T2)
+          listener: endTypeVariable(>, 1, null, null)
+          listener: handleNoType(T1)
+          listener: endTypeVariable(,, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(T1)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T1, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T1, typeReference)
+                listener: handleNoTypeArguments(t1)
+                listener: handleType(T1, null)
+                ensureIdentifier(T1, formalParameterDeclaration)
+                  listener: handleIdentifier(t1, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue(,)
+                listener: endFormalParameter(null, null, t1, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              parseFormalParameter(,, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(,)
+                  listener: beginMetadataStar(T2)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T2, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T2, typeReference)
+                listener: handleNoTypeArguments(t2)
+                listener: handleType(T2, null)
+                ensureIdentifier(T2, formalParameterDeclaration)
+                  listener: handleIdentifier(t2, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, t2, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(2, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(main)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(main)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(main)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, main)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(main, topLevelFunctionDeclaration)
+        parseMethodTypeVar(main)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(main, main, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(main, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, Function)
+          parseStatement({)
+            parseStatementX({)
+              parseExpressionStatementOrDeclaration({, false)
+                parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, false)
+                  looksLikeLocalFunction(f2)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(f2)
+                  listener: handleType(Function, null)
+                  listener: beginVariablesDeclaration(f2, null, null)
+                  parseVariablesDeclarationRest(Function, true)
+                    parseOptionallyInitializedIdentifier(Function)
+                      ensureIdentifier(Function, localVariableDeclaration)
+                        listener: handleIdentifier(f2, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f2)
+                      parseVariableInitializerOpt(f2)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(bar, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(bar)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(bar, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f2)
+                    ensureSemicolon(bar)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f2)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f2)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f2, expression)
+                              listener: handleNonNullAssertExpression(!)
+                              listener: beginTypeArguments(<)
+                              listener: handleIdentifier(int, typeReference)
+                              listener: handleNoTypeArguments(,)
+                              listener: handleType(int, null)
+                              listener: handleIdentifier(String, typeReference)
+                              listener: handleNoTypeArguments(>)
+                              listener: handleType(String, null)
+                              listener: endTypeArguments(2, <, >)
+                              parseArgumentsOpt(>)
+                                parseArguments(>)
+                                  parseArgumentsRest(()
+                                    listener: beginArguments(()
+                                    parseExpression(()
+                                      parsePrecedenceExpression((, 1, true)
+                                        parseUnaryExpression((, true)
+                                          parsePrimary((, expression)
+                                            parseLiteralInt(()
+                                              listener: handleLiteralInt(42)
+                                    parseExpression(,)
+                                      parsePrecedenceExpression(,, 1, true)
+                                        parseUnaryExpression(,, true)
+                                          parsePrimary(,, expression)
+                                            parseLiteralString(,)
+                                              parseSingleLiteralString(,)
+                                                listener: beginLiteralString("42")
+                                                listener: endLiteralString(0, ))
+                                    listener: endArguments(2, (, ))
+                              listener: handleSend(f2, ;)
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(2, {, })
+        listener: endTopLevelMethod(main, null, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(Object)
+  listener: endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.parser.expect
new file mode 100644
index 0000000..d1e654e
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.parser.expect
@@ -0,0 +1,15 @@
+Object bar<T1, T2>(T1 t1, T2 t2) => 42;
+
+main() {
+Function f2 = bar;
+f2!<int, String>(42, "42");
+}
+
+
+Object[StringToken] bar[StringToken]<[BeginToken]T1[StringToken],[SimpleToken] T2[StringToken]>[SimpleToken]([BeginToken]T1[StringToken] t1[StringToken],[SimpleToken] T2[StringToken] t2[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]![SimpleToken]<[BeginToken]int[StringToken],[SimpleToken] String[StringToken]>[SimpleToken]([BeginToken]42[StringToken],[SimpleToken] "42"[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.scanner.expect
new file mode 100644
index 0000000..d1e654e
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime3.dart.scanner.expect
@@ -0,0 +1,15 @@
+Object bar<T1, T2>(T1 t1, T2 t2) => 42;
+
+main() {
+Function f2 = bar;
+f2!<int, String>(42, "42");
+}
+
+
+Object[StringToken] bar[StringToken]<[BeginToken]T1[StringToken],[SimpleToken] T2[StringToken]>[SimpleToken]([BeginToken]T1[StringToken] t1[StringToken],[SimpleToken] T2[StringToken] t2[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]![SimpleToken]<[BeginToken]int[StringToken],[SimpleToken] String[StringToken]>[SimpleToken]([BeginToken]42[StringToken],[SimpleToken] "42"[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart
new file mode 100644
index 0000000..1bc0e9e
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart
@@ -0,0 +1,6 @@
+Object bar<T1, T2>(T1 t1, T2 t2) => 42;
+
+main() {
+  Function f2 = bar;
+  f2<int, String>(42, "42");
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.expect
new file mode 100644
index 0000000..d0773b0
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.expect
@@ -0,0 +1,96 @@
+beginCompilationUnit(Object)
+  beginMetadataStar(Object)
+  endMetadataStar(0)
+  beginTopLevelMember(Object)
+    beginTopLevelMethod(, null)
+      handleIdentifier(Object, typeReference)
+      handleNoTypeArguments(bar)
+      handleType(Object, null)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      beginTypeVariables(<)
+        beginMetadataStar(T1)
+        endMetadataStar(0)
+        handleIdentifier(T1, typeVariableDeclaration)
+        beginTypeVariable(T1)
+          beginMetadataStar(T2)
+          endMetadataStar(0)
+          handleIdentifier(T2, typeVariableDeclaration)
+          beginTypeVariable(T2)
+            handleTypeVariablesDefined(T2, 2)
+            handleNoType(T2)
+          endTypeVariable(>, 1, null, null)
+          handleNoType(T1)
+        endTypeVariable(,, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(T1)
+        endMetadataStar(0)
+        beginFormalParameter(T1, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T1, typeReference)
+          handleNoTypeArguments(t1)
+          handleType(T1, null)
+          handleIdentifier(t1, formalParameterDeclaration)
+          handleFormalParameterWithoutValue(,)
+        endFormalParameter(null, null, t1, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+        beginMetadataStar(T2)
+        endMetadataStar(0)
+        beginFormalParameter(T2, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(T2, typeReference)
+          handleNoTypeArguments(t2)
+          handleType(T2, null)
+          handleIdentifier(t2, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, t2, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(2, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(Object, null, ;)
+  endTopLevelDeclaration(main)
+  beginMetadataStar(main)
+  endMetadataStar(0)
+  beginTopLevelMember(main)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(main, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      beginBlockFunctionBody({)
+        beginMetadataStar(Function)
+        endMetadataStar(0)
+        handleIdentifier(Function, typeReference)
+        handleNoTypeArguments(f2)
+        handleType(Function, null)
+        beginVariablesDeclaration(f2, null, null)
+          handleIdentifier(f2, localVariableDeclaration)
+          beginInitializedIdentifier(f2)
+            beginVariableInitializer(=)
+              handleIdentifier(bar, expression)
+              handleNoTypeArguments(;)
+              handleNoArguments(;)
+              handleSend(bar, ;)
+            endVariableInitializer(=)
+          endInitializedIdentifier(f2)
+        endVariablesDeclaration(1, ;)
+        handleIdentifier(f2, expression)
+        beginTypeArguments(<)
+          handleIdentifier(int, typeReference)
+          handleNoTypeArguments(,)
+          handleType(int, null)
+          handleIdentifier(String, typeReference)
+          handleNoTypeArguments(>)
+          handleType(String, null)
+        endTypeArguments(2, <, >)
+        beginArguments(()
+          handleLiteralInt(42)
+          beginLiteralString("42")
+          endLiteralString(0, ))
+        endArguments(2, (, ))
+        handleSend(f2, ;)
+        handleExpressionStatement(;)
+      endBlockFunctionBody(2, {, })
+    endTopLevelMethod(main, null, })
+  endTopLevelDeclaration()
+endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.intertwined.expect
new file mode 100644
index 0000000..4e8a043
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.intertwined.expect
@@ -0,0 +1,191 @@
+parseUnit(Object)
+  skipErrorTokens(Object)
+  listener: beginCompilationUnit(Object)
+  syntheticPreviousToken(Object)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(Object)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(Object)
+      parseTopLevelMethod(, null, , Instance of 'SimpleType', null, bar)
+        listener: beginTopLevelMethod(, null)
+        listener: handleIdentifier(Object, typeReference)
+        listener: handleNoTypeArguments(bar)
+        listener: handleType(Object, null)
+        ensureIdentifier(Object, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          listener: beginTypeVariables(<)
+          parseMetadataStar(<)
+            listener: beginMetadataStar(T1)
+            listener: endMetadataStar(0)
+          ensureIdentifier(<, typeVariableDeclaration)
+            listener: handleIdentifier(T1, typeVariableDeclaration)
+          listener: beginTypeVariable(T1)
+          parseMetadataStar(,)
+            listener: beginMetadataStar(T2)
+            listener: endMetadataStar(0)
+          ensureIdentifier(,, typeVariableDeclaration)
+            listener: handleIdentifier(T2, typeVariableDeclaration)
+          listener: beginTypeVariable(T2)
+          listener: handleTypeVariablesDefined(T2, 2)
+          listener: handleNoType(T2)
+          listener: endTypeVariable(>, 1, null, null)
+          listener: handleNoType(T1)
+          listener: endTypeVariable(,, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(T1)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T1, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T1, typeReference)
+                listener: handleNoTypeArguments(t1)
+                listener: handleType(T1, null)
+                ensureIdentifier(T1, formalParameterDeclaration)
+                  listener: handleIdentifier(t1, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue(,)
+                listener: endFormalParameter(null, null, t1, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              parseFormalParameter(,, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(,)
+                  listener: beginMetadataStar(T2)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(T2, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(T2, typeReference)
+                listener: handleNoTypeArguments(t2)
+                listener: handleType(T2, null)
+                ensureIdentifier(T2, formalParameterDeclaration)
+                  listener: handleIdentifier(t2, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, t2, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(2, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(Object, null, ;)
+  listener: endTopLevelDeclaration(main)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(main)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(main)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, main)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(main, topLevelFunctionDeclaration)
+        parseMethodTypeVar(main)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(main, main, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(main, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          listener: beginBlockFunctionBody({)
+          notEofOrValue(}, Function)
+          parseStatement({)
+            parseStatementX({)
+              parseExpressionStatementOrDeclaration({, false)
+                parseExpressionStatementOrDeclarationAfterModifiers({, {, null, null, null, false)
+                  looksLikeLocalFunction(f2)
+                  listener: beginMetadataStar(Function)
+                  listener: endMetadataStar(0)
+                  listener: handleIdentifier(Function, typeReference)
+                  listener: handleNoTypeArguments(f2)
+                  listener: handleType(Function, null)
+                  listener: beginVariablesDeclaration(f2, null, null)
+                  parseVariablesDeclarationRest(Function, true)
+                    parseOptionallyInitializedIdentifier(Function)
+                      ensureIdentifier(Function, localVariableDeclaration)
+                        listener: handleIdentifier(f2, localVariableDeclaration)
+                      listener: beginInitializedIdentifier(f2)
+                      parseVariableInitializerOpt(f2)
+                        listener: beginVariableInitializer(=)
+                        parseExpression(=)
+                          parsePrecedenceExpression(=, 1, true)
+                            parseUnaryExpression(=, true)
+                              parsePrimary(=, expression)
+                                parseSendOrFunctionLiteral(=, expression)
+                                  parseSend(=, expression)
+                                    ensureIdentifier(=, expression)
+                                      listener: handleIdentifier(bar, expression)
+                                    listener: handleNoTypeArguments(;)
+                                    parseArgumentsOpt(bar)
+                                      listener: handleNoArguments(;)
+                                    listener: handleSend(bar, ;)
+                        listener: endVariableInitializer(=)
+                      listener: endInitializedIdentifier(f2)
+                    ensureSemicolon(bar)
+                    listener: endVariablesDeclaration(1, ;)
+          notEofOrValue(}, f2)
+          parseStatement(;)
+            parseStatementX(;)
+              parseExpressionStatementOrDeclarationAfterModifiers(;, ;, null, null, null, false)
+                looksLikeLocalFunction(f2)
+                parseExpressionStatement(;)
+                  parseExpression(;)
+                    parsePrecedenceExpression(;, 1, true)
+                      parseUnaryExpression(;, true)
+                        parsePrimary(;, expression)
+                          parseSendOrFunctionLiteral(;, expression)
+                            looksLikeFunctionBody(;)
+                            parseSend(;, expression)
+                              ensureIdentifier(;, expression)
+                                listener: handleIdentifier(f2, expression)
+                              listener: beginTypeArguments(<)
+                              listener: handleIdentifier(int, typeReference)
+                              listener: handleNoTypeArguments(,)
+                              listener: handleType(int, null)
+                              listener: handleIdentifier(String, typeReference)
+                              listener: handleNoTypeArguments(>)
+                              listener: handleType(String, null)
+                              listener: endTypeArguments(2, <, >)
+                              parseArgumentsOpt(>)
+                                parseArguments(>)
+                                  parseArgumentsRest(()
+                                    listener: beginArguments(()
+                                    parseExpression(()
+                                      parsePrecedenceExpression((, 1, true)
+                                        parseUnaryExpression((, true)
+                                          parsePrimary((, expression)
+                                            parseLiteralInt(()
+                                              listener: handleLiteralInt(42)
+                                    parseExpression(,)
+                                      parsePrecedenceExpression(,, 1, true)
+                                        parseUnaryExpression(,, true)
+                                          parsePrimary(,, expression)
+                                            parseLiteralString(,)
+                                              parseSingleLiteralString(,)
+                                                listener: beginLiteralString("42")
+                                                listener: endLiteralString(0, ))
+                                    listener: endArguments(2, (, ))
+                              listener: handleSend(f2, ;)
+                  ensureSemicolon())
+                  listener: handleExpressionStatement(;)
+          notEofOrValue(}, })
+          listener: endBlockFunctionBody(2, {, })
+        listener: endTopLevelMethod(main, null, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(Object)
+  listener: endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.parser.expect
new file mode 100644
index 0000000..562a360
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.parser.expect
@@ -0,0 +1,15 @@
+Object bar<T1, T2>(T1 t1, T2 t2) => 42;
+
+main() {
+Function f2 = bar;
+f2<int, String>(42, "42");
+}
+
+
+Object[StringToken] bar[StringToken]<[BeginToken]T1[StringToken],[SimpleToken] T2[StringToken]>[SimpleToken]([BeginToken]T1[StringToken] t1[StringToken],[SimpleToken] T2[StringToken] t2[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]<[BeginToken]int[StringToken],[SimpleToken] String[StringToken]>[SimpleToken]([BeginToken]42[StringToken],[SimpleToken] "42"[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.scanner.expect
new file mode 100644
index 0000000..562a360
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime4.dart.scanner.expect
@@ -0,0 +1,15 @@
+Object bar<T1, T2>(T1 t1, T2 t2) => 42;
+
+main() {
+Function f2 = bar;
+f2<int, String>(42, "42");
+}
+
+
+Object[StringToken] bar[StringToken]<[BeginToken]T1[StringToken],[SimpleToken] T2[StringToken]>[SimpleToken]([BeginToken]T1[StringToken] t1[StringToken],[SimpleToken] T2[StringToken] t2[StringToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+
+main[StringToken]([BeginToken])[SimpleToken] {[BeginToken]
+Function[KeywordToken] f2[StringToken] =[SimpleToken] bar[StringToken];[SimpleToken]
+f2[StringToken]<[BeginToken]int[StringToken],[SimpleToken] String[StringToken]>[SimpleToken]([BeginToken]42[StringToken],[SimpleToken] "42"[StringToken])[SimpleToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart
new file mode 100644
index 0000000..48f0bfd
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart
@@ -0,0 +1,2 @@
+foo!() => 42;
+bar!<T>() => 42;
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.expect
new file mode 100644
index 0000000..3804bfe
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.expect
@@ -0,0 +1,50 @@
+Problems reported:
+
+parser/nnbd/issue_39776_prime5:1:4: Unexpected token '!'.
+foo!() => 42;
+   ^
+
+parser/nnbd/issue_39776_prime5:2:4: Unexpected token '!'.
+bar!<T>() => 42;
+   ^
+
+beginCompilationUnit(foo)
+  beginMetadataStar(foo)
+  endMetadataStar(0)
+  beginTopLevelMember(foo)
+    beginTopLevelMethod(, null)
+      handleNoType()
+      handleIdentifier(foo, topLevelFunctionDeclaration)
+      handleRecoverableError(Message[UnexpectedToken, Unexpected token '!'., null, {token: !}], !, !)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(foo, null, ;)
+  endTopLevelDeclaration(bar)
+  beginMetadataStar(bar)
+  endMetadataStar(0)
+  beginTopLevelMember(bar)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      handleRecoverableError(Message[UnexpectedToken, Unexpected token '!'., null, {token: !}], !, !)
+      beginTypeVariables(<)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        handleIdentifier(T, typeVariableDeclaration)
+        beginTypeVariable(T)
+          handleTypeVariablesDefined(T, 1)
+          handleNoType(T)
+        endTypeVariable(>, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(bar, null, ;)
+  endTopLevelDeclaration()
+endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.intertwined.expect
new file mode 100644
index 0000000..80648cc
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.intertwined.expect
@@ -0,0 +1,86 @@
+parseUnit(foo)
+  skipErrorTokens(foo)
+  listener: beginCompilationUnit(foo)
+  syntheticPreviousToken(foo)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(foo)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(foo)
+      parseTopLevelMethod(, null, , Instance of 'NoType', null, foo)
+        listener: beginTopLevelMethod(, null)
+        listener: handleNoType()
+        ensureIdentifier(, topLevelFunctionDeclaration)
+          listener: handleIdentifier(foo, topLevelFunctionDeclaration)
+        parseMethodTypeVar(foo)
+          reportRecoverableErrorWithToken(!, Instance of 'Template<(Token) => Message>')
+            listener: handleRecoverableError(Message[UnexpectedToken, Unexpected token '!'., null, {token: !}], !, !)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(!, foo, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(!, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(foo, null, ;)
+  listener: endTopLevelDeclaration(bar)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(bar)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(bar)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, bar)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          reportRecoverableErrorWithToken(!, Instance of 'Template<(Token) => Message>')
+            listener: handleRecoverableError(Message[UnexpectedToken, Unexpected token '!'., null, {token: !}], !, !)
+          listener: beginTypeVariables(<)
+          listener: beginMetadataStar(T)
+          listener: endMetadataStar(0)
+          listener: handleIdentifier(T, typeVariableDeclaration)
+          listener: beginTypeVariable(T)
+          listener: handleTypeVariablesDefined(T, 1)
+          listener: handleNoType(T)
+          listener: endTypeVariable(>, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(bar, null, ;)
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(foo)
+  listener: endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.parser.expect
new file mode 100644
index 0000000..8eb1e98
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.parser.expect
@@ -0,0 +1,7 @@
+foo!() => 42;
+bar!<T>() => 42;
+
+
+foo[StringToken]![SimpleToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+bar[StringToken]![SimpleToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.scanner.expect
new file mode 100644
index 0000000..8eb1e98
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime5.dart.scanner.expect
@@ -0,0 +1,7 @@
+foo!() => 42;
+bar!<T>() => 42;
+
+
+foo[StringToken]![SimpleToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+bar[StringToken]![SimpleToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart
new file mode 100644
index 0000000..31e50bf
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart
@@ -0,0 +1,2 @@
+foo() => 42;
+bar<T>() => 42;
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.expect
new file mode 100644
index 0000000..f0bd214
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.expect
@@ -0,0 +1,38 @@
+beginCompilationUnit(foo)
+  beginMetadataStar(foo)
+  endMetadataStar(0)
+  beginTopLevelMember(foo)
+    beginTopLevelMethod(, null)
+      handleNoType()
+      handleIdentifier(foo, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(foo, null, ;)
+  endTopLevelDeclaration(bar)
+  beginMetadataStar(bar)
+  endMetadataStar(0)
+  beginTopLevelMember(bar)
+    beginTopLevelMethod(;, null)
+      handleNoType(;)
+      handleIdentifier(bar, topLevelFunctionDeclaration)
+      beginTypeVariables(<)
+        beginMetadataStar(T)
+        endMetadataStar(0)
+        handleIdentifier(T, typeVariableDeclaration)
+        beginTypeVariable(T)
+          handleTypeVariablesDefined(T, 1)
+          handleNoType(T)
+        endTypeVariable(>, 0, null, null)
+      endTypeVariables(<, >)
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+      endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleLiteralInt(42)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(bar, null, ;)
+  endTopLevelDeclaration()
+endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.intertwined.expect
new file mode 100644
index 0000000..ecc48b8
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.intertwined.expect
@@ -0,0 +1,82 @@
+parseUnit(foo)
+  skipErrorTokens(foo)
+  listener: beginCompilationUnit(foo)
+  syntheticPreviousToken(foo)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(foo)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl()
+      listener: beginTopLevelMember(foo)
+      parseTopLevelMethod(, null, , Instance of 'NoType', null, foo)
+        listener: beginTopLevelMethod(, null)
+        listener: handleNoType()
+        ensureIdentifier(, topLevelFunctionDeclaration)
+          listener: handleIdentifier(foo, topLevelFunctionDeclaration)
+        parseMethodTypeVar(foo)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(foo, foo, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(foo, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(foo, null, ;)
+  listener: endTopLevelDeclaration(bar)
+  parseTopLevelDeclarationImpl(;, Instance of 'DirectiveContext')
+    parseMetadataStar(;)
+      listener: beginMetadataStar(bar)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(;)
+      listener: beginTopLevelMember(bar)
+      parseTopLevelMethod(;, null, ;, Instance of 'NoType', null, bar)
+        listener: beginTopLevelMethod(;, null)
+        listener: handleNoType(;)
+        ensureIdentifier(;, topLevelFunctionDeclaration)
+          listener: handleIdentifier(bar, topLevelFunctionDeclaration)
+        parseMethodTypeVar(bar)
+          listener: beginTypeVariables(<)
+          listener: beginMetadataStar(T)
+          listener: endMetadataStar(0)
+          listener: handleIdentifier(T, typeVariableDeclaration)
+          listener: beginTypeVariable(T)
+          listener: handleTypeVariablesDefined(T, 1)
+          listener: handleNoType(T)
+          listener: endTypeVariable(>, 0, null, null)
+          listener: endTypeVariables(<, >)
+        parseGetterOrFormalParameters(>, bar, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(>, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(0, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseLiteralInt(=>)
+                      listener: handleLiteralInt(42)
+            ensureSemicolon(42)
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(bar, null, ;)
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(foo)
+  listener: endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.parser.expect
new file mode 100644
index 0000000..0cb7627
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.parser.expect
@@ -0,0 +1,7 @@
+foo() => 42;
+bar<T>() => 42;
+
+
+foo[StringToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.scanner.expect
new file mode 100644
index 0000000..0cb7627
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39776_prime6.dart.scanner.expect
@@ -0,0 +1,7 @@
+foo() => 42;
+bar<T>() => 42;
+
+
+foo[StringToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+bar[StringToken]<[BeginToken]T[StringToken]>[SimpleToken]([BeginToken])[SimpleToken] =>[SimpleToken] 42[StringToken];[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858.dart b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart
new file mode 100644
index 0000000..66a84ff
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart
@@ -0,0 +1,8 @@
+class X {
+  late x1;
+  static late x2;
+  covariant late x3;
+  late x4 = 0;
+  static late x5 = 0;
+  covariant late x6 = 0;
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.expect
new file mode 100644
index 0000000..65df0a7
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.expect
@@ -0,0 +1,103 @@
+Problems reported:
+
+parser/nnbd/issue_39858:2:8: Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
+  late x1;
+       ^^
+
+parser/nnbd/issue_39858:3:15: Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
+  static late x2;
+              ^^
+
+parser/nnbd/issue_39858:4:18: Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
+  covariant late x3;
+                 ^^
+
+parser/nnbd/issue_39858:5:8: Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
+  late x4 = 0;
+       ^^
+
+parser/nnbd/issue_39858:6:15: Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
+  static late x5 = 0;
+              ^^
+
+parser/nnbd/issue_39858:7:18: Variables must be declared using the keywords 'const', 'final', 'var' or a type name.
+  covariant late x6 = 0;
+                 ^^
+
+beginCompilationUnit(class)
+  beginMetadataStar(class)
+  endMetadataStar(0)
+  beginClassOrNamedMixinApplicationPrelude(class)
+    handleIdentifier(X, classOrMixinDeclaration)
+    handleNoTypeVariables({)
+    beginClassDeclaration(class, null, X)
+      handleNoType(X)
+      handleClassExtends(null)
+      handleClassNoWithClause()
+      handleClassOrMixinImplements(null, 0)
+      handleClassHeader(class, class, null)
+      beginClassOrMixinBody(DeclarationKind.Class, {)
+        beginMetadataStar(late)
+        endMetadataStar(0)
+        beginMember()
+          handleRecoverableError(MissingConstFinalVarOrType, x1, x1)
+          handleNoType(late)
+          handleIdentifier(x1, fieldDeclaration)
+          handleNoFieldInitializer(;)
+        endClassFields(null, null, late, null, 1, late, ;)
+      endMember()
+      beginMetadataStar(static)
+      endMetadataStar(0)
+      beginMember()
+        handleRecoverableError(MissingConstFinalVarOrType, x2, x2)
+        handleNoType(late)
+        handleIdentifier(x2, fieldDeclaration)
+        handleNoFieldInitializer(;)
+      endClassFields(static, null, late, null, 1, static, ;)
+    endMember()
+    beginMetadataStar(covariant)
+    endMetadataStar(0)
+    beginMember()
+      handleRecoverableError(MissingConstFinalVarOrType, x3, x3)
+      handleNoType(late)
+      handleIdentifier(x3, fieldDeclaration)
+      handleNoFieldInitializer(;)
+    endClassFields(null, covariant, late, null, 1, covariant, ;)
+  endMember()
+  beginMetadataStar(late)
+  endMetadataStar(0)
+  beginMember()
+    handleRecoverableError(MissingConstFinalVarOrType, x4, x4)
+    handleNoType(late)
+    handleIdentifier(x4, fieldDeclaration)
+    beginFieldInitializer(=)
+      handleLiteralInt(0)
+    endFieldInitializer(=, ;)
+  endClassFields(null, null, late, null, 1, late, ;)
+endMember()
+beginMetadataStar(static)
+endMetadataStar(0)
+beginMember()
+  handleRecoverableError(MissingConstFinalVarOrType, x5, x5)
+  handleNoType(late)
+  handleIdentifier(x5, fieldDeclaration)
+  beginFieldInitializer(=)
+    handleLiteralInt(0)
+  endFieldInitializer(=, ;)
+endClassFields(static, null, late, null, 1, static, ;)
+endMember()
+beginMetadataStar(covariant)
+endMetadataStar(0)
+beginMember()
+handleRecoverableError(MissingConstFinalVarOrType, x6, x6)
+handleNoType(late)
+handleIdentifier(x6, fieldDeclaration)
+beginFieldInitializer(=)
+  handleLiteralInt(0)
+endFieldInitializer(=, ;)
+endClassFields(null, covariant, late, null, 1, covariant, ;)
+endMember()
+endClassOrMixinBody(DeclarationKind.Class, 6, {, })
+endClassDeclaration(class, })
+endTopLevelDeclaration()
+endCompilationUnit(1, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.intertwined.expect
new file mode 100644
index 0000000..ac34652
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.intertwined.expect
@@ -0,0 +1,151 @@
+parseUnit(class)
+  skipErrorTokens(class)
+  listener: beginCompilationUnit(class)
+  syntheticPreviousToken(class)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(class)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(, class, Instance of 'DirectiveContext')
+      parseClassDeclarationModifiers(, class)
+      parseClassOrNamedMixinApplication(null, class)
+        listener: beginClassOrNamedMixinApplicationPrelude(class)
+        ensureIdentifier(class, classOrMixinDeclaration)
+          listener: handleIdentifier(X, classOrMixinDeclaration)
+        listener: handleNoTypeVariables({)
+        listener: beginClassDeclaration(class, null, X)
+        parseClass(X, class, class, X)
+          parseClassHeaderOpt(X, class, class)
+            parseClassExtendsOpt(X)
+              listener: handleNoType(X)
+              listener: handleClassExtends(null)
+            parseWithClauseOpt(X)
+              listener: handleClassNoWithClause()
+            parseClassOrMixinImplementsOpt(X)
+              listener: handleClassOrMixinImplements(null, 0)
+            listener: handleClassHeader(class, class, null)
+          parseClassOrMixinOrExtensionBody(X, DeclarationKind.Class, X)
+            listener: beginClassOrMixinBody(DeclarationKind.Class, {)
+            notEofOrValue(}, late)
+            parseClassOrMixinOrExtensionMemberImpl({, DeclarationKind.Class, X)
+              parseMetadataStar({)
+                listener: beginMetadataStar(late)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields({, null, null, null, late, null, late, Instance of 'NoType', x1, DeclarationKind.Class)
+                reportRecoverableError(x1, MissingConstFinalVarOrType)
+                  listener: handleRecoverableError(MissingConstFinalVarOrType, x1, x1)
+                listener: handleNoType(late)
+                ensureIdentifier(late, fieldDeclaration)
+                  listener: handleIdentifier(x1, fieldDeclaration)
+                parseFieldInitializerOpt(x1, x1, late, null, DeclarationKind.Class)
+                  listener: handleNoFieldInitializer(;)
+                listener: endClassFields(null, null, late, null, 1, late, ;)
+              listener: endMember()
+            notEofOrValue(}, static)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(static)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, static, null, late, null, late, Instance of 'NoType', x2, DeclarationKind.Class)
+                reportRecoverableError(x2, MissingConstFinalVarOrType)
+                  listener: handleRecoverableError(MissingConstFinalVarOrType, x2, x2)
+                listener: handleNoType(late)
+                ensureIdentifier(late, fieldDeclaration)
+                  listener: handleIdentifier(x2, fieldDeclaration)
+                parseFieldInitializerOpt(x2, x2, late, null, DeclarationKind.Class)
+                  listener: handleNoFieldInitializer(;)
+                listener: endClassFields(static, null, late, null, 1, static, ;)
+              listener: endMember()
+            notEofOrValue(}, covariant)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(covariant)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, null, covariant, late, null, late, Instance of 'NoType', x3, DeclarationKind.Class)
+                reportRecoverableError(x3, MissingConstFinalVarOrType)
+                  listener: handleRecoverableError(MissingConstFinalVarOrType, x3, x3)
+                listener: handleNoType(late)
+                ensureIdentifier(late, fieldDeclaration)
+                  listener: handleIdentifier(x3, fieldDeclaration)
+                parseFieldInitializerOpt(x3, x3, late, null, DeclarationKind.Class)
+                  listener: handleNoFieldInitializer(;)
+                listener: endClassFields(null, covariant, late, null, 1, covariant, ;)
+              listener: endMember()
+            notEofOrValue(}, late)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(late)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, null, null, late, null, late, Instance of 'NoType', x4, DeclarationKind.Class)
+                reportRecoverableError(x4, MissingConstFinalVarOrType)
+                  listener: handleRecoverableError(MissingConstFinalVarOrType, x4, x4)
+                listener: handleNoType(late)
+                ensureIdentifier(late, fieldDeclaration)
+                  listener: handleIdentifier(x4, fieldDeclaration)
+                parseFieldInitializerOpt(x4, x4, late, null, DeclarationKind.Class)
+                  listener: beginFieldInitializer(=)
+                  parseExpression(=)
+                    parsePrecedenceExpression(=, 1, true)
+                      parseUnaryExpression(=, true)
+                        parsePrimary(=, expression)
+                          parseLiteralInt(=)
+                            listener: handleLiteralInt(0)
+                  listener: endFieldInitializer(=, ;)
+                listener: endClassFields(null, null, late, null, 1, late, ;)
+              listener: endMember()
+            notEofOrValue(}, static)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(static)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, static, null, late, null, late, Instance of 'NoType', x5, DeclarationKind.Class)
+                reportRecoverableError(x5, MissingConstFinalVarOrType)
+                  listener: handleRecoverableError(MissingConstFinalVarOrType, x5, x5)
+                listener: handleNoType(late)
+                ensureIdentifier(late, fieldDeclaration)
+                  listener: handleIdentifier(x5, fieldDeclaration)
+                parseFieldInitializerOpt(x5, x5, late, null, DeclarationKind.Class)
+                  listener: beginFieldInitializer(=)
+                  parseExpression(=)
+                    parsePrecedenceExpression(=, 1, true)
+                      parseUnaryExpression(=, true)
+                        parsePrimary(=, expression)
+                          parseLiteralInt(=)
+                            listener: handleLiteralInt(0)
+                  listener: endFieldInitializer(=, ;)
+                listener: endClassFields(static, null, late, null, 1, static, ;)
+              listener: endMember()
+            notEofOrValue(}, covariant)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(covariant)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, null, covariant, late, null, late, Instance of 'NoType', x6, DeclarationKind.Class)
+                reportRecoverableError(x6, MissingConstFinalVarOrType)
+                  listener: handleRecoverableError(MissingConstFinalVarOrType, x6, x6)
+                listener: handleNoType(late)
+                ensureIdentifier(late, fieldDeclaration)
+                  listener: handleIdentifier(x6, fieldDeclaration)
+                parseFieldInitializerOpt(x6, x6, late, null, DeclarationKind.Class)
+                  listener: beginFieldInitializer(=)
+                  parseExpression(=)
+                    parsePrecedenceExpression(=, 1, true)
+                      parseUnaryExpression(=, true)
+                        parsePrimary(=, expression)
+                          parseLiteralInt(=)
+                            listener: handleLiteralInt(0)
+                  listener: endFieldInitializer(=, ;)
+                listener: endClassFields(null, covariant, late, null, 1, covariant, ;)
+              listener: endMember()
+            notEofOrValue(}, })
+            listener: endClassOrMixinBody(DeclarationKind.Class, 6, {, })
+          listener: endClassDeclaration(class, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(class)
+  listener: endCompilationUnit(1, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.parser.expect
new file mode 100644
index 0000000..13080f9
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.parser.expect
@@ -0,0 +1,19 @@
+class X {
+late x1;
+static late x2;
+covariant late x3;
+late x4 = 0;
+static late x5 = 0;
+covariant late x6 = 0;
+}
+
+
+class[KeywordToken] X[StringToken] {[BeginToken]
+late[KeywordToken] x1[StringToken];[SimpleToken]
+static[KeywordToken] late[KeywordToken] x2[StringToken];[SimpleToken]
+covariant[KeywordToken] late[KeywordToken] x3[StringToken];[SimpleToken]
+late[KeywordToken] x4[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+static[KeywordToken] late[KeywordToken] x5[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+covariant[KeywordToken] late[KeywordToken] x6[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.scanner.expect
new file mode 100644
index 0000000..13080f9
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858.dart.scanner.expect
@@ -0,0 +1,19 @@
+class X {
+late x1;
+static late x2;
+covariant late x3;
+late x4 = 0;
+static late x5 = 0;
+covariant late x6 = 0;
+}
+
+
+class[KeywordToken] X[StringToken] {[BeginToken]
+late[KeywordToken] x1[StringToken];[SimpleToken]
+static[KeywordToken] late[KeywordToken] x2[StringToken];[SimpleToken]
+covariant[KeywordToken] late[KeywordToken] x3[StringToken];[SimpleToken]
+late[KeywordToken] x4[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+static[KeywordToken] late[KeywordToken] x5[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+covariant[KeywordToken] late[KeywordToken] x6[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart
new file mode 100644
index 0000000..a4672b8
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart
@@ -0,0 +1,8 @@
+class X {
+  var x1;
+  static var x2;
+  covariant var x3;
+  var x4 = 0;
+  static var x5 = 0;
+  covariant var x6 = 0;
+}
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.expect
new file mode 100644
index 0000000..d41f4e7
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.expect
@@ -0,0 +1,71 @@
+beginCompilationUnit(class)
+  beginMetadataStar(class)
+  endMetadataStar(0)
+  beginClassOrNamedMixinApplicationPrelude(class)
+    handleIdentifier(X, classOrMixinDeclaration)
+    handleNoTypeVariables({)
+    beginClassDeclaration(class, null, X)
+      handleNoType(X)
+      handleClassExtends(null)
+      handleClassNoWithClause()
+      handleClassOrMixinImplements(null, 0)
+      handleClassHeader(class, class, null)
+      beginClassOrMixinBody(DeclarationKind.Class, {)
+        beginMetadataStar(var)
+        endMetadataStar(0)
+        beginMember()
+          handleNoType(var)
+          handleIdentifier(x1, fieldDeclaration)
+          handleNoFieldInitializer(;)
+        endClassFields(null, null, null, var, 1, var, ;)
+      endMember()
+      beginMetadataStar(static)
+      endMetadataStar(0)
+      beginMember()
+        handleNoType(var)
+        handleIdentifier(x2, fieldDeclaration)
+        handleNoFieldInitializer(;)
+      endClassFields(static, null, null, var, 1, static, ;)
+    endMember()
+    beginMetadataStar(covariant)
+    endMetadataStar(0)
+    beginMember()
+      handleNoType(var)
+      handleIdentifier(x3, fieldDeclaration)
+      handleNoFieldInitializer(;)
+    endClassFields(null, covariant, null, var, 1, covariant, ;)
+  endMember()
+  beginMetadataStar(var)
+  endMetadataStar(0)
+  beginMember()
+    handleNoType(var)
+    handleIdentifier(x4, fieldDeclaration)
+    beginFieldInitializer(=)
+      handleLiteralInt(0)
+    endFieldInitializer(=, ;)
+  endClassFields(null, null, null, var, 1, var, ;)
+endMember()
+beginMetadataStar(static)
+endMetadataStar(0)
+beginMember()
+  handleNoType(var)
+  handleIdentifier(x5, fieldDeclaration)
+  beginFieldInitializer(=)
+    handleLiteralInt(0)
+  endFieldInitializer(=, ;)
+endClassFields(static, null, null, var, 1, static, ;)
+endMember()
+beginMetadataStar(covariant)
+endMetadataStar(0)
+beginMember()
+handleNoType(var)
+handleIdentifier(x6, fieldDeclaration)
+beginFieldInitializer(=)
+  handleLiteralInt(0)
+endFieldInitializer(=, ;)
+endClassFields(null, covariant, null, var, 1, covariant, ;)
+endMember()
+endClassOrMixinBody(DeclarationKind.Class, 6, {, })
+endClassDeclaration(class, })
+endTopLevelDeclaration()
+endCompilationUnit(1, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.intertwined.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.intertwined.expect
new file mode 100644
index 0000000..f0ecb2d
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.intertwined.expect
@@ -0,0 +1,139 @@
+parseUnit(class)
+  skipErrorTokens(class)
+  listener: beginCompilationUnit(class)
+  syntheticPreviousToken(class)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(class)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(, class, Instance of 'DirectiveContext')
+      parseClassDeclarationModifiers(, class)
+      parseClassOrNamedMixinApplication(null, class)
+        listener: beginClassOrNamedMixinApplicationPrelude(class)
+        ensureIdentifier(class, classOrMixinDeclaration)
+          listener: handleIdentifier(X, classOrMixinDeclaration)
+        listener: handleNoTypeVariables({)
+        listener: beginClassDeclaration(class, null, X)
+        parseClass(X, class, class, X)
+          parseClassHeaderOpt(X, class, class)
+            parseClassExtendsOpt(X)
+              listener: handleNoType(X)
+              listener: handleClassExtends(null)
+            parseWithClauseOpt(X)
+              listener: handleClassNoWithClause()
+            parseClassOrMixinImplementsOpt(X)
+              listener: handleClassOrMixinImplements(null, 0)
+            listener: handleClassHeader(class, class, null)
+          parseClassOrMixinOrExtensionBody(X, DeclarationKind.Class, X)
+            listener: beginClassOrMixinBody(DeclarationKind.Class, {)
+            notEofOrValue(}, var)
+            parseClassOrMixinOrExtensionMemberImpl({, DeclarationKind.Class, X)
+              parseMetadataStar({)
+                listener: beginMetadataStar(var)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields({, null, null, null, null, var, var, Instance of 'NoType', x1, DeclarationKind.Class)
+                listener: handleNoType(var)
+                ensureIdentifier(var, fieldDeclaration)
+                  listener: handleIdentifier(x1, fieldDeclaration)
+                parseFieldInitializerOpt(x1, x1, null, var, DeclarationKind.Class)
+                  listener: handleNoFieldInitializer(;)
+                listener: endClassFields(null, null, null, var, 1, var, ;)
+              listener: endMember()
+            notEofOrValue(}, static)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(static)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, static, null, null, var, var, Instance of 'NoType', x2, DeclarationKind.Class)
+                listener: handleNoType(var)
+                ensureIdentifier(var, fieldDeclaration)
+                  listener: handleIdentifier(x2, fieldDeclaration)
+                parseFieldInitializerOpt(x2, x2, null, var, DeclarationKind.Class)
+                  listener: handleNoFieldInitializer(;)
+                listener: endClassFields(static, null, null, var, 1, static, ;)
+              listener: endMember()
+            notEofOrValue(}, covariant)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(covariant)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, null, covariant, null, var, var, Instance of 'NoType', x3, DeclarationKind.Class)
+                listener: handleNoType(var)
+                ensureIdentifier(var, fieldDeclaration)
+                  listener: handleIdentifier(x3, fieldDeclaration)
+                parseFieldInitializerOpt(x3, x3, null, var, DeclarationKind.Class)
+                  listener: handleNoFieldInitializer(;)
+                listener: endClassFields(null, covariant, null, var, 1, covariant, ;)
+              listener: endMember()
+            notEofOrValue(}, var)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(var)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, null, null, null, var, var, Instance of 'NoType', x4, DeclarationKind.Class)
+                listener: handleNoType(var)
+                ensureIdentifier(var, fieldDeclaration)
+                  listener: handleIdentifier(x4, fieldDeclaration)
+                parseFieldInitializerOpt(x4, x4, null, var, DeclarationKind.Class)
+                  listener: beginFieldInitializer(=)
+                  parseExpression(=)
+                    parsePrecedenceExpression(=, 1, true)
+                      parseUnaryExpression(=, true)
+                        parsePrimary(=, expression)
+                          parseLiteralInt(=)
+                            listener: handleLiteralInt(0)
+                  listener: endFieldInitializer(=, ;)
+                listener: endClassFields(null, null, null, var, 1, var, ;)
+              listener: endMember()
+            notEofOrValue(}, static)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(static)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, static, null, null, var, var, Instance of 'NoType', x5, DeclarationKind.Class)
+                listener: handleNoType(var)
+                ensureIdentifier(var, fieldDeclaration)
+                  listener: handleIdentifier(x5, fieldDeclaration)
+                parseFieldInitializerOpt(x5, x5, null, var, DeclarationKind.Class)
+                  listener: beginFieldInitializer(=)
+                  parseExpression(=)
+                    parsePrecedenceExpression(=, 1, true)
+                      parseUnaryExpression(=, true)
+                        parsePrimary(=, expression)
+                          parseLiteralInt(=)
+                            listener: handleLiteralInt(0)
+                  listener: endFieldInitializer(=, ;)
+                listener: endClassFields(static, null, null, var, 1, static, ;)
+              listener: endMember()
+            notEofOrValue(}, covariant)
+            parseClassOrMixinOrExtensionMemberImpl(;, DeclarationKind.Class, X)
+              parseMetadataStar(;)
+                listener: beginMetadataStar(covariant)
+                listener: endMetadataStar(0)
+              listener: beginMember()
+              parseFields(;, null, null, covariant, null, var, var, Instance of 'NoType', x6, DeclarationKind.Class)
+                listener: handleNoType(var)
+                ensureIdentifier(var, fieldDeclaration)
+                  listener: handleIdentifier(x6, fieldDeclaration)
+                parseFieldInitializerOpt(x6, x6, null, var, DeclarationKind.Class)
+                  listener: beginFieldInitializer(=)
+                  parseExpression(=)
+                    parsePrecedenceExpression(=, 1, true)
+                      parseUnaryExpression(=, true)
+                        parsePrimary(=, expression)
+                          parseLiteralInt(=)
+                            listener: handleLiteralInt(0)
+                  listener: endFieldInitializer(=, ;)
+                listener: endClassFields(null, covariant, null, var, 1, covariant, ;)
+              listener: endMember()
+            notEofOrValue(}, })
+            listener: endClassOrMixinBody(DeclarationKind.Class, 6, {, })
+          listener: endClassDeclaration(class, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(class)
+  listener: endCompilationUnit(1, )
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.parser.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.parser.expect
new file mode 100644
index 0000000..e774ae5
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.parser.expect
@@ -0,0 +1,19 @@
+class X {
+var x1;
+static var x2;
+covariant var x3;
+var x4 = 0;
+static var x5 = 0;
+covariant var x6 = 0;
+}
+
+
+class[KeywordToken] X[StringToken] {[BeginToken]
+var[KeywordToken] x1[StringToken];[SimpleToken]
+static[KeywordToken] var[KeywordToken] x2[StringToken];[SimpleToken]
+covariant[KeywordToken] var[KeywordToken] x3[StringToken];[SimpleToken]
+var[KeywordToken] x4[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+static[KeywordToken] var[KeywordToken] x5[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+covariant[KeywordToken] var[KeywordToken] x6[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.scanner.expect b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.scanner.expect
new file mode 100644
index 0000000..e774ae5
--- /dev/null
+++ b/pkg/front_end/parser_testcases/nnbd/issue_39858_prime1.dart.scanner.expect
@@ -0,0 +1,19 @@
+class X {
+var x1;
+static var x2;
+covariant var x3;
+var x4 = 0;
+static var x5 = 0;
+covariant var x6 = 0;
+}
+
+
+class[KeywordToken] X[StringToken] {[BeginToken]
+var[KeywordToken] x1[StringToken];[SimpleToken]
+static[KeywordToken] var[KeywordToken] x2[StringToken];[SimpleToken]
+covariant[KeywordToken] var[KeywordToken] x3[StringToken];[SimpleToken]
+var[KeywordToken] x4[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+static[KeywordToken] var[KeywordToken] x5[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+covariant[KeywordToken] var[KeywordToken] x6[StringToken] =[SimpleToken] 0[StringToken];[SimpleToken]
+}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart
new file mode 100644
index 0000000..439fe5f
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, 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.
+
+void foo({required String s}) {}
+void Function({required String s}) g;
+
+class A {
+  A({required int x});
+  foo({required int y}) {}
+  void Function({required String s}) f;
+}
+
+bar() {
+  foo();
+  new A();
+  var a = new A(x: 42);
+  a.foo();
+  a.f();
+  g();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.outline.expect b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.outline.expect
new file mode 100644
index 0000000..18f2365
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.outline.expect
@@ -0,0 +1,18 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  field ({required s: core::String}) → void f;
+  constructor •({required core::int x}) → self::A
+    ;
+  method foo({required core::int y}) → dynamic
+    ;
+}
+static field ({required s: core::String}) → void g;
+static method foo({required core::String s}) → void
+  ;
+static method bar() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.strong.expect b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.strong.expect
new file mode 100644
index 0000000..f8923db
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.strong.expect
@@ -0,0 +1,59 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:15:6: Error: Required named parameter 's' must be provided.
+//   foo();
+//      ^
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:5:6: Context: Found this candidate, but the arguments don't match.
+// void foo({required String s}) {}
+//      ^^^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:16:8: Error: Required named parameter 'x' must be provided.
+//   new A();
+//        ^
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:9:3: Context: Found this candidate, but the arguments don't match.
+//   A({required int x});
+//   ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:18:8: Error: Required named parameter 'y' must be provided.
+//   a.foo();
+//        ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:19:6: Error: Required named parameter 's' must be provided.
+//   a.f();
+//      ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:20:4: Error: Required named parameter 's' must be provided.
+//   g();
+//    ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  field ({required s: core::String}) → void f = null;
+  constructor •({required core::int x = #C1}) → self::A
+    : super core::Object::•()
+    ;
+  method foo({required core::int y = #C1}) → dynamic {}
+}
+static field ({required s: core::String}) → void g;
+static method foo({required core::String s = #C1}) → void {}
+static method bar() → dynamic {
+  invalid-expression "pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:15:6: Error: Required named parameter 's' must be provided.
+  foo();
+     ^";
+  invalid-expression "pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:16:8: Error: Required named parameter 'x' must be provided.
+  new A();
+       ^";
+  self::A a = new self::A::•(x: 42);
+  a.{self::A::foo}();
+  a.{self::A::f}();
+  self::g.call();
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.strong.transformed.expect
new file mode 100644
index 0000000..f8923db
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.strong.transformed.expect
@@ -0,0 +1,59 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:15:6: Error: Required named parameter 's' must be provided.
+//   foo();
+//      ^
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:5:6: Context: Found this candidate, but the arguments don't match.
+// void foo({required String s}) {}
+//      ^^^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:16:8: Error: Required named parameter 'x' must be provided.
+//   new A();
+//        ^
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:9:3: Context: Found this candidate, but the arguments don't match.
+//   A({required int x});
+//   ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:18:8: Error: Required named parameter 'y' must be provided.
+//   a.foo();
+//        ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:19:6: Error: Required named parameter 's' must be provided.
+//   a.f();
+//      ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:20:4: Error: Required named parameter 's' must be provided.
+//   g();
+//    ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  field ({required s: core::String}) → void f = null;
+  constructor •({required core::int x = #C1}) → self::A
+    : super core::Object::•()
+    ;
+  method foo({required core::int y = #C1}) → dynamic {}
+}
+static field ({required s: core::String}) → void g;
+static method foo({required core::String s = #C1}) → void {}
+static method bar() → dynamic {
+  invalid-expression "pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:15:6: Error: Required named parameter 's' must be provided.
+  foo();
+     ^";
+  invalid-expression "pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:16:8: Error: Required named parameter 'x' must be provided.
+  new A();
+       ^";
+  self::A a = new self::A::•(x: 42);
+  a.{self::A::foo}();
+  a.{self::A::f}();
+  self::g.call();
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.weak.expect b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.weak.expect
new file mode 100644
index 0000000..b6243a6
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.weak.expect
@@ -0,0 +1,49 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:15:6: Warning: Missing required named parameter 's'.
+//   foo();
+//      ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:16:8: Warning: Missing required named parameter 'x'.
+//   new A();
+//        ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:18:8: Warning: Missing required named parameter 'y'.
+//   a.foo();
+//        ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:19:6: Warning: Missing required named parameter 's'.
+//   a.f();
+//      ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:20:4: Warning: Missing required named parameter 's'.
+//   g();
+//    ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  field ({required s: core::String}) → void f = null;
+  constructor •({required core::int x = #C1}) → self::A
+    : super core::Object::•()
+    ;
+  method foo({required core::int y = #C1}) → dynamic {}
+}
+static field ({required s: core::String}) → void g;
+static method foo({required core::String s = #C1}) → void {}
+static method bar() → dynamic {
+  self::foo();
+  new self::A::•();
+  self::A a = new self::A::•(x: 42);
+  a.{self::A::foo}();
+  a.{self::A::f}();
+  self::g.call();
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.weak.transformed.expect
new file mode 100644
index 0000000..b6243a6
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart.weak.transformed.expect
@@ -0,0 +1,49 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:15:6: Warning: Missing required named parameter 's'.
+//   foo();
+//      ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:16:8: Warning: Missing required named parameter 'x'.
+//   new A();
+//        ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:18:8: Warning: Missing required named parameter 'y'.
+//   a.foo();
+//        ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:19:6: Warning: Missing required named parameter 's'.
+//   a.f();
+//      ^
+//
+// pkg/front_end/testcases/nnbd/missing_required_named_parameter.dart:20:4: Warning: Missing required named parameter 's'.
+//   g();
+//    ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  field ({required s: core::String}) → void f = null;
+  constructor •({required core::int x = #C1}) → self::A
+    : super core::Object::•()
+    ;
+  method foo({required core::int y = #C1}) → dynamic {}
+}
+static field ({required s: core::String}) → void g;
+static method foo({required core::String s = #C1}) → void {}
+static method bar() → dynamic {
+  self::foo();
+  new self::A::•();
+  self::A a = new self::A::•(x: 42);
+  a.{self::A::foo}();
+  a.{self::A::f}();
+  self::g.call();
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index eca92fb..d96b9d1 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -940,6 +940,7 @@
 nnbd/late: TextSerializationFailure
 nnbd/messages_with_types_opt_in: TypeCheckError
 nnbd/messages_with_types_opt_out: TypeCheckError
+nnbd/missing_required_named_parameter: TextSerializationFailure
 nnbd/no_null_shorting: TextSerializationFailure
 nnbd/no_null_shorting_explicit_extension: TextSerializationFailure
 nnbd/no_null_shorting_extension: TextSerializationFailure
diff --git a/pkg/front_end/tool/perf.dart b/pkg/front_end/tool/perf.dart
index 6c82961..2c6fd5b 100644
--- a/pkg/front_end/tool/perf.dart
+++ b/pkg/front_end/tool/perf.dart
@@ -198,8 +198,8 @@
 /// sources.
 Future setup(Uri entryUri) async {
   var provider = PhysicalResourceProvider.INSTANCE;
-  var packageMap = new ContextBuilder(provider, null, null)
-      .convertPackagesToMap(await findPackages(entryUri));
+  var packageMap = ContextBuilder.convertPackagesToMap(
+      provider, await findPackages(entryUri));
   sources = new SourceFactory([
     new ResourceUriResolver(provider),
     new PackageMapUriResolver(provider, packageMap),
diff --git a/pkg/nnbd_migration/lib/instrumentation.dart b/pkg/nnbd_migration/lib/instrumentation.dart
index 5afa1c3..dd0a250 100644
--- a/pkg/nnbd_migration/lib/instrumentation.dart
+++ b/pkg/nnbd_migration/lib/instrumentation.dart
@@ -143,6 +143,7 @@
   isCheckComponentType,
   isCheckMainType,
   literal,
+  listLengthConstructor,
   namedParameterNotSupplied,
   nonNullableBoolType,
   nonNullableObjectSuperclass,
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index 019900d..0e7e047 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -95,10 +95,14 @@
   /// [FixBuilder] infrastructure.  Once FixBuilder is at feature parity with
   /// the old implementation, this option will be removed and FixBuilder will
   /// be used unconditionally.
+  ///
+  /// Optional parameter [removeViaComments] indicates whether dead code should
+  /// be removed in its entirety (the default) or removed by commenting it out.
   factory NullabilityMigration(NullabilityMigrationListener listener,
       {bool permissive,
       NullabilityMigrationInstrumentation instrumentation,
-      bool useFixBuilder}) = NullabilityMigrationImpl;
+      bool useFixBuilder,
+      bool removeViaComments}) = NullabilityMigrationImpl;
 
   void finalizeInput(ResolvedUnitResult result);
 
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 4940535..d9e823d 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -883,6 +883,14 @@
         decoratedTypeArguments = const [];
       }
     }
+
+    if (node.staticType.isDartCoreList &&
+        callee.name == '' &&
+        node.argumentList.arguments.length == 1) {
+      _graph.connect(_graph.always, decoratedTypeArguments[0].node,
+          ListLengthConstructorOrigin(source, node));
+    }
+
     var nullabilityNode = NullabilityNode.forInferredType();
     _graph.makeNonNullable(
         nullabilityNode, InstanceCreationOrigin(source, node));
diff --git a/pkg/nnbd_migration/lib/src/edge_origin.dart b/pkg/nnbd_migration/lib/src/edge_origin.dart
index 40feb43..5f9eb0a 100644
--- a/pkg/nnbd_migration/lib/src/edge_origin.dart
+++ b/pkg/nnbd_migration/lib/src/edge_origin.dart
@@ -238,6 +238,16 @@
   EdgeOriginKind get kind => EdgeOriginKind.isCheckMainType;
 }
 
+/// An edge origin used for the type argument of a list constructor that
+/// specified an initial length, because that type argument must be nullable.
+class ListLengthConstructorOrigin extends EdgeOrigin {
+  ListLengthConstructorOrigin(Source source, AstNode node)
+      : super(source, node);
+
+  @override
+  EdgeOriginKind get kind => EdgeOriginKind.listLengthConstructor;
+}
+
 /// An edge origin used for edges that originated because a literal expression
 /// has a known nullability.
 class LiteralOrigin extends EdgeOrigin {
diff --git a/pkg/nnbd_migration/lib/src/edit_plan.dart b/pkg/nnbd_migration/lib/src/edit_plan.dart
index 75aa12b..e93001c 100644
--- a/pkg/nnbd_migration/lib/src/edit_plan.dart
+++ b/pkg/nnbd_migration/lib/src/edit_plan.dart
@@ -10,9 +10,9 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:meta/meta.dart';
 
-/// Abstract base class representing a single atomic change to a source file,
-/// decoupled from the location at which the change is made.  The [EditPlan]
-/// class performs its duties by creating and manipulating [AtomicEdit] objects.
+/// A single atomic change to a source file, decoupled from the location at
+/// which the change is made. The [EditPlan] class performs its duties by
+/// creating and manipulating [AtomicEdit] objects.
 ///
 /// A list of [AtomicEdit]s may be converted to a [SourceEdit] using the
 /// extension [AtomicEditList], and a map of offsets to lists of [AtomicEdit]s
@@ -20,34 +20,50 @@
 /// [AtomicEditMap].
 ///
 /// May be subclassed to allow additional information to be recorded about the
-/// deletion.
-abstract class AtomicEdit {
-  const AtomicEdit();
-
-  /// Queries the number of source characters that should be deleted by this
-  /// edit, or 0 if no characters should be deleted.
-  int get length;
-
-  /// Queries the source characters that should be inserted by this edit, or
-  /// the empty string if no characters should be inserted.
-  String get replacement;
-}
-
-/// Implementation of [AtomicEdit] that deletes characters of text.
-///
-/// May be subclassed to allow additional information to be recorded about the
-/// deletion.
-class DeleteText extends AtomicEdit {
-  @override
+/// edit.
+class AtomicEdit {
+  /// The number of characters that should be deleted by this edit, or `0` if no
+  /// characters should be deleted.
   final int length;
 
-  const DeleteText(this.length);
+  /// The characters that should be inserted by this edit, or the empty string
+  /// if no characters should be inserted.
+  final String replacement;
+
+  /// Initialize an edit to delete [length] characters.
+  const AtomicEdit.delete(this.length)
+      : assert(length > 0),
+        replacement = '';
+
+  /// Initialize an edit to insert the [replacement] characters.
+  const AtomicEdit.insert(this.replacement)
+      : assert(replacement.length > 0),
+        length = 0;
+
+  /// Initialize an edit to replace [length] characters with the [replacement]
+  /// characters.
+  const AtomicEdit.replace(this.length, this.replacement)
+      : assert(length > 0 || replacement.length > 0);
+
+  /// Return `true` if this edit is a deletion (no characters added).
+  bool get isDeletion => replacement.length == 0;
+
+  /// Return `true` if this edit is an insertion (no characters removed).
+  bool get isInsertion => length == 0;
+
+  /// Return `true` if this edit is a replacement.
+  bool get isReplacement => length > 0 && replacement.length > 0;
 
   @override
-  String get replacement => '';
-
-  @override
-  String toString() => 'DeleteText($length)';
+  String toString() {
+    if (isInsertion) {
+      return 'InsertText(${json.encode(replacement)})';
+    } else if (isDeletion) {
+      return 'DeleteText($length)';
+    } else {
+      return 'ReplaceText($length, ${json.encode(replacement)})';
+    }
+  }
 }
 
 /// An [EditPlan] is a builder capable of accumulating a set of edits to be
@@ -65,89 +81,27 @@
 /// [EditPlan.finalize] to convert into a representation of the concrete edits
 /// that need to be made to the source file.
 abstract class EditPlan {
-  /// The AST node to which the edit plan applies.
-  final AstNode sourceNode;
+  EditPlan._();
 
-  EditPlan._(this.sourceNode);
+  /// Returns the "parent" of the node edited by this [EditPlan].  For edit
+  /// plans that replace one AST node with another, this is the parent of the
+  /// AST node being replaced.  For edit plans that insert or delete AST nodes,
+  /// this is the parent of the AST nodes that will be inserted or deleted.
+  AstNode get parentNode;
 
-  /// If the result of executing this [EditPlan] will be an expression,
-  /// indicates whether the expression will end in an unparenthesized cascade.
-  @visibleForTesting
-  bool get endsInCascade;
-
-  /// Converts this [EditPlan] a representation of the concrete edits that need
-  /// to be made to the source file.  These edits may be converted into
-  /// [SourceEdit]s using the extensions [AtomicEditList] and [AtomicEditMap].
+  /// Returns a new [EditPlan] that replicates this [EditPlan], but may
+  /// incorporate relevant information obtained from the parent of [sourceNode].
+  /// For example, if this [EditPlan] would produce an expression that might or
+  /// might not need parentheses, and the parent of [sourceNode] is a
+  /// [ParenthesizedExpression], then an [EditPlan] is produced that will either
+  /// preserve the existing parentheses, or remove them, as appropriate.
   ///
-  /// Finalizing an [EditPlan] is a destructive operation; it should not be used
-  /// again after it is finalized.
-  Map<int, List<AtomicEdit>> finalize() {
-    var plan = _incorporateParenParentIfPresent(null);
-    return plan._getChanges(plan.parensNeededFromContext(null));
-  }
-
-  /// Determines whether the text produced by this [EditPlan] would need
-  /// parentheses if it were to be used as a replacement for its [sourceNode].
-  ///
-  /// If this [EditPlan] would produce an expression that ends in a cascade, it
-  /// will be necessary to search the [sourceNode]'s ancestors to see if any of
-  /// them represents a cascade section (and hence, parentheses are required).
-  /// If a non-null value is provided for [cascadeSearchLimit], it is the most
-  /// distant ancestor that will be searched.
-  @visibleForTesting
-  bool parensNeededFromContext(AstNode cascadeSearchLimit) {
-    if (sourceNode is! Expression) return false;
-    var parent = sourceNode.parent;
-    return parent == null
-        ? false
-        : parent
-            .accept(_ParensNeededFromContextVisitor(this, cascadeSearchLimit));
-  }
-
-  /// Modifies [changes] to insert parentheses enclosing the [sourceNode].  This
-  /// works even if [changes] already includes modifications at the beginning or
-  /// end of [sourceNode]--the parentheses are inserted outside of any
-  /// pre-existing changes.
-  Map<int, List<AtomicEdit>> _createAddParenChanges(
-      Map<int, List<AtomicEdit>> changes) {
-    changes ??= {};
-    (changes[sourceNode.offset] ??= []).insert(0, const InsertText('('));
-    (changes[sourceNode.end] ??= []).add(const InsertText(')'));
-    return changes;
-  }
-
-  /// Computes the necessary set of [changes] for this [EditPlan], either
-  /// including or not including parentheses depending on the value of [parens].
-  ///
-  /// An [EditPlan] for which [_getChanges] has been called is considered to be
-  /// finalized.
-  Map<int, List<AtomicEdit>> _getChanges(bool parens);
-
-  /// If the [sourceNode]'s parent is a [ParenthesizedExpression] returns a
-  /// [_ProvisionalParenEditPlan] which will keep or discard the enclosing
-  /// parentheses as necessary based on precedence.  Otherwise, returns this.
-  ///
-  /// If [limit] is provided, and it is the same as [sourceNode]'s parent, then
-  /// the parent is ignored.  This is used to avoid trying to remove parentheses
-  /// twice.
+  /// May return `this`, if no information needs to be incorporated from the
+  /// parent.
   ///
   /// This method is used when composing and finalizing plans, to ensure that
   /// parentheses are removed when they are no longer needed.
-  EditPlan _incorporateParenParentIfPresent(AstNode limit) {
-    var parent = sourceNode.parent;
-    if (!identical(parent, limit) && parent is ParenthesizedExpression) {
-      return _ProvisionalParenEditPlan(parent, this);
-    } else {
-      return this;
-    }
-  }
-
-  /// Determines if the text that would be produced by [EditPlan] needs to be
-  /// surrounded by parens, based on the context in which it will be used.
-  bool _parensNeeded(
-      {@required Precedence threshold,
-      bool associative = false,
-      bool allowCascade = false});
+  NodeProducingEditPlan _incorporateParent();
 }
 
 /// Factory class for creating [EditPlan]s.
@@ -170,18 +124,33 @@
   /// [innerPlan] will be finalized as a side effect (either immediately or when
   /// the newly created plan is finalized), so it should not be re-used by the
   /// caller.
-  EditPlan extract(AstNode sourceNode, EditPlan innerPlan) {
-    innerPlan = innerPlan._incorporateParenParentIfPresent(sourceNode);
+  NodeProducingEditPlan extract(
+      AstNode sourceNode, NodeProducingEditPlan innerPlan) {
+    if (!identical(innerPlan.sourceNode.parent, sourceNode)) {
+      innerPlan = innerPlan._incorporateParent();
+    }
     return _ExtractEditPlan(sourceNode, innerPlan, this);
   }
 
+  /// Converts [plan] to a representation of the concrete edits that need
+  /// to be made to the source file.  These edits may be converted into
+  /// [SourceEdit]s using the extensions [AtomicEditList] and [AtomicEditMap].
+  ///
+  /// Finalizing an [EditPlan] is a destructive operation; it should not be used
+  /// again after it is finalized.
+  Map<int, List<AtomicEdit>> finalize(EditPlan plan) {
+    var incorporatedPlan = plan._incorporateParent();
+    return incorporatedPlan
+        ._getChanges(incorporatedPlan.parensNeededFromContext(null));
+  }
+
   /// Creates a new edit plan that makes no changes to [node], but may make
   /// changes to some of its descendants (specified via [innerPlans]).
   ///
   /// All plans in [innerPlans] will be finalized as a side effect (either
   /// immediately or when the newly created plan is finalized), so they should
   /// not be re-used by the caller.
-  EditPlan passThrough(AstNode node,
+  NodeProducingEditPlan passThrough(AstNode node,
       {Iterable<EditPlan> innerPlans = const []}) {
     if (node is ParenthesizedExpression) {
       return _ProvisionalParenEditPlan(
@@ -213,9 +182,9 @@
   /// Note that [endsInCascade] is ignored if there is no [suffix] (since in
   /// this situation, whether the final plan ends in a cascade section will be
   /// determined by [innerPlan]).
-  EditPlan surround(EditPlan innerPlan,
-      {List<InsertText> prefix,
-      List<InsertText> suffix,
+  NodeProducingEditPlan surround(NodeProducingEditPlan innerPlan,
+      {List<AtomicEdit> prefix,
+      List<AtomicEdit> suffix,
       Precedence outerPrecedence = Precedence.primary,
       Precedence innerPrecedence = Precedence.none,
       bool associative = false,
@@ -243,21 +212,76 @@
   }
 }
 
-/// Implementation of [AtomicEdit] that inserts a string of new text.
-///
-/// May be subclassed to allow additional information to be recorded about the
-/// insertion.
-class InsertText extends AtomicEdit {
-  @override
-  final String replacement;
+/// Specialization of [EditPlan] for the situation where the text being produced
+/// represents a single expression (i.e. an expression, statement, class
+/// declaration, etc.)
+abstract class NodeProducingEditPlan extends EditPlan {
+  /// The AST node to which the edit plan applies.
+  final AstNode sourceNode;
 
-  const InsertText(this.replacement);
+  NodeProducingEditPlan._(this.sourceNode) : super._();
+
+  /// If the result of executing this [EditPlan] will be an expression,
+  /// indicates whether the expression will end in an unparenthesized cascade.
+  @visibleForTesting
+  bool get endsInCascade;
 
   @override
-  int get length => 0;
+  AstNode get parentNode => sourceNode.parent;
+
+  /// Determines whether the text produced by this [EditPlan] would need
+  /// parentheses if it were to be used as a replacement for its [sourceNode].
+  ///
+  /// If this [EditPlan] would produce an expression that ends in a cascade, it
+  /// will be necessary to search the [sourceNode]'s ancestors to see if any of
+  /// them represents a cascade section (and hence, parentheses are required).
+  /// If a non-null value is provided for [cascadeSearchLimit], it is the most
+  /// distant ancestor that will be searched.
+  @visibleForTesting
+  bool parensNeededFromContext(AstNode cascadeSearchLimit) {
+    if (sourceNode is! Expression) return false;
+    var parent = sourceNode.parent;
+    return parent == null
+        ? false
+        : parent
+            .accept(_ParensNeededFromContextVisitor(this, cascadeSearchLimit));
+  }
+
+  /// Modifies [changes] to insert parentheses enclosing the [sourceNode].  This
+  /// works even if [changes] already includes modifications at the beginning or
+  /// end of [sourceNode]--the parentheses are inserted outside of any
+  /// pre-existing changes.
+  Map<int, List<AtomicEdit>> _createAddParenChanges(
+      Map<int, List<AtomicEdit>> changes) {
+    changes ??= {};
+    (changes[sourceNode.offset] ??= []).insert(0, const AtomicEdit.insert('('));
+    (changes[sourceNode.end] ??= []).add(const AtomicEdit.insert(')'));
+    return changes;
+  }
+
+  /// Computes the necessary set of [changes] for this [EditPlan], either
+  /// including or not including parentheses depending on the value of [parens].
+  ///
+  /// An [EditPlan] for which [_getChanges] has been called is considered to be
+  /// finalized.
+  Map<int, List<AtomicEdit>> _getChanges(bool parens);
 
   @override
-  String toString() => 'InsertText(${json.encode(replacement)})';
+  NodeProducingEditPlan _incorporateParent() {
+    var parent = sourceNode.parent;
+    if (parent is ParenthesizedExpression) {
+      return _ProvisionalParenEditPlan(parent, this);
+    } else {
+      return this;
+    }
+  }
+
+  /// Determines if the text that would be produced by [EditPlan] needs to be
+  /// surrounded by parens, based on the context in which it will be used.
+  bool _parensNeeded(
+      {@required Precedence threshold,
+      bool associative = false,
+      bool allowCascade = false});
 }
 
 /// Visitor that determines whether a given [AstNode] ends in a cascade.
@@ -288,7 +312,8 @@
 class _ExtractEditPlan extends _NestedEditPlan {
   final EditPlanner _planner;
 
-  _ExtractEditPlan(AstNode sourceNode, EditPlan innerPlan, this._planner)
+  _ExtractEditPlan(
+      AstNode sourceNode, NodeProducingEditPlan innerPlan, this._planner)
       : super(sourceNode, innerPlan);
 
   @override
@@ -326,17 +351,17 @@
       switch (removalStyle) {
         case _RemovalStyle.commentSpace:
           return {
-            offset: [InsertText('/* ')],
-            end: [InsertText('*/ ')]
+            offset: [AtomicEdit.insert('/* ')],
+            end: [AtomicEdit.insert('*/ ')]
           };
         case _RemovalStyle.delete:
           return {
-            offset: [DeleteText(end - offset)]
+            offset: [AtomicEdit.delete(end - offset)]
           };
         case _RemovalStyle.spaceComment:
           return {
-            offset: [InsertText(' /*')],
-            end: [InsertText(' */')]
+            offset: [AtomicEdit.insert(' /*')],
+            end: [AtomicEdit.insert(' */')]
           };
       }
       throw StateError('Null value for removalStyle');
@@ -351,8 +376,8 @@
 ///
 /// By default, defers computation of whether parentheses are needed to the
 /// inner plan.
-abstract class _NestedEditPlan extends EditPlan {
-  final EditPlan innerPlan;
+abstract class _NestedEditPlan extends NodeProducingEditPlan {
+  final NodeProducingEditPlan innerPlan;
 
   _NestedEditPlan(AstNode sourceNode, this.innerPlan) : super._(sourceNode);
 
@@ -374,7 +399,7 @@
 /// based on the context surrounding its source node.  To use this class, visit
 /// the source node's parent.
 class _ParensNeededFromContextVisitor extends GeneralizingAstVisitor<bool> {
-  final EditPlan _editPlan;
+  final NodeProducingEditPlan _editPlan;
 
   /// If [_editPlan] would produce an expression that ends in a cascade, it
   /// will be necessary to search the [_target]'s ancestors to see if any of
@@ -590,19 +615,26 @@
     bool /*?*/ endsInCascade = node is CascadeExpression ? true : null;
     Map<int, List<AtomicEdit>> changes;
     for (var innerPlan in innerPlans) {
-      innerPlan = innerPlan._incorporateParenParentIfPresent(node);
-      var parensNeeded = innerPlan.parensNeededFromContext(node);
-      assert(_checkParenLogic(innerPlan, parensNeeded));
-      if (!parensNeeded && innerPlan is _ProvisionalParenEditPlan) {
-        var innerInnerPlan = innerPlan.innerPlan;
-        if (innerInnerPlan is _PassThroughEditPlan) {
-          // Input source code had redundant parens, so keep them.
-          parensNeeded = true;
-        }
+      if (!identical(innerPlan.parentNode, node)) {
+        innerPlan = innerPlan._incorporateParent();
       }
-      changes += innerPlan._getChanges(parensNeeded);
-      if (endsInCascade == null && innerPlan.sourceNode.end == node.end) {
-        endsInCascade = !parensNeeded && innerPlan.endsInCascade;
+      if (innerPlan is NodeProducingEditPlan) {
+        var parensNeeded = innerPlan.parensNeededFromContext(node);
+        assert(_checkParenLogic(innerPlan, parensNeeded));
+        if (!parensNeeded && innerPlan is _ProvisionalParenEditPlan) {
+          var innerInnerPlan = innerPlan.innerPlan;
+          if (innerInnerPlan is _PassThroughEditPlan) {
+            // Input source code had redundant parens, so keep them.
+            parensNeeded = true;
+          }
+        }
+        changes += innerPlan._getChanges(parensNeeded);
+        if (endsInCascade == null && innerPlan.sourceNode.end == node.end) {
+          endsInCascade = !parensNeeded && innerPlan.endsInCascade;
+        }
+      } else {
+        // TODO(paulberry): handle this case.
+        throw UnimplementedError('Inner plan is not node-producing');
       }
     }
     return _PassThroughEditPlan._(
@@ -640,7 +672,8 @@
   /// Caller should not re-use [innerPlan] after this call--it (and the data
   /// structures it points to) may be incorporated into this edit plan and later
   /// modified.
-  _ProvisionalParenEditPlan(ParenthesizedExpression node, EditPlan innerPlan)
+  _ProvisionalParenEditPlan(
+      ParenthesizedExpression node, NodeProducingEditPlan innerPlan)
       : super(node, innerPlan);
 
   @override
@@ -648,8 +681,8 @@
     var changes = innerPlan._getChanges(false);
     if (!parens) {
       changes ??= {};
-      (changes[sourceNode.offset] ??= []).insert(0, const DeleteText(1));
-      (changes[sourceNode.end - 1] ??= []).add(const DeleteText(1));
+      (changes[sourceNode.offset] ??= []).insert(0, const AtomicEdit.delete(1));
+      (changes[sourceNode.end - 1] ??= []).add(const AtomicEdit.delete(1));
     }
     return changes;
   }
@@ -672,7 +705,7 @@
 
 /// Implementation of [EditPlan] underlying simple cases where no computation
 /// needs to be deferred.
-class _SimpleEditPlan extends EditPlan {
+class _SimpleEditPlan extends NodeProducingEditPlan {
   final Precedence _precedence;
 
   @override
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index 67174a4..ffdd042 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -13,14 +13,15 @@
 /// TODO(paulberry): store additional information necessary to include in the
 /// preview.
 class AddRequiredKeyword extends _NestableChange {
-  const AddRequiredKeyword([NodeChange inner = const NoChange()])
+  const AddRequiredKeyword(
+      [NodeChange<NodeProducingEditPlan> inner = const NoChange()])
       : super(inner);
 
   @override
   EditPlan apply(AstNode node, FixAggregator aggregator) {
     var innerPlan = _inner.apply(node, aggregator);
     return aggregator.planner
-        .surround(innerPlan, prefix: [const InsertText('required ')]);
+        .surround(innerPlan, prefix: [const AtomicEdit.insert('required ')]);
   }
 }
 
@@ -40,7 +41,7 @@
 
   /// Gathers all the changes to nodes descended from [node] into a single
   /// [EditPlan].
-  EditPlan innerPlanForNode(AstNode node) {
+  NodeProducingEditPlan innerPlanForNode(AstNode node) {
     var previousPlans = _plans;
     try {
       _plans = [];
@@ -77,7 +78,7 @@
     } else {
       plan = planner.passThrough(unit, innerPlans: aggregator._plans);
     }
-    return plan.finalize();
+    return planner.finalize(plan);
   }
 }
 
@@ -90,14 +91,15 @@
   /// TODO(paulberry): shouldn't be a String
   final String type;
 
-  const IntroduceAs(this.type, [NodeChange inner = const NoChange()])
+  const IntroduceAs(this.type,
+      [NodeChange<NodeProducingEditPlan> inner = const NoChange()])
       : super(inner);
 
   @override
   EditPlan apply(AstNode node, FixAggregator aggregator) {
     var innerPlan = _inner.apply(node, aggregator);
     return aggregator.planner.surround(innerPlan,
-        suffix: [InsertText(' as $type')],
+        suffix: [AtomicEdit.insert(' as $type')],
         outerPrecedence: Precedence.relational,
         innerPrecedence: Precedence.relational);
   }
@@ -109,31 +111,33 @@
 /// TODO(paulberry): store additional information necessary to include in the
 /// preview.
 class MakeNullable extends _NestableChange {
-  const MakeNullable([NodeChange inner = const NoChange()]) : super(inner);
+  const MakeNullable(
+      [NodeChange<NodeProducingEditPlan> inner = const NoChange()])
+      : super(inner);
 
   @override
   EditPlan apply(AstNode node, FixAggregator aggregator) {
     var innerPlan = _inner.apply(node, aggregator);
     return aggregator.planner
-        .surround(innerPlan, suffix: [const InsertText('?')]);
+        .surround(innerPlan, suffix: [const AtomicEdit.insert('?')]);
   }
 }
 
 /// Implementation of [NodeChange] representing no change at all.  This class
 /// is intended to be used as a base class for changes that wrap around other
 /// changes.
-class NoChange extends NodeChange {
+class NoChange extends NodeChange<NodeProducingEditPlan> {
   const NoChange();
 
   @override
-  EditPlan apply(AstNode node, FixAggregator aggregator) {
+  NodeProducingEditPlan apply(AstNode node, FixAggregator aggregator) {
     return aggregator.innerPlanForNode(node);
   }
 }
 
 /// Base class representing a kind of change that [FixAggregator] might make to a
 /// particular AST node.
-abstract class NodeChange {
+abstract class NodeChange<P extends EditPlan> {
   const NodeChange();
 
   /// Applies this change to the given [node], producing an [EditPlan].  The
@@ -145,7 +149,7 @@
   /// below them (e.g. dropping an unnecessary cast), so those changes need to
   /// be able to call the appropriate [aggregator] methods just on the nodes
   /// they need.
-  EditPlan apply(AstNode node, FixAggregator aggregator);
+  P apply(AstNode node, FixAggregator aggregator);
 }
 
 /// Implementation of [NodeChange] representing the addition of a null check to
@@ -154,13 +158,14 @@
 /// TODO(paulberry): store additional information necessary to include in the
 /// preview.
 class NullCheck extends _NestableChange {
-  const NullCheck([NodeChange inner = const NoChange()]) : super(inner);
+  const NullCheck([NodeChange<NodeProducingEditPlan> inner = const NoChange()])
+      : super(inner);
 
   @override
   EditPlan apply(AstNode node, FixAggregator aggregator) {
     var innerPlan = _inner.apply(node, aggregator);
     return aggregator.planner.surround(innerPlan,
-        suffix: [const InsertText('!')],
+        suffix: [const AtomicEdit.insert('!')],
         outerPrecedence: Precedence.postfix,
         innerPrecedence: Precedence.postfix,
         associative: true);
@@ -173,7 +178,8 @@
 /// TODO(paulberry): store additional information necessary to include in the
 /// preview.
 class RemoveAs extends _NestableChange {
-  const RemoveAs([NodeChange inner = const NoChange()]) : super(inner);
+  const RemoveAs([NodeChange<NodeProducingEditPlan> inner = const NoChange()])
+      : super(inner);
 
   @override
   EditPlan apply(AstNode node, FixAggregator aggregator) {
@@ -184,7 +190,7 @@
 
 /// Shared base class for [NodeChange]s that are based on an [_inner] change.
 abstract class _NestableChange extends NodeChange {
-  final NodeChange _inner;
+  final NodeChange<NodeProducingEditPlan> _inner;
 
   const _NestableChange(this._inner);
 }
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index 13653e7..cff8c09 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -54,10 +54,15 @@
   /// [FixBuilder] infrastructure.  Once FixBuilder is at feature parity with
   /// the old implementation, this option will be removed and FixBuilder will
   /// be used unconditionally.
+  ///
+  /// Optional parameter [removeViaComments] indicates whether dead code should
+  /// be removed in its entirety (the default) or removed by commenting it out.
+  /// TODO(paulberry): wire this up.
   NullabilityMigrationImpl(NullabilityMigrationListener listener,
       {bool permissive: false,
       NullabilityMigrationInstrumentation instrumentation,
-      bool useFixBuilder: false})
+      bool useFixBuilder: false,
+      bool removeViaComments = false})
       : this._(listener, NullabilityGraph(instrumentation: instrumentation),
             permissive, instrumentation, useFixBuilder);
 
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 7c88bbf..8aaf324 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -41,14 +41,20 @@
 
   /// Verifies that migration of the files in [input] produces the output in
   /// [expectedOutput].
+  ///
+  /// Optional parameter [removeViaComments] indicates whether dead code should
+  /// be removed in its entirety (the default) or removed by commenting it out.
   Future<void> _checkMultipleFileChanges(
-      Map<String, String> input, Map<String, String> expectedOutput) async {
+      Map<String, String> input, Map<String, String> expectedOutput,
+      {bool removeViaComments = false}) async {
     for (var path in input.keys) {
       driver.getFileSync(newFile(path, content: input[path]).path);
     }
     var listener = new TestMigrationListener();
     var migration = NullabilityMigration(listener,
-        permissive: _usePermissiveMode, useFixBuilder: _useFixBuilder);
+        permissive: _usePermissiveMode,
+        useFixBuilder: _useFixBuilder,
+        removeViaComments: removeViaComments);
     for (var path in input.keys) {
       migration.prepareInput(await session.getResolvedUnit(path));
     }
@@ -77,10 +83,15 @@
 
   /// Verifies that migraiton of the single file with the given [content]
   /// produces the [expected] output.
-  Future<void> _checkSingleFileChanges(String content, String expected) async {
+  ///
+  /// Optional parameter [removeViaComments] indicates whether dead code should
+  /// be removed in its entirety (the default) or removed by commenting it out.
+  Future<void> _checkSingleFileChanges(String content, String expected,
+      {bool removeViaComments = false}) async {
     var sourcePath = convertPath('/home/test/lib/test.dart');
     await _checkMultipleFileChanges(
-        {sourcePath: content}, {sourcePath: expected});
+        {sourcePath: content}, {sourcePath: expected},
+        removeViaComments: removeViaComments);
   }
 }
 
@@ -993,7 +1004,7 @@
   } */
 }
 ''';
-    await _checkSingleFileChanges(content, expected);
+    await _checkSingleFileChanges(content, expected, removeViaComments: true);
   }
 
   Future<void> test_downcast_dynamic_function_to_functionType() async {
@@ -3129,7 +3140,7 @@
   f(1, null, null);
 }
 ''';
-    await _checkSingleFileChanges(content, expected);
+    await _checkSingleFileChanges(content, expected, removeViaComments: true);
   }
 
   Future<void> test_prefix_minus() async {
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 58d5387..a63a9a3 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -3401,6 +3401,30 @@
     // Passes if no exceptions are thrown.
   }
 
+  Future<void> test_list_constructor_length() async {
+    await analyze('''
+void main() {
+  List<int/*1*/> list = List<int/*2*/>(10);
+}
+''');
+    final variableParam = decoratedTypeAnnotation('int/*1*/');
+    final filledParam = decoratedTypeAnnotation('int/*2*/');
+
+    assertEdge(filledParam.node, variableParam.node, hard: false);
+    assertEdge(always, filledParam.node, hard: false);
+  }
+
+  Future<void> test_list_constructor_length_implicitParam() async {
+    await analyze('''
+void main() {
+  List<int/*1*/> list = List(10);
+}
+''');
+    final variableParam = decoratedTypeAnnotation('int/*1*/');
+
+    assertEdge(inSet(alwaysPlus), variableParam.node, hard: false);
+  }
+
   Future<void> test_listLiteral_noTypeArgument_noNullableElements() async {
     await analyze('''
 List<String> f() {
diff --git a/pkg/nnbd_migration/test/edit_plan_test.dart b/pkg/nnbd_migration/test/edit_plan_test.dart
index 6b0cd5e..8287884 100644
--- a/pkg/nnbd_migration/test/edit_plan_test.dart
+++ b/pkg/nnbd_migration/test/edit_plan_test.dart
@@ -31,10 +31,10 @@
   }
 
   void checkPlan(EditPlan plan, String expected) {
-    expect(plan.finalize().applyTo(code), expected);
+    expect(planner.finalize(plan).applyTo(code), expected);
   }
 
-  EditPlan extract(AstNode inner, AstNode outer) =>
+  NodeProducingEditPlan extract(AstNode inner, AstNode outer) =>
       planner.extract(outer, planner.passThrough(inner));
 
   Future<void> test_cascadeSearchLimit() async {
@@ -52,7 +52,7 @@
     // The tests below will be based on an inner plan that adds `..isEven` after
     // the `1`.
     EditPlan makeInnerPlan() => planner.surround(planner.passThrough(one),
-        suffix: [InsertText('..isEven')], endsInCascade: true);
+        suffix: [AtomicEdit.insert('..isEven')], endsInCascade: true);
     {
       // If we make a plan that passes through `c = 1`, containing a plan that
       // adds `..isEven` to `1`, then we don't necessarily want to add parens yet,
@@ -154,7 +154,7 @@
     await analyze('var x = 0;');
     checkPlan(
         planner.surround(planner.passThrough(testUnit),
-            suffix: [InsertText(' var y = 0;')]),
+            suffix: [AtomicEdit.insert(' var y = 0;')]),
         'var x = 0; var y = 0;');
   }
 
@@ -162,11 +162,11 @@
     await analyze('f(x) => 1..isEven;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.cascade('..')),
-            prefix: [InsertText('x..y = ')]),
+            prefix: [AtomicEdit.insert('x..y = ')]),
         'f(x) => x..y = (1..isEven);');
     checkPlan(
         planner.surround(planner.passThrough(findNode.cascade('..')),
-            prefix: [InsertText('x = ')], allowCascade: true),
+            prefix: [AtomicEdit.insert('x = ')], allowCascade: true),
         'f(x) => x = 1..isEven;');
   }
 
@@ -174,13 +174,14 @@
     await analyze('var x = 1 - 2;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.binary('-')),
-            suffix: [InsertText(' - 3')],
+            suffix: [AtomicEdit.insert(' - 3')],
             innerPrecedence: Precedence.additive,
             associative: true),
         'var x = 1 - 2 - 3;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.binary('-')),
-            prefix: [InsertText('0 - ')], innerPrecedence: Precedence.additive),
+            prefix: [AtomicEdit.insert('0 - ')],
+            innerPrecedence: Precedence.additive),
         'var x = 0 - (1 - 2);');
   }
 
@@ -188,11 +189,11 @@
     await analyze('f(x) => x..y = 1;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.integerLiteral('1')),
-            suffix: [InsertText(' + 2')]),
+            suffix: [AtomicEdit.insert(' + 2')]),
         'f(x) => x..y = 1 + 2;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.integerLiteral('1')),
-            suffix: [InsertText('..isEven')], endsInCascade: true),
+            suffix: [AtomicEdit.insert('..isEven')], endsInCascade: true),
         'f(x) => x..y = (1..isEven);');
   }
 
@@ -202,17 +203,17 @@
     checkPlan(
         planner.surround(
             planner.surround(planner.passThrough(findNode.cascade('..')),
-                prefix: [InsertText('1 + ')],
+                prefix: [AtomicEdit.insert('1 + ')],
                 innerPrecedence: Precedence.additive),
-            prefix: [InsertText('true ? ')],
-            suffix: [InsertText(' : 2')]),
+            prefix: [AtomicEdit.insert('true ? ')],
+            suffix: [AtomicEdit.insert(' : 2')]),
         'f(a) => true ? 1 + (a..b = 0) : 2;');
     checkPlan(
         planner.surround(
             planner.surround(planner.passThrough(findNode.cascade('..')),
-                prefix: [InsertText('throw ')], allowCascade: true),
-            prefix: [InsertText('true ? ')],
-            suffix: [InsertText(' : 2')]),
+                prefix: [AtomicEdit.insert('throw ')], allowCascade: true),
+            prefix: [AtomicEdit.insert('true ? ')],
+            suffix: [AtomicEdit.insert(' : 2')]),
         'f(a) => true ? (throw a..b = 0) : 2;');
   }
 
@@ -220,7 +221,7 @@
     await analyze('f(x, g) => g(0, throw x, 1);');
     checkPlan(
         planner.surround(planner.passThrough(findNode.simple('x, 1')),
-            suffix: [InsertText('..y')], endsInCascade: true),
+            suffix: [AtomicEdit.insert('..y')], endsInCascade: true),
         'f(x, g) => g(0, throw x..y, 1);');
   }
 
@@ -229,16 +230,16 @@
     checkPlan(
         planner.surround(
             planner.surround(planner.passThrough(findNode.cascade('..')),
-                prefix: [InsertText('throw ')], allowCascade: true),
-            prefix: [InsertText('true ? ')],
-            suffix: [InsertText(' : 2')]),
+                prefix: [AtomicEdit.insert('throw ')], allowCascade: true),
+            prefix: [AtomicEdit.insert('true ? ')],
+            suffix: [AtomicEdit.insert(' : 2')]),
         'f(a) => true ? (throw a..b = 0) : 2;');
     checkPlan(
         planner.surround(
             planner.surround(planner.passThrough(findNode.integerLiteral('0')),
-                prefix: [InsertText('throw ')], allowCascade: true),
-            prefix: [InsertText('true ? ')],
-            suffix: [InsertText(' : 2')]),
+                prefix: [AtomicEdit.insert('throw ')], allowCascade: true),
+            prefix: [AtomicEdit.insert('true ? ')],
+            suffix: [AtomicEdit.insert(' : 2')]),
         'f(a) => a..b = true ? throw 0 : 2;');
   }
 
@@ -246,12 +247,12 @@
     await analyze('var x = 1 == true;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.integerLiteral('1')),
-            suffix: [InsertText(' < 2')],
+            suffix: [AtomicEdit.insert(' < 2')],
             outerPrecedence: Precedence.relational),
         'var x = 1 < 2 == true;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.integerLiteral('1')),
-            suffix: [InsertText(' == 2')],
+            suffix: [AtomicEdit.insert(' == 2')],
             outerPrecedence: Precedence.equality),
         'var x = (1 == 2) == true;');
   }
@@ -260,7 +261,7 @@
     await analyze('var x = 1;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.integerLiteral('1')),
-            prefix: [InsertText('throw ')]),
+            prefix: [AtomicEdit.insert('throw ')]),
         'var x = throw 1;');
   }
 
@@ -268,7 +269,7 @@
     await analyze('var x = 1;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.integerLiteral('1')),
-            suffix: [InsertText('..isEven')]),
+            suffix: [AtomicEdit.insert('..isEven')]),
         'var x = 1..isEven;');
   }
 
@@ -276,12 +277,12 @@
     await analyze('var x = 1 < 2;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.binary('<')),
-            suffix: [InsertText(' == true')],
+            suffix: [AtomicEdit.insert(' == true')],
             innerPrecedence: Precedence.equality),
         'var x = 1 < 2 == true;');
     checkPlan(
         planner.surround(planner.passThrough(findNode.binary('<')),
-            suffix: [InsertText(' as bool')],
+            suffix: [AtomicEdit.insert(' as bool')],
             innerPrecedence: Precedence.relational),
         'var x = (1 < 2) as bool;');
   }
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index fec91a2..ee03cff 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -836,7 +836,6 @@
         changes: {findNode.simple('y +'): isNullCheck});
   }
 
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39642')
   Future<void> test_binaryExpression_question_question_nullChecked() async {
     await analyze('''
 Object/*!*/ _f(int/*?*/ x, double/*?*/ y) {
@@ -1385,7 +1384,6 @@
     visitSubexpression(findNode.postfix('++'), '_C');
   }
 
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/38833')
   Future<void>
       test_postfixExpression_combined_nullable_noProblem_dynamic() async {
     await analyze('''
diff --git a/runtime/bin/dart_embedder_api_impl.cc b/runtime/bin/dart_embedder_api_impl.cc
index a7fde47..6af5d82 100644
--- a/runtime/bin/dart_embedder_api_impl.cc
+++ b/runtime/bin/dart_embedder_api_impl.cc
@@ -69,8 +69,8 @@
 
 Dart_Isolate CreateVmServiceIsolate(const IsolateCreationData& data,
                                     const VmServiceConfiguration& config,
-                                    const uint8_t* kernel_buffer,
-                                    intptr_t kernel_buffer_size,
+                                    const uint8_t* isolate_data,
+                                    const uint8_t* isolate_instr,
                                     char** error) {
   if (data.flags == nullptr) {
     *error = strdup("Expected non-null flags");
@@ -78,6 +78,40 @@
   }
   data.flags->load_vmservice_library = true;
 
+  Dart_Isolate service_isolate = Dart_CreateIsolateGroup(
+      data.script_uri, data.main, isolate_data, isolate_instr, data.flags,
+      data.isolate_group_data, data.isolate_data, error);
+  if (service_isolate == nullptr) {
+    return nullptr;
+  }
+
+  Dart_EnterScope();
+  // Load embedder specific bits and return.
+  if (!bin::VmService::Setup(config.ip, config.port, config.dev_mode,
+                             config.disable_auth_codes,
+                             config.write_service_info_filename,
+                             /*trace_loading=*/false, config.deterministic)) {
+    *error = strdup(bin::VmService::GetErrorMessage());
+    return nullptr;
+  }
+
+  Dart_ExitScope();
+  Dart_ExitIsolate();
+  return service_isolate;
+}
+
+Dart_Isolate CreateVmServiceIsolateFromKernel(
+    const IsolateCreationData& data,
+    const VmServiceConfiguration& config,
+    const uint8_t* kernel_buffer,
+    intptr_t kernel_buffer_size,
+    char** error) {
+  if (data.flags == nullptr) {
+    *error = strdup("Expected non-null flags");
+    return nullptr;
+  }
+  data.flags->load_vmservice_library = true;
+
   Dart_Isolate service_isolate = Dart_CreateIsolateGroupFromKernel(
       data.script_uri, data.main, kernel_buffer, kernel_buffer_size, data.flags,
       data.isolate_group_data, data.isolate_data, error);
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index aff0896..6b5ccdf 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -857,12 +857,18 @@
  *
  * \return NULL if successful. Returns an error message otherwise.
  *  The caller is responsible for freeing the error message.
+ *
+ * NOTE: This call does not store references to the passed in c-strings.
  */
 DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_SetVMFlags(int argc,
                                                           const char** argv);
 
 /**
- * Returns true if the named VM flag is set.
+ * Returns true if the named VM flag is of boolean type, specified, and set to
+ * true.
+ *
+ * \param flag_name The name of the flag without leading punctuation
+ *                  (example: "enable_asserts").
  */
 DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name);
 
diff --git a/runtime/include/dart_embedder_api.h b/runtime/include/dart_embedder_api.h
index 5b283ca..c9860dd 100644
--- a/runtime/include/dart_embedder_api.h
+++ b/runtime/include/dart_embedder_api.h
@@ -70,18 +70,28 @@
   bool disable_auth_codes;
 };
 
-// Create and initialize vm-service isolate. This method should be used
-// when VM invokes isolate creation callback with DART_VM_SERVICE_ISOLATE_NAME
-// as script_uri.
-// The isolate is created from the given kernel binary that is expected to
-// contain all necessary vmservice libraries.
+// Create and initialize vm-service isolate from the given AOT snapshot, which
+// is expected to contain all necessary 'vm-service' libraries.
+// This method should be used when VM invokes isolate creation callback with
+// DART_VM_SERVICE_ISOLATE_NAME as script_uri.
 DART_WARN_UNUSED_RESULT Dart_Isolate
 CreateVmServiceIsolate(const IsolateCreationData& data,
                        const VmServiceConfiguration& config,
-                       const uint8_t* kernel_buffer,
-                       intptr_t kernel_buffer_size,
+                       const uint8_t* isolate_data,
+                       const uint8_t* isolate_instr,
                        char** error);
 
+// Create and initialize vm-service isolate from the given kernel binary, which
+// is expected to contain all necessary 'vm-service' libraries.
+// This method should be used when VM invokes isolate creation callback with
+// DART_VM_SERVICE_ISOLATE_NAME as script_uri.
+DART_WARN_UNUSED_RESULT Dart_Isolate
+CreateVmServiceIsolateFromKernel(const IsolateCreationData& data,
+                                 const VmServiceConfiguration& config,
+                                 const uint8_t* kernel_buffer,
+                                 intptr_t kernel_buffer_size,
+                                 char** error);
+
 }  // namespace embedder
 }  // namespace dart
 
diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart
index 18426bb..1bf79ca 100644
--- a/runtime/observatory/lib/object_graph.dart
+++ b/runtime/observatory/lib/object_graph.dart
@@ -335,9 +335,15 @@
   _VerticesIterator(this._graph);
 
   bool moveNext() {
-    if (_nextId == _graph._N) return false;
-    current = new _SnapshotObject._(_nextId++, _graph, "");
-    return true;
+    while (_nextId <= _graph._N) {
+      // A retained size of zero means the object was unreachable.
+      if (_graph._retainedSizes[_nextId] != 0) {
+        current = new _SnapshotObject._(_nextId++, _graph, "");
+        return true;
+      }
+      _nextId++;
+    }
+    return false;
   }
 }
 
@@ -360,8 +366,10 @@
   _InstancesIterator(this._graph, this._cid);
 
   bool moveNext() {
-    while (_nextId < _graph._N) {
-      if (_graph._cids[_nextId] == _cid) {
+    while (_nextId <= _graph._N) {
+      // A retained size of zero means the object was unreachable.
+      if (_graph._cids[_nextId] == _cid &&
+          _graph._retainedSizes[_nextId] != 0) {
         current = new _SnapshotObject._(_nextId++, _graph, "");
         return true;
       }
@@ -1177,10 +1185,13 @@
       cls.liveInstanceCount++;
     }
 
-    // Start with retained size as shallow size + external size.
+    // Start with retained size as shallow size + external size. For reachable
+    // objects only; leave unreachable objects with a retained size of 0 so
+    // they can be filtered during graph iterations.
     var retainedSizes = new Uint32List(N + 1);
-    for (var i = 0; i < N + 1; i++) {
-      retainedSizes[i] = internalSizes[i] + externalSizes[i];
+    for (var i = 0; i <= Nconnected; i++) {
+      var v = vertex[i];
+      retainedSizes[v] = internalSizes[v] + externalSizes[v];
     }
 
     // In post order (bottom up), add retained size to dominator's retained
diff --git a/runtime/observatory/tests/service/object_graph_vm_test.dart b/runtime/observatory/tests/service/object_graph_vm_test.dart
index dd4b936..5458b49 100644
--- a/runtime/observatory/tests/service/object_graph_vm_test.dart
+++ b/runtime/observatory/tests/service/object_graph_vm_test.dart
@@ -82,6 +82,69 @@
     // Check that the short list retains more than the long list inside.
     // and specifically, that it retains exactly itself + the long one.
     expect(first.retainedSize, equals(first.shallowSize + second.shallowSize));
+
+    // Verify sizes of classes are the appropriates sums of their instances.
+    // This also verifies that the class instance iterators are visiting the
+    // correct set of objects (e.g., not including dead objects).
+    for (SnapshotClass klass in graph.classes) {
+      int shallowSum = 0;
+      int internalSum = 0;
+      int externalSum = 0;
+      for (SnapshotObject instance in klass.instances) {
+        if (instance == graph.root) {
+          // The root may have 0 self size.
+          expect(instance.internalSize, greaterThanOrEqualTo(0));
+          expect(instance.externalSize, greaterThanOrEqualTo(0));
+          expect(instance.shallowSize, greaterThanOrEqualTo(0));
+        } else {
+          // All other objects are heap objects with positive size.
+          expect(instance.internalSize, greaterThan(0));
+          expect(instance.externalSize, greaterThanOrEqualTo(0));
+          expect(instance.shallowSize, greaterThan(0));
+        }
+        expect(instance.retainedSize, greaterThan(0));
+        expect(instance.shallowSize,
+            equals(instance.internalSize + instance.externalSize));
+        shallowSum += instance.shallowSize;
+        internalSum += instance.internalSize;
+        externalSum += instance.externalSize;
+      }
+      expect(shallowSum, equals(klass.shallowSize));
+      expect(internalSum, equals(klass.internalSize));
+      expect(externalSum, equals(klass.externalSize));
+      expect(
+          klass.shallowSize, equals(klass.internalSize + klass.externalSize));
+    }
+
+    // Verify sizes of the overall graph are the appropriates sums of all
+    // instances. This also verifies that the all instances iterator is visiting
+    // the correct set of objects (e.g., not including dead objects).
+    int shallowSum = 0;
+    int internalSum = 0;
+    int externalSum = 0;
+    for (SnapshotObject instance in graph.objects) {
+      if (instance == graph.root) {
+        // The root may have 0 self size.
+        expect(instance.internalSize, greaterThanOrEqualTo(0));
+        expect(instance.externalSize, greaterThanOrEqualTo(0));
+        expect(instance.shallowSize, greaterThanOrEqualTo(0));
+      } else {
+        // All other objects are heap objects with positive size.
+        expect(instance.internalSize, greaterThan(0));
+        expect(instance.externalSize, greaterThanOrEqualTo(0));
+        expect(instance.shallowSize, greaterThan(0));
+      }
+      expect(instance.retainedSize, greaterThan(0));
+      expect(instance.shallowSize,
+          equals(instance.internalSize + instance.externalSize));
+      shallowSum += instance.shallowSize;
+      internalSum += instance.internalSize;
+      externalSum += instance.externalSize;
+    }
+    expect(shallowSum, equals(graph.size));
+    expect(internalSum, equals(graph.internalSize));
+    expect(externalSum, equals(graph.externalSize));
+    expect(graph.size, equals(graph.internalSize + graph.externalSize));
   },
 ];
 
diff --git a/runtime/tests/vm/dart/arm32_boxmint_test.dart b/runtime/tests/vm/dart/arm32_boxmint_test.dart
deleted file mode 100644
index 4c33ba6..0000000
--- a/runtime/tests/vm/dart/arm32_boxmint_test.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// Test case that tests boxing mint
-
-// VMOptions=--shared-slow-path-triggers-gc
-
-import 'dart:typed_data';
-import 'package:expect/expect.dart';
-
-final l = Uint64List(10);
-int sum = 0;
-
-foobar() {
-  for (int i = 0; i < l.length; ++i) {
-    sum += l[i];
-  }
-  Expect.equals(sum, 1481763717120);
-}
-
-main() {
-  for (int i = 0; i < 10; i++) {
-    l[i] = (i + 30) << 32;
-  }
-  foobar();
-}
diff --git a/runtime/tests/vm/dart/boxmint_test.dart b/runtime/tests/vm/dart/boxmint_test.dart
new file mode 100644
index 0000000..bcc90b0
--- /dev/null
+++ b/runtime/tests/vm/dart/boxmint_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2019, 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.
+
+// Test case that tests boxing mint
+
+// VMOptions=--optimization_counter_threshold=10 --deterministic --use-slow-path --shared-slow-path-triggers-gc --stacktrace_filter=foobar
+
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+
+final gSize = 100;
+final l = Uint64List(gSize);
+int sum = 0;
+
+foobar() {
+  for (int i = 0; i < l.length; ++i) {
+    sum += l[i];
+  }
+  Expect.equals(-9223372036854775808, sum);
+}
+
+main() {
+  for (int i = 0; i < gSize; i++) {
+    l[i] = (i + 30) << 62;
+  }
+  foobar();
+}
diff --git a/runtime/tests/vm/dart/use_flag_test_helper.dart b/runtime/tests/vm/dart/use_flag_test_helper.dart
index 685fdb5..c6fa872 100644
--- a/runtime/tests/vm/dart/use_flag_test_helper.dart
+++ b/runtime/tests/vm/dart/use_flag_test_helper.dart
@@ -24,6 +24,91 @@
 final aotRuntime = path.join(
     buildDir, 'dart_precompiled_runtime' + (Platform.isWindows ? '.exe' : ''));
 
+String get clangBuildToolsDir {
+  String archDir;
+  if (Platform.isLinux) {
+    archDir = 'linux-x64';
+  } else if (Platform.isMacOS) {
+    archDir = 'mac-x64';
+  } else {
+    return null;
+  }
+  var clangDir = path.join(sdkDir, 'buildtools', archDir, 'clang', 'bin');
+  return Directory(clangDir).existsSync() ? clangDir : null;
+}
+
+Future<void> assembleSnapshot(String assemblyPath, String snapshotPath) async {
+  if (!Platform.isLinux && !Platform.isMacOS) {
+    throw "Unsupported platform ${Platform.operatingSystem} for assembling";
+  }
+
+  final ccFlags = <String>[];
+  final ldFlags = <String>[];
+  String cc = 'gcc';
+  String shared = '-shared';
+
+  if (Platform.isMacOS) {
+    cc = 'clang';
+  } else if (buildDir.endsWith('SIMARM') || buildDir.endsWith('SIMARM64')) {
+    if (clangBuildToolsDir != null) {
+      cc = path.join(clangBuildToolsDir, 'clang');
+    } else {
+      throw 'Cannot assemble for ${path.basename(buildDir)} '
+          'without //buildtools on ${Platform.operatingSystem}';
+    }
+  }
+
+  if (Platform.isMacOS) {
+    shared = '-dynamiclib';
+    // Tell Mac linker to give up generating eh_frame from dwarf.
+    ldFlags.add('-Wl,-no_compact_unwind');
+  } else if (buildDir.endsWith('SIMARM')) {
+    ccFlags.add('--target=armv7-linux-gnueabihf');
+  } else if (buildDir.endsWith('SIMARM64')) {
+    ccFlags.add('--target=aarch64-linux-gnu');
+  }
+
+  if (buildDir.endsWith('X64') || buildDir.endsWith('SIMARM64')) {
+    ccFlags.add('-m64');
+  }
+
+  await run(cc, <String>[
+    ...ccFlags,
+    ...ldFlags,
+    shared,
+    '-nostdlib',
+    '-o',
+    snapshotPath,
+    assemblyPath,
+  ]);
+}
+
+Future<void> stripSnapshot(String snapshotPath, String strippedPath,
+    {bool forceElf = false}) async {
+  if (!Platform.isLinux && !Platform.isMacOS) {
+    throw "Unsupported platform ${Platform.operatingSystem} for stripping";
+  }
+
+  var strip = 'strip';
+
+  if ((Platform.isLinux &&
+          (buildDir.endsWith('SIMARM') || buildDir.endsWith('SIMARM64'))) ||
+      (Platform.isMacOS && forceElf)) {
+    if (clangBuildToolsDir != null) {
+      strip = path.join(clangBuildToolsDir, 'llvm-strip');
+    } else {
+      throw 'Cannot strip ELF files for ${path.basename(buildDir)} '
+          'without //buildtools on ${Platform.operatingSystem}';
+    }
+  }
+
+  await run(strip, <String>[
+    '-o',
+    strippedPath,
+    snapshotPath,
+  ]);
+}
+
 Future<ProcessResult> runHelper(String executable, List<String> args) async {
   print('Running $executable ${args.join(' ')}');
 
@@ -82,11 +167,16 @@
   return result.stderr.split(RegExp(r'[\r\n]'));
 }
 
+const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES';
+
 Future<void> withTempDir(String name, Future<void> fun(String dir)) async {
   final tempDir = Directory.systemTemp.createTempSync(name);
   try {
     await fun(tempDir.path);
   } finally {
-    tempDir.deleteSync(recursive: true);
+    if (!Platform.environment.containsKey(keepTempKey) ||
+        Platform.environment[keepTempKey].isEmpty) {
+      tempDir.deleteSync(recursive: true);
+    }
   }
 }
diff --git a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
index 2c6b877..965590b 100644
--- a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
+++ b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
@@ -6,104 +6,102 @@
 import "dart:io";
 
 import "package:expect/expect.dart";
+import 'package:path/path.dart' as path;
 import "package:vm/v8_snapshot_profile.dart";
 
-String path(List<String> segments) {
-  return "/" + segments.join("/");
-}
+import 'use_flag_test_helper.dart';
 
-test(String sdkRoot, {bool useAsm: false}) async {
-  if (Platform.isMacOS || Platform.isWindows) {
-    // Missing necessary utilities.
-    return;
-  }
+test({String dillPath, bool useAsm, bool stripFlag, bool stripUtil}) async {
+  // The assembler may add extra unnecessary information to the compiled
+  // snapshot whether or not we generate DWARF information in the assembly, so
+  // we force the use of a utility when generating assembly.
+  if (useAsm) Expect.isTrue(stripUtil);
 
-  // Generate the snapshot profile.
-  final String thisTestPath =
-      "$sdkRoot/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart";
+  // We must strip the output in some way when generating ELF snapshots,
+  // else the debugging information added will cause the test to fail.
+  if (!stripUtil) Expect.isTrue(stripFlag);
 
-  final Directory temp = await Directory.systemTemp.createTemp();
-  final String snapshotPath = temp.path + "/test.snap";
+  final tempDirPrefix = 'v8-snapshot-profile' +
+      (useAsm ? '-assembly' : '-elf') +
+      (stripFlag ? '-intstrip' : '') +
+      (stripUtil ? '-extstrip' : '');
 
-  final List<String> precompiler2Args = [
-    "--write-v8-snapshot-profile-to=${temp.path}/profile.heapsnapshot",
-    thisTestPath,
-    snapshotPath,
-  ];
+  await withTempDir(tempDirPrefix, (String tempDir) async {
+    // Generate the snapshot profile.
+    final profilePath = path.join(tempDir, 'profile.heapsnapshot');
+    final snapshotPath = path.join(tempDir, 'test.snap');
+    final commonSnapshotArgs = [
+      if (stripFlag) '--strip',
+      "--write-v8-snapshot-profile-to=$profilePath",
+      dillPath,
+    ];
 
-  if (useAsm) {
-    precompiler2Args.insert(0, "--build-assembly");
-  }
-
-  ProcessResult result = await Process.run(
-    "pkg/vm/tool/precompiler2",
-    precompiler2Args,
-    workingDirectory: sdkRoot,
-    runInShell: true,
-  );
-
-  // The precompiler2 script tried using GCC for the wrong architecture. We
-  // don't have a workaround for this now.
-  if (result.exitCode != 0 && result.stderr.contains("Assembler messages")) {
-    return;
-  }
-
-  print(precompiler2Args);
-  print(result.stderr);
-  print(result.stdout);
-
-  Expect.equals(result.exitCode, 0);
-  Expect.equals("", result.stderr);
-  Expect.equals("", result.stdout);
-
-  result = await Process.run("strip", [snapshotPath]);
-
-  // Same issue: strip can't process a cross-compiled ELF.
-  if (result.exitCode != 0 &&
-      (result.stderr.contains("Unable to recognise") ||
-          result.stderr.contains("non-object and non-archive"))) {
-    return;
-  }
-
-  final V8SnapshotProfile profile = V8SnapshotProfile.fromJson(JsonDecoder()
-      .convert(File("${temp.path}/profile.heapsnapshot").readAsStringSync()));
-
-  // Verify that there are no "unknown" nodes. These are emitted when we see a
-  // reference to an some object but no other metadata about the object was
-  // recorded. We should at least record the type for every object in the graph
-  // (in some cases the shallow size can legitimately be 0, e.g. for "base
-  // objects").
-  for (final int node in profile.nodes) {
-    if (profile[node].type == "Unknown") {
-      print(profile[node].id);
-    }
-    Expect.notEquals(profile[node].type, "Unknown");
-  }
-
-  // Verify that all nodes are reachable from the declared roots.
-  int unreachableNodes = 0;
-  Set<int> nodesReachableFromRoots = profile.preOrder(profile.root).toSet();
-  for (final int node in profile.nodes) {
-    if (!nodesReachableFromRoots.contains(node)) {
-      ++unreachableNodes;
-    }
-  }
-  Expect.equals(unreachableNodes, 0);
-
-  // Verify that the actual size of the snapshot is close to the sum of the
-  // shallow sizes of all objects in the profile. They will not be exactly equal
-  // because of global headers and padding.
-  final int actual = await File(snapshotPath).length();
-  final int expected = profile.accountedBytes;
-  print("Expected size: $expected, actual size: $actual.");
-  if ((actual - expected).abs() / actual > 0.03) {
     if (useAsm) {
-      print("Failure on Assembly snapshot type.");
+      final assemblyPath = path.join(tempDir, 'test.S');
+
+      await run(genSnapshot, <String>[
+        '--snapshot-kind=app-aot-assembly',
+        '--assembly=$assemblyPath',
+        ...commonSnapshotArgs,
+      ]);
+
+      await assembleSnapshot(assemblyPath, snapshotPath);
     } else {
-      print("Failure on ELF snapshot type.");
+      await run(genSnapshot, <String>[
+        '--snapshot-kind=app-aot-elf',
+        '--elf=$snapshotPath',
+        ...commonSnapshotArgs,
+      ]);
     }
-    Expect.fail("Difference cannot be greater than 3% of actual.");
-  }
+
+    String strippedPath;
+    if (stripUtil) {
+      strippedPath = snapshotPath + '.stripped';
+      await stripSnapshot(snapshotPath, strippedPath, forceElf: !useAsm);
+    } else {
+      strippedPath = snapshotPath;
+    }
+
+    final V8SnapshotProfile profile = V8SnapshotProfile.fromJson(
+        JsonDecoder().convert(File(profilePath).readAsStringSync()));
+
+    // Verify that there are no "unknown" nodes. These are emitted when we see a
+    // reference to an some object but no other metadata about the object was
+    // recorded. We should at least record the type for every object in the
+    // graph (in some cases the shallow size can legitimately be 0, e.g. for
+    // "base objects").
+    for (final int node in profile.nodes) {
+      Expect.notEquals(profile[node].type, "Unknown",
+          "unknown node at ID ${profile[node].id}");
+    }
+
+    // Verify that all nodes are reachable from the declared roots.
+    int unreachableNodes = 0;
+    Set<int> nodesReachableFromRoots = profile.preOrder(profile.root).toSet();
+    for (final int node in profile.nodes) {
+      Expect.isTrue(nodesReachableFromRoots.contains(node),
+          "unreachable node at ID ${profile[node].id}");
+    }
+
+    // Verify that the actual size of the snapshot is close to the sum of the
+    // shallow sizes of all objects in the profile. They will not be exactly
+    // equal because of global headers and padding.
+    final actual = await File(strippedPath).length();
+    final expected = profile.accountedBytes;
+
+    final fileType = useAsm ? "assembly" : "ELF";
+    String stripPrefix = "";
+    if (stripFlag && stripUtil) {
+      stripPrefix = "internally and externally stripped ";
+    } else if (stripFlag) {
+      stripPrefix = "internally stripped ";
+    } else if (stripUtil) {
+      stripPrefix = "externally stripped ";
+    }
+
+    Expect.approxEquals(expected, actual, 0.03 * actual,
+        "failed on $stripPrefix$fileType snapshot type.");
+  });
 }
 
 Match matchComplete(RegExp regexp, String line) {
@@ -116,14 +114,15 @@
 // All fields of "Raw..." classes defined in "raw_object.h" must be included in
 // the giant macro in "raw_object_fields.cc". This function attempts to check
 // that with some basic regexes.
-testMacros(String sdkRoot) async {
+testMacros() async {
   const String className = "([a-z0-9A-Z]+)";
   const String rawClass = "Raw$className";
   const String fieldName = "([a-z0-9A-Z_]+)";
 
   final Map<String, Set<String>> fields = {};
 
-  final String rawObjectFieldsPath = "$sdkRoot/runtime/vm/raw_object_fields.cc";
+  final String rawObjectFieldsPath =
+      path.join(sdkDir, 'runtime', 'vm', 'raw_object_fields.cc');
   final RegExp fieldEntry = RegExp(" *F\\($className, $fieldName\\) *\\\\?");
 
   await for (String line in File(rawObjectFieldsPath)
@@ -143,7 +142,8 @@
   final RegExp classEnd = RegExp("}");
   final RegExp field = RegExp("  $rawClass. +$fieldName;.*");
 
-  final String rawObjectPath = "$sdkRoot/runtime/vm/raw_object.h";
+  final String rawObjectPath =
+      path.join(sdkDir, 'runtime', 'vm', 'raw_object.h');
 
   String currentClass;
   bool hasMissingFields = false;
@@ -179,21 +179,65 @@
   }
 
   if (hasMissingFields) {
-    Expect.fail(
-        "runtime/vm/raw_object_fields.cc misses some fields. Please update it to match raw_object.h.");
+    Expect.fail("$rawObjectFieldsPath is missing some fields. "
+        "Please update it to match $rawObjectPath.");
   }
 }
 
 main() async {
-  if (Platform.isWindows) return;
+  void printSkip(String description) =>
+      print('Skipping $description for ${path.basename(buildDir)} '
+              'on ${Platform.operatingSystem}' +
+          (clangBuildToolsDir == null ? ' without //buildtools' : ''));
 
-  final List<String> sdkBaseSegments =
-      Uri.file(Platform.resolvedExecutable).pathSegments.toList();
-  sdkBaseSegments
-      .replaceRange(sdkBaseSegments.length - 3, sdkBaseSegments.length, []);
-  String sdkRoot = path(sdkBaseSegments);
+  // We don't have access to the SDK on Android.
+  if (Platform.isAndroid) {
+    printSkip('all tests');
+    return;
+  }
 
-  await test(sdkRoot, useAsm: true);
-  await test(sdkRoot, useAsm: false);
-  testMacros(sdkRoot);
+  await testMacros();
+
+  await withTempDir('v8-snapshot-profile-writer', (String tempDir) async {
+    // We only need to generate the dill file once.
+    final _thisTestPath = path.join(sdkDir, 'runtime', 'tests', 'vm', 'dart',
+        'v8_snapshot_profile_writer_test.dart');
+    final dillPath = path.join(tempDir, 'test.dill');
+    await run(genKernel,
+        <String>['--platform', platformDill, '-o', dillPath, _thisTestPath]);
+
+    // Test stripped ELF generation directly.
+    await test(
+        dillPath: dillPath, stripFlag: true, stripUtil: false, useAsm: false);
+
+    // We neither generate assembly nor have a stripping utility on Windows.
+    if (Platform.isWindows) {
+      printSkip('external stripping and assembly tests');
+      return;
+    }
+
+    // The native strip utility on Mac OS X doesn't recognize ELF files.
+    if (Platform.isMacOS && clangBuildToolsDir == null) {
+      printSkip('ELF external stripping test');
+    } else {
+      // Test unstripped ELF generation that is then stripped externally.
+      await test(
+          dillPath: dillPath, stripFlag: false, stripUtil: true, useAsm: false);
+    }
+
+    // TODO(sstrickl): Currently we can't assemble for SIMARM64 on MacOSX.
+    // For example, the test runner still uses blobs for dartkp-mac-*-simarm64.
+    // Change assembleSnapshot and remove this check when we can.
+    if (Platform.isMacOS && buildDir.endsWith('SIMARM64')) {
+      printSkip('assembly tests');
+      return;
+    }
+
+    // Test stripped assembly generation that is then compiled and stripped.
+    await test(
+        dillPath: dillPath, stripFlag: true, stripUtil: true, useAsm: true);
+    // Test unstripped assembly generation that is then compiled and stripped.
+    await test(
+        dillPath: dillPath, stripFlag: false, stripUtil: true, useAsm: true);
+  });
 }
diff --git a/runtime/tools/dartfuzz/dartfuzz.dart b/runtime/tools/dartfuzz/dartfuzz.dart
index 2326327..3807646 100644
--- a/runtime/tools/dartfuzz/dartfuzz.dart
+++ b/runtime/tools/dartfuzz/dartfuzz.dart
@@ -14,7 +14,7 @@
 // Version of DartFuzz. Increase this each time changes are made
 // to preserve the property that a given version of DartFuzz yields
 // the same fuzzed program for a deterministic random seed.
-const String version = '1.84';
+const String version = '1.85';
 
 // Restriction on statements and expressions.
 const int stmtDepth = 1;
diff --git a/runtime/tools/dartfuzz/dartfuzz_type_table.dart b/runtime/tools/dartfuzz/dartfuzz_type_table.dart
index ede65ee..6e15099 100644
--- a/runtime/tools/dartfuzz/dartfuzz_type_table.dart
+++ b/runtime/tools/dartfuzz/dartfuzz_type_table.dart
@@ -63517,12 +63517,6 @@
           FLOAT32X4,
         ],
       },
-      '/': {
-        [
-          FLOAT32X4,
-          FLOAT32X4,
-        ],
-      },
       '??': {
         [
           FLOAT32X4,
@@ -63549,12 +63543,6 @@
           FLOAT64X2,
         ],
       },
-      '/': {
-        [
-          FLOAT64X2,
-          FLOAT64X2,
-        ],
-      },
       '??': {
         [
           FLOAT64X2,
@@ -64981,9 +64969,6 @@
       '*=': {
         FLOAT32X4,
       },
-      '/=': {
-        FLOAT32X4,
-      },
     },
     FLOAT32X4LIST: {
       '=': {
@@ -65017,9 +65002,6 @@
       '*=': {
         FLOAT64X2,
       },
-      '/=': {
-        FLOAT64X2,
-      },
     },
     FLOAT64X2LIST: {
       '=': {
@@ -139751,12 +139733,6 @@
           DartType.FLOAT32X4,
         ],
       },
-      '/': {
-        [
-          DartType.FLOAT32X4,
-          DartType.FLOAT32X4,
-        ],
-      },
       '??': {
         [
           DartType.FLOAT32X4,
@@ -139783,12 +139759,6 @@
           DartType.FLOAT64X2,
         ],
       },
-      '/': {
-        [
-          DartType.FLOAT64X2,
-          DartType.FLOAT64X2,
-        ],
-      },
       '??': {
         [
           DartType.FLOAT64X2,
@@ -140247,9 +140217,6 @@
       '*=': {
         DartType.FLOAT32X4,
       },
-      '/=': {
-        DartType.FLOAT32X4,
-      },
     },
     DartType.FLOAT32X4LIST: {
       '=': {
@@ -140283,9 +140250,6 @@
       '*=': {
         DartType.FLOAT64X2,
       },
-      '/=': {
-        DartType.FLOAT64X2,
-      },
     },
     DartType.FLOAT64X2LIST: {
       '=': {
diff --git a/runtime/tools/dartfuzz/gen_type_table.dart b/runtime/tools/dartfuzz/gen_type_table.dart
index 2d5243a..0dfae17 100644
--- a/runtime/tools/dartfuzz/gen_type_table.dart
+++ b/runtime/tools/dartfuzz/gen_type_table.dart
@@ -866,6 +866,20 @@
   }
 }
 
+// Filters methods based on a manually maintained blacklist.
+//
+// Blacklisted methods should be associated with an issue number so they can be
+// re-enabled after the issue is resolved.
+bool isBlacklistedMethod(InterfaceType tp, MethodElement method) {
+  // TODO(bkonyi): Enable operator / for these types after we resolve
+  // https://github.com/dart-lang/sdk/issues/39890
+  if (((tp.displayName == 'Float32x4') && (method.name == '/')) ||
+      ((tp.displayName == 'Float64x2') && (method.name == '/'))) {
+    return true;
+  }
+  return false;
+}
+
 // Extract all binary and unary operators for tp.
 // Operators are stored by return type in the following way:
 // return type: { operator: { parameter types } }
@@ -874,6 +888,9 @@
 // Does not recurse into interfaces and superclasses of tp.
 void getOperatorsForTyp(String typName, InterfaceType tp, fromLiteral) {
   for (MethodElement method in tp.methods) {
+    // If the method is manually blacklisted, skip it.
+    if (isBlacklistedMethod(tp, method)) continue;
+
     // Detect whether tp can be parsed from a literal (dartfuzz.dart can
     // already handle that).
     // This is usually indicated by the presence of the static constructor
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index 721a931..5084ee1 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -1894,6 +1894,48 @@
   }
 }
 
+bool Assembler::CanGenerateXCbzTbz(Register rn, Condition cond) {
+  if (rn == CSP) {
+    return false;
+  }
+  switch (cond) {
+    case EQ:  // equal
+    case NE:  // not equal
+    case MI:  // minus/negative
+    case LT:  // signed less than
+    case PL:  // plus/positive or zero
+    case GE:  // signed greater than or equal
+      return true;
+    default:
+      return false;
+  }
+}
+
+void Assembler::GenerateXCbzTbz(Register rn, Condition cond, Label* label) {
+  constexpr int32_t bit_no = 63;
+  constexpr OperandSize sz = kDoubleWord;
+  ASSERT(rn != CSP);
+  switch (cond) {
+    case EQ:  // equal
+      cbz(label, rn, sz);
+      return;
+    case NE:  // not equal
+      cbnz(label, rn, sz);
+      return;
+    case MI:  // minus/negative
+    case LT:  // signed less than
+      tbnz(label, rn, bit_no);
+      return;
+    case PL:  // plus/positive or zero
+    case GE:  // signed greater than or equal
+      tbz(label, rn, bit_no);
+      return;
+    default:
+      // Only conditions above allow single instruction emission.
+      UNREACHABLE();
+  }
+}
+
 }  // namespace compiler
 
 }  // namespace dart
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 0493e08..48d5c06 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1002,6 +1002,11 @@
     EmitCompareAndBranch(CBNZ, rt, label, sz);
   }
 
+  // Generate 64-bit compare with zero and branch when condition allows to use
+  // a single instruction: cbz/cbnz/tbz/tbnz.
+  bool CanGenerateXCbzTbz(Register rn, Condition cond);
+  void GenerateXCbzTbz(Register rn, Condition cond, Label* label);
+
   // Test bit and branch if zero.
   void tbz(Label* label, Register rt, intptr_t bit_number) {
     EmitTestAndBranch(TBZ, rt, bit_number, label);
diff --git a/runtime/vm/compiler/backend/compile_type.h b/runtime/vm/compiler/backend/compile_type.h
index 9aa145d..dc2e64c 100644
--- a/runtime/vm/compiler/backend/compile_type.h
+++ b/runtime/vm/compiler/backend/compile_type.h
@@ -57,6 +57,7 @@
     return *this;
   }
 
+  // TODO(regis): Should we also consider type_.nullability() here?
   bool is_nullable() const { return is_nullable_; }
 
   // Return type such that concrete value's type in runtime is guaranteed to
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index 3592d56..1016626 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -798,8 +798,8 @@
   Definition* def = instr->value()->definition();
   const Object& value = def->constant_value();
   const AbstractType& checked_type = instr->type();
-  // TODO(regis): Revisit the evaluation of the type test.
-  if (checked_type.IsTopType()) {
+  // If the checked type is a top type, the result is always true.
+  if (checked_type.IsTopType(instr->nnbd_mode())) {
     SetValue(instr, Bool::True());
   } else if (IsNonConstant(value)) {
     intptr_t value_cid = instr->value()->definition()->Type()->ToCid();
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index 021fbbe..521bc11 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -504,10 +504,12 @@
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
+    // TODO(regis): Revisit the bound check taking NNBD into consideration.
+    // TODO(alexmarkov): Fix issue #40066.
     // If it's guaranteed, by type-parameter bound, that the type parameter will
     // never have a value of a function type, then we can safely do a 4-type
     // test instead of a 6-type test.
-    auto test_kind = !bound.IsTopType() && !bound.IsFunctionType() &&
+    auto test_kind = !bound.NNBD_IsTopType() && !bound.IsFunctionType() &&
                              !bound.IsDartFunctionType() && bound.IsType()
                          ? kTestTypeSixArgs
                          : kTestTypeFourArgs;
@@ -611,7 +613,7 @@
 
 // If instanceof type test cannot be performed successfully at compile time and
 // therefore eliminated, optimize it by adding inlined tests for:
-// - NULL -> return type == Null (type is not Object or dynamic).
+// - Null -> see comment below.
 // - Smi -> compile time subtype check (only if dst class is not parameterized).
 // - Class equality (only if class is not parameterized).
 // Inputs:
@@ -626,24 +628,39 @@
                                            NNBDMode mode,
                                            LocationSummary* locs) {
   ASSERT(type.IsFinalized());
-  ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
+  ASSERT(!type.IsTopType(mode));  // Already checked.
   const Register kInstantiatorTypeArgumentsReg = R2;
   const Register kFunctionTypeArgumentsReg = R1;
   __ PushList((1 << kInstantiatorTypeArgumentsReg) |
               (1 << kFunctionTypeArgumentsReg));
+
   compiler::Label is_instance, is_not_instance;
-  // If type is instantiated and non-parameterized, we can inline code
-  // checking whether the tested instance is a Smi.
-  if (type.IsInstantiated()) {
-    // A null object is only an instance of Null, Object, and dynamic.
-    // Object and dynamic have already been checked above (if the type is
-    // instantiated). So we can return false here if the instance is null,
-    // unless the type is Null (and if the type is instantiated).
-    // We can only inline this null check if the type is instantiated at compile
-    // time, since an uninstantiated type at compile time could be Null, Object,
-    // or dynamic at run time.
-    __ CompareObject(R0, Object::null_object());
-    __ b(type.IsNullType() ? &is_instance : &is_not_instance, EQ);
+  // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+  // In addition, 'null' is an instance of Never and Object with legacy testing,
+  // or of any nullable or legacy type with nnbd testing.
+  // It is also an instance of FutureOr<T> if it is an instance of T.
+  if (mode == NNBDMode::kLegacyLib) {
+    // See Legacy_NullIsInstanceOf().
+    if (type.IsInstantiated()) {
+      AbstractType& type_arg = AbstractType::Handle(type.raw());
+      while (type_arg.IsFutureOr(&type_arg)) {
+      }
+      __ CompareObject(R0, Object::null_object());
+      __ b((type_arg.IsNullType() || type_arg.Legacy_IsTopType())
+               ? &is_instance
+               : &is_not_instance,
+           EQ);
+    }
+  } else {
+    ASSERT(mode == NNBDMode::kOptedInLib);
+    // Test is valid on uninstantiated type, unless nullability is undetermined.
+    // See NNBD_NullIsInstanceOf().
+    if (!type.IsUndetermined()) {
+      __ CompareObject(R0, Object::null_object());
+      __ b((type.IsNullable() || type.IsLegacy()) ? &is_instance
+                                                  : &is_not_instance,
+           EQ);
+    }
   }
 
   // Generate inline instanceof test.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index 9e8c06b..b4ee10f 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -483,10 +483,12 @@
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
+    // TODO(regis): Revisit the bound check taking NNBD into consideration.
+    // TODO(alexmarkov): Fix issue #40066.
     // If it's guaranteed, by type-parameter bound, that the type parameter will
     // never have a value of a function type, then we can safely do a 4-type
     // test instead of a 6-type test.
-    auto test_kind = !bound.IsTopType() && !bound.IsFunctionType() &&
+    auto test_kind = !bound.NNBD_IsTopType() && !bound.IsFunctionType() &&
                              !bound.IsDartFunctionType() && bound.IsType()
                          ? kTestTypeSixArgs
                          : kTestTypeFourArgs;
@@ -588,7 +590,7 @@
 
 // If instanceof type test cannot be performed successfully at compile time and
 // therefore eliminated, optimize it by adding inlined tests for:
-// - NULL -> return type == Null (type is not Object or dynamic).
+// - Null -> see comment below.
 // - Smi -> compile time subtype check (only if dst class is not parameterized).
 // - Class equality (only if class is not parameterized).
 // Inputs:
@@ -603,23 +605,38 @@
                                            NNBDMode mode,
                                            LocationSummary* locs) {
   ASSERT(type.IsFinalized());
-  ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
+  ASSERT(!type.IsTopType(mode));  // Already checked.
   const Register kInstantiatorTypeArgumentsReg = R1;
   const Register kFunctionTypeArgumentsReg = R2;
   __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg);
+
   compiler::Label is_instance, is_not_instance;
-  // If type is instantiated and non-parameterized, we can inline code
-  // checking whether the tested instance is a Smi.
-  if (type.IsInstantiated()) {
-    // A null object is only an instance of Null, Object, and dynamic.
-    // Object and dynamic have already been checked above (if the type is
-    // instantiated). So we can return false here if the instance is null,
-    // unless the type is Null (and if the type is instantiated).
-    // We can only inline this null check if the type is instantiated at compile
-    // time, since an uninstantiated type at compile time could be Null, Object,
-    // or dynamic at run time.
-    __ CompareObject(R0, Object::null_object());
-    __ b(type.IsNullType() ? &is_instance : &is_not_instance, EQ);
+  // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+  // In addition, 'null' is an instance of Never and Object with legacy testing,
+  // or of any nullable or legacy type with nnbd testing.
+  // It is also an instance of FutureOr<T> if it is an instance of T.
+  if (mode == NNBDMode::kLegacyLib) {
+    // See Legacy_NullIsInstanceOf().
+    if (type.IsInstantiated()) {
+      AbstractType& type_arg = AbstractType::Handle(type.raw());
+      while (type_arg.IsFutureOr(&type_arg)) {
+      }
+      __ CompareObject(R0, Object::null_object());
+      __ b((type_arg.IsNullType() || type_arg.Legacy_IsTopType())
+               ? &is_instance
+               : &is_not_instance,
+           EQ);
+    }
+  } else {
+    ASSERT(mode == NNBDMode::kOptedInLib);
+    // Test is valid on uninstantiated type, unless nullability is undetermined.
+    // See NNBD_NullIsInstanceOf().
+    if (!type.IsUndetermined()) {
+      __ CompareObject(R0, Object::null_object());
+      __ b((type.IsNullable() || type.IsLegacy()) ? &is_instance
+                                                  : &is_not_instance,
+           EQ);
+    }
   }
 
   // Generate inline instanceof test.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index d62d585..594ac84 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -482,9 +482,11 @@
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
+    // TODO(regis): Revisit the bound check taking NNBD into consideration.
+    // TODO(alexmarkov): Fix issue #40066.
     // If it's guaranteed, by type-parameter bound, that the type parameter will
     // never have a value of a function type.
-    auto test_kind = !bound.IsTopType() && !bound.IsFunctionType() &&
+    auto test_kind = !bound.NNBD_IsTopType() && !bound.IsFunctionType() &&
                              !bound.IsDartFunctionType() && bound.IsType()
                          ? kTestTypeSixArgs
                          : kTestTypeFourArgs;
@@ -590,7 +592,7 @@
 
 // If instanceof type test cannot be performed successfully at compile time and
 // therefore eliminated, optimize it by adding inlined tests for:
-// - NULL -> return type == Null (type is not Object or dynamic).
+// - Null -> see comment below.
 // - Smi -> compile time subtype check (only if dst class is not parameterized).
 // - Class equality (only if class is not parameterized).
 // Inputs:
@@ -605,7 +607,7 @@
                                            NNBDMode mode,
                                            LocationSummary* locs) {
   ASSERT(type.IsFinalized());
-  ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
+  ASSERT(!type.IsTopType(mode));  // Already checked.
 
   __ pushl(EDX);  // Store instantiator type arguments.
   __ pushl(ECX);  // Store function type arguments.
@@ -613,18 +615,30 @@
   const compiler::Immediate& raw_null =
       compiler::Immediate(reinterpret_cast<intptr_t>(Object::null()));
   compiler::Label is_instance, is_not_instance;
-  // If type is instantiated and non-parameterized, we can inline code
-  // checking whether the tested instance is a Smi.
-  if (type.IsInstantiated()) {
-    // A null object is only an instance of Null, Object, and dynamic.
-    // Object and dynamic have already been checked above (if the type is
-    // instantiated). So we can return false here if the instance is null,
-    // unless the type is Null (and if the type is instantiated).
-    // We can only inline this null check if the type is instantiated at compile
-    // time, since an uninstantiated type at compile time could be Null, Object,
-    // or dynamic at run time.
-    __ cmpl(EAX, raw_null);
-    __ j(EQUAL, type.IsNullType() ? &is_instance : &is_not_instance);
+  // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+  // In addition, 'null' is an instance of Never and Object with legacy testing,
+  // or of any nullable or legacy type with nnbd testing.
+  // It is also an instance of FutureOr<T> if it is an instance of T.
+  if (mode == NNBDMode::kLegacyLib) {
+    // See Legacy_NullIsInstanceOf().
+    if (type.IsInstantiated()) {
+      AbstractType& type_arg = AbstractType::Handle(type.raw());
+      while (type_arg.IsFutureOr(&type_arg)) {
+      }
+      __ cmpl(EAX, raw_null);
+      __ j(EQUAL, (type_arg.IsNullType() || type_arg.Legacy_IsTopType())
+                      ? &is_instance
+                      : &is_not_instance);
+    }
+  } else {
+    ASSERT(mode == NNBDMode::kOptedInLib);
+    // Test is valid on uninstantiated type, unless nullability is undetermined.
+    // See NNBD_NullIsInstanceOf().
+    if (!type.IsUndetermined()) {
+      __ cmpl(EAX, raw_null);
+      __ j(EQUAL, (type.IsNullable() || type.IsLegacy()) ? &is_instance
+                                                         : &is_not_instance);
+    }
   }
 
   // Generate inline instanceof test.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index 735fc46..9485ea4 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -509,10 +509,12 @@
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
+    // TODO(regis): Revisit the bound check taking NNBD into consideration.
+    // TODO(alexmarkov): Fix issue #40066.
     // If it's guaranteed, by type-parameter bound, that the type parameter will
     // never have a value of a function type, then we can safely do a 4-type
     // test instead of a 6-type test.
-    auto test_kind = !bound.IsTopType() && !bound.IsFunctionType() &&
+    auto test_kind = !bound.NNBD_IsTopType() && !bound.IsFunctionType() &&
                              !bound.IsDartFunctionType() && bound.IsType()
                          ? kTestTypeSixArgs
                          : kTestTypeFourArgs;
@@ -612,7 +614,7 @@
 
 // If instanceof type test cannot be performed successfully at compile time and
 // therefore eliminated, optimize it by adding inlined tests for:
-// - NULL -> return type == Null (type is not Object or dynamic).
+// - Null -> see comment below.
 // - Smi -> compile time subtype check (only if dst class is not parameterized).
 // - Class equality (only if class is not parameterized).
 // Inputs:
@@ -627,21 +629,33 @@
                                            NNBDMode mode,
                                            LocationSummary* locs) {
   ASSERT(type.IsFinalized());
-  ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
+  ASSERT(!type.IsTopType(mode));  // Already checked.
 
   compiler::Label is_instance, is_not_instance;
-  // If type is instantiated and non-parameterized, we can inline code
-  // checking whether the tested instance is a Smi.
-  if (type.IsInstantiated()) {
-    // A null object is only an instance of Null, Object, void and dynamic.
-    // Object void and dynamic have already been checked above (if the type is
-    // instantiated). So we can return false here if the instance is null,
-    // unless the type is Null (and if the type is instantiated).
-    // We can only inline this null check if the type is instantiated at compile
-    // time, since an uninstantiated type at compile time could be Null, Object,
-    // or dynamic at run time.
-    __ CompareObject(RAX, Object::null_object());
-    __ j(EQUAL, type.IsNullType() ? &is_instance : &is_not_instance);
+  // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+  // In addition, 'null' is an instance of Never and Object with legacy testing,
+  // or of any nullable or legacy type with nnbd testing.
+  // It is also an instance of FutureOr<T> if it is an instance of T.
+  if (mode == NNBDMode::kLegacyLib) {
+    // See Legacy_NullIsInstanceOf().
+    if (type.IsInstantiated()) {
+      AbstractType& type_arg = AbstractType::Handle(type.raw());
+      while (type_arg.IsFutureOr(&type_arg)) {
+      }
+      __ CompareObject(RAX, Object::null_object());
+      __ j(EQUAL, (type_arg.IsNullType() || type_arg.Legacy_IsTopType())
+                      ? &is_instance
+                      : &is_not_instance);
+    }
+  } else {
+    ASSERT(mode == NNBDMode::kOptedInLib);
+    // Test is valid on uninstantiated type, unless nullability is undetermined.
+    // See NNBD_NullIsInstanceOf().
+    if (!type.IsUndetermined()) {
+      __ CompareObject(RAX, Object::null_object());
+      __ j(EQUAL, (type.IsNullable() || type.IsLegacy()) ? &is_instance
+                                                         : &is_not_instance);
+    }
   }
 
   // Generate inline instanceof test.
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 6bdb4d3..d78a065 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -3242,21 +3242,14 @@
 }
 
 static bool MayBeNumber(CompileType* type) {
-  // TODO(regis): Does this function depend on NNBDMode?
   if (type->IsNone()) {
     return false;
   }
   auto& compile_type = AbstractType::Handle(type->ToAbstractType()->raw());
-  if (compile_type.IsType() &&
-      Class::Handle(compile_type.type_class()).IsFutureOrClass()) {
-    const auto& type_args = TypeArguments::Handle(compile_type.arguments());
-    if (type_args.IsNull()) {
-      return true;
-    }
-    compile_type = type_args.TypeAt(0);
+  while (compile_type.IsFutureOr(&compile_type)) {
   }
   // Note that type 'Number' is a subtype of itself.
-  return compile_type.IsTopType() || compile_type.IsTypeParameter() ||
+  return compile_type.Legacy_IsTopType() || compile_type.IsTypeParameter() ||
          compile_type.IsSubtypeOf(NNBDMode::kLegacyLib,
                                   Type::Handle(Type::Number()), Heap::kOld);
 }
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 2f5a6e9..45a4987 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -638,9 +638,48 @@
   }
 }
 
+static bool AreLabelsNull(BranchLabels labels) {
+  return (labels.true_label == nullptr && labels.false_label == nullptr &&
+          labels.fall_through == nullptr);
+}
+
+static bool CanUseCbzTbzForComparison(FlowGraphCompiler* compiler,
+                                      Register rn,
+                                      Condition cond,
+                                      BranchLabels labels) {
+  return !AreLabelsNull(labels) && __ CanGenerateXCbzTbz(rn, cond);
+}
+
+static void EmitCbzTbz(Register reg,
+                       FlowGraphCompiler* compiler,
+                       Condition true_condition,
+                       BranchLabels labels) {
+  ASSERT(CanUseCbzTbzForComparison(compiler, reg, true_condition, labels));
+  if (labels.fall_through == labels.false_label) {
+    // If the next block is the false successor we will fall through to it.
+    __ GenerateXCbzTbz(reg, true_condition, labels.true_label);
+  } else {
+    // If the next block is not the false successor we will branch to it.
+    Condition false_condition = NegateCondition(true_condition);
+    __ GenerateXCbzTbz(reg, false_condition, labels.false_label);
+
+    // Fall through or jump to the true successor.
+    if (labels.fall_through != labels.true_label) {
+      __ b(labels.true_label);
+    }
+  }
+}
+
+// Similar to ComparisonInstr::EmitComparisonCode, may either:
+//   - emit comparison code and return a valid condition in which case the
+//     caller is expected to emit a branch to the true label based on that
+//     condition (or a branch to the false label on the opposite condition).
+//   - emit comparison code with a branch directly to the labels and return
+//     kInvalidCondition.
 static Condition EmitInt64ComparisonOp(FlowGraphCompiler* compiler,
                                        LocationSummary* locs,
-                                       Token::Kind kind) {
+                                       Token::Kind kind,
+                                       BranchLabels labels) {
   Location left = locs->in(0);
   Location right = locs->in(1);
   ASSERT(!left.IsConstant() || !right.IsConstant());
@@ -660,6 +699,13 @@
     }
 
     if (right_constant->IsUnboxedSignedIntegerConstant()) {
+      const int64_t constant =
+          right_constant->GetUnboxedSignedIntegerConstantValue();
+      if (constant == 0 && CanUseCbzTbzForComparison(compiler, left.reg(),
+                                                     true_condition, labels)) {
+        EmitCbzTbz(left.reg(), compiler, true_condition, labels);
+        return kInvalidCondition;
+      }
       __ CompareImmediate(
           left.reg(), right_constant->GetUnboxedSignedIntegerConstantValue());
     } else {
@@ -741,7 +787,7 @@
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
   if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
-    return EmitInt64ComparisonOp(compiler, locs(), kind());
+    return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
   } else {
     ASSERT(operation_cid() == kDoubleCid);
     return EmitDoubleComparisonOp(compiler, locs(), labels, kind());
@@ -862,7 +908,7 @@
 Condition RelationalOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                 BranchLabels labels) {
   if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
-    return EmitInt64ComparisonOp(compiler, locs(), kind());
+    return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
   } else {
     ASSERT(operation_cid() == kDoubleCid);
     return EmitDoubleComparisonOp(compiler, locs(), labels, kind());
@@ -3385,7 +3431,7 @@
 Condition CheckedSmiComparisonInstr::EmitComparisonCode(
     FlowGraphCompiler* compiler,
     BranchLabels labels) {
-  return EmitInt64ComparisonOp(compiler, locs(), kind());
+  return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
 }
 
 #define EMIT_SMI_CHECK                                                         \
@@ -3414,8 +3460,9 @@
   compiler->AddSlowPathCode(slow_path);
   EMIT_SMI_CHECK;
   Condition true_condition = EmitComparisonCode(compiler, labels);
-  ASSERT(true_condition != kInvalidCondition);
-  EmitBranchOnCondition(compiler, true_condition, labels);
+  if (true_condition != kInvalidCondition) {
+    EmitBranchOnCondition(compiler, true_condition, labels);
+  }
   // No need to bind slow_path->exit_label() as slow path exits through
   // true/false branch labels.
 }
@@ -3435,8 +3482,9 @@
   compiler->AddSlowPathCode(slow_path);
   EMIT_SMI_CHECK;
   Condition true_condition = EmitComparisonCode(compiler, labels);
-  ASSERT(true_condition != kInvalidCondition);
-  EmitBranchOnCondition(compiler, true_condition, labels);
+  if (true_condition != kInvalidCondition) {
+    EmitBranchOnCondition(compiler, true_condition, labels);
+  }
   Register result = locs()->out(0).reg();
   __ Bind(false_label);
   __ LoadObject(result, Bool::False());
@@ -3928,13 +3976,35 @@
                                                     bool opt) const {
   const intptr_t kNumInputs = 1;
   const intptr_t kNumTemps = ValueFitsSmi() ? 0 : 1;
-  LocationSummary* summary = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps,
-                      ValueFitsSmi() ? LocationSummary::kNoCall
-                                     : LocationSummary::kCallOnSlowPath);
+  // Shared slow path is used in BoxInt64Instr::EmitNativeCode in
+  // FLAG_use_bare_instructions mode and only after VM isolate stubs where
+  // replaced with isolate-specific stubs.
+  const bool stubs_in_vm_isolate = (Isolate::Current()
+                                        ->object_store()
+                                        ->allocate_mint_with_fpu_regs_stub()
+                                        ->InVMIsolateHeap() ||
+                                    Isolate::Current()
+                                        ->object_store()
+                                        ->allocate_mint_without_fpu_regs_stub()
+                                        ->InVMIsolateHeap());
+  const bool shared_slow_path_call = SlowPathSharingSupported(opt) &&
+                                     FLAG_use_bare_instructions &&
+                                     !stubs_in_vm_isolate;
+  LocationSummary* summary = new (zone) LocationSummary(
+      zone, kNumInputs, kNumTemps,
+      ValueFitsSmi()
+          ? LocationSummary::kNoCall
+          : shared_slow_path_call ? LocationSummary::kCallOnSharedSlowPath
+                                  : LocationSummary::kCallOnSlowPath);
+
   summary->set_in(0, Location::RequiresRegister());
-  summary->set_out(0, Location::RequiresRegister());
-  if (!ValueFitsSmi()) {
+  if (ValueFitsSmi()) {
+    summary->set_out(0, Location::RequiresRegister());
+  } else if (shared_slow_path_call) {
+    summary->set_out(0, Location::RegisterLocation(R0));
+    summary->set_temp(0, Location::RegisterLocation(R1));
+  } else {
+    summary->set_out(0, Location::RequiresRegister());
     summary->set_temp(0, Location::RequiresRegister());
   }
   return summary;
@@ -3947,7 +4017,6 @@
     __ SmiTag(out, in);
     return;
   }
-
   ASSERT(kSmiTag == 0);
   __ LslImmediate(out, in, kSmiTagSize);
   compiler::Label done;
@@ -3955,8 +4024,37 @@
   __ b(&done, EQ);
 
   Register temp = locs()->temp(0).reg();
-  BoxAllocationSlowPath::Allocate(compiler, this, compiler->mint_class(), out,
-                                  temp);
+
+  if (compiler->intrinsic_mode()) {
+    __ TryAllocate(compiler->mint_class(),
+                   compiler->intrinsic_slow_path_label(), out, temp);
+  } else {
+    if (locs()->call_on_shared_slow_path()) {
+      auto object_store = compiler->isolate()->object_store();
+      const bool live_fpu_regs =
+          locs()->live_registers()->FpuRegisterCount() > 0;
+      const auto& stub = Code::ZoneHandle(
+          compiler->zone(),
+          live_fpu_regs ? object_store->allocate_mint_with_fpu_regs_stub()
+                        : object_store->allocate_mint_without_fpu_regs_stub());
+      // BoxInt64Instr uses shared slow path only if stub can be reached
+      // via PC-relative call.
+      ASSERT(FLAG_use_bare_instructions);
+      ASSERT(!stub.InVMIsolateHeap());
+      __ GenerateUnRelocatedPcRelativeCall();
+      compiler->AddPcRelativeCallStubTarget(stub);
+
+      ASSERT(!locs()->live_registers()->ContainsRegister(R0));
+
+      auto extended_env = compiler->SlowPathEnvironmentFor(this, 0);
+      compiler->EmitCallsiteMetadata(token_pos(), DeoptId::kNone,
+                                     RawPcDescriptors::kOther, locs(),
+                                     extended_env);
+    } else {
+      BoxAllocationSlowPath::Allocate(compiler, this, compiler->mint_class(),
+                                      out, temp);
+    }
+  }
 
   __ StoreToOffset(in, out, Mint::value_offset() - kHeapObjectTag);
   __ Bind(&done);
@@ -6360,6 +6458,28 @@
   return locs;
 }
 
+static Condition EmitComparisonCodeRegConstant(FlowGraphCompiler* compiler,
+                                               Token::Kind kind,
+                                               BranchLabels labels,
+                                               Register reg,
+                                               const Object& obj,
+                                               bool needs_number_check,
+                                               TokenPosition token_pos,
+                                               intptr_t deopt_id) {
+  Condition orig_cond = (kind == Token::kEQ_STRICT) ? EQ : NE;
+  if (!needs_number_check && compiler::target::IsSmi(obj) &&
+      compiler::target::ToRawSmi(obj) == 0 &&
+      CanUseCbzTbzForComparison(compiler, reg, orig_cond, labels)) {
+    EmitCbzTbz(reg, compiler, orig_cond, labels);
+    return kInvalidCondition;
+  } else {
+    Condition true_condition = compiler->EmitEqualityRegConstCompare(
+        reg, obj, needs_number_check, token_pos, deopt_id);
+    return (kind != Token::kEQ_STRICT) ? NegateCondition(true_condition)
+                                       : true_condition;
+  }
+}
+
 Condition StrictCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                  BranchLabels labels) {
   Location left = locs()->in(0);
@@ -6367,22 +6487,19 @@
   ASSERT(!left.IsConstant() || !right.IsConstant());
   Condition true_condition;
   if (left.IsConstant()) {
-    true_condition = compiler->EmitEqualityRegConstCompare(
-        right.reg(), left.constant(), needs_number_check(), token_pos(),
-        deopt_id_);
+    return EmitComparisonCodeRegConstant(compiler, kind(), labels, right.reg(),
+                                         left.constant(), needs_number_check(),
+                                         token_pos(), deopt_id_);
   } else if (right.IsConstant()) {
-    true_condition = compiler->EmitEqualityRegConstCompare(
-        left.reg(), right.constant(), needs_number_check(), token_pos(),
-        deopt_id_);
+    return EmitComparisonCodeRegConstant(compiler, kind(), labels, left.reg(),
+                                         right.constant(), needs_number_check(),
+                                         token_pos(), deopt_id_);
   } else {
     true_condition = compiler->EmitEqualityRegRegCompare(
         left.reg(), right.reg(), needs_number_check(), token_pos(), deopt_id_);
+    return (kind() != Token::kEQ_STRICT) ? NegateCondition(true_condition)
+                                         : true_condition;
   }
-  if (kind() != Token::kEQ_STRICT) {
-    ASSERT(kind() == Token::kNE_STRICT);
-    true_condition = NegateCondition(true_condition);
-  }
-  return true_condition;
 }
 
 void ComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index de51c81..0fd3634 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -791,8 +791,7 @@
                                          bool is_nullable,
                                          bool* is_instance) {
   ASSERT(is_instance != NULL);
-  // TODO(regis): Take mode into consideration.
-  if (type.IsDynamicType() || type.IsObjectType() || type.IsVoidType()) {
+  if (type.IsTopType(mode)) {
     *is_instance = true;
     return true;
   }
@@ -804,13 +803,32 @@
   // Consider the compile type of the value.
   const AbstractType& compile_type = *ToAbstractType();
 
-  // The null instance is an instance of Null, of Object, and of dynamic.
+  // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+  // In addition, 'null' is an instance of Never and Object with legacy testing,
+  // or of any nullable or legacy type with nnbd testing.
+  // It is also an instance of FutureOr<T> if it is an instance of T.
   // Functions that do not explicitly return a value, implicitly return null,
   // except generative constructors, which return the object being constructed.
   // It is therefore acceptable for void functions to return null.
   if (compile_type.IsNullType()) {
-    *is_instance = is_nullable || type.IsObjectType() || type.IsDynamicType() ||
-                   type.IsNullType() || type.IsVoidType();
+    if (mode == NNBDMode::kLegacyLib) {
+      // Using legacy testing.
+      if (!type.IsInstantiated()) {
+        return false;
+      }
+      AbstractType& type_arg = AbstractType::Handle(type.raw());
+      while (type_arg.IsFutureOr(&type_arg)) {
+      }
+      *is_instance =
+          is_nullable || type_arg.IsNullType() || type_arg.Legacy_IsTopType();
+    } else {
+      ASSERT(mode == NNBDMode::kOptedInLib);
+      // Using nnbd testing.
+      if (type.IsUndetermined()) {
+        return false;
+      }
+      *is_instance = is_nullable || type.IsNullable() || type.IsLegacy();
+    }
     return true;
   }
 
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index 7535d0e..ac5f06e 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -854,9 +854,9 @@
 
   // Build an AssertAssignable if necessary.
   const AbstractType& dst_type = AbstractType::ZoneHandle(zone(), field.type());
-  if (I->argument_type_checks() && !dst_type.IsTopType()) {
+  if (I->argument_type_checks() && !dst_type.IsTopType(target.nnbd_mode())) {
     // Compute if we need to type check the value. Always type check if
-    // not in strong mode or if at a dynamic invocation.
+    // at a dynamic invocation.
     bool needs_check = true;
     if (!instr->interface_target().IsNull()) {
       if (field.is_covariant()) {
@@ -1095,18 +1095,31 @@
     if (cls.NumTypeArguments() > 0) {
       return Bool::null();
     }
-    // As of Dart 1.5, the Null type is a subtype of (and is more specific than)
-    // any type. However, we are checking instances here and not types. The
-    // null instance is only an instance of Null, Object, and dynamic.
-    // TODO(regis): Consider nullability of type if mode is not kLegacy, or if
-    // strong mode is enabled.
-    const bool is_subtype =
-        cls.IsNullClass()
-            ? (type_class.IsNullClass() || type_class.IsObjectClass() ||
-               type_class.IsDynamicClass())
-            : Class::IsSubtypeOf(mode, cls, Object::null_type_arguments(),
-                                 type_class, Object::null_type_arguments(),
-                                 Heap::kOld);
+    bool is_subtype = false;
+    if (cls.IsNullClass()) {
+      // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+      // In addition, 'null' is an instance of Never and Object with legacy
+      // testing, or of any nullable or legacy type with nnbd testing.
+      // It is also an instance of FutureOr<T> if it is an instance of T.
+      if (mode == NNBDMode::kLegacyLib) {
+        // Using legacy testing.
+        ASSERT(type.IsInstantiated());
+        AbstractType& type_arg = AbstractType::Handle(type.raw());
+        while (type_arg.IsFutureOr(&type_arg)) {
+        }
+        is_subtype = type_arg.IsNullType() || type_arg.Legacy_IsTopType();
+      } else {
+        ASSERT(mode == NNBDMode::kOptedInLib);
+        // Using nnbd testing.
+        ASSERT(!type.IsUndetermined());
+        is_subtype =
+            type.IsNullable() || type.IsLegacy() || type.NNBD_IsTopType();
+      }
+    } else {
+      is_subtype = Class::IsSubtypeOf(mode, cls, Object::null_type_arguments(),
+                                      type_class, Object::null_type_arguments(),
+                                      Heap::kOld);
+    }
     results->Add(cls.id());
     results->Add(static_cast<intptr_t>(is_subtype));
     if (prev.IsNull()) {
@@ -1136,10 +1149,6 @@
     return false;
   }
 
-  if (mode != NNBDMode::kLegacyLib) {
-    return false;  // TODO(regis): Implement.
-  }
-
   // Private classes cannot be subclassed by later loaded libs.
   if (!type_class.IsPrivate()) {
     // In AOT mode we can't use CHA deoptimizations.
@@ -1169,7 +1178,15 @@
         TypeArguments::Handle(type.arguments());
     const bool is_raw_type = type_arguments.IsNull() ||
                              type_arguments.IsRaw(from_index, num_type_params);
-    return is_raw_type;
+    if (!is_raw_type) {
+      return false;
+    }
+  }
+  if (mode == NNBDMode::kOptedInLib && !type.IsNonNullable()) {
+    // A class id check is not sufficient, since a null instance also satisfies
+    // the test against a nullable or legacy type.
+    // TODO(regis): Add a null check in addition to the class id check?
+    return false;
   }
   return true;
 }
@@ -1188,17 +1205,33 @@
   ASSERT(I->can_use_strong_mode_types());
   ASSERT(Token::IsTypeTestOperator(call->token_kind()));
 
-  if (mode != NNBDMode::kLegacyLib) {
-    return false;  // TODO(regis): Implement.
-  }
-
-  if (type.IsDynamicType() || type.IsObjectType() || !type.IsInstantiated()) {
+  // The goal is to emit code that will determine the result of 'x is type'
+  // depending solely on the fact that x == null or not.
+  // 'null' is an instance of Null, Object*, Object?, void, and dynamic.
+  // In addition, 'null' is an instance of Never and Object with legacy testing,
+  // or of any nullable or legacy type with nnbd testing.
+  // It is also an instance of FutureOr<T> if it is an instance of T.
+  if (mode == NNBDMode::kLegacyLib) {
+    // Using legacy testing of null instance.
+    if (type.Legacy_IsTopType() || !type.IsInstantiated()) {
+      return false;  // Always true or cannot tell.
+    }
+  } else {
+    ASSERT(mode == NNBDMode::kOptedInLib);
+    // Checking whether the receiver is null cannot help in an opted-in library.
+    // Indeed, its static type would have to be nullable to legally hold null.
+    // For the static type to be a subtype of the tested type, the tested type
+    // has to be nullable as well, in which case it makes no difference if the
+    // receiver is null or not.
     return false;
   }
 
   const intptr_t receiver_index = call->FirstArgIndex();
   Value* left_value = call->ArgumentValueAt(receiver_index);
 
+  // If the static type of the receiver is a subtype of the tested type,
+  // replace 'receiver is type' with 'receiver == null' if type is Null
+  // or with 'receiver != null' otherwise.
   if (left_value->Type()->IsSubtypeOf(mode, type)) {
     Definition* replacement = new (Z) StrictCompareInstr(
         call->token_pos(),
@@ -1246,8 +1279,6 @@
         Smi::Cast(call->ArgumentAt(4)->AsConstant()->value()).Value());
   }
 
-  // TODO(regis): Revisit call_specializer for NNBD.
-
   if (I->can_use_strong_mode_types() &&
       TryOptimizeInstanceOfUsingStaticTypes(nnbd_mode, call, type)) {
     return;
@@ -1281,8 +1312,7 @@
         Z, InstanceOfAsBool(nnbd_mode, unary_checks, type, results));
     if (as_bool.IsNull() || FLAG_precompiled_mode) {
       if (results->length() == number_of_checks * 2) {
-        const bool can_deopt =
-            SpecializeTestCidsForNumericTypes(nnbd_mode, results, type);
+        const bool can_deopt = SpecializeTestCidsForNumericTypes(results, type);
         if (can_deopt &&
             !speculative_policy_->IsAllowedForInlining(call->deopt_id())) {
           // Guard against repeated speculative inlining.
@@ -1429,7 +1459,6 @@
 }
 
 bool CallSpecializer::SpecializeTestCidsForNumericTypes(
-    NNBDMode mode,
     ZoneGrowableArray<intptr_t>* results,
     const AbstractType& type) {
   ASSERT(results->length() >= 2);  // At least on entry.
@@ -1437,11 +1466,10 @@
   if ((*results)[0] != kSmiCid) {
     const Class& smi_class = Class::Handle(class_table.At(kSmiCid));
     const Class& type_class = Class::Handle(type.type_class());
-    // TODO(regis): Consider nullability of type if mode is not kLegacy, or if
-    // strong mode is enabled.
+    // When testing '42 is type', the nullability of type is irrelevant.
     const bool smi_is_subtype = Class::IsSubtypeOf(
-        mode, smi_class, Object::null_type_arguments(), type_class,
-        Object::null_type_arguments(), Heap::kOld);
+        NNBDMode::kLegacyLib, smi_class, Object::null_type_arguments(),
+        type_class, Object::null_type_arguments(), Heap::kOld);
     results->Add((*results)[results->length() - 2]);
     results->Add((*results)[results->length() - 2]);
     for (intptr_t i = results->length() - 3; i > 1; --i) {
@@ -1565,12 +1593,12 @@
       value_type = call->ArgumentAt(receiver_index + 2)->Type();
     }
 
-    // TODO(regis): Revisit for NNBD support.
-
     auto& type_class = Class::Handle(zone_);
 #define TRY_INLINE(iface, member_name, type, cid)                              \
   if (!member_name.IsNull()) {                                                 \
-    if (receiver_type->IsAssignableTo(NNBDMode::kLegacyLib, member_name)) {    \
+    const NNBDMode mode =                                                      \
+        member_name.IsLegacy() ? NNBDMode::kLegacyLib : NNBDMode::kOptedInLib; \
+    if (receiver_type->IsAssignableTo(mode, member_name)) {                    \
       if (is_length_getter) {                                                  \
         type_class = member_name.type_class();                                 \
         ReplaceWithLengthGetter(call);                                         \
@@ -1580,7 +1608,7 @@
         ReplaceWithIndexGet(call, cid);                                        \
       } else {                                                                 \
         if (!index_type->IsNullableInt()) return;                              \
-        if (!value_type->IsAssignableTo(NNBDMode::kLegacyLib, type)) return;   \
+        if (!value_type->IsAssignableTo(mode, type)) return;                   \
         type_class = member_name.type_class();                                 \
         ReplaceWithIndexSet(call, cid);                                        \
       }                                                                        \
diff --git a/runtime/vm/compiler/call_specializer.h b/runtime/vm/compiler/call_specializer.h
index 80f4afe..940243d 100644
--- a/runtime/vm/compiler/call_specializer.h
+++ b/runtime/vm/compiler/call_specializer.h
@@ -168,7 +168,6 @@
   // necessary for common number-related type tests.  Unconditionally adds an
   // entry for the Smi type to the start of the array.
   static bool SpecializeTestCidsForNumericTypes(
-      NNBDMode mode,
       ZoneGrowableArray<intptr_t>* results,
       const AbstractType& type);
 
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index 7c9a9db..f14cd8b 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -384,6 +384,7 @@
   checks.Add(function);
   checks.Add(default_args);
 
+  const NNBDMode nnbd_mode = function.nnbd_mode();
   const auto& type_params =
       TypeArguments::Handle(Z, function.type_parameters());
   if (!type_params.IsNull()) {
@@ -392,7 +393,7 @@
     for (intptr_t i = 0, n = type_params.Length(); i < n; ++i) {
       type_param ^= type_params.TypeAt(i);
       bound = type_param.bound();
-      if (!bound.IsTopType() && !type_param.IsGenericCovariantImpl()) {
+      if (!bound.IsTopType(nnbd_mode) && !type_param.IsGenericCovariantImpl()) {
         name = type_param.name();
         ASSERT(type_param.IsFinalized());
         check = ParameterTypeCheck::New();
@@ -418,7 +419,7 @@
   const bool has_optional_parameters = function.HasOptionalParameters();
   for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
     type = function.ParameterTypeAt(i);
-    if (!type.IsTopType() && !is_generic_covariant_impl.Contains(i) &&
+    if (!type.IsTopType(nnbd_mode) && !is_generic_covariant_impl.Contains(i) &&
         !is_covariant.Contains(i)) {
       name = function.ParameterNameAt(i);
       intptr_t index;
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index b4f6ad2..a57fa3c 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -3572,9 +3572,8 @@
   const NNBDMode nnbd_mode = parsed_function()->function().nnbd_mode();
 
   // The VM does not like an instanceOf call with a dynamic type. We need to
-  // special case this situation by detecting a top type (using non-nullable
-  // semantics which is safe in all cases).
-  if (type.NNBD_IsTopType()) {
+  // special case this situation by detecting a top type.
+  if (type.IsTopType(nnbd_mode)) {
     // Evaluate the expression on the left but ignore its result.
     instructions += Drop();
 
@@ -3623,7 +3622,8 @@
   Fragment instructions = BuildExpression();  // read operand.
 
   const AbstractType& type = T.BuildType();  // read type.
-  if (type.IsInstantiated() && type.IsTopType()) {
+  const NNBDMode nnbd_mode = parsed_function()->function().nnbd_mode();
+  if (type.IsInstantiated() && type.IsTopType(nnbd_mode)) {
     // We already evaluated the operand on the left and just leave it there as
     // the result of the `obj as dynamic` expression.
   } else {
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index c7afab66..67af895 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1701,11 +1701,14 @@
   String& name = String::Handle(Z);
   AbstractType& bound = AbstractType::Handle(Z);
   Fragment check_bounds;
+  const NNBDMode nnbd_mode = forwarding_target != nullptr
+                                 ? forwarding_target->nnbd_mode()
+                                 : dart_function.nnbd_mode();
   for (intptr_t i = 0; i < num_type_params; ++i) {
     type_param ^= type_parameters.TypeAt(i);
 
     bound = type_param.bound();
-    if (bound.IsTopType()) {
+    if (bound.IsTopType(nnbd_mode)) {
       continue;
     }
 
@@ -1762,7 +1765,7 @@
           &AbstractType::ZoneHandle(Z, forwarding_target->ParameterTypeAt(i));
     }
 
-    if (target_type->IsTopType()) continue;
+    if (target_type->IsTopType(nnbd_mode)) continue;
 
     const bool is_covariant = param->is_explicit_covariant_parameter();
     Fragment* checks = is_covariant ? explicit_checks : implicit_checks;
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 7c491dd..0af2249 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -1162,12 +1162,15 @@
 // Called for allocation of Mint.
 void StubCodeCompiler::GenerateAllocateMintWithFPURegsStub(
     Assembler* assembler) {
-  Label slow_case;
-  __ TryAllocate(compiler::MintClass(), &slow_case, /*instance_reg=*/R0,
-                 /*temp_reg=*/R1);
-  __ Ret();
+  // For test purpose call allocation stub without inline allocation attempt.
+  if (!FLAG_use_slow_path) {
+    Label slow_case;
+    __ TryAllocate(compiler::MintClass(), &slow_case, /*instance_reg=*/R0,
+                   /*temp_reg=*/R1);
+    __ Ret();
 
-  __ Bind(&slow_case);
+    __ Bind(&slow_case);
+  }
   GenerateSharedStub(assembler, /*save_fpu_registers=*/true,
                      &kAllocateMintRuntimeEntry,
                      target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
@@ -1178,12 +1181,15 @@
 // Called for allocation of Mint.
 void StubCodeCompiler::GenerateAllocateMintWithoutFPURegsStub(
     Assembler* assembler) {
-  Label slow_case;
-  __ TryAllocate(compiler::MintClass(), &slow_case, /*instance_reg=*/R0,
-                 /*temp_reg=*/R1);
-  __ Ret();
+  // For test purpose call allocation stub without inline allocation attempt.
+  if (!FLAG_use_slow_path) {
+    Label slow_case;
+    __ TryAllocate(compiler::MintClass(), &slow_case, /*instance_reg=*/R0,
+                   /*temp_reg=*/R1);
+    __ Ret();
 
-  __ Bind(&slow_case);
+    __ Bind(&slow_case);
+  }
   GenerateSharedStub(
       assembler, /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
       target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 45a8ef7..a1162b8 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -180,7 +180,8 @@
                         bool save_fpu_registers,
                         const RuntimeEntry* target,
                         intptr_t self_code_stub_offset_from_thread,
-                        bool allow_return) {
+                        bool allow_return,
+                        bool store_runtime_result_in_r0 = false) {
   // We want the saved registers to appear like part of the caller's frame, so
   // we push them before calling EnterStubFrame.
   RegisterSet all_registers;
@@ -192,12 +193,28 @@
   __ PushRegisters(all_registers);
   __ ldr(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
   __ EnterStubFrame();
+  if (store_runtime_result_in_r0) {
+    ASSERT(all_registers.ContainsRegister(R0));
+    ASSERT(allow_return);
+
+    // Push an even value so it will not be seen as a pointer
+    __ Push(LR);
+  }
   __ CallRuntime(*target, /*argument_count=*/0);
+
+  if (store_runtime_result_in_r0) {
+    __ Pop(R0);
+  }
   if (!allow_return) {
     __ Breakpoint();
     return;
   }
   __ LeaveStubFrame();
+  if (store_runtime_result_in_r0) {
+    // Stores the runtime result in stack where R0 was pushed ( R0 is the very
+    // last register to be pushed by __ PushRegisters(all_registers) )
+    __ str(R0, Address(SP));
+  }
   __ PopRegisters(all_registers);
   __ Pop(LR);
   __ ret(LR);
@@ -1235,12 +1252,38 @@
 
 void StubCodeCompiler::GenerateAllocateMintWithFPURegsStub(
     Assembler* assembler) {
-  __ Stop("Unimplemented");
+  // For test purpose call allocation stub without inline allocation attempt.
+  if (!FLAG_use_slow_path) {
+    Label slow_case;
+    __ TryAllocate(compiler::MintClass(), &slow_case, /*instance_reg=*/R0,
+                   /*temp_reg=*/R1);
+    __ Ret();
+
+    __ Bind(&slow_case);
+  }
+  GenerateSharedStub(assembler, /*save_fpu_registers=*/true,
+                     &kAllocateMintRuntimeEntry,
+                     target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
+                     /*allow_return=*/true,
+                     /*store_runtime_result_in_r0=*/true);
 }
 
 void StubCodeCompiler::GenerateAllocateMintWithoutFPURegsStub(
     Assembler* assembler) {
-  __ Stop("Unimplemented");
+  // For test purpose call allocation stub without inline allocation attempt.
+  if (!FLAG_use_slow_path) {
+    Label slow_case;
+    __ TryAllocate(compiler::MintClass(), &slow_case, /*instance_reg=*/R0,
+                   /*temp_reg=*/R1);
+    __ Ret();
+
+    __ Bind(&slow_case);
+  }
+  GenerateSharedStub(
+      assembler, /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
+      target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
+      /*allow_return=*/true,
+      /*store_runtime_result_in_r0=*/true);
 }
 
 // Called when invoking Dart code from C++ (VM code).
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index d9317ad..6c701a1 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6142,8 +6142,7 @@
   StreamingWriteStream debug_stream(generate_debug ? kInitialDebugSize : 0,
                                     callback, debug_callback_data);
 
-  Elf* elf =
-      generate_debug ? new (Z) Elf(Z, nullptr, strip, &debug_stream) : nullptr;
+  Elf* elf = generate_debug ? new (Z) Elf(Z, &debug_stream) : nullptr;
 
   AssemblyImageWriter image_writer(T, callback, callback_data, strip, elf);
   uint8_t* vm_snapshot_data_buffer = NULL;
@@ -6217,12 +6216,11 @@
   StreamingWriteStream debug_stream(generate_debug ? kInitialDebugSize : 0,
                                     callback, debug_callback_data);
 
-  Elf* elf = new (Z)
-      Elf(Z, &elf_stream, strip, generate_debug ? &debug_stream : nullptr);
-  Dwarf* dwarf = nullptr;
-  if (!strip || generate_debug) {
-    dwarf = new (Z) Dwarf(Z, nullptr, elf);
-  }
+  Elf* elf = new (Z) Elf(Z, &elf_stream);
+  Dwarf* elf_dwarf = strip ? nullptr : new (Z) Dwarf(Z, nullptr, elf);
+  Elf* debug_elf = generate_debug ? new (Z) Elf(Z, &debug_stream) : nullptr;
+  Dwarf* debug_dwarf =
+      generate_debug ? new (Z) Dwarf(Z, nullptr, debug_elf) : nullptr;
 
   // Note that the BSS section must come first because it cannot be placed in
   // between any two non-writable segments, due to a bug in Jelly Bean's ELF
@@ -6231,11 +6229,11 @@
       elf->AddBSSData("_kDartBSSData", sizeof(compiler::target::uword));
 
   BlobImageWriter vm_image_writer(T, &vm_snapshot_instructions_buffer,
-                                  ApiReallocate, kInitialSize, bss_base, elf,
-                                  dwarf);
+                                  ApiReallocate, kInitialSize, debug_dwarf,
+                                  bss_base, elf, elf_dwarf);
   BlobImageWriter isolate_image_writer(T, &isolate_snapshot_instructions_buffer,
-                                       ApiReallocate, kInitialSize, bss_base,
-                                       elf, dwarf);
+                                       ApiReallocate, kInitialSize, debug_dwarf,
+                                       bss_base, elf, elf_dwarf);
   FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer,
                             &isolate_snapshot_data_buffer, ApiReallocate,
                             &vm_image_writer, &isolate_image_writer);
@@ -6245,12 +6243,17 @@
                  writer.VmIsolateSnapshotSize());
   elf->AddROData("_kDartIsolateSnapshotData", isolate_snapshot_data_buffer,
                  writer.IsolateSnapshotSize());
-  if (dwarf != nullptr) {
+
+  if (elf_dwarf != nullptr) {
     // TODO(rmacnak): Generate .debug_frame / .eh_frame / .arm.exidx to
     // provide unwinding information.
-    dwarf->Write();
+    elf_dwarf->Write();
   }
   elf->Finalize();
+  if (generate_debug) {
+    debug_dwarf->Write();
+    debug_elf->Finalize();
+  }
 
   return Api::Success();
 #endif
@@ -6300,16 +6303,14 @@
   Elf* elf = nullptr;
   Dwarf* dwarf = nullptr;
   if (generate_debug) {
-    elf = new (Z) Elf(Z, nullptr, /*strip=*/false, &debug_stream);
+    elf = new (Z) Elf(Z, &debug_stream);
     dwarf = new (Z) Dwarf(Z, nullptr, elf);
   }
 
   BlobImageWriter vm_image_writer(T, vm_snapshot_instructions_buffer,
-                                  ApiReallocate, kInitialSize, /*bss_base=*/0,
-                                  elf, dwarf);
+                                  ApiReallocate, kInitialSize, dwarf);
   BlobImageWriter isolate_image_writer(T, isolate_snapshot_instructions_buffer,
-                                       ApiReallocate, kInitialSize,
-                                       /*bss_base=*/0, elf, dwarf);
+                                       ApiReallocate, kInitialSize, dwarf);
   FullSnapshotWriter writer(Snapshot::kFullAOT, vm_snapshot_data_buffer,
                             isolate_snapshot_data_buffer, ApiReallocate,
                             &vm_image_writer, &isolate_image_writer);
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index dbf3db6..60e9b62 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -7,6 +7,7 @@
 #include "platform/elf.h"
 #include "platform/text_buffer.h"
 #include "vm/cpu.h"
+#include "vm/hash_map.h"
 #include "vm/thread.h"
 
 namespace dart {
@@ -29,96 +30,335 @@
 static const intptr_t kElfSymbolHashTableEntrySize = 4;
 #endif
 
+#define DEFINE_LINEAR_FIELD_METHODS(name, type, init)                          \
+  type name() const {                                                          \
+    ASSERT(name##_ != init);                                                   \
+    return name##_;                                                            \
+  }                                                                            \
+  void set_##name(type value) {                                                \
+    ASSERT(name##_ == init);                                                   \
+    name##_ = value;                                                           \
+  }
+
+#define DEFINE_LINEAR_FIELD(name, type, init) type name##_ = init;
+
 class Section : public ZoneAllocated {
  public:
-  Section() {}
+  Section(intptr_t type,
+          intptr_t segment_type,
+          bool allocate,
+          bool executable,
+          bool writable,
+          intptr_t alignment = 1)
+      : section_type(type),
+        section_flags(EncodeSectionFlags(allocate, executable, writable)),
+        alignment(allocate ? SegmentAlignment(alignment) : alignment),
+        segment_type(segment_type),
+        segment_flags(EncodeSegmentFlags(allocate, executable, writable)),
+        // Non-segments will never have a memory offset, here represented by 0.
+        memory_offset_(allocate ? -1 : 0) {
+    // Only the reserved section (type 0) should have an alignment of 0.
+    ASSERT(type == 0 || alignment > 0);
+  }
+
+  // The constructor that most subclasses will use.
+  Section(intptr_t type,
+          bool allocate,
+          bool executable,
+          bool writable,
+          intptr_t alignment = 1)
+      : Section(type,
+                /*segment_type=*/allocate ? elf::PT_LOAD : 0,
+                allocate,
+                executable,
+                writable,
+                alignment) {}
 
   virtual ~Section() {}
-  virtual void Write(StreamingWriteStream* stream) = 0;
 
   // Linker view.
-  intptr_t section_name = -1;  // Index into shstrtab_.
-  intptr_t section_type = 0;
-  intptr_t section_flags = 0;
-  intptr_t section_index = -1;
+  const intptr_t section_type;
+  const intptr_t section_flags;
+  const intptr_t alignment;
+
+  // These are fields that only are not set for most kinds of sections and so we
+  // set them to a reasonable default.
   intptr_t section_link = elf::SHN_UNDEF;
   intptr_t section_info = 0;
   intptr_t section_entry_size = 0;
-  intptr_t file_size = 0;
-  intptr_t file_offset = -1;
 
-  intptr_t alignment = 1;
+#define FOR_EACH_SECTION_LINEAR_FIELD(M)                                       \
+  M(section_name, intptr_t, -1)                                                \
+  M(section_index, intptr_t, -1)                                               \
+  M(file_offset, intptr_t, -1)
+
+  FOR_EACH_SECTION_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
+
+  virtual intptr_t FileSize() = 0;
 
   // Loader view.
-  intptr_t segment_type = -1;
-  intptr_t segment_flags = 0;
-  intptr_t memory_size = 0;
-  intptr_t memory_offset = -1;
+  const intptr_t segment_type;
+  const intptr_t segment_flags;
 
-  enum OutputType {
-    kMainOutput,
-    kDebugOutput,
-    kAllOutput,
-  };
+#define FOR_EACH_SEGMENT_LINEAR_FIELD(M) M(memory_offset, intptr_t, -1)
 
-  // When this section should be output, if we are stripping and/or splitting
-  // debugging information. Only a few sections are not part of the main
-  // (non-debugging) output, so we use kMainOutput as the default value.
-  OutputType output_type = kMainOutput;
+  FOR_EACH_SEGMENT_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
+
+  virtual intptr_t MemorySize() = 0;
+
+  // Other methods.
+  virtual void Write(Elf* stream) = 0;
+
+  void WriteSegmentEntry(Elf* stream, bool dynamic = false) {
+    // This should never be used on either the reserved 0-filled section or
+    // on sections without a segment.
+    ASSERT(MemorySize() > 0);
+    // dynamic should only be true if this section is the dynamic table.
+    ASSERT(!dynamic || section_type == elf::SHT_DYNAMIC);
+#if defined(TARGET_ARCH_IS_32_BIT)
+    stream->WriteWord(dynamic ? elf::PT_DYNAMIC : segment_type);
+    stream->WriteOff(file_offset());
+    stream->WriteAddr(memory_offset());  // Virtual address.
+    stream->WriteAddr(memory_offset());  // Physical address, not used.
+    stream->WriteWord(FileSize());
+    stream->WriteWord(MemorySize());
+    stream->WriteWord(segment_flags);
+    stream->WriteWord(alignment);
+#else
+    stream->WriteWord(dynamic ? elf::PT_DYNAMIC : segment_type);
+    stream->WriteWord(segment_flags);
+    stream->WriteOff(file_offset());
+    stream->WriteAddr(memory_offset());  // Virtual address.
+    stream->WriteAddr(memory_offset());  // Physical address, not used.
+    stream->WriteXWord(FileSize());
+    stream->WriteXWord(MemorySize());
+    stream->WriteXWord(alignment);
+#endif
+  }
+
+  void WriteSectionEntry(Elf* stream) {
+#if defined(TARGET_ARCH_IS_32_BIT)
+    stream->WriteWord(section_name());
+    stream->WriteWord(section_type);
+    stream->WriteWord(section_flags);
+    stream->WriteAddr(memory_offset());
+    stream->WriteOff(file_offset());
+    stream->WriteWord(FileSize());  // Has different meaning for BSS.
+    stream->WriteWord(section_link);
+    stream->WriteWord(section_info);
+    stream->WriteWord(alignment);
+    stream->WriteWord(section_entry_size);
+#else
+    stream->WriteWord(section_name());
+    stream->WriteWord(section_type);
+    stream->WriteXWord(section_flags);
+    stream->WriteAddr(memory_offset());
+    stream->WriteOff(file_offset());
+    stream->WriteXWord(FileSize());  // Has different meaning for BSS.
+    stream->WriteWord(section_link);
+    stream->WriteWord(section_info);
+    stream->WriteXWord(alignment);
+    stream->WriteXWord(section_entry_size);
+#endif
+  }
+
+ private:
+  static intptr_t EncodeSectionFlags(bool allocate,
+                                     bool executable,
+                                     bool writable) {
+    if (!allocate) return 0;
+    intptr_t flags = elf::SHF_ALLOC;
+    if (executable) flags |= elf::SHF_EXECINSTR;
+    if (writable) flags |= elf::SHF_WRITE;
+    return flags;
+  }
+
+  static intptr_t EncodeSegmentFlags(bool allocate,
+                                     bool executable,
+                                     bool writable) {
+    if (!allocate) return 0;
+    intptr_t flags = elf::PF_R;
+    if (executable) flags |= elf::PF_X;
+    if (writable) flags |= elf::PF_W;
+    return flags;
+  }
+
+  static intptr_t SegmentAlignment(intptr_t alignment) {
+    return alignment < Elf::kPageSize ? Elf::kPageSize : alignment;
+  }
+
+  FOR_EACH_SECTION_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
+  FOR_EACH_SEGMENT_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
+
+#undef FOR_EACH_SECTION_LINEAR_FIELD
+#undef FOR_EACH_SEGMENT_LINEAR_FIELD
 };
 
-class ProgramBits : public Section {
+#undef DEFINE_LINEAR_FIELD
+#undef DEFINE_LINEAR_FIELD_METHODS
+
+// Represents the first entry in the section table, which should only contain
+// zero values. Only used for WriteSectionEntry and should never actually appear
+// in sections_.
+class ReservedSection : public Section {
+ public:
+  ReservedSection()
+      : Section(/*type=*/0,
+                /*allocate=*/false,
+                /*executable=*/false,
+                /*writable=*/false,
+                /*alignment=*/0) {
+    set_section_name(0);
+    set_section_index(0);
+    set_file_offset(0);
+  }
+
+  intptr_t FileSize() { return 0; }
+  intptr_t MemorySize() { return 0; }
+  void Write(Elf* stream) { UNREACHABLE(); }
+};
+
+class BlobSection : public Section {
+ public:
+  BlobSection(intptr_t type,
+              intptr_t segment_type,
+              bool allocate,
+              bool executable,
+              bool writable,
+              intptr_t filesz,
+              intptr_t memsz,
+              int alignment = 1)
+      : Section(type, segment_type, allocate, executable, writable, alignment),
+        file_size_(filesz),
+        memory_size_(allocate ? memsz : 0) {}
+
+  BlobSection(intptr_t type,
+              bool allocate,
+              bool executable,
+              bool writable,
+              intptr_t filesz,
+              intptr_t memsz,
+              int alignment = 1)
+      : BlobSection(type,
+                    /*segment_type=*/allocate ? elf::PT_LOAD : 0,
+                    allocate,
+                    executable,
+                    writable,
+                    filesz,
+                    memsz,
+                    alignment) {}
+
+  intptr_t FileSize() { return file_size_; }
+  intptr_t MemorySize() { return memory_size_; }
+
+  virtual void Write(Elf* stream) = 0;
+
+ private:
+  const intptr_t file_size_;
+  const intptr_t memory_size_;
+};
+
+// A section for representing the program header segment in the program header
+// table. Only used for WriteSegmentEntry.
+class ProgramTable : public BlobSection {
+ public:
+  ProgramTable(intptr_t offset, intptr_t size)
+      : BlobSection(/*type=*/0,
+                    /*segment_type=*/elf::PT_PHDR,
+                    /*allocate=*/true,
+                    /*executable=*/false,
+                    /*writable=*/false,
+                    /*filesz=*/size,
+                    /*memsz=*/size) {
+    set_file_offset(offset);
+    set_memory_offset(offset);
+  }
+
+  // This should never actually be added to sections_ or segments_.
+  void Write(Elf* stream) { UNREACHABLE(); }
+};
+
+// A section for representing the program header table load segment in the
+// program header table. Only used for WriteSegmentEntry.
+class ProgramTableLoad : public BlobSection {
+ public:
+  // The Android dynamic linker in Jelly Bean incorrectly assumes that all
+  // non-writable segments are continguous. Since the BSS segment comes directly
+  // after the program header segment, we must make this segment writable so
+  // later non-writable segments does not cause the BSS to be also marked as
+  // read-only.
+  //
+  // The bug is here:
+  //   https://github.com/aosp-mirror/platform_bionic/blob/94963af28e445384e19775a838a29e6a71708179/linker/linker.c#L1991-L2001
+  explicit ProgramTableLoad(intptr_t size)
+      : BlobSection(/*type=*/0,
+                    /*allocate=*/true,
+                    /*executable=*/false,
+                    /*writable=*/true,
+                    /*filesz=*/size,
+                    /*memsz=*/size) {
+    set_file_offset(0);
+    set_memory_offset(0);
+  }
+
+  // This should never actually be added to sections_ or segments_.
+  void Write(Elf* stream) { UNREACHABLE(); }
+};
+
+class ProgramBits : public BlobSection {
  public:
   ProgramBits(bool allocate,
               bool executable,
               bool writable,
               const uint8_t* bytes,
               intptr_t filesz,
-              intptr_t memsz = -1) {
-    if (memsz == -1) memsz = filesz;
+              intptr_t memsz = -1)
+      : BlobSection(elf::SHT_PROGBITS,
+                    allocate,
+                    executable,
+                    writable,
+                    filesz,
+                    memsz != -1 ? memsz : filesz),
+        bytes_(ASSERT_NOTNULL(bytes)) {}
 
-    section_type = elf::SHT_PROGBITS;
-    if (allocate) {
-      section_flags = elf::SHF_ALLOC;
-      if (executable) section_flags |= elf::SHF_EXECINSTR;
-      if (writable) section_flags |= elf::SHF_WRITE;
-
-      segment_type = elf::PT_LOAD;
-      segment_flags = elf::PF_R;
-      if (executable) segment_flags |= elf::PF_X;
-      if (writable) segment_flags |= elf::PF_W;
-    }
-
-    bytes_ = bytes;
-    file_size = filesz;
-    memory_size = memsz;
-  }
-
-  void Write(StreamingWriteStream* stream) {
-    if (bytes_ != nullptr) {
-      Elf::WriteBytes(stream, bytes_, file_size);
-    }
-  }
+  void Write(Elf* stream) { stream->WriteBytes(bytes_, FileSize()); }
 
   const uint8_t* bytes_;
 };
 
+class NoBits : public BlobSection {
+ public:
+  NoBits(bool allocate, bool executable, bool writable, intptr_t memsz)
+      : BlobSection(elf::SHT_NOBITS,
+                    allocate,
+                    executable,
+                    writable,
+                    /*filesz=*/0,
+                    memsz) {}
+
+  void Write(Elf* stream) {}
+};
+
 class StringTable : public Section {
  public:
-  explicit StringTable(bool dynamic) : text_(128), text_indices_() {
-    section_type = elf::SHT_STRTAB;
-    if (dynamic) {
-      section_flags = elf::SHF_ALLOC;
-      segment_type = elf::PT_LOAD;
-      segment_flags = elf::PF_R;
-    } else {
-      section_flags = 0;
-      memory_offset = 0;  // No segments for static tables.
-    }
-
+  explicit StringTable(bool allocate)
+      : Section(elf::SHT_STRTAB,
+                allocate,
+                /*executable=*/false,
+                /*writable=*/false),
+        dynamic_(allocate),
+        text_(128),
+        text_indices_() {
     text_.AddChar('\0');
     text_indices_.Insert({"", 1});
-    memory_size = file_size = text_.length();
+  }
+
+  intptr_t FileSize() { return text_.length(); }
+  intptr_t MemorySize() { return dynamic_ ? FileSize() : 0; }
+
+  void Write(Elf* stream) {
+    stream->WriteBytes(reinterpret_cast<const uint8_t*>(text_.buf()),
+                       text_.length());
   }
 
   intptr_t AddString(const char* str) {
@@ -127,20 +367,10 @@
     text_.AddString(str);
     text_.AddChar('\0');
     text_indices_.Insert({str, offset + 1});
-    memory_size = file_size = text_.length();
     return offset;
   }
 
-  const char* GetString(intptr_t index) {
-    ASSERT(index >= 0 && index < text_.length());
-    return text_.buf() + index;
-  }
-
-  void Write(StreamingWriteStream* stream) {
-    Elf::WriteBytes(stream, reinterpret_cast<const uint8_t*>(text_.buf()),
-                    text_.length());
-  }
-
+  const bool dynamic_;
   TextBuffer text_;
   // To avoid kNoValue for intptr_t (0), we store an index n as n + 1.
   CStringMap<intptr_t> text_indices_;
@@ -148,97 +378,84 @@
 
 class Symbol : public ZoneAllocated {
  public:
-  const char* cstr;
-  intptr_t name;
-  intptr_t info;
-  intptr_t section;
-  intptr_t offset;
-  intptr_t size;
+  Symbol(const char* cstr,
+         intptr_t name,
+         intptr_t info,
+         intptr_t section,
+         intptr_t offset,
+         intptr_t size)
+      : cstr_(cstr),
+        name_index_(name),
+        info_(info),
+        section_index_(section),
+        offset_(offset),
+        size_(size) {}
+
+  void Write(Elf* stream) const {
+    stream->WriteWord(name_index_);
+#if defined(TARGET_ARCH_IS_32_BIT)
+    stream->WriteAddr(offset_);
+    stream->WriteWord(size_);
+    stream->WriteByte(info_);
+    stream->WriteByte(0);
+    stream->WriteHalf(section_index_);
+#else
+    stream->WriteByte(info_);
+    stream->WriteByte(0);
+    stream->WriteHalf(section_index_);
+    stream->WriteAddr(offset_);
+    stream->WriteXWord(size_);
+#endif
+  }
+
+ private:
+  friend class SymbolHashTable;  // For cstr_ access.
+
+  const char* cstr_;
+  intptr_t name_index_;
+  intptr_t info_;
+  intptr_t section_index_;
+  intptr_t offset_;
+  intptr_t size_;
 };
 
 class SymbolTable : public Section {
  public:
-  explicit SymbolTable(bool dynamic) {
-    if (dynamic) {
-      section_type = elf::SHT_DYNSYM;
-      section_flags = elf::SHF_ALLOC;
-      segment_type = elf::PT_LOAD;
-      segment_flags = elf::PF_R;
-    } else {
-      // No need to load the static symbol table at runtime since it's ignored
-      // by the dynamic linker.
-      section_type = elf::SHT_SYMTAB;
-      section_flags = 0;
-      memory_offset = 0;  // No segments for static tables.
-      alignment = compiler::target::kWordSize;
-    }
-
+  explicit SymbolTable(bool dynamic)
+      : Section(dynamic ? elf::SHT_DYNSYM : elf::SHT_SYMTAB,
+                dynamic,
+                /*executable=*/false,
+                /*writable=*/false,
+                compiler::target::kWordSize),
+        dynamic_(dynamic),
+        reserved_("", 0, 0, 0, 0, 0) {
     section_entry_size = kElfSymbolTableEntrySize;
-    section_info = 0;
-    AddSymbol(nullptr);
-  }
-
-  void AddSymbol(Symbol* symbol) {
-    // Adjust section_info to contain the count of local symbols, including the
-    // reserved first entry (represented by the nullptr value).
-    if (symbol == nullptr || ((symbol->info >> 4) == elf::STB_LOCAL)) {
-      section_info += 1;
-    }
-    symbols_.Add(symbol);
-    memory_size += kElfSymbolTableEntrySize;
-    file_size += kElfSymbolTableEntrySize;
-  }
-
-  void Write(StreamingWriteStream* stream) {
     // The first symbol table entry is reserved and must be all zeros.
-    {
-      const intptr_t start = stream->position();
-#if defined(TARGET_ARCH_IS_32_BIT)
-      Elf::WriteWord(stream, 0);
-      Elf::WriteAddr(stream, 0);
-      Elf::WriteWord(stream, 0);
-      Elf::WriteByte(stream, 0);
-      Elf::WriteByte(stream, 0);
-      Elf::WriteHalf(stream, 0);
-#else
-      Elf::WriteWord(stream, 0);
-      Elf::WriteByte(stream, 0);
-      Elf::WriteByte(stream, 0);
-      Elf::WriteHalf(stream, 0);
-      Elf::WriteAddr(stream, 0);
-      Elf::WriteXWord(stream, 0);
-#endif
-      const intptr_t end = stream->position();
-      ASSERT((end - start) == kElfSymbolTableEntrySize);
-    }
+    symbols_.Add(&reserved_);
+    section_info = 1;  // One "local" symbol, the reserved first entry.
+  }
 
-    for (intptr_t i = 1; i < symbols_.length(); i++) {
-      Symbol* symbol = symbols_[i];
+  intptr_t FileSize() { return Length() * kElfSymbolTableEntrySize; }
+  intptr_t MemorySize() { return dynamic_ ? FileSize() : 0; }
+
+  void Write(Elf* stream) {
+    for (intptr_t i = 0; i < Length(); i++) {
+      auto const symbol = At(i);
       const intptr_t start = stream->position();
-#if defined(TARGET_ARCH_IS_32_BIT)
-      Elf::WriteWord(stream, symbol->name);
-      Elf::WriteAddr(stream, symbol->offset);
-      Elf::WriteWord(stream, symbol->size);
-      Elf::WriteByte(stream, symbol->info);
-      Elf::WriteByte(stream, 0);
-      Elf::WriteHalf(stream, symbol->section);
-#else
-      Elf::WriteWord(stream, symbol->name);
-      Elf::WriteByte(stream, symbol->info);
-      Elf::WriteByte(stream, 0);
-      Elf::WriteHalf(stream, symbol->section);
-      Elf::WriteAddr(stream, symbol->offset);
-      Elf::WriteXWord(stream, symbol->size);
-#endif
+      symbol->Write(stream);
       const intptr_t end = stream->position();
       ASSERT((end - start) == kElfSymbolTableEntrySize);
     }
   }
 
-  intptr_t length() const { return symbols_.length(); }
-  Symbol* at(intptr_t i) const { return symbols_[i]; }
+  void AddSymbol(const Symbol* symbol) { symbols_.Add(symbol); }
+  intptr_t Length() const { return symbols_.length(); }
+  const Symbol* At(intptr_t i) const { return symbols_[i]; }
 
-  GrowableArray<Symbol*> symbols_;
+ private:
+  const bool dynamic_;
+  const Symbol reserved_;
+  GrowableArray<const Symbol*> symbols_;
 };
 
 static uint32_t ElfHash(const unsigned char* name) {
@@ -254,46 +471,49 @@
 
 class SymbolHashTable : public Section {
  public:
-  SymbolHashTable(StringTable* strtab, SymbolTable* symtab) {
-    section_type = elf::SHT_HASH;
-    section_flags = elf::SHF_ALLOC;
-    section_link = symtab->section_index;
+  SymbolHashTable(StringTable* strtab, SymbolTable* symtab)
+      : Section(elf::SHT_HASH,
+                /*allocate=*/true,
+                /*executable=*/false,
+                /*writable=*/false,
+                compiler::target::kWordSize) {
+    section_link = symtab->section_index();
     section_entry_size = kElfSymbolHashTableEntrySize;
-    segment_type = elf::PT_LOAD;
-    segment_flags = elf::PF_R;
 
-    nchain_ = symtab->length();
-    nbucket_ = symtab->length();
+    nchain_ = symtab->Length();
+    nbucket_ = symtab->Length();
 
-    bucket_ = Thread::Current()->zone()->Alloc<int32_t>(nbucket_);
+    auto zone = Thread::Current()->zone();
+    bucket_ = zone->Alloc<int32_t>(nbucket_);
     for (intptr_t i = 0; i < nbucket_; i++) {
       bucket_[i] = elf::STN_UNDEF;
     }
 
-    chain_ = Thread::Current()->zone()->Alloc<int32_t>(nchain_);
+    chain_ = zone->Alloc<int32_t>(nchain_);
     for (intptr_t i = 0; i < nchain_; i++) {
       chain_[i] = elf::STN_UNDEF;
     }
 
-    for (intptr_t i = 1; i < symtab->length(); i++) {
-      Symbol* symbol = symtab->at(i);
-      uint32_t hash = ElfHash((const unsigned char*)symbol->cstr);
+    for (intptr_t i = 1; i < symtab->Length(); i++) {
+      auto const symbol = symtab->At(i);
+      uint32_t hash = ElfHash((const unsigned char*)symbol->cstr_);
       uint32_t probe = hash % nbucket_;
       chain_[i] = bucket_[probe];  // next = head
       bucket_[probe] = i;          // head = symbol
     }
-
-    memory_size = file_size = 4 * (nbucket_ + nchain_ + 2);
   }
 
-  void Write(StreamingWriteStream* stream) {
-    Elf::WriteWord(stream, nbucket_);
-    Elf::WriteWord(stream, nchain_);
+  intptr_t FileSize() { return 4 * (nbucket_ + nchain_ + 2); }
+  intptr_t MemorySize() { return FileSize(); }
+
+  void Write(Elf* stream) {
+    stream->WriteWord(nbucket_);
+    stream->WriteWord(nchain_);
     for (intptr_t i = 0; i < nbucket_; i++) {
-      Elf::WriteWord(stream, bucket_[i]);
+      stream->WriteWord(bucket_[i]);
     }
     for (intptr_t i = 0; i < nchain_; i++) {
-      Elf::WriteWord(stream, chain_[i]);
+      stream->WriteWord(chain_[i]);
     }
   }
 
@@ -306,34 +526,35 @@
 
 class DynamicTable : public Section {
  public:
-  DynamicTable(StringTable* strtab,
-               SymbolTable* symtab,
-               SymbolHashTable* hash) {
-    section_type = elf::SHT_DYNAMIC;
-    section_link = strtab->section_index;
-    section_flags = elf::SHF_ALLOC | elf::SHF_WRITE;
+  DynamicTable(StringTable* strtab, SymbolTable* symtab, SymbolHashTable* hash)
+      : Section(elf::SHT_DYNAMIC,
+                /*allocate=*/true,
+                /*executable=*/false,
+                /*writable=*/true,
+                compiler::target::kWordSize) {
+    section_link = strtab->section_index();
     section_entry_size = kElfDynamicTableEntrySize;
 
-    segment_type = elf::PT_LOAD;
-    segment_flags = elf::PF_R | elf::PF_W;
-
-    AddEntry(elf::DT_HASH, hash->memory_offset);
-    AddEntry(elf::DT_STRTAB, strtab->memory_offset);
-    AddEntry(elf::DT_STRSZ, strtab->memory_size);
-    AddEntry(elf::DT_SYMTAB, symtab->memory_offset);
+    AddEntry(elf::DT_HASH, hash->memory_offset());
+    AddEntry(elf::DT_STRTAB, strtab->memory_offset());
+    AddEntry(elf::DT_STRSZ, strtab->MemorySize());
+    AddEntry(elf::DT_SYMTAB, symtab->memory_offset());
     AddEntry(elf::DT_SYMENT, kElfSymbolTableEntrySize);
     AddEntry(elf::DT_NULL, 0);
   }
 
-  void Write(StreamingWriteStream* stream) {
+  intptr_t FileSize() { return entries_.length() * kElfDynamicTableEntrySize; }
+  intptr_t MemorySize() { return FileSize(); }
+
+  void Write(Elf* stream) {
     for (intptr_t i = 0; i < entries_.length(); i++) {
       const intptr_t start = stream->position();
 #if defined(TARGET_ARCH_IS_32_BIT)
-      Elf::WriteWord(stream, entries_[i]->tag);
-      Elf::WriteAddr(stream, entries_[i]->value);
+      stream->WriteWord(entries_[i]->tag);
+      stream->WriteAddr(entries_[i]->value);
 #else
-      Elf::WriteXWord(stream, entries_[i]->tag);
-      Elf::WriteAddr(stream, entries_[i]->value);
+      stream->WriteXWord(entries_[i]->tag);
+      stream->WriteAddr(entries_[i]->value);
 #endif
       const intptr_t end = stream->position();
       ASSERT((end - start) == kElfDynamicTableEntrySize);
@@ -351,9 +572,6 @@
     entry->tag = tag;
     entry->value = value;
     entries_.Add(entry);
-
-    memory_size += kElfDynamicTableEntrySize;
-    file_size += kElfDynamicTableEntrySize;
   }
 
  private:
@@ -369,102 +587,82 @@
 
 static const intptr_t kProgramTableSegmentSize = Elf::kPageSize;
 
-Elf::Elf(Zone* zone,
-         StreamingWriteStream* stream,
-         bool strip,
-         StreamingWriteStream* debug_stream)
-    : zone_(ASSERT_NOTNULL(zone)),
-      strip_(strip),
+Elf::Elf(Zone* zone, StreamingWriteStream* stream)
+    : zone_(zone),
       stream_(stream),
-      debug_stream_(debug_stream),
-      sections_(zone, 2),
-      segments_(zone, 2),
-      active_sections_(zone, 2),
-      output_sections_(zone, 2),
-      adjusted_indices_(zone),
-      file_sizes_(zone, 2),
-      section_names_(zone, 2),
-      section_types_(zone, 2) {
-  // We should be outputting at least one file.
-  ASSERT(stream_ != nullptr || debug_stream_ != nullptr);
-  // Stripping the main output only makes sense if it'll be output.
-  ASSERT(!strip || stream_ != nullptr);
-
+      shstrtab_(new (zone) StringTable(/*allocate=*/false)),
+      dynstrtab_(new (zone) StringTable(/*allocate=*/true)),
+      dynsym_(new (zone) SymbolTable(/*dynamic=*/true)),
+      memory_offset_(kProgramTableSegmentSize) {
   // Assumed by various offset logic in this file.
-  ASSERT(stream_ == nullptr || stream_->position() == 0);
-  ASSERT(debug_stream_ == nullptr || debug_stream_->position() == 0);
-
-  // All our strings would fit in a single page. However, we use separate
-  // .shstrtab and .dynstr to work around a bug in Android's strip utility.
-  shstrtab_ = new (zone_) StringTable(/*dynamic=*/false);
-  shstrtab_->output_type = Section::kAllOutput;
-
-  dynstrtab_ = new (zone_) StringTable(/*dynamic=*/true);
-  dynsym_ = new (zone_) SymbolTable(/*dynamic=*/true);
-
-  // The (non-section header) static tables are not needed in stripped output.
-  strtab_ = new (zone_) StringTable(/*dynamic=*/false);
-  strtab_->output_type = Section::kDebugOutput;
-  symtab_ = new (zone_) SymbolTable(/*dynamic=*/false);
-  symtab_->output_type = Section::kDebugOutput;
-
-  // Allocate regular segments after the program table.
-  memory_offset_ = kProgramTableSegmentSize;
+  ASSERT(stream_->position() == 0);
 }
 
 void Elf::AddSection(Section* section, const char* name) {
-  section->section_index = NextSectionIndex();
-  section->section_name = shstrtab_->AddString(name);
+  ASSERT(shstrtab_ != nullptr);
+  section->set_section_name(shstrtab_->AddString(name));
+  section->set_section_index(sections_.length() + kNumInvalidSections);
   sections_.Add(section);
+  if (section->MemorySize() > 0) {
+    memory_offset_ = Utils::RoundUp(memory_offset_, section->alignment);
+    section->set_memory_offset(memory_offset_);
+    segments_.Add(section);
+    memory_offset_ += section->MemorySize();
+    memory_offset_ = Utils::RoundUp(memory_offset_, kPageSize);
+  }
 }
 
-void Elf::AddSegment(Section* section) {
-  if (section->alignment < kPageSize) {
-    section->alignment = kPageSize;
-  }
-
-  memory_offset_ = Utils::RoundUp(memory_offset_, section->alignment);
-  section->memory_offset = memory_offset_;
-  memory_offset_ += section->memory_size;
-  segments_.Add(section);
-  memory_offset_ = Utils::RoundUp(memory_offset_, kPageSize);
+intptr_t Elf::NextMemoryOffset() const {
+  return memory_offset_;
 }
 
 intptr_t Elf::NextSectionIndex() const {
   return sections_.length() + kNumInvalidSections;
 }
 
-intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
-  ProgramBits* image = new (zone_) ProgramBits(true, true, false, bytes, size);
-  AddSection(image, ".text");
-  AddSegment(image);
-
-  Symbol* symbol = new (zone_) Symbol();
-  symbol->cstr = name;
-  symbol->name = dynstrtab_->AddString(name);
-  symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC;
-  symbol->section = image->section_index;
+intptr_t Elf::AddSectionSymbol(const Section* section,
+                               const char* name,
+                               intptr_t size) {
+  auto const name_index = dynstrtab_->AddString(name);
+  auto const info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC;
+  auto const section_index = section->section_index();
   // For shared libraries, this is the offset from the DSO base. For static
   // libraries, this is section relative.
-  symbol->offset = image->memory_offset;
-  symbol->size = size;
+  auto const memory_offset = section->memory_offset();
+  auto const symbol = new (zone_)
+      Symbol(name, name_index, info, section_index, memory_offset, size);
   dynsym_->AddSymbol(symbol);
 
-  return symbol->offset;
+  return memory_offset;
+}
+
+intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
+  Section* image = nullptr;
+  if (bytes != nullptr) {
+    image = new (zone_) ProgramBits(true, true, false, bytes, size);
+  } else {
+    image = new (zone_) NoBits(true, true, false, size);
+  }
+  AddSection(image, ".text");
+
+  return AddSectionSymbol(image, name, size);
 }
 
 void Elf::AddStaticSymbol(intptr_t section,
                           const char* name,
                           size_t memory_offset) {
-  Symbol* symbol = new (zone_) Symbol();
-  symbol->cstr = name;
-  symbol->name = strtab_->AddString(name);
-  symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC;
-  symbol->section = section;
-  // For shared libraries, this is the offset from the DSO base. For static
-  // libraries, this is section relative.
-  symbol->offset = memory_offset;
-  symbol->size = 0;
+  // Lazily allocate the static string and symbol tables, as we only add static
+  // symbols in unstripped ELF files.
+  if (strtab_ == nullptr) {
+    ASSERT(symtab_ == nullptr);
+    strtab_ = new (zone_) StringTable(/* allocate= */ false);
+    symtab_ = new (zone_) SymbolTable(/*dynamic=*/false);
+  }
+
+  auto const name_index = strtab_->AddString(name);
+  auto const info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC;
+  Symbol* symbol = new (zone_)
+      Symbol(name, name_index, info, section, memory_offset, /*size=*/0);
   symtab_->AddSymbol(symbol);
 }
 
@@ -474,332 +672,82 @@
   // file-size.
   //
   // Therefore we must insert zero-filled pages for the BSS.
-  uint8_t* const bytes = Thread::Current()->zone()->Alloc<uint8_t>(size);
+  uint8_t* const bytes = zone_->Alloc<uint8_t>(size);
   memset(bytes, 0, size);
 
-  ProgramBits* const image = new (zone_)
-      ProgramBits(true, false, true, bytes, /*filesz=*/size, /*memsz=*/size);
+  ProgramBits* const image =
+      new (zone_) ProgramBits(true, false, true, bytes, size);
   AddSection(image, ".bss");
-  AddSegment(image);
 
-  Symbol* symbol = new (zone_) Symbol();
-  symbol->cstr = name;
-  symbol->name = dynstrtab_->AddString(name);
-  symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_OBJECT;
-  symbol->section = image->section_index;
-  // For shared libraries, this is the offset from the DSO base. For static
-  // libraries, this is section relative.
-  symbol->offset = image->memory_offset;
-  symbol->size = size;
-  dynsym_->AddSymbol(symbol);
-
-  return symbol->offset;
+  return AddSectionSymbol(image, name, size);
 }
 
 intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
+  ASSERT(bytes != nullptr);
   ProgramBits* image = new (zone_) ProgramBits(true, false, false, bytes, size);
   AddSection(image, ".rodata");
-  AddSegment(image);
 
-  Symbol* symbol = new (zone_) Symbol();
-  symbol->cstr = name;
-  symbol->name = dynstrtab_->AddString(name);
-  symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_OBJECT;
-  symbol->section = image->section_index;
-  // For shared libraries, this is the offset from the DSO base. For static
-  // libraries, this is section relative.
-  symbol->offset = image->memory_offset;
-  symbol->size = size;
-  dynsym_->AddSymbol(symbol);
-
-  return symbol->offset;
+  return AddSectionSymbol(image, name, size);
 }
 
 void Elf::AddDebug(const char* name, const uint8_t* bytes, intptr_t size) {
+  ASSERT(bytes != nullptr);
   ProgramBits* image =
       new (zone_) ProgramBits(false, false, false, bytes, size);
-  image->output_type = Section::kDebugOutput;
   AddSection(image, name);
 }
 
 void Elf::Finalize() {
-  SymbolHashTable* hash = new (zone_) SymbolHashTable(dynstrtab_, dynsym_);
-
-  AddSection(hash, ".hash");
-  AddSection(dynsym_, ".dynsym");
   AddSection(dynstrtab_, ".dynstr");
+  AddSection(dynsym_, ".dynsym");
+  dynsym_->section_link = dynstrtab_->section_index();
 
-  dynsym_->section_link = dynstrtab_->section_index;
-  hash->section_link = dynsym_->section_index;
+  auto const hash = new (zone_) SymbolHashTable(dynstrtab_, dynsym_);
+  AddSection(hash, ".hash");
 
-  // Finalizes memory size of string and symbol tables.
-  AddSegment(hash);
-  AddSegment(dynsym_);
-  AddSegment(dynstrtab_);
+  if (symtab_ != nullptr) {
+    ASSERT(strtab_ != nullptr);
+    AddSection(strtab_, ".strtab");
+    AddSection(symtab_, ".symtab");
+    symtab_->section_link = strtab_->section_index();
+  }
 
   dynamic_ = new (zone_) DynamicTable(dynstrtab_, dynsym_, hash);
   AddSection(dynamic_, ".dynamic");
-  AddSegment(dynamic_);
 
-  // We only output the static symbol and string tables if they are non-empty.
-  // We only need to check symtab_, since entries are added to strtab_ whenever
-  // we add symbols. Here, an "empty" static symbol table only has one entry
-  // (a nullptr value for the initial reserved entry).
-  if (symtab_->symbols_.length() > 1) {
-    AddSection(symtab_, ".symtab");
-    AddSection(strtab_, ".strtab");
-    symtab_->section_link = strtab_->section_index;
-  }
-
-  // The section header string table should come last.
   AddSection(shstrtab_, ".shstrtab");
 
-  if (debug_stream_ != nullptr) {
-    PrepareDebugOutputInfo();
-    WriteHeader(debug_stream_);
-    WriteProgramTable(debug_stream_);
-    WriteSections(debug_stream_);
-    WriteSectionTable(debug_stream_);
-  }
+  ComputeFileOffsets();
 
-  if (stream_ != nullptr) {
-    PrepareMainOutputInfo();
-    WriteHeader(stream_);
-    WriteProgramTable(stream_);
-    WriteSections(stream_);
-    WriteSectionTable(stream_);
-  }
+  WriteHeader();
+  WriteProgramTable();
+  WriteSections();
+  WriteSectionTable();
 }
 
-void Elf::ClearOutputInfo() {
-  active_sections_.Clear();
-  output_sections_.Clear();
-  adjusted_indices_.Clear();
-  file_sizes_.Clear();
-  section_names_.Clear();
-  section_types_.Clear();
+void Elf::ComputeFileOffsets() {
+  intptr_t file_offset = kElfHeaderSize;
 
-  // These don't need to be cleared normally, but doing so in DEBUG mode
-  // may help us catch issues.
-#if defined(DEBUG)
-  section_table_entry_count_ = -1;
-  section_table_file_offset_ = -1;
-  program_table_entry_count_ = -1;
-  program_table_file_offset_ = -1;
-  for (auto section : sections_) {
-    section->file_offset = -1;
-  }
-#endif
-}
+  program_table_file_offset_ = file_offset;
+  program_table_file_size_ =
+      (segments_.length() + kNumImplicitSegments) * kElfProgramTableEntrySize;
+  file_offset += program_table_file_size_;
 
-intptr_t Elf::ActiveSectionsIndex(intptr_t section_index) const {
-  // This assumes all invalid sections come first in the table.
-  ASSERT(section_index >= kNumInvalidSections);
-  return SectionTableIndex(section_index) - kNumInvalidSections;
-}
-
-intptr_t Elf::SectionTableIndex(intptr_t section_index) const {
-  // This assumes all invalid sections come first in the table.
-  if (section_index < kNumInvalidSections) return section_index;
-  ASSERT(adjusted_indices_.HasKey(section_index));
-  return adjusted_indices_.LookupValue(section_index);
-}
-
-intptr_t Elf::ProgramTableSize() const {
-  ASSERT(program_table_entry_count_ >= 0);
-  return program_table_entry_count_ * kElfProgramTableEntrySize;
-}
-
-intptr_t Elf::SectionTableSize() const {
-  ASSERT(section_table_entry_count_ >= 0);
-  return section_table_entry_count_ * kElfSectionTableEntrySize;
-}
-
-void Elf::VerifyOutputInfo() const {
-#if defined(DEBUG)
-  // The section header string table should always be the last section. We can't
-  // check for shstrtab_ because we recreate it to trim and reorder entries.
-  ASSERT(active_sections_.Last()->section_type == elf::SHT_STRTAB);
-  ASSERT(active_sections_.Last()->section_flags == 0);
-  auto const shstrtab = reinterpret_cast<StringTable*>(active_sections_.Last());
-
-  ASSERT(file_sizes_.length() == active_sections_.length());
-  ASSERT(section_names_.length() == active_sections_.length());
-  ASSERT(section_types_.length() == active_sections_.length());
-  // Need this to output the section header.
-  ASSERT(adjusted_indices_.HasKey(shstrtab->section_index));
-  // Need this to output the dynamic section of the program table.
-  ASSERT(adjusted_indices_.HasKey(dynamic_->section_index));
-
-  // Perform extra checks on the Section GrowableArrays used in output
-  // (segments_, active_sections_, and output_sections_), including that
-  // they appear in the same order as in sections_.
-  intptr_t last_index = 0;
-  for (auto section : segments_) {
-    ASSERT(section->file_offset != -1);
-    ASSERT(section->section_index > last_index);
-    last_index = section->section_index;
-    auto const index = ActiveSectionsIndex(section->section_index);
-    ASSERT(index >= 0 && index < active_sections_.length());
-    ASSERT(file_sizes_.At(index) == 0 ||
-           file_sizes_.At(index) == section->file_size);
-  }
-
-  last_index = 0;
-  for (auto section : active_sections_) {
-    ASSERT(section->file_offset != -1);
-    ASSERT(section->section_index > last_index);
-    last_index = section->section_index;
-    auto const index = ActiveSectionsIndex(section->section_index);
-    ASSERT(section_types_.At(index) == section->section_type ||
-           section_types_.At(index) == elf::SHT_NOBITS);
-    auto const link_index = SectionTableIndex(section->section_link);
-    ASSERT(link_index >= 0 && link_index < section_table_entry_count_);
-    auto const name_index = section_names_.At(index);
-    ASSERT(name_index >= 0 && name_index < shstrtab->text_.length());
-    // All (non-reserved) section names start with '.'.
-    ASSERT(shstrtab->GetString(name_index)[0] == '.');
-  }
-
-  // Here, we primarily check that output_sections_ is a subset of
-  // active_sections_, and thus all output sections are in the section table,
-  // and that all the sections are continguous in the file modulo alignment.
-  intptr_t file_offset = program_table_file_offset_ + ProgramTableSize();
-  for (auto section : output_sections_) {
-    auto const index = ActiveSectionsIndex(section->section_index);
+  for (intptr_t i = 0; i < sections_.length(); i++) {
+    Section* section = sections_[i];
     file_offset = Utils::RoundUp(file_offset, section->alignment);
-    ASSERT(section->file_offset == file_offset);
-    file_offset += section->file_size;
-    ASSERT(index >= 0 && index < active_sections_.length());
-  }
-  ASSERT(Utils::RoundUp(file_offset, kElfSectionTableAlignment) ==
-         section_table_file_offset_);
-#endif
-}
-
-intptr_t Elf::PrepareDebugSection(Section* section,
-                                  intptr_t file_offset,
-                                  bool use_fake_info) {
-  // All sections are output in the section table, even for debugging.
-  active_sections_.Add(section);
-  adjusted_indices_.Insert(section->section_index, section->section_index);
-  if (use_fake_info) {
-    // The fake offset of this section will be the aligned offset immediately
-    // after the program table.
-    auto const fake_offset = program_table_file_offset_ + ProgramTableSize();
-    // No actual data will be output for these sections.
-    section_types_.Add(elf::SHT_NOBITS);
-    file_sizes_.Add(0);
-    section->file_offset = Utils::RoundUp(fake_offset, section->alignment);
-    return file_offset;
-  }
-  output_sections_.Add(section);
-  section_types_.Add(section->section_type);
-  file_sizes_.Add(section->file_size);
-  section->file_offset = Utils::RoundUp(file_offset, section->alignment);
-  return section->file_offset + section->file_size;
-}
-
-intptr_t Elf::PrepareMainSection(Section* section,
-                                 intptr_t file_offset,
-                                 intptr_t skipped_sections) {
-  active_sections_.Add(section);
-  output_sections_.Add(section);
-  file_sizes_.Add(section->file_size);
-  section_types_.Add(section->section_type);
-  adjusted_indices_.Insert(section->section_index,
-                           section->section_index - skipped_sections);
-  section->file_offset = Utils::RoundUp(file_offset, section->alignment);
-  return section->file_offset + section->file_size;
-}
-
-StringTable* Elf::CreateSectionHeaderStringTable() {
-  // If there are no dropped sections prior to adding the section header string
-  // table, we can just use the current name indices and shstrtab_.
-  if (active_sections_.length() == (sections_.length() - 1)) {
-    for (auto section : active_sections_) {
-      section_names_.Add(section->section_name);
-    }
-    section_names_.Add(shstrtab_->section_name);
-    return shstrtab_;
-  }
-
-  auto ret = new (zone_) StringTable(/*allocate=*/false);
-  // Fill fields set outside of methods in Section and its subclasses.
-  ret->section_name = shstrtab_->section_name;
-  ret->section_index = shstrtab_->section_index;
-  ret->output_type = Section::kAllOutput;
-
-  for (auto section : active_sections_) {
-    auto const cstr = shstrtab_->GetString(section->section_name);
-    section_names_.Add(ret->AddString(cstr));
-  }
-  // Now add the name for the section header string table itself.
-  section_names_.Add(ret->AddString(shstrtab_->GetString(ret->section_name)));
-  return ret;
-}
-
-Section* Elf::AdjustForActiveSections(Section* section) {
-  // Possibly trim shstrtab_ to remove names for dropped sections.
-  if (section == shstrtab_) return CreateSectionHeaderStringTable();
-  // No other section currently needs adjustment.
-  return section;
-}
-
-void Elf::PrepareDebugOutputInfo() {
-  ClearOutputInfo();
-
-  intptr_t file_offset = kElfHeaderSize;
-
-  // This is the same for both the debugging and stripped output.
-  program_table_file_offset_ = file_offset;
-  program_table_entry_count_ = segments_.length() + kNumImplicitSegments;
-  file_offset += ProgramTableSize();
-
-  for (auto section : sections_) {
-    // When splitting out debugging information, we only output the contents
-    // of debug sections and the section header string table, so change the
-    // section header information appropriately for other sections.
-    auto const use_fake_info = section->output_type == Section::kMainOutput;
-    section = AdjustForActiveSections(section);
-    file_offset = PrepareDebugSection(section, file_offset, use_fake_info);
+    section->set_file_offset(file_offset);
+    file_offset += section->FileSize();
   }
 
   file_offset = Utils::RoundUp(file_offset, kElfSectionTableAlignment);
   section_table_file_offset_ = file_offset;
-  section_table_entry_count_ = active_sections_.length() + kNumInvalidSections;
-  file_offset += SectionTableSize();
-
-  VerifyOutputInfo();
+  section_table_file_size_ =
+      (sections_.length() + kNumInvalidSections) * kElfSectionTableEntrySize;
+  file_offset += section_table_file_size_;
 }
 
-void Elf::PrepareMainOutputInfo() {
-  ClearOutputInfo();
-  intptr_t file_offset = kElfHeaderSize;
-
-  program_table_file_offset_ = file_offset;
-  program_table_entry_count_ = segments_.length() + kNumImplicitSegments;
-  file_offset += ProgramTableSize();
-
-  intptr_t skipped_sections = 0;
-  for (auto section : sections_) {
-    if (strip_ && section->output_type == Section::kDebugOutput) {
-      skipped_sections += 1;
-      continue;
-    }
-    section = AdjustForActiveSections(section);
-    file_offset = PrepareMainSection(section, file_offset, skipped_sections);
-  }
-
-  file_offset = Utils::RoundUp(file_offset, kElfSectionTableAlignment);
-  section_table_file_offset_ = file_offset;
-  section_table_entry_count_ = active_sections_.length() + kNumInvalidSections;
-  file_offset += SectionTableSize();
-
-  VerifyOutputInfo();
-}
-
-void Elf::WriteHeader(StreamingWriteStream* stream) {
+void Elf::WriteHeader() {
 #if defined(TARGET_ARCH_IS_32_BIT)
   uint8_t size = elf::ELFCLASS32;
 #else
@@ -821,26 +769,26 @@
                          0,
                          0,
                          0};
-  WriteBytes(stream, e_ident, 16);
+  stream_->WriteBytes(e_ident, 16);
 
-  WriteHalf(stream, elf::ET_DYN);  // Shared library.
+  WriteHalf(elf::ET_DYN);  // Shared library.
 
 #if defined(TARGET_ARCH_IA32)
-  WriteHalf(stream, elf::EM_386);
+  WriteHalf(elf::EM_386);
 #elif defined(TARGET_ARCH_X64)
-  WriteHalf(stream, elf::EM_X86_64);
+  WriteHalf(elf::EM_X86_64);
 #elif defined(TARGET_ARCH_ARM)
-  WriteHalf(stream, elf::EM_ARM);
+  WriteHalf(elf::EM_ARM);
 #elif defined(TARGET_ARCH_ARM64)
-  WriteHalf(stream, elf::EM_AARCH64);
+  WriteHalf(elf::EM_AARCH64);
 #else
   FATAL("Unknown ELF architecture");
 #endif
 
-  WriteWord(stream, elf::EV_CURRENT);  // Version
-  WriteAddr(stream, 0);                // "Entry point"
-  WriteOff(stream, program_table_file_offset_);
-  WriteOff(stream, section_table_file_offset_);
+  WriteWord(elf::EV_CURRENT);  // Version
+  WriteAddr(0);                // "Entry point"
+  WriteOff(program_table_file_offset_);
+  WriteOff(section_table_file_offset_);
 
 #if defined(TARGET_ARCH_ARM)
   uword flags = elf::EF_ARM_ABI | (TargetCPUFeatures::hardfp_supported()
@@ -849,48 +797,31 @@
 #else
   uword flags = 0;
 #endif
-  WriteWord(stream, flags);
+  WriteWord(flags);
 
-  WriteHalf(stream, kElfHeaderSize);
-  WriteHalf(stream, kElfProgramTableEntrySize);
-  WriteHalf(stream, program_table_entry_count_);
-  WriteHalf(stream, kElfSectionTableEntrySize);
-  WriteHalf(stream, section_table_entry_count_);
-  // The section header string table is always last in the active sections.
-  WriteHalf(stream, SectionTableIndex(active_sections_.Last()->section_index));
+  WriteHalf(kElfHeaderSize);
+  WriteHalf(kElfProgramTableEntrySize);
+  WriteHalf(segments_.length() + kNumImplicitSegments);
+  WriteHalf(kElfSectionTableEntrySize);
+  WriteHalf(sections_.length() + kNumInvalidSections);
+  WriteHalf(shstrtab_->section_index());
 
-  ASSERT(stream->position() == kElfHeaderSize);
+  ASSERT(stream_->position() == kElfHeaderSize);
 }
 
-void Elf::WriteProgramTable(StreamingWriteStream* stream) {
-  ASSERT(stream->position() == program_table_file_offset_);
-  auto const program_table_file_size = ProgramTableSize();
+void Elf::WriteProgramTable() {
+  ASSERT(stream_->position() == program_table_file_offset_);
 
   // Self-reference to program header table. Required by Android but not by
   // Linux. Must appear before any PT_LOAD entries.
   {
+    ProgramTable program_table(program_table_file_offset_,
+                               program_table_file_size_);
+
     ASSERT(kNumImplicitSegments == 3);
-    const intptr_t start = stream->position();
-#if defined(TARGET_ARCH_IS_32_BIT)
-    WriteWord(stream, elf::PT_PHDR);
-    WriteOff(stream, program_table_file_offset_);   // File offset.
-    WriteAddr(stream, program_table_file_offset_);  // Virtual address.
-    WriteAddr(stream, program_table_file_offset_);  // Physical address, unused.
-    WriteWord(stream, program_table_file_size);
-    WriteWord(stream, program_table_file_size);
-    WriteWord(stream, elf::PF_R);
-    WriteWord(stream, kPageSize);
-#else
-    WriteWord(stream, elf::PT_PHDR);
-    WriteWord(stream, elf::PF_R);
-    WriteOff(stream, program_table_file_offset_);   // File offset.
-    WriteAddr(stream, program_table_file_offset_);  // Virtual address.
-    WriteAddr(stream, program_table_file_offset_);  // Physical address, unused.
-    WriteXWord(stream, program_table_file_size);
-    WriteXWord(stream, program_table_file_size);
-    WriteXWord(stream, kPageSize);
-#endif
-    const intptr_t end = stream->position();
+    const intptr_t start = stream_->position();
+    program_table.WriteSegmentEntry(this);
+    const intptr_t end = stream_->position();
     ASSERT((end - start) == kElfProgramTableEntrySize);
   }
   // Load for self-reference to program header table. Required by Android but
@@ -901,71 +832,26 @@
     // fixed num of segments based on the four pieces of a snapshot, but if we
     // use more in the future we'll likely need to do something more compilated
     // to generate DWARF without knowing a piece's virtual address in advance.
-    RELEASE_ASSERT((program_table_file_offset_ + program_table_file_size) <
-                   kProgramTableSegmentSize);
+    auto const program_table_segment_size =
+        program_table_file_offset_ + program_table_file_size_;
+    RELEASE_ASSERT(program_table_segment_size < kProgramTableSegmentSize);
+
+    // We create a section that, when printed as a segment, contains the
+    // appropriate info for the program table.
+    ProgramTableLoad program_table_load(program_table_segment_size);
 
     ASSERT(kNumImplicitSegments == 3);
-    const intptr_t start = stream->position();
-
-    // The Android dynamic linker in Jelly Bean incorrectly assumes that all
-    // non-writable segments are continguous. We put BSS first, so we must make
-    // this segment writable so it does not mark the BSS as read-only.
-    //
-    // The bug is here:
-    //   https://github.com/aosp-mirror/platform_bionic/blob/94963af28e445384e19775a838a29e6a71708179/linker/linker.c#L1991-L2001
-#if defined(TARGET_ARCH_IS_32_BIT)
-    WriteWord(stream, elf::PT_LOAD);
-    WriteOff(stream, 0);   // File offset.
-    WriteAddr(stream, 0);  // Virtual address.
-    WriteAddr(stream, 0);  // Physical address, not used.
-    WriteWord(stream, program_table_file_offset_ + program_table_file_size);
-    WriteWord(stream, program_table_file_offset_ + program_table_file_size);
-    WriteWord(stream, elf::PF_R | elf::PF_W);
-    WriteWord(stream, kPageSize);
-#else
-    WriteWord(stream, elf::PT_LOAD);
-    WriteWord(stream, elf::PF_R | elf::PF_W);
-    WriteOff(stream, 0);   // File offset.
-    WriteAddr(stream, 0);  // Virtual address.
-    WriteAddr(stream, 0);  // Physical address, not used.
-    WriteXWord(stream, program_table_file_offset_ + program_table_file_size);
-    WriteXWord(stream, program_table_file_offset_ + program_table_file_size);
-    WriteXWord(stream, kPageSize);
-#endif
-    const intptr_t end = stream->position();
+    const intptr_t start = stream_->position();
+    program_table_load.WriteSegmentEntry(this);
+    const intptr_t end = stream_->position();
     ASSERT((end - start) == kElfProgramTableEntrySize);
   }
 
-  // We need to write out the segment headers even in the debugging info,
-  // even though there won't be any contents of those segments here and
-  // so we should report sizes of 0.
-  for (const auto section : segments_) {
-    const intptr_t start = stream->position();
-    // file_sizes_ corresponds to active_sections_, so we first need to
-    // find the offset of this section in there.
-    auto const active_sections_index =
-        ActiveSectionsIndex(section->section_index);
-    auto const file_size = file_sizes_.At(active_sections_index);
-#if defined(TARGET_ARCH_IS_32_BIT)
-    WriteWord(stream, section->segment_type);
-    WriteOff(stream, section->file_offset);
-    WriteAddr(stream, section->memory_offset);  // Virtual address.
-    WriteAddr(stream, section->memory_offset);  // Physical address, not used.
-    WriteWord(stream, file_size);
-    WriteWord(stream, section->memory_size);
-    WriteWord(stream, section->segment_flags);
-    WriteWord(stream, section->alignment);
-#else
-    WriteWord(stream, section->segment_type);
-    WriteWord(stream, section->segment_flags);
-    WriteOff(stream, section->file_offset);
-    WriteAddr(stream, section->memory_offset);  // Virtual address.
-    WriteAddr(stream, section->memory_offset);  // Physical address, not used.
-    WriteXWord(stream, file_size);
-    WriteXWord(stream, section->memory_size);
-    WriteXWord(stream, section->alignment);
-#endif
-    const intptr_t end = stream->position();
+  for (intptr_t i = 0; i < segments_.length(); i++) {
+    Section* section = segments_[i];
+    const intptr_t start = stream_->position();
+    section->WriteSegmentEntry(this);
+    const intptr_t end = stream_->position();
     ASSERT((end - start) == kElfProgramTableEntrySize);
   }
 
@@ -973,111 +859,44 @@
   // header table entries.
   {
     ASSERT(kNumImplicitSegments == 3);
-    const intptr_t start = stream->position();
-    auto const active_sections_index =
-        ActiveSectionsIndex(dynamic_->section_index);
-    auto const file_size = file_sizes_.At(active_sections_index);
-#if defined(TARGET_ARCH_IS_32_BIT)
-    WriteWord(stream, elf::PT_DYNAMIC);
-    WriteOff(stream, dynamic_->file_offset);
-    WriteAddr(stream, dynamic_->memory_offset);  // Virtual address.
-    WriteAddr(stream, dynamic_->memory_offset);  // Physical address, not used.
-    WriteWord(stream, file_size);
-    WriteWord(stream, dynamic_->memory_size);
-    WriteWord(stream, dynamic_->segment_flags);
-    WriteWord(stream, dynamic_->alignment);
-#else
-    WriteWord(stream, elf::PT_DYNAMIC);
-    WriteWord(stream, dynamic_->segment_flags);
-    WriteOff(stream, dynamic_->file_offset);
-    WriteAddr(stream, dynamic_->memory_offset);  // Virtual address.
-    WriteAddr(stream, dynamic_->memory_offset);  // Physical address, not used.
-    WriteXWord(stream, file_size);
-    WriteXWord(stream, dynamic_->memory_size);
-    WriteXWord(stream, dynamic_->alignment);
-#endif
-    const intptr_t end = stream->position();
+    const intptr_t start = stream_->position();
+    dynamic_->WriteSegmentEntry(this, /*dynamic=*/true);
+    const intptr_t end = stream_->position();
     ASSERT((end - start) == kElfProgramTableEntrySize);
   }
 }
 
-void Elf::WriteSectionTable(StreamingWriteStream* stream) {
-  stream->Align(kElfSectionTableAlignment);
-  ASSERT(stream->position() == section_table_file_offset_);
+void Elf::WriteSectionTable() {
+  stream_->Align(kElfSectionTableAlignment);
+
+  ASSERT(stream_->position() == section_table_file_offset_);
 
   {
     // The first entry in the section table is reserved and must be all zeros.
     ASSERT(kNumInvalidSections == 1);
-    const intptr_t start = stream->position();
-#if defined(TARGET_ARCH_IS_32_BIT)
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteAddr(stream, 0);
-    WriteOff(stream, 0);
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-#else
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteXWord(stream, 0);
-    WriteAddr(stream, 0);
-    WriteOff(stream, 0);
-    WriteXWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteWord(stream, 0);
-    WriteXWord(stream, 0);
-    WriteXWord(stream, 0);
-#endif
-    const intptr_t end = stream->position();
+    const intptr_t start = stream_->position();
+    ReservedSection reserved;
+    reserved.WriteSectionEntry(this);
+    const intptr_t end = stream_->position();
     ASSERT((end - start) == kElfSectionTableEntrySize);
   }
 
-  for (intptr_t i = 0; i < active_sections_.length(); i++) {
-    Section* section = active_sections_[i];
-    auto const name = section_names_.At(i);
-    auto const type = section_types_.At(i);
-    auto const file_size = file_sizes_.At(i);
-    auto const link = SectionTableIndex(section->section_link);
-
-    const intptr_t start = stream->position();
-#if defined(TARGET_ARCH_IS_32_BIT)
-    WriteWord(stream, name);
-    WriteWord(stream, type);
-    WriteWord(stream, section->section_flags);
-    WriteAddr(stream, section->memory_offset);
-    WriteOff(stream, section->file_offset);
-    WriteWord(stream, file_size);  // Has different meaning for BSS.
-    WriteWord(stream, link);
-    WriteWord(stream, section->section_info);
-    WriteWord(stream, section->alignment);
-    WriteWord(stream, section->section_entry_size);
-#else
-    WriteWord(stream, name);
-    WriteWord(stream, type);
-    WriteXWord(stream, section->section_flags);
-    WriteAddr(stream, section->memory_offset);
-    WriteOff(stream, section->file_offset);
-    WriteXWord(stream, file_size);  // Has different meaning for BSS.
-    WriteWord(stream, link);
-    WriteWord(stream, section->section_info);
-    WriteXWord(stream, section->alignment);
-    WriteXWord(stream, section->section_entry_size);
-#endif
-    const intptr_t end = stream->position();
+  for (intptr_t i = 0; i < sections_.length(); i++) {
+    Section* section = sections_[i];
+    const intptr_t start = stream_->position();
+    section->WriteSectionEntry(this);
+    const intptr_t end = stream_->position();
     ASSERT((end - start) == kElfSectionTableEntrySize);
   }
 }
 
-void Elf::WriteSections(StreamingWriteStream* stream) {
-  for (auto section : output_sections_) {
-    stream->Align(section->alignment);
-    ASSERT(stream->position() == section->file_offset);
-    section->Write(stream);
-    ASSERT(stream->position() == section->file_offset + section->file_size);
+void Elf::WriteSections() {
+  for (intptr_t i = 0; i < sections_.length(); i++) {
+    Section* section = sections_[i];
+    stream_->Align(section->alignment);
+    ASSERT(stream_->position() == section->file_offset());
+    section->Write(this);
+    ASSERT(stream_->position() == section->file_offset() + section->FileSize());
   }
 }
 
diff --git a/runtime/vm/elf.h b/runtime/vm/elf.h
index f6ac23f..f270dac 100644
--- a/runtime/vm/elf.h
+++ b/runtime/vm/elf.h
@@ -9,7 +9,6 @@
 #include "vm/compiler/runtime_api.h"
 #include "vm/datastream.h"
 #include "vm/growable_array.h"
-#include "vm/hash_map.h"
 #include "vm/zone.h"
 
 namespace dart {
@@ -22,14 +21,11 @@
 
 class Elf : public ZoneAllocated {
  public:
-  Elf(Zone* zone,
-      StreamingWriteStream* stream,
-      bool strip,
-      StreamingWriteStream* debug_stream = nullptr);
+  Elf(Zone* zone, StreamingWriteStream* stream);
 
   static const intptr_t kPageSize = 4096;
 
-  intptr_t NextMemoryOffset() const { return memory_offset_; }
+  intptr_t NextMemoryOffset() const;
   intptr_t NextSectionIndex() const;
   intptr_t AddText(const char* name, const uint8_t* bytes, intptr_t size);
   intptr_t AddROData(const char* name, const uint8_t* bytes, intptr_t size);
@@ -41,108 +37,66 @@
 
   void Finalize();
 
-  static void WriteBytes(StreamingWriteStream* stream,
-                         const uint8_t* bytes,
-                         intptr_t size) {
-    stream->WriteBytes(bytes, size);
+  intptr_t position() const { return stream_->position(); }
+  void WriteBytes(const uint8_t* b, intptr_t size) {
+    stream_->WriteBytes(b, size);
   }
-  static void WriteByte(StreamingWriteStream* stream, uint8_t value) {
-    stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+  void WriteByte(uint8_t value) {
+    stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
-  static void WriteHalf(StreamingWriteStream* stream, uint16_t value) {
-    stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+  void WriteHalf(uint16_t value) {
+    stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
-  static void WriteWord(StreamingWriteStream* stream, uint32_t value) {
-    stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+  void WriteWord(uint32_t value) {
+    stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
-  static void WriteAddr(StreamingWriteStream* stream,
-                        compiler::target::uword value) {
-    stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+  void WriteAddr(compiler::target::uword value) {
+    stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
-  static void WriteOff(StreamingWriteStream* stream,
-                       compiler::target::uword value) {
-    stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+  void WriteOff(compiler::target::uword value) {
+    stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
 #if defined(TARGET_ARCH_IS_64_BIT)
-  static void WriteXWord(StreamingWriteStream* stream, uint64_t value) {
-    stream->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+  void WriteXWord(uint64_t value) {
+    stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
 #endif
 
  private:
   void AddSection(Section* section, const char* name);
-  void AddSegment(Section* section);
+  intptr_t AddSectionSymbol(const Section* section,
+                            const char* name,
+                            intptr_t size);
 
-  intptr_t ActiveSectionsIndex(intptr_t section_index) const;
-  intptr_t SectionTableIndex(intptr_t section_index) const;
-  intptr_t ProgramTableSize() const;
-  intptr_t SectionTableSize() const;
-
-  void ClearOutputInfo();
-  // Checks that the output information used by the writing methods has been
-  // properly constructed.
-  void VerifyOutputInfo() const;
-
-  // Creates a new version of shstrtab_ that only copies over names of active
-  // sections. Sets the contents of section_names_ to indices in the new table.
-  StringTable* CreateSectionHeaderStringTable();
-  // Either returns the original section or a section like the old one that
-  // also accounts for what sections are currently active.
-  Section* AdjustForActiveSections(Section* section);
-
-  // These methods return the new file offset taking into consideration the
-  // alignment and size of the section.
-  intptr_t PrepareDebugSection(Section* section,
-                               intptr_t start_offset,
-                               bool use_fake_info);
-  intptr_t PrepareMainSection(Section* section,
-                              intptr_t start_offset,
-                              intptr_t skipped_sections);
-
-  // These methods set up:
-  //   * Various information about file offsets
-  //   * The number of entries in the program and section tables
-  //   * An array of the active sections (i.e., those in the section table).
-  //   * An array of the sections that will be fully output.
-  //   * Some arrays of information used instead of the values of their
-  //     corresponding Section fields when creating the section table.
-  void PrepareDebugOutputInfo();
-  void PrepareMainOutputInfo();
-
-  void WriteHeader(StreamingWriteStream* s);
-  void WriteProgramTable(StreamingWriteStream* s);
-  void WriteSectionTable(StreamingWriteStream* s);
-  void WriteSections(StreamingWriteStream* s);
+  void ComputeFileOffsets();
+  void WriteHeader();
+  void WriteSectionTable();
+  void WriteProgramTable();
+  void WriteSections();
 
   Zone* const zone_;
-  const bool strip_;
   StreamingWriteStream* const stream_;
-  StreamingWriteStream* const debug_stream_;
-  GrowableArray<Section*> sections_;
-  GrowableArray<Section*> segments_;
 
-  intptr_t memory_offset_ = 0;
-  StringTable* shstrtab_ = nullptr;
-  StringTable* dynstrtab_ = nullptr;
-  SymbolTable* dynsym_ = nullptr;
-  StringTable* strtab_ = nullptr;
-  SymbolTable* symtab_ = nullptr;
+  // All our strings would fit in a single page. However, we use separate
+  // .shstrtab and .dynstr to work around a bug in Android's strip utility.
+  StringTable* const shstrtab_;
+  StringTable* const dynstrtab_;
+  SymbolTable* const dynsym_;
+
+  // Can only be created once the dynamic symbol table is complete.
   DynamicTable* dynamic_ = nullptr;
 
-  // Filled out during the Prepare*OutputInfo methods and used by the Write*
-  // instance methods, as these values will differ between stripped and
-  // debugging outputs.
-  GrowableArray<Section*> active_sections_;
-  GrowableArray<Section*> output_sections_;
-  IntMap<intptr_t> adjusted_indices_;
-  // These should all contain entries for the sections in active_sections_.
-  GrowableArray<intptr_t> file_sizes_;
-  GrowableArray<intptr_t> section_names_;
-  GrowableArray<intptr_t> section_types_;
+  // The static tables are lazily created when static symbols are added.
+  StringTable* strtab_ = nullptr;
+  SymbolTable* symtab_ = nullptr;
+
+  GrowableArray<Section*> sections_;
+  GrowableArray<Section*> segments_;
+  intptr_t memory_offset_;
   intptr_t section_table_file_offset_ = -1;
-  intptr_t section_table_entry_count_ = -1;
+  intptr_t section_table_file_size_ = -1;
   intptr_t program_table_file_offset_ = -1;
-  intptr_t program_table_entry_count_ = -1;
+  intptr_t program_table_file_size_ = -1;
 };
 
 }  // namespace dart
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 6c5a066..15700c1 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -221,12 +221,6 @@
     : freelist_(),
       heap_(heap),
       pages_lock_(),
-      pages_(NULL),
-      pages_tail_(NULL),
-      exec_pages_(NULL),
-      exec_pages_tail_(NULL),
-      large_pages_(NULL),
-      image_pages_(NULL),
       bump_top_(0),
       bump_end_(0),
       max_capacity_in_words_(max_capacity_in_words),
@@ -273,49 +267,95 @@
   return page_size >> kWordSizeLog2;
 }
 
+void PageSpace::AddPageLocked(HeapPage* page) {
+  if (pages_ == nullptr) {
+    pages_ = page;
+  } else {
+    pages_tail_->set_next(page);
+  }
+  pages_tail_ = page;
+}
+
+void PageSpace::AddLargePageLocked(HeapPage* page) {
+  if (large_pages_ == nullptr) {
+    large_pages_ = page;
+  } else {
+    large_pages_tail_->set_next(page);
+  }
+  large_pages_tail_ = page;
+}
+
+void PageSpace::AddExecPageLocked(HeapPage* page) {
+  if (exec_pages_ == nullptr) {
+    exec_pages_ = page;
+  } else {
+    if (FLAG_write_protect_code) {
+      exec_pages_tail_->WriteProtect(false);
+    }
+    exec_pages_tail_->set_next(page);
+    if (FLAG_write_protect_code) {
+      exec_pages_tail_->WriteProtect(true);
+    }
+  }
+  exec_pages_tail_ = page;
+}
+
+void PageSpace::RemovePageLocked(HeapPage* page, HeapPage* previous_page) {
+  if (previous_page != NULL) {
+    previous_page->set_next(page->next());
+  } else {
+    pages_ = page->next();
+  }
+  if (page == pages_tail_) {
+    pages_tail_ = previous_page;
+  }
+}
+
+void PageSpace::RemoveLargePageLocked(HeapPage* page, HeapPage* previous_page) {
+  if (previous_page != NULL) {
+    previous_page->set_next(page->next());
+  } else {
+    large_pages_ = page->next();
+  }
+  if (page == large_pages_tail_) {
+    large_pages_tail_ = previous_page;
+  }
+}
+
+void PageSpace::RemoveExecPageLocked(HeapPage* page, HeapPage* previous_page) {
+  if (previous_page != NULL) {
+    previous_page->set_next(page->next());
+  } else {
+    exec_pages_ = page->next();
+  }
+  if (page == exec_pages_tail_) {
+    exec_pages_tail_ = previous_page;
+  }
+}
+
 HeapPage* PageSpace::AllocatePage(HeapPage::PageType type, bool link) {
   {
     MutexLocker ml(&pages_lock_);
     if (!CanIncreaseCapacityInWordsLocked(kPageSizeInWords)) {
-      return NULL;
+      return nullptr;
     }
     IncreaseCapacityInWordsLocked(kPageSizeInWords);
   }
   const bool is_exec = (type == HeapPage::kExecutable);
   const char* name = Heap::RegionName(is_exec ? Heap::kCode : Heap::kOld);
   HeapPage* page = HeapPage::Allocate(kPageSizeInWords, type, name);
-  if (page == NULL) {
+  if (page == nullptr) {
     RELEASE_ASSERT(!FLAG_abort_on_oom);
     IncreaseCapacityInWords(-kPageSizeInWords);
-    return NULL;
+    return nullptr;
   }
 
   MutexLocker ml(&pages_lock_);
   if (link) {
-    if (!is_exec) {
-      if (pages_ == NULL) {
-        pages_ = page;
-      } else {
-        pages_tail_->set_next(page);
-      }
-      pages_tail_ = page;
+    if (is_exec) {
+      AddExecPageLocked(page);
     } else {
-      // Should not allocate executable pages when running from a precompiled
-      // snapshot.
-      ASSERT(Dart::vm_snapshot_kind() != Snapshot::kFullAOT);
-
-      if (exec_pages_ == NULL) {
-        exec_pages_ = page;
-      } else {
-        if (FLAG_write_protect_code) {
-          exec_pages_tail_->WriteProtect(false);
-        }
-        exec_pages_tail_->set_next(page);
-        if (FLAG_write_protect_code) {
-          exec_pages_tail_->WriteProtect(true);
-        }
-      }
-      exec_pages_tail_ = page;
+      AddPageLocked(page);
     }
   }
 
@@ -332,26 +372,28 @@
   {
     MutexLocker ml(&pages_lock_);
     if (!CanIncreaseCapacityInWordsLocked(page_size_in_words)) {
-      return NULL;
+      return nullptr;
     }
     IncreaseCapacityInWordsLocked(page_size_in_words);
   }
   const bool is_exec = (type == HeapPage::kExecutable);
   const char* name = Heap::RegionName(is_exec ? Heap::kCode : Heap::kOld);
   HeapPage* page = HeapPage::Allocate(page_size_in_words, type, name);
-  {
-    MutexLocker ml(&pages_lock_);
-    if (page == nullptr) {
-      IncreaseCapacityInWordsLocked(-page_size_in_words);
-      return nullptr;
-    }
-    page->set_next(large_pages_);
-    large_pages_ = page;
 
-    // Only one object in this page (at least until Array::MakeFixedLength
-    // is called).
-    page->set_object_end(page->object_start() + size);
+  MutexLocker ml(&pages_lock_);
+  if (page == nullptr) {
+    IncreaseCapacityInWordsLocked(-page_size_in_words);
+    return nullptr;
   }
+  if (is_exec) {
+    AddExecPageLocked(page);
+  } else {
+    AddLargePageLocked(page);
+  }
+
+  // Only one object in this page (at least until Array::MakeFixedLength
+  // is called).
+  page->set_object_end(page->object_start() + size);
   return page;
 }
 
@@ -376,26 +418,10 @@
   {
     MutexLocker ml(&pages_lock_);
     IncreaseCapacityInWordsLocked(-(page->memory_->size() >> kWordSizeLog2));
-    if (!is_exec) {
-      // Remove the page from the list of data pages.
-      if (previous_page != NULL) {
-        previous_page->set_next(page->next());
-      } else {
-        pages_ = page->next();
-      }
-      if (page == pages_tail_) {
-        pages_tail_ = previous_page;
-      }
+    if (is_exec) {
+      RemoveExecPageLocked(page, previous_page);
     } else {
-      // Remove the page from the list of executable pages.
-      if (previous_page != NULL) {
-        previous_page->set_next(page->next());
-      } else {
-        exec_pages_ = page->next();
-      }
-      if (page == exec_pages_tail_) {
-        exec_pages_tail_ = previous_page;
-      }
+      RemovePageLocked(page, previous_page);
     }
   }
   // TODO(iposva): Consider adding to a pool of empty pages.
@@ -403,16 +429,10 @@
 }
 
 void PageSpace::FreeLargePage(HeapPage* page, HeapPage* previous_page) {
-  // Thread should be at a safepoint when this code is called and hence
-  // it is not necessary to lock large_pages_.
-  ASSERT(Thread::Current()->IsAtSafepoint());
-  IncreaseCapacityInWords(-(page->memory_->size() >> kWordSizeLog2));
-  // Remove the page from the list.
-  if (previous_page != NULL) {
-    previous_page->set_next(page->next());
-  } else {
-    large_pages_ = page->next();
-  }
+  ASSERT(page->type() != HeapPage::kExecutable);
+  MutexLocker ml(&pages_lock_);
+  IncreaseCapacityInWordsLocked(-(page->memory_->size() >> kWordSizeLog2));
+  RemoveLargePageLocked(page, previous_page);
   page->Deallocate();
 }
 
@@ -1132,36 +1152,18 @@
       OS::PrintErr(" done.\n");
     }
 
-    TIMELINE_FUNCTION_GC_DURATION(thread, "SweepLargeAndExecutablePages");
+    // Executable pages are always swept immediately to simplify
+    // code protection.
+
+    TIMELINE_FUNCTION_GC_DURATION(thread, "SweepExecutable");
     GCSweeper sweeper;
-
-    // During stop-the-world phases we should use bulk lock when adding
-    // elements to the free list.
-    MutexLocker mld(freelist_[HeapPage::kData].mutex());
-    MutexLocker mle(freelist_[HeapPage::kExecutable].mutex());
-
-    // Large and executable pages are always swept immediately.
     HeapPage* prev_page = NULL;
-    HeapPage* page = large_pages_;
-    while (page != NULL) {
-      HeapPage* next_page = page->next();
-      const intptr_t words_to_end = sweeper.SweepLargePage(page);
-      if (words_to_end == 0) {
-        FreeLargePage(page, prev_page);
-      } else {
-        TruncateLargePage(page, words_to_end << kWordSizeLog2);
-        prev_page = page;
-      }
-      // Advance to the next page.
-      page = next_page;
-    }
-
-    prev_page = NULL;
-    page = exec_pages_;
+    HeapPage* page = exec_pages_;
     FreeList* freelist = &freelist_[HeapPage::kExecutable];
+    MutexLocker ml(freelist->mutex());
     while (page != NULL) {
       HeapPage* next_page = page->next();
-      bool page_in_use = sweeper.SweepPage(page, freelist, true);
+      bool page_in_use = sweeper.SweepPage(page, freelist, true /*is_locked*/);
       if (page_in_use) {
         prev_page = page;
       } else {
@@ -1175,12 +1177,14 @@
   }
 
   if (compact) {
+    SweepLarge();
     Compact(thread);
     set_phase(kDone);
   } else if (FLAG_concurrent_sweep) {
     ConcurrentSweep(isolate);
   } else {
-    BlockingSweep();
+    SweepLarge();
+    Sweep();
     set_phase(kDone);
   }
 
@@ -1213,19 +1217,38 @@
   }
 }
 
-void PageSpace::BlockingSweep() {
+void PageSpace::SweepLarge() {
+  TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "SweepLarge");
+
+  GCSweeper sweeper;
+  HeapPage* prev_page = nullptr;
+  HeapPage* page = large_pages_;
+  while (page != nullptr) {
+    HeapPage* next_page = page->next();
+    const intptr_t words_to_end = sweeper.SweepLargePage(page);
+    if (words_to_end == 0) {
+      FreeLargePage(page, prev_page);
+    } else {
+      TruncateLargePage(page, words_to_end << kWordSizeLog2);
+      prev_page = page;
+    }
+    // Advance to the next page.
+    page = next_page;
+  }
+}
+
+void PageSpace::Sweep() {
   TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "Sweep");
 
-  MutexLocker mld(freelist_[HeapPage::kData].mutex());
-  MutexLocker mle(freelist_[HeapPage::kExecutable].mutex());
-
-  // Sweep all regular sized pages now.
   GCSweeper sweeper;
-  HeapPage* prev_page = NULL;
+  HeapPage* prev_page = nullptr;
   HeapPage* page = pages_;
-  while (page != NULL) {
+  FreeList* freelist = &freelist_[HeapPage::kData];
+  MutexLocker ml(freelist_->mutex());
+  while (page != nullptr) {
     HeapPage* next_page = page->next();
-    bool page_in_use = sweeper.SweepPage(page, &freelist_[page->type()], true);
+    ASSERT(page->type() == HeapPage::kData);
+    bool page_in_use = sweeper.SweepPage(page, freelist, true /*is_locked*/);
     if (page_in_use) {
       prev_page = page;
     } else {
@@ -1244,8 +1267,8 @@
 
 void PageSpace::ConcurrentSweep(Isolate* isolate) {
   // Start the concurrent sweeper task now.
-  GCSweeper::SweepConcurrent(isolate, pages_, pages_tail_,
-                             &freelist_[HeapPage::kData]);
+  GCSweeper::SweepConcurrent(isolate, pages_, pages_tail_, large_pages_,
+                             large_pages_tail_, &freelist_[HeapPage::kData]);
 }
 
 void PageSpace::Compact(Thread* thread) {
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 83ca216..417e14d 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -479,10 +479,19 @@
 
   // Makes bump block walkable; do not call concurrently with mutator.
   void MakeIterable() const;
+
+  void AddPageLocked(HeapPage* page);
+  void AddLargePageLocked(HeapPage* page);
+  void AddExecPageLocked(HeapPage* page);
+  void RemovePageLocked(HeapPage* page, HeapPage* previous_page);
+  void RemoveLargePageLocked(HeapPage* page, HeapPage* previous_page);
+  void RemoveExecPageLocked(HeapPage* page, HeapPage* previous_page);
+
   HeapPage* AllocatePage(HeapPage::PageType type, bool link = true);
-  void FreePage(HeapPage* page, HeapPage* previous_page);
   HeapPage* AllocateLargePage(intptr_t size, HeapPage::PageType type);
+
   void TruncateLargePage(HeapPage* page, intptr_t new_object_size_in_bytes);
+  void FreePage(HeapPage* page, HeapPage* previous_page);
   void FreeLargePage(HeapPage* page, HeapPage* previous_page);
   void FreePages(HeapPage* pages);
 
@@ -490,7 +499,8 @@
                                  bool finalize,
                                  int64_t pre_wait_for_sweepers,
                                  int64_t pre_safe_point);
-  void BlockingSweep();
+  void SweepLarge();
+  void Sweep();
   void ConcurrentSweep(Isolate* isolate);
   void Compact(Thread* thread);
 
@@ -513,12 +523,13 @@
 
   // Use ExclusivePageIterator for safe access to these.
   mutable Mutex pages_lock_;
-  HeapPage* pages_;
-  HeapPage* pages_tail_;
-  HeapPage* exec_pages_;
-  HeapPage* exec_pages_tail_;
-  HeapPage* large_pages_;
-  HeapPage* image_pages_;
+  HeapPage* pages_ = nullptr;
+  HeapPage* pages_tail_ = nullptr;
+  HeapPage* exec_pages_ = nullptr;
+  HeapPage* exec_pages_tail_ = nullptr;
+  HeapPage* large_pages_ = nullptr;
+  HeapPage* large_pages_tail_ = nullptr;
+  HeapPage* image_pages_ = nullptr;
 
   // A block of memory in a data page, managed by bump allocation. The remainder
   // is kept formatted as a FreeListElement, but is not in any freelist.
diff --git a/runtime/vm/heap/sweeper.cc b/runtime/vm/heap/sweeper.cc
index fa294d9..2786390 100644
--- a/runtime/vm/heap/sweeper.cc
+++ b/runtime/vm/heap/sweeper.cc
@@ -110,11 +110,15 @@
                         PageSpace* old_space,
                         HeapPage* first,
                         HeapPage* last,
+                        HeapPage* large_first,
+                        HeapPage* large_last,
                         FreeList* freelist)
       : task_isolate_(isolate),
         old_space_(old_space),
         first_(first),
         last_(last),
+        large_first_(large_first),
+        large_last_(large_last),
         freelist_(freelist) {
     ASSERT(task_isolate_ != NULL);
     ASSERT(first_ != NULL);
@@ -132,14 +136,35 @@
     ASSERT(result);
     {
       Thread* thread = Thread::Current();
+      ASSERT(thread->BypassSafepoints());  // Or we should be checking in.
       TIMELINE_FUNCTION_GC_DURATION(thread, "ConcurrentSweep");
       GCSweeper sweeper;
 
-      HeapPage* page = first_;
+      HeapPage* page = large_first_;
       HeapPage* prev_page = NULL;
-
       while (page != NULL) {
-        ASSERT(thread->BypassSafepoints());  // Or we should be checking in.
+        HeapPage* next_page;
+        if (page == large_last_) {
+          // Don't access page->next(), which would be a race with mutator
+          // allocating new pages.
+          next_page = NULL;
+        } else {
+          next_page = page->next();
+        }
+        ASSERT(page->type() == HeapPage::kData);
+        const intptr_t words_to_end = sweeper.SweepLargePage(page);
+        if (words_to_end == 0) {
+          old_space_->FreeLargePage(page, prev_page);
+        } else {
+          old_space_->TruncateLargePage(page, words_to_end << kWordSizeLog2);
+          prev_page = page;
+        }
+        page = next_page;
+      }
+
+      page = first_;
+      prev_page = NULL;
+      while (page != NULL) {
         HeapPage* next_page;
         if (page == last_) {
           // Don't access page->next(), which would be a race with mutator
@@ -181,15 +206,20 @@
   PageSpace* old_space_;
   HeapPage* first_;
   HeapPage* last_;
+  HeapPage* large_first_;
+  HeapPage* large_last_;
   FreeList* freelist_;
 };
 
 void GCSweeper::SweepConcurrent(Isolate* isolate,
                                 HeapPage* first,
                                 HeapPage* last,
+                                HeapPage* large_first,
+                                HeapPage* large_last,
                                 FreeList* freelist) {
   bool result = Dart::thread_pool()->Run<ConcurrentSweeperTask>(
-      isolate, isolate->heap()->old_space(), first, last, freelist);
+      isolate, isolate->heap()->old_space(), first, last, large_first,
+      large_last, freelist);
   ASSERT(result);
 }
 
diff --git a/runtime/vm/heap/sweeper.h b/runtime/vm/heap/sweeper.h
index 609b962..068a30d 100644
--- a/runtime/vm/heap/sweeper.h
+++ b/runtime/vm/heap/sweeper.h
@@ -38,6 +38,8 @@
   static void SweepConcurrent(Isolate* isolate,
                               HeapPage* first,
                               HeapPage* last,
+                              HeapPage* large_first,
+                              HeapPage* large_last,
                               FreeList* freelist);
 };
 
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index ee269b4..f3bae71 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -447,20 +447,28 @@
                                          Elf* debug_elf)
     : ImageWriter(thread->heap()),
       assembly_stream_(512 * KB, callback, callback_data),
-      dwarf_(nullptr) {
+      assembly_dwarf_(nullptr),
+      debug_dwarf_(nullptr) {
 #if defined(DART_PRECOMPILER)
   Zone* zone = Thread::Current()->zone();
-  if (!strip || debug_elf != nullptr) {
-    dwarf_ =
-        new (zone) Dwarf(zone, strip ? nullptr : &assembly_stream_, debug_elf);
+  if (!strip) {
+    assembly_dwarf_ =
+        new (zone) Dwarf(zone, &assembly_stream_, /*elf=*/nullptr);
+  }
+  if (debug_elf != nullptr) {
+    debug_dwarf_ =
+        new (zone) Dwarf(zone, /*assembly_stream=*/nullptr, debug_elf);
   }
 #endif
 }
 
 void AssemblyImageWriter::Finalize() {
 #ifdef DART_PRECOMPILER
-  if (dwarf_ != nullptr) {
-    dwarf_->Write();
+  if (assembly_dwarf_ != nullptr) {
+    assembly_dwarf_->Write();
+  }
+  if (debug_dwarf_ != nullptr) {
+    debug_dwarf_->Write();
   }
 #endif
 }
@@ -551,9 +559,9 @@
 #if defined(DART_PRECOMPILER)
   const char* bss_symbol =
       vm ? "_kDartVmSnapshotBss" : "_kDartIsolateSnapshotBss";
-  intptr_t segment_base = 0;
-  if ((dwarf_ != nullptr) && (dwarf_->elf() != nullptr)) {
-    segment_base = dwarf_->elf()->NextMemoryOffset();
+  intptr_t debug_segment_base = 0;
+  if (debug_dwarf_ != nullptr) {
+    debug_segment_base = debug_dwarf_->elf()->NextMemoryOffset();
   }
 #endif
 
@@ -670,12 +678,13 @@
     intptr_t dwarf_index = i;
 #ifdef DART_PRECOMPILER
     // Create a label for use by DWARF.
-    if (dwarf_ != nullptr) {
-      intptr_t virtual_address = -1;
-      if (auto const elf = dwarf_->elf()) {
-        virtual_address = segment_base + Image::kHeaderSize + text_offset;
-      }
-      dwarf_index = dwarf_->AddCode(code, virtual_address);
+    if (assembly_dwarf_ != nullptr) {
+      dwarf_index = assembly_dwarf_->AddCode(code);
+    }
+    if (debug_dwarf_ != nullptr) {
+      auto const virtual_address =
+          debug_segment_base + Image::kHeaderSize + text_offset;
+      debug_dwarf_->AddCode(code, virtual_address);
     }
 #endif
     // 2. Write a label at the entry point.
@@ -737,7 +746,7 @@
   FrameUnwindEpilogue();
 
 #if defined(DART_PRECOMPILER)
-  if ((dwarf_ != nullptr) && (dwarf_->elf() != nullptr)) {
+  if (debug_dwarf_ != nullptr) {
     // We need to generate a text segment of the appropriate size in the ELF
     // for two reasons:
     //
@@ -752,8 +761,10 @@
     //
     // Since we won't actually be adding the instructions to the ELF output,
     // we can pass nullptr for the bytes of the section/segment.
-    dwarf_->elf()->AddText(instructions_symbol, /*bytes=*/nullptr,
-                           Image::kHeaderSize + text_offset);
+    auto const debug_segment_base2 =
+        debug_dwarf_->elf()->AddText(instructions_symbol, /*bytes=*/nullptr,
+                                     Image::kHeaderSize + text_offset);
+    ASSERT(debug_segment_base2 == debug_segment_base);
   }
 
   assembly_stream_.Print(".bss\n");
@@ -874,19 +885,22 @@
                                  uint8_t** instructions_blob_buffer,
                                  ReAlloc alloc,
                                  intptr_t initial_size,
+                                 Dwarf* debug_dwarf,
                                  intptr_t bss_base,
                                  Elf* elf,
-                                 Dwarf* dwarf)
+                                 Dwarf* elf_dwarf)
     : ImageWriter(thread->heap()),
       instructions_blob_stream_(instructions_blob_buffer, alloc, initial_size),
       elf_(elf),
-      dwarf_(dwarf),
-      bss_base_(bss_base) {
+      elf_dwarf_(elf_dwarf),
+      bss_base_(bss_base),
+      debug_dwarf_(debug_dwarf) {
 #if defined(DART_PRECOMPILER)
-  RELEASE_ASSERT(bss_base_ == 0 || elf_ != nullptr);
+  RELEASE_ASSERT(elf_ == nullptr || elf_dwarf_ == nullptr ||
+                 elf_dwarf_->elf() == elf_);
 #else
   RELEASE_ASSERT(elf_ == nullptr);
-  RELEASE_ASSERT(dwarf_ == nullptr);
+  RELEASE_ASSERT(elf_dwarf_ == nullptr);
 #endif
 }
 
@@ -904,6 +918,10 @@
   if (elf_ != nullptr) {
     segment_base = elf_->NextMemoryOffset();
   }
+  intptr_t debug_segment_base = 0;
+  if (debug_dwarf_ != nullptr) {
+    debug_segment_base = debug_dwarf_->elf()->NextMemoryOffset();
+  }
 #endif
 
   // This header provides the gap to make the instructions snapshot look like a
@@ -911,7 +929,7 @@
   instructions_blob_stream_.WriteTargetWord(instructions_length);
 #if defined(DART_PRECOMPILER)
   instructions_blob_stream_.WriteTargetWord(
-      bss_base_ != 0 ? bss_base_ - segment_base : 0);
+      elf_ != nullptr ? bss_base_ - segment_base : 0);
 #else
   instructions_blob_stream_.WriteTargetWord(0);  // No relocations.
 #endif
@@ -1000,19 +1018,24 @@
 #endif
 
 #if defined(DART_PRECOMPILER)
-    if (elf_ != nullptr && dwarf_ != nullptr) {
+    if (elf_ != nullptr && elf_dwarf_ != nullptr) {
+      const auto& code = *instructions_[i].code_;
       auto const virtual_address = segment_base + payload_stream_start;
-      const Code& code = *instructions_[i].code_;
-      dwarf_->AddCode(code, virtual_address);
+      elf_dwarf_->AddCode(code, virtual_address);
       elf_->AddStaticSymbol(elf_->NextSectionIndex(),
                             namer.AssemblyNameFor(i, code), virtual_address);
     }
+    if (debug_dwarf_ != nullptr) {
+      const auto& code = *instructions_[i].code_;
+      auto const virtual_address = debug_segment_base + payload_stream_start;
+      debug_dwarf_->AddCode(code, virtual_address);
+    }
 
     // Don't patch the relocation if we're not generating ELF. The regular blobs
     // format does not yet support these relocations. Use
     // Code::VerifyBSSRelocations to check whether the relocations are patched
     // or not after loading.
-    if (bss_base_ != 0) {
+    if (elf_ != nullptr) {
       const intptr_t current_stream_position =
           instructions_blob_stream_.Position();
 
@@ -1056,14 +1079,20 @@
   ASSERT(instructions_blob_stream_.bytes_written() == instructions_length);
 
 #ifdef DART_PRECOMPILER
+  const char* instructions_symbol =
+      vm ? "_kDartVmSnapshotInstructions" : "_kDartIsolateSnapshotInstructions";
   if (elf_ != nullptr) {
-    const char* instructions_symbol = vm ? "_kDartVmSnapshotInstructions"
-                                         : "_kDartIsolateSnapshotInstructions";
-    intptr_t segment_base2 =
+    auto const segment_base2 =
         elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(),
                       instructions_blob_stream_.bytes_written());
     ASSERT(segment_base == segment_base2);
   }
+  if (debug_dwarf_ != nullptr) {
+    auto const debug_segment_base2 = debug_dwarf_->elf()->AddText(
+        instructions_symbol, instructions_blob_stream_.buffer(),
+        instructions_blob_stream_.bytes_written());
+    ASSERT(debug_segment_base == debug_segment_base2);
+  }
 #endif
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index 4b1aa9a..691f22c 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -346,7 +346,8 @@
   }
 
   StreamingWriteStream assembly_stream_;
-  Dwarf* dwarf_;
+  Dwarf* assembly_dwarf_;
+  Dwarf* debug_dwarf_;
 
   DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
 };
@@ -357,9 +358,10 @@
                   uint8_t** instructions_blob_buffer,
                   ReAlloc alloc,
                   intptr_t initial_size,
+                  Dwarf* debug_dwarf = nullptr,
                   intptr_t bss_base = 0,
                   Elf* elf = nullptr,
-                  Dwarf* dwarf = nullptr);
+                  Dwarf* elf_dwarf = nullptr);
 
   virtual void WriteText(WriteStream* clustered_stream, bool vm);
 
@@ -372,8 +374,9 @@
 
   WriteStream instructions_blob_stream_;
   Elf* const elf_;
-  Dwarf* const dwarf_;
+  Dwarf* const elf_dwarf_;
   const intptr_t bss_base_;
+  Dwarf* const debug_dwarf_;
 
   DISALLOW_COPY_AND_ASSIGN(BlobImageWriter);
 };
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index cb89d2c..c971495 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -750,13 +750,14 @@
 
   const auto& type_params =
       TypeArguments::Handle(zone, function.type_parameters());
+  const NNBDMode nnbd_mode = function.nnbd_mode();
   if (!type_params.IsNull()) {
     auto& type_param = TypeParameter::Handle(zone);
     auto& bound = AbstractType::Handle(zone);
     for (intptr_t i = 0, n = type_params.Length(); i < n; ++i) {
       type_param ^= type_params.TypeAt(i);
       bound = type_param.bound();
-      if (!bound.IsTopType() && !type_param.IsGenericCovariantImpl()) {
+      if (!bound.IsTopType(nnbd_mode) && !type_param.IsGenericCovariantImpl()) {
         return true;
       }
     }
@@ -770,7 +771,7 @@
   auto& type = AbstractType::Handle(zone);
   for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
     type = function.ParameterTypeAt(i);
-    if (!type.IsTopType() && !is_generic_covariant_impl.Contains(i) &&
+    if (!type.IsTopType(nnbd_mode) && !is_generic_covariant_impl.Contains(i) &&
         !is_covariant.Contains(i)) {
       return true;
     }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 48565d2..8564b07 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -4547,7 +4547,7 @@
       }
       const AbstractType& other_type_arg =
           AbstractType::Handle(zone, other_type_arguments.TypeAt(0));
-      if (other_type_arg.IsTopType()) {
+      if (other_type_arg.IsTopType(mode)) {
         return true;
       }
       if (!type_arguments.IsNull() && this_class.IsFutureClass()) {
@@ -5366,7 +5366,7 @@
   AbstractType& type = AbstractType::Handle();
   for (intptr_t i = 0; i < len; i++) {
     type = TypeAt(from_index + i);
-    if (type.IsNull() || !type.IsTopType()) {
+    if (type.IsNull() || !type.IsTopType(mode)) {
       return false;
     }
   }
@@ -7379,7 +7379,7 @@
                                         Heap::Space space) const {
   const AbstractType& param_type =
       AbstractType::Handle(ParameterTypeAt(parameter_position));
-  if (param_type.IsTopType()) {
+  if (param_type.IsTopType(mode)) {
     return true;
   }
   const AbstractType& other_param_type =
@@ -7457,7 +7457,7 @@
   const AbstractType& other_res_type =
       AbstractType::Handle(zone, other.result_type());
   // 'void Function()' is a subtype of 'Object Function()'.
-  if (!other_res_type.IsTopType()) {
+  if (!other_res_type.IsTopType(mode)) {
     const AbstractType& res_type = AbstractType::Handle(zone, result_type());
     if (!res_type.IsSubtypeOf(mode, other_res_type, space)) {
       return false;
@@ -16886,37 +16886,25 @@
   ASSERT(!other.IsTypeRef());  // Must be dereferenced at compile time.
   // Note that Object::sentinel() has Null class, but !IsNull().
   ASSERT(raw() != Object::sentinel().raw());
-  if (FLAG_strong_non_nullable_type_checks) {
-    if (IsNull()) {
-      // In a legacy library, compute
-      //   NNBD_SUBTYPE(other, Null) || NNBD_SUBTYPE(Object, other)
-      // In an opted-in library, compute
-      //   NNBD_SUBTYPE(Null, other).
-      return Instance::NullIsInstanceOf(mode, other,
-                                        other_instantiator_type_arguments,
-                                        other_function_type_arguments);
-    }
-    // Compute NNBD_SUBTYPE(runtimeType, other).
-    return RuntimeTypeIsSubtypeOf(mode, other,
-                                  other_instantiator_type_arguments,
-                                  other_function_type_arguments);
-  }
   if (IsNull()) {
     if (mode == NNBDMode::kOptedInLib) {
-      // Compute NNBD_SUBTYPE(Null, other).
+      // Compute NNBD_SUBTYPE(Null, other), either in weak or strong mode.
       return Instance::NNBD_NullIsInstanceOf(other,
                                              other_instantiator_type_arguments,
                                              other_function_type_arguments);
     }
     ASSERT(mode == NNBDMode::kLegacyLib);
-    // Compute the same subtyping result as pre-nnbd Dart:
-    // LEGACY_SUBTYPE(other, Null) || LEGACY_SUBTYPE(Object, other).
-    return Instance::NullIsInstanceOf(NNBDMode::kLegacyLib, other,
-                                      other_instantiator_type_arguments,
-                                      other_function_type_arguments);
+    // In weak mode,
+    //   compute LEGACY_SUBTYPE(other, Null) || LEGACY_SUBTYPE(Object, other).
+    // In strong mode,
+    //   compute NNBD_SUBTYPE(other, Null) || NNBD_SUBTYPE(Object, other).
+    // Note that both expressions yield the same results for any 'other' type.
+    return Instance::Legacy_NullIsInstanceOf(other,
+                                             other_instantiator_type_arguments,
+                                             other_function_type_arguments);
   }
-  // Compute the same subtyping result as pre-nnbd Dart:
-  // LEGACY_SUBTYPE(runtimeType, other).
+  // In strong mode, compute NNBD_SUBTYPE(runtimeType, other).
+  // In weak mode, compute LEGACY_SUBTYPE(runtimeType, other).
   return RuntimeTypeIsSubtypeOf(mode, other, other_instantiator_type_arguments,
                                 other_function_type_arguments);
 }
@@ -16943,47 +16931,38 @@
 
 // In strong mode, for kLegacyLib mode:
 //   return NNBD_SUBTYPE(other, Null) || NNBD_SUBTYPE(Object, other).
-// In strong mode, for kOptedInLib mode:
-//   return NNBD_SUBTYPE(Null, other).
 // In weak mode, for kLegacyLib mode:
 //   return LEGACY_SUBTYPE(other, Null) || LEGACY_SUBTYPE(Object, other).
-// The NNBDMode::kOptedInLib mode is never passed in weak mode.
-bool Instance::NullIsInstanceOf(
-    NNBDMode mode,
+// Note that both expressions yield the same results.
+// Ignore value of strong flag value.
+bool Instance::Legacy_NullIsInstanceOf(
     const AbstractType& other,
     const TypeArguments& other_instantiator_type_arguments,
     const TypeArguments& other_function_type_arguments) {
-  ASSERT(FLAG_strong_non_nullable_type_checks || mode != NNBDMode::kOptedInLib);
-  if (other.IsNullType() || other.IsTopType()) {
+  if (other.IsNullType() || other.Legacy_IsTopType()) {
     return true;
   }
   AbstractType& instantiated_other = AbstractType::Handle(other.raw());
   if (!other.IsInstantiated()) {
     instantiated_other = other.InstantiateFrom(
-        mode, other_instantiator_type_arguments, other_function_type_arguments,
-        kAllFree, NULL, Heap::kOld);
+        NNBDMode::kLegacyLib, other_instantiator_type_arguments,
+        other_function_type_arguments, kAllFree, NULL, Heap::kOld);
     if (instantiated_other.IsTypeRef()) {
       instantiated_other = TypeRef::Cast(instantiated_other).type();
     }
-    if (instantiated_other.IsNullType() || instantiated_other.IsTopType()) {
-      return true;
-    }
   }
   // instantiated_other is not modified if not FutureOr<T>.
   while (instantiated_other.IsFutureOr(&instantiated_other)) {
   }
-  if (instantiated_other.IsNullType() || instantiated_other.IsTopType()) {
-    return true;
-  }
-  if (FLAG_strong_non_nullable_type_checks) {
-    const Nullability other_nullability = instantiated_other.nullability();
-    return other_nullability == Nullability::kNullable ||
-           other_nullability == Nullability::kLegacy;
-  }
-  return false;
+  return instantiated_other.IsNullType() ||
+         instantiated_other.Legacy_IsTopType();
 }
 
-// Return NNBD_SUBTYPE(Null, other) independently of strong flag value.
+// In strong mode, for kOptedInLib mode:
+//   return NNBD_SUBTYPE(Null, other).
+// In weak mode, for kOptedInLib mode:
+//   return NNBD_SUBTYPE(Null, other).
+// Ignore value of strong flag value.
 bool Instance::NNBD_NullIsInstanceOf(
     const AbstractType& other,
     const TypeArguments& other_instantiator_type_arguments,
@@ -17024,7 +17003,7 @@
   ASSERT(!other.IsDynamicType());
   ASSERT(!other.IsTypeRef());  // Must be dereferenced at compile time.
   // Instance may not have runtimeType dynamic, void, or Never.
-  if (other.IsTopType()) {
+  if (other.IsTopType(mode)) {
     return true;
   }
   // In weak testing mode, Null type is a subtype of any type.
@@ -17046,7 +17025,7 @@
       if (instantiated_other.IsTypeRef()) {
         instantiated_other = TypeRef::Cast(instantiated_other).type();
       }
-      if (instantiated_other.IsTopType() ||
+      if (instantiated_other.IsTopType(mode) ||
           instantiated_other.IsDartFunctionType()) {
         return true;
       }
@@ -17086,7 +17065,7 @@
     if (instantiated_other.IsTypeRef()) {
       instantiated_other = TypeRef::Cast(instantiated_other).type();
     }
-    if (instantiated_other.IsTopType()) {
+    if (instantiated_other.IsTopType(mode)) {
       return true;
     }
   }
@@ -17123,7 +17102,7 @@
         TypeArguments::Handle(zone, other.arguments());
     const AbstractType& other_type_arg =
         AbstractType::Handle(zone, other_type_arguments.TypeAt(0));
-    if (other_type_arg.IsTopType()) {
+    if (other_type_arg.IsTopType(mode)) {
       return true;
     }
     if (Class::Handle(zone, clazz()).IsFutureClass()) {
@@ -17724,32 +17703,24 @@
          !IsNullable();
 }
 
-bool AbstractType::IsTopType() const {
+bool AbstractType::IsTopType(NNBDMode mode) const {
+  return (FLAG_strong_non_nullable_type_checks || mode == NNBDMode::kOptedInLib)
+             ? NNBD_IsTopType()
+             : Legacy_IsTopType();
+}
+
+bool AbstractType::Legacy_IsTopType() const {
   const classid_t cid = type_class_id();
   if (cid == kIllegalCid) {  // Includes TypeParameter.
     return false;
   }
-  if (cid == kDynamicCid || cid == kVoidCid) {
+  if (cid == kDynamicCid || cid == kVoidCid || cid == kInstanceCid) {
     return true;
   }
-  if (cid == kInstanceCid) {  // Object type.
-    return !FLAG_strong_non_nullable_type_checks ||
-           !IsNonNullable();  // kLegacy or kNullable.
-  }
   // FutureOr<T> where T is a top type behaves as a top type.
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  if (Class::Handle(zone, type_class()).IsFutureOrClass()) {
-    if (arguments() == TypeArguments::null()) {
-      return true;
-    }
-    const TypeArguments& type_arguments =
-        TypeArguments::Handle(zone, arguments());
-    const AbstractType& type_arg =
-        AbstractType::Handle(zone, type_arguments.TypeAt(0));
-    if (type_arg.IsTopType()) {
-      return true;
-    }
+  AbstractType& type_arg = AbstractType::Handle(raw());
+  if (IsFutureOr(&type_arg)) {
+    return type_arg.Legacy_IsTopType();
   }
   return false;
 }
@@ -17765,21 +17736,10 @@
   if (cid == kInstanceCid) {  // Object type.
     return !IsNonNullable();  // kLegacy or kNullable.
   }
-
   // FutureOr<T> where T is a top type behaves as a top type.
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  if (Class::Handle(zone, type_class()).IsFutureOrClass()) {
-    if (arguments() == TypeArguments::null()) {
-      return true;
-    }
-    const TypeArguments& type_arguments =
-        TypeArguments::Handle(zone, arguments());
-    const AbstractType& type_arg =
-        AbstractType::Handle(zone, type_arguments.TypeAt(0));
-    if (type_arg.NNBD_IsTopType()) {
-      return true;
-    }
+  AbstractType& type_arg = AbstractType::Handle(raw());
+  if (IsFutureOr(&type_arg)) {
+    return type_arg.NNBD_IsTopType();
   }
   return false;
 }
@@ -17839,6 +17799,9 @@
     Class& cls = thread->ClassHandle();
     cls = type_class();
     if (cls.IsFutureOrClass()) {
+      if (type_arg == nullptr) {
+        return true;
+      }
       if (arguments() == TypeArguments::null()) {
         *type_arg = Type::dynamic_type().raw();
         return true;
@@ -17858,7 +17821,7 @@
                                Heap::Space space) const {
   ASSERT(IsFinalized());
   ASSERT(other.IsFinalized());
-  if (other.IsTopType() || IsNeverType()) {
+  if (other.IsTopType(mode) || IsNeverType()) {
     return true;
   }
   if (IsDynamicType() || IsVoidType()) {
@@ -17995,7 +17958,7 @@
         TypeArguments::Handle(zone, other.arguments());
     const AbstractType& other_type_arg =
         AbstractType::Handle(zone, other_type_arguments.TypeAt(0));
-    if (other_type_arg.IsTopType()) {
+    if (other_type_arg.IsTopType(mode)) {
       return true;
     }
     // Retry the IsSubtypeOf check after unwrapping type arg of FutureOr.
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 400eeaa..c36d28c 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -6639,15 +6639,17 @@
       const TypeArguments& other_instantiator_type_arguments,
       const TypeArguments& other_function_type_arguments) const;
 
-  // Return true if the null instance is an instance of other type.
-  static bool NullIsInstanceOf(
-      NNBDMode mode,
+  // Return true if the null instance is an instance of other type according to
+  // legacy semantics (independently of the current value of the strong flag).
+  // It only makes sense in a legacy library.
+  static bool Legacy_NullIsInstanceOf(
       const AbstractType& other,
       const TypeArguments& other_instantiator_type_arguments,
       const TypeArguments& other_function_type_arguments);
 
   // Return true if the null instance is an instance of other type according to
   // NNBD semantics (independently of the current value of the strong flag).
+  // It only makes sense in an opted-in library.
   static bool NNBD_NullIsInstanceOf(
       const AbstractType& other,
       const TypeArguments& other_instantiator_type_arguments,
@@ -7123,10 +7125,16 @@
   bool IsObjectType() const { return type_class_id() == kInstanceCid; }
 
   // Check if this type represents a top type.
-  bool IsTopType() const;
+  bool IsTopType(NNBDMode mode) const;
+
+  // Check if this type represents a top type according to legacy
+  // semantics (independently of the current value of the strong flag or of the
+  // nnbd mode).
+  bool Legacy_IsTopType() const;
 
   // Check if this type represents a top type according to NNBD
-  // semantics (independently of the current value of the strong flag).
+  // semantics (independently of the current value of the strong flag or of the
+  // nnbd mode).
   bool NNBD_IsTopType() const;
 
   // Check if this type represents the 'bool' type.
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index 94e8ef0..4b38e2d 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -678,20 +678,24 @@
   }
 }
 
-CountingPage* HeapSnapshotWriter::FindCountingPage(RawObject* obj) const {
-  if (obj->IsOldObject()) {
-    const uword addr = RawObject::ToAddr(obj);
-    for (intptr_t i = 0; i < kMaxImagePages; i++) {
-      if ((addr - image_page_ranges_[i].base) < image_page_ranges_[i].size) {
-        return nullptr;  // On an image page.
-      }
+bool HeapSnapshotWriter::OnImagePage(RawObject* obj) const {
+  const uword addr = RawObject::ToAddr(obj);
+  for (intptr_t i = 0; i < kMaxImagePages; i++) {
+    if ((addr - image_page_ranges_[i].base) < image_page_ranges_[i].size) {
+      return true;
     }
+  }
+  return false;
+}
+
+CountingPage* HeapSnapshotWriter::FindCountingPage(RawObject* obj) const {
+  if (obj->IsOldObject() && !OnImagePage(obj)) {
     // On a regular or large page.
     HeapPage* page = HeapPage::Of(obj);
     return reinterpret_cast<CountingPage*>(page->forwarding_page());
   }
 
-  // In new space.
+  // On an image page or in new space.
   return nullptr;
 }
 
@@ -713,14 +717,22 @@
     return 0;
   }
 
+  if (FLAG_write_protect_code && obj->IsInstructions() && !OnImagePage(obj)) {
+    // A non-writable alias mapping may exist for instruction pages.
+    obj = HeapPage::ToWritable(obj);
+  }
+
   CountingPage* counting_page = FindCountingPage(obj);
+  intptr_t id;
   if (counting_page != nullptr) {
     // Likely: object on an ordinary page.
-    return counting_page->Lookup(RawObject::ToAddr(obj));
+    id = counting_page->Lookup(RawObject::ToAddr(obj));
   } else {
     // Unlikely: new space object, or object on a large or image page.
-    return thread()->heap()->GetObjectId(obj);
+    id = thread()->heap()->GetObjectId(obj);
   }
+  ASSERT(id != 0);
+  return id;
 }
 
 void HeapSnapshotWriter::ClearObjectIds() {
diff --git a/runtime/vm/object_graph.h b/runtime/vm/object_graph.h
index 2f2f80a..f05af3d 100644
--- a/runtime/vm/object_graph.h
+++ b/runtime/vm/object_graph.h
@@ -190,6 +190,7 @@
   static const intptr_t kPreferredChunkSize = MB;
 
   void SetupCountingPages();
+  bool OnImagePage(RawObject* obj) const;
   CountingPage* FindCountingPage(RawObject* obj) const;
 
   void EnsureAvailable(intptr_t needed);
diff --git a/runtime/vm/os_fuchsia.cc b/runtime/vm/os_fuchsia.cc
index 055a382..05985ab 100644
--- a/runtime/vm/os_fuchsia.cc
+++ b/runtime/vm/os_fuchsia.cc
@@ -8,6 +8,9 @@
 #include "vm/os.h"
 
 #include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+
 #include <fuchsia/deprecatedtimezone/cpp/fidl.h>
 #include <lib/sys/cpp/service_directory.h>
 #include <zircon/process.h>
@@ -20,6 +23,27 @@
 
 namespace dart {
 
+// The data directory containing ICU timezone data files.
+static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le";
+
+// Initializes the source of timezone data if available.  Timezone data file in
+// Fuchsia is at a fixed directory path.  Returns true on success.
+bool InitializeTZData() {
+  // Try opening the path to check if present.  No need to verify that it is a
+  // directory since ICU loading will return an error if the TZ data path is
+  // wrong.
+  int fd = openat(AT_FDCWD, kICUTZDataDir, O_RDONLY);
+  if (fd < 0) {
+    return false;
+  }
+  // 0 == Not overwriting the env var if already set.
+  setenv("ICU_TIMEZONE_FILES_DIR", kICUTZDataDir, 0);
+  if (!close(fd)) {
+    return false;
+  }
+  return true;
+}
+
 #ifndef PRODUCT
 
 DEFINE_FLAG(bool,
@@ -239,6 +263,7 @@
 }
 
 void OS::Init() {
+  InitializeTZData();
   auto services = sys::ServiceDirectory::CreateFromNamespace();
   services->Connect(tz.NewRequest());
 }
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index bc3a571..76e30bd 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1244,7 +1244,7 @@
   // call originated in a legacy library. Note that the type test against a
   // non-legacy type (even in a legacy library) such as dynamic, void, or Null
   // yield the same result independently of the mode used.
-  NNBDMode mode =
+  const NNBDMode mode =
       type.IsLegacy() ? NNBDMode::kLegacyLib : NNBDMode::kOptedInLib;
   const bool result = receiver.IsInstanceOf(
       mode, type, Object::null_type_arguments(), Object::null_type_arguments());
@@ -2409,8 +2409,9 @@
   if (FLAG_shared_slow_path_triggers_gc) {
     isolate->heap()->CollectAllGarbage();
   }
-  const auto& integer_box =
-      Integer::Handle(zone, Integer::NewFromUint64(0x00ff00ff00ff0000));
+  constexpr uint64_t val = 0x7fffffff7fffffff;
+  ASSERT(!Smi::IsValid(static_cast<int64_t>(val)));
+  const auto& integer_box = Integer::Handle(zone, Integer::NewFromUint64(val));
   arguments.SetReturn(integer_box);
 };
 
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 2200cb1..6c3a3fa 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -100,20 +100,16 @@
   // `null` and patch these later in `Object::FinishInit()`.
   if (!StubCode::HasBeenInitialized()) {
     ASSERT(type.IsType());
-    ASSERT(cid == kDynamicCid || cid == kVoidCid | cid == kNeverCid);
+    ASSERT(cid == kDynamicCid || cid == kVoidCid || cid == kNeverCid);
     return Code::null();
   }
 
-  // TODO(regis): Revisit when type checking mode is not kLegacy anymore.
-  if (cid == kDynamicCid || cid == kVoidCid || cid == kInstanceCid) {
+  if (cid == kDynamicCid || cid == kVoidCid ||
+      (cid == kInstanceCid &&
+       (!FLAG_strong_non_nullable_type_checks || !type.IsNonNullable()))) {
     return StubCode::TopTypeTypeTest().raw();
   }
 
-  if (cid == kNeverCid) {
-    // TODO(regis): Revisit.
-    return StubCode::StubCode::DefaultTypeTest().raw();
-  }
-
   if (type.IsType() || type.IsTypeParameter()) {
     const bool should_specialize = !FLAG_precompiled_mode && lazy_specialize;
     return should_specialize ? StubCode::LazySpecializeTypeTest().raw()
diff --git a/sdk/lib/_http/http_date.dart b/sdk/lib/_http/http_date.dart
index edeb98f..ab24b87 100644
--- a/sdk/lib/_http/http_date.dart
+++ b/sdk/lib/_http/http_date.dart
@@ -116,38 +116,6 @@
       "Nov",
       "Dec"
     ];
-    const List wkdaysLowerCase = const [
-      "mon",
-      "tue",
-      "wed",
-      "thu",
-      "fri",
-      "sat",
-      "sun"
-    ];
-    const List weekdaysLowerCase = const [
-      "monday",
-      "tuesday",
-      "wednesday",
-      "thursday",
-      "friday",
-      "saturday",
-      "sunday"
-    ];
-    const List monthsLowerCase = const [
-      "jan",
-      "feb",
-      "mar",
-      "apr",
-      "may",
-      "jun",
-      "jul",
-      "aug",
-      "sep",
-      "oct",
-      "nov",
-      "dec"
-    ];
 
     final int formatRfc1123 = 0;
     final int formatRfc850 = 1;
@@ -221,7 +189,7 @@
       try {
         int value = int.parse(tmp);
         return value;
-      } on FormatException catch (e) {
+      } on FormatException {
         throw new HttpException("Invalid HTTP date $date");
       }
     }
diff --git a/sdk/lib/_http/http_headers.dart b/sdk/lib/_http/http_headers.dart
index 028fd2d..30532d5 100644
--- a/sdk/lib/_http/http_headers.dart
+++ b/sdk/lib/_http/http_headers.dart
@@ -205,7 +205,7 @@
     if (values != null) {
       try {
         return HttpDate.parse(values[0]);
-      } on Exception catch (e) {
+      } on Exception {
         return null;
       }
     }
@@ -224,7 +224,7 @@
     if (values != null) {
       try {
         return HttpDate.parse(values[0]);
-      } on Exception catch (e) {
+      } on Exception {
         return null;
       }
     }
@@ -243,7 +243,7 @@
     if (values != null) {
       try {
         return HttpDate.parse(values[0]);
-      } on Exception catch (e) {
+      } on Exception {
         return null;
       }
     }
@@ -399,7 +399,7 @@
         } else {
           try {
             _port = int.parse(value.substring(pos + 1));
-          } on FormatException catch (e) {
+          } on FormatException {
             _port = null;
           }
         }
@@ -888,14 +888,6 @@
       return s.substring(start, index).trim();
     }
 
-    void expect(String expected) {
-      if (done()) throw new HttpException("Failed to parse header value [$s]");
-      if (s[index] != expected) {
-        throw new HttpException("Failed to parse header value [$s]");
-      }
-      index++;
-    }
-
     void parseAttributes() {
       String parseAttributeName() {
         int start = index;
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index caf1d44..336c6d0 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -2628,7 +2628,6 @@
   bool get _isActive => _state == _ACTIVE;
   bool get _isIdle => _state == _IDLE;
   bool get _isClosing => _state == _CLOSING;
-  bool get _isDetached => _state == _DETACHED;
 
   String get _serviceTypePath => 'io/http/serverconnections';
   String get _serviceTypeName => 'HttpServerConnection';
@@ -2970,7 +2969,7 @@
           int port;
           try {
             port = int.parse(portString);
-          } on FormatException catch (e) {
+          } on FormatException {
             throw new HttpException(
                 "Invalid proxy configuration $configuration, "
                 "invalid port '$portString'");
@@ -3270,7 +3269,6 @@
     String qop;
     String cnonce;
     String nc;
-    var x;
     hasher = new _MD5()..add(credentials.ha1.codeUnits)..add([_CharCode.COLON]);
     if (credentials.qop == "auth") {
       qop = credentials.qop;
diff --git a/sdk/lib/_http/http_parser.dart b/sdk/lib/_http/http_parser.dart
index 8ef7ab0..8d64a50 100644
--- a/sdk/lib/_http/http_parser.dart
+++ b/sdk/lib/_http/http_parser.dart
@@ -972,7 +972,7 @@
     return true;
   }
 
-  int _expect(int val1, int val2) {
+  void _expect(int val1, int val2) {
     if (val1 != val2) {
       throw new HttpException("Failed to parse HTTP");
     }
diff --git a/sdk/lib/_http/websocket.dart b/sdk/lib/_http/websocket.dart
index 1d27852..77b5008 100644
--- a/sdk/lib/_http/websocket.dart
+++ b/sdk/lib/_http/websocket.dart
@@ -145,8 +145,7 @@
         throw new ArgumentError("Illegal 0 padding on value.");
       } else {
         mwb = serverMaxWindowBits == null
-            ? int.parse(part,
-                onError: (source) => _WebSocketImpl.DEFAULT_WINDOW_BITS)
+            ? int.tryParse(part) ?? _WebSocketImpl.DEFAULT_WINDOW_BITS
             : serverMaxWindowBits;
         info.headerValue = "; server_max_window_bits=${mwb}";
         info.maxWindowBits = mwb;
@@ -335,7 +334,7 @@
    * Set and get the interval for sending ping signals. If a ping message is not
    * answered by a pong message from the peer, the `WebSocket` is assumed
    * disconnected and the connection is closed with a
-   * [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the
+   * [WebSocketStatus.goingAway] close code. When a ping signal is sent, the
    * pong message must be received within [pingInterval].
    *
    * There are never two outstanding pings at any given time, and the next ping
@@ -451,7 +450,7 @@
   /**
    * Closes the WebSocket connection. Set the optional [code] and [reason]
    * arguments to send close information to the remote peer. If they are
-   * omitted, the peer will see [WebSocketStatus.NO_STATUS_RECEIVED] code
+   * omitted, the peer will see [WebSocketStatus.noStatusReceived] code
    * with no reason.
    */
   Future close([int code, String reason]);
diff --git a/sdk/lib/_http/websocket_impl.dart b/sdk/lib/_http/websocket_impl.dart
index d93f366..35ee68b 100644
--- a/sdk/lib/_http/websocket_impl.dart
+++ b/sdk/lib/_http/websocket_impl.dart
@@ -91,7 +91,7 @@
   int _remainingPayloadBytes = -1;
   int _unmaskingIndex = 0;
   int _currentMessageType = _WebSocketMessageType.NONE;
-  int closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
+  int closeCode = WebSocketStatus.noStatusReceived;
   String closeReason = "";
 
   EventSink<dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ > _eventSink;
@@ -348,14 +348,14 @@
   void _controlFrameEnd() {
     switch (_opcode) {
       case _WebSocketOpcode.CLOSE:
-        closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
+        closeCode = WebSocketStatus.noStatusReceived;
         var payload = _payload.takeBytes();
         if (payload.length > 0) {
           if (payload.length == 1) {
             throw new WebSocketException("Protocol error");
           }
           closeCode = payload[0] << 8 | payload[1];
-          if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) {
+          if (closeCode == WebSocketStatus.noStatusReceived) {
             throw new WebSocketException("Protocol error");
           }
           if (payload.length > 2) {
@@ -1112,7 +1112,7 @@
           return DEFAULT_WINDOW_BITS;
         }
 
-        return int.parse(o, onError: (s) => DEFAULT_WINDOW_BITS);
+        return int.tryParse(o) ?? DEFAULT_WINDOW_BITS;
       }
 
       return new _WebSocketPerMessageDeflate(
@@ -1146,9 +1146,9 @@
     }, onError: (error, stackTrace) {
       if (_closeTimer != null) _closeTimer.cancel();
       if (error is FormatException) {
-        _close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA);
+        _close(WebSocketStatus.invalidFramePayloadData);
       } else {
-        _close(WebSocketStatus.PROTOCOL_ERROR);
+        _close(WebSocketStatus.protocolError);
       }
       // An error happened, set the close code set above.
       _closeCode = _outCloseCode;
@@ -1204,7 +1204,7 @@
       _consumer.add(new _WebSocketPing());
       _pingTimer = new Timer(_pingInterval, () {
         // No pong received.
-        _close(WebSocketStatus.GOING_AWAY);
+        _close(WebSocketStatus.goingAway);
       });
     });
   }
@@ -1309,12 +1309,12 @@
 
   static bool _isReservedStatusCode(int code) {
     return code != null &&
-        (code < WebSocketStatus.NORMAL_CLOSURE ||
-            code == WebSocketStatus.RESERVED_1004 ||
-            code == WebSocketStatus.NO_STATUS_RECEIVED ||
-            code == WebSocketStatus.ABNORMAL_CLOSURE ||
-            (code > WebSocketStatus.INTERNAL_SERVER_ERROR &&
-                code < WebSocketStatus.RESERVED_1015) ||
-            (code >= WebSocketStatus.RESERVED_1015 && code < 3000));
+        (code < WebSocketStatus.normalClosure ||
+            code == WebSocketStatus.reserved1004 ||
+            code == WebSocketStatus.noStatusReceived ||
+            code == WebSocketStatus.abnormalClosure ||
+            (code > WebSocketStatus.internalServerError &&
+                code < WebSocketStatus.reserved1015) ||
+            (code >= WebSocketStatus.reserved1015 && code < 3000));
   }
 }
diff --git a/sdk/lib/_internal/js_dev_runtime/libraries.dart b/sdk/lib/_internal/js_dev_runtime/libraries.dart
index b844996..283133c 100644
--- a/sdk/lib/_internal/js_dev_runtime/libraries.dart
+++ b/sdk/lib/_internal/js_dev_runtime/libraries.dart
@@ -86,10 +86,11 @@
       categories: "Client,Server",
       maturity: Maturity.STABLE,
       dart2jsPatchPath: "_internal/js_runtime/lib/isolate_patch.dart"),
-  "js": const LibraryInfo("js/dart2js/js_dart2js.dart",
+  "js": const LibraryInfo("js/js.dart",
       categories: "Client",
       maturity: Maturity.STABLE,
-      platforms: DART2JS_PLATFORM),
+      platforms: DART2JS_PLATFORM,
+      dart2jsPatchPath: "_internal/js_runtime/lib/js_patch.dart"),
   "js_util": const LibraryInfo("js_util/dart2js/js_util_dart2js.dart",
       categories: "Client",
       maturity: Maturity.STABLE,
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart
new file mode 100644
index 0000000..e0c10e8
--- /dev/null
+++ b/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart
@@ -0,0 +1,443 @@
+// Copyright (c) 2020, 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.6
+
+// Patch file for dart:js library.
+library dart.js;
+
+import 'dart:collection' show HashMap, ListMixin;
+
+import 'dart:_js_helper' show patch, Primitives;
+import 'dart:_foreign_helper' show JS;
+import 'dart:_runtime' as dart;
+
+@patch
+JsObject get context => _context;
+
+final JsObject _context = _wrapToDart(dart.global_);
+
+@patch
+class JsObject {
+  // The wrapped JS object.
+  final dynamic _jsObject;
+
+  // This should only be called from _wrapToDart
+  JsObject._fromJs(this._jsObject) {
+    assert(_jsObject != null);
+  }
+
+  @patch
+  factory JsObject(JsFunction constructor, [List arguments]) {
+    var ctor = constructor._jsObject;
+    if (arguments == null) {
+      return _wrapToDart(JS('', 'new #()', ctor));
+    }
+    var unwrapped = List.from(arguments.map(_convertToJS));
+    return _wrapToDart(JS('', 'new #(...#)', ctor, unwrapped));
+  }
+
+  @patch
+  factory JsObject.fromBrowserObject(object) {
+    if (object is num || object is String || object is bool || object == null) {
+      throw ArgumentError("object cannot be a num, string, bool, or null");
+    }
+    return _wrapToDart(_convertToJS(object));
+  }
+
+  @patch
+  factory JsObject.jsify(object) {
+    if ((object is! Map) && (object is! Iterable)) {
+      throw ArgumentError("object must be a Map or Iterable");
+    }
+    return _wrapToDart(_convertDataTree(object));
+  }
+
+  static _convertDataTree(data) {
+    var _convertedObjects = HashMap.identity();
+
+    _convert(o) {
+      if (_convertedObjects.containsKey(o)) {
+        return _convertedObjects[o];
+      }
+      if (o is Map) {
+        final convertedMap = JS('', '{}');
+        _convertedObjects[o] = convertedMap;
+        for (var key in o.keys) {
+          JS('', '#[#] = #', convertedMap, key, _convert(o[key]));
+        }
+        return convertedMap;
+      } else if (o is Iterable) {
+        var convertedList = [];
+        _convertedObjects[o] = convertedList;
+        convertedList.addAll(o.map(_convert));
+        return convertedList;
+      } else {
+        return _convertToJS(o);
+      }
+    }
+
+    return _convert(data);
+  }
+
+  @patch
+  dynamic operator [](Object property) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    return _convertToDart(JS('', '#[#]', _jsObject, property));
+  }
+
+  @patch
+  void operator []=(Object property, value) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
+  }
+
+  @patch
+  bool operator ==(other) =>
+      other is JsObject && JS<bool>('!', '# === #', _jsObject, other._jsObject);
+
+  @patch
+  bool hasProperty(property) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    return JS<bool>('!', '# in #', property, _jsObject);
+  }
+
+  @patch
+  void deleteProperty(property) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    JS<bool>('!', 'delete #[#]', _jsObject, property);
+  }
+
+  @patch
+  bool instanceof(JsFunction type) {
+    return JS<bool>('!', '# instanceof #', _jsObject, _convertToJS(type));
+  }
+
+  @patch
+  String toString() {
+    try {
+      return JS<String>('!', 'String(#)', _jsObject);
+    } catch (e) {
+      return super.toString();
+    }
+  }
+
+  @patch
+  dynamic callMethod(method, [List args]) {
+    if (method is! String && method is! num) {
+      throw ArgumentError("method is not a String or num");
+    }
+    if (args != null) args = List.from(args.map(_convertToJS));
+    var fn = JS('', '#[#]', _jsObject, method);
+    if (JS<bool>('!', 'typeof(#) !== "function"', fn)) {
+      throw NoSuchMethodError(_jsObject, Symbol(method), args, {});
+    }
+    return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args));
+  }
+}
+
+@patch
+class JsFunction extends JsObject {
+  @patch
+  factory JsFunction.withThis(Function f) {
+    return JsFunction._fromJs(JS(
+        '',
+        'function(/*...arguments*/) {'
+            '  let args = [#(this)];'
+            '  for (let arg of arguments) {'
+            '    args.push(#(arg));'
+            '  }'
+            '  return #(#(...args));'
+            '}',
+        _convertToDart,
+        _convertToDart,
+        _convertToJS,
+        f));
+  }
+
+  JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
+
+  @patch
+  dynamic apply(List args, {thisArg}) => _convertToDart(JS(
+      '',
+      '#.apply(#, #)',
+      _jsObject,
+      _convertToJS(thisArg),
+      args == null ? null : List.from(args.map(_convertToJS))));
+}
+
+// TODO(jmesserly): this is totally unnecessary in dev_compiler.
+@patch
+class JsArray<E> extends JsObject with ListMixin<E> {
+  @patch
+  factory JsArray() => JsArray<E>._fromJs([]);
+
+  @patch
+  factory JsArray.from(Iterable<E> other) =>
+      JsArray<E>._fromJs([]..addAll(other.map(_convertToJS)));
+
+  JsArray._fromJs(jsObject) : super._fromJs(jsObject);
+
+  _checkIndex(int index) {
+    if (index is int && (index < 0 || index >= length)) {
+      throw RangeError.range(index, 0, length);
+    }
+  }
+
+  _checkInsertIndex(int index) {
+    if (index is int && (index < 0 || index >= length + 1)) {
+      throw RangeError.range(index, 0, length);
+    }
+  }
+
+  static _checkRange(int start, int end, int length) {
+    if (start < 0 || start > length) {
+      throw RangeError.range(start, 0, length);
+    }
+    if (end < start || end > length) {
+      throw RangeError.range(end, start, length);
+    }
+  }
+
+  @patch
+  E operator [](Object index) {
+    // TODO(justinfagnani): fix the semantics for non-ints
+    // dartbug.com/14605
+    if (index is num && index == index.toInt()) {
+      _checkIndex(index);
+    }
+    return super[index] as E;
+  }
+
+  @patch
+  void operator []=(Object index, value) {
+    // TODO(justinfagnani): fix the semantics for non-ints
+    // dartbug.com/14605
+    if (index is num && index == index.toInt()) {
+      _checkIndex(index);
+    }
+    super[index] = value;
+  }
+
+  @patch
+  int get length {
+    // Check the length honours the List contract.
+    var len = JS('', '#.length', _jsObject);
+    // JavaScript arrays have lengths which are unsigned 32-bit integers.
+    if (JS<bool>(
+        '!', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) {
+      return JS<int>('!', '#', len);
+    }
+    throw StateError('Bad JsArray length');
+  }
+
+  @patch
+  void set length(int length) {
+    super['length'] = length;
+  }
+
+  @patch
+  void add(E value) {
+    callMethod('push', [value]);
+  }
+
+  @patch
+  void addAll(Iterable<E> iterable) {
+    var list = (JS<bool>('!', '# instanceof Array', iterable))
+        ? iterable
+        : List.from(iterable);
+    callMethod('push', list);
+  }
+
+  @patch
+  void insert(int index, E element) {
+    _checkInsertIndex(index);
+    callMethod('splice', [index, 0, element]);
+  }
+
+  @patch
+  E removeAt(int index) {
+    _checkIndex(index);
+    return callMethod('splice', [index, 1])[0] as E;
+  }
+
+  @patch
+  E removeLast() {
+    if (length == 0) throw RangeError(-1);
+    return callMethod('pop') as E;
+  }
+
+  @patch
+  void removeRange(int start, int end) {
+    _checkRange(start, end, length);
+    callMethod('splice', [start, end - start]);
+  }
+
+  @patch
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
+    _checkRange(start, end, this.length);
+    int length = end - start;
+    if (length == 0) return;
+    if (skipCount < 0) throw ArgumentError(skipCount);
+    var args = <Object>[start, length]
+      ..addAll(iterable.skip(skipCount).take(length));
+    callMethod('splice', args);
+  }
+
+  @patch
+  void sort([int compare(E a, E b)]) {
+    // Note: arr.sort(null) is a type error in FF
+    callMethod('sort', compare == null ? [] : [compare]);
+  }
+}
+
+// Cross frame objects should not be considered browser types.
+// We include the instanceof Object test to filter out cross frame objects
+// on FireFox. Surprisingly on FireFox the instanceof Window test succeeds for
+// cross frame windows while the instanceof Object test fails.
+bool _isBrowserType(o) => JS(
+    'bool',
+    '# instanceof Object && ('
+        '# instanceof Blob || '
+        '# instanceof Event || '
+        '(window.KeyRange && # instanceof KeyRange) || '
+        '(window.IDBKeyRange && # instanceof IDBKeyRange) || '
+        '# instanceof ImageData || '
+        '# instanceof Node || '
+        // Int8Array.__proto__ is TypedArray.
+        '(window.Int8Array && # instanceof Int8Array.__proto__) || '
+        '# instanceof Window)',
+    o,
+    o,
+    o,
+    o,
+    o,
+    o,
+    o,
+    o,
+    o);
+
+class _DartObject {
+  final _dartObj;
+  _DartObject(this._dartObj);
+}
+
+dynamic _convertToJS(dynamic o) {
+  if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) {
+    return o;
+  } else if (o is DateTime) {
+    return Primitives.lazyAsJsDate(o);
+  } else if (o is JsObject) {
+    return o._jsObject;
+  } else if (o is Function) {
+    return _putIfAbsent(_jsProxies, o, _wrapDartFunction);
+  } else {
+    // TODO(jmesserly): for now, we wrap other objects, to keep compatibility
+    // with the original dart:js behavior.
+    return _putIfAbsent(_jsProxies, o, (o) => _DartObject(o));
+  }
+}
+
+dynamic _wrapDartFunction(f) {
+  var wrapper = JS(
+      '',
+      'function(/*...arguments*/) {'
+          '  let args = Array.prototype.map.call(arguments, #);'
+          '  return #(#(...args));'
+          '}',
+      _convertToDart,
+      _convertToJS,
+      f);
+  JS('', '#.set(#, #)', _dartProxies, wrapper, f);
+
+  return wrapper;
+}
+
+// converts a Dart object to a reference to a native JS object
+// which might be a DartObject JS->Dart proxy
+Object _convertToDart(o) {
+  if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) {
+    return o;
+  } else if (JS('!', '# instanceof Date', o)) {
+    num ms = JS('!', '#.getTime()', o);
+    return DateTime.fromMillisecondsSinceEpoch(ms);
+  } else if (o is _DartObject &&
+      !identical(dart.getReifiedType(o), dart.jsobject)) {
+    return o._dartObj;
+  } else {
+    return _wrapToDart(o);
+  }
+}
+
+Object _wrapToDart(o) => _putIfAbsent(_dartProxies, o, _wrapToDartHelper);
+
+Object _wrapToDartHelper(o) {
+  if (JS<bool>('!', 'typeof # == "function"', o)) {
+    return JsFunction._fromJs(o);
+  }
+  if (JS<bool>('!', '# instanceof Array', o)) {
+    return JsArray._fromJs(o);
+  }
+  return JsObject._fromJs(o);
+}
+
+final _dartProxies = JS('', 'new WeakMap()');
+final _jsProxies = JS('', 'new WeakMap()');
+
+Object _putIfAbsent(weakMap, o, getValue(o)) {
+  var value = JS('', '#.get(#)', weakMap, o);
+  if (value == null) {
+    value = getValue(o);
+    JS('', '#.set(#, #)', weakMap, o, value);
+  }
+  return value;
+}
+
+Expando<Function> _interopExpando = Expando<Function>();
+
+@patch
+F allowInterop<F extends Function>(F f) {
+  if (!dart.isDartFunction(f)) return f;
+  var ret = _interopExpando[f];
+  if (ret == null) {
+    ret = JS(
+        '',
+        'function (...args) {'
+            ' return #(#, args);'
+            '}',
+        dart.dcall,
+        f);
+    _interopExpando[f] = ret;
+  }
+  return ret;
+}
+
+Expando<Function> _interopCaptureThisExpando = Expando<Function>();
+
+@patch
+Function allowInteropCaptureThis(Function f) {
+  if (!dart.isDartFunction(f)) return f;
+  var ret = _interopCaptureThisExpando[f];
+  if (ret == null) {
+    ret = JS(
+        '',
+        'function(...arguments) {'
+            '  let args = [this];'
+            '  args.push.apply(args, arguments);'
+            '  return #(#, args);'
+            '}',
+        dart.dcall,
+        f);
+    _interopCaptureThisExpando[f] = ret;
+  }
+  return ret;
+}
diff --git a/sdk_nnbd/lib/js/dart2js/js_dart2js.dart b/sdk/lib/_internal/js_runtime/lib/js_patch.dart
similarity index 67%
rename from sdk_nnbd/lib/js/dart2js/js_dart2js.dart
rename to sdk/lib/_internal/js_runtime/lib/js_patch.dart
index 4155113..ab4a332 100644
--- a/sdk_nnbd/lib/js/dart2js/js_dart2js.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_patch.dart
@@ -1,109 +1,22 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2020, 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.5
+// @dart = 2.6
 
-/// Low-level support for interoperating with JavaScript.
-///
-/// You should usually use `package:js` instead of this library. For more
-/// information, see the [JS interop page](https://dart.dev/web/js-interop).
-///
-/// This library provides access to JavaScript objects from Dart, allowing
-/// Dart code to get and set properties, and call methods of JavaScript objects
-/// and invoke JavaScript functions. The library takes care of converting
-/// between Dart and JavaScript objects where possible, or providing proxies if
-/// conversion isn't possible.
-///
-/// This library does not make Dart objects usable from JavaScript, their
-/// methods and properties are not accessible, though it does allow Dart
-/// functions to be passed into and called from JavaScript.
-///
-/// [JsObject] is the core type and represents a proxy of a JavaScript object.
-/// JsObject gives access to the underlying JavaScript objects properties and
-/// methods. `JsObject`s can be acquired by calls to JavaScript, or they can be
-/// created from proxies to JavaScript constructors.
-///
-/// The top-level getter [context] provides a [JsObject] that represents the
-/// global object in JavaScript, usually `window`.
-///
-/// The following example shows an alert dialog via a JavaScript call to the
-/// global function `alert()`:
-///
-///     import 'dart:js';
-///
-///     main() => context.callMethod('alert', ['Hello from Dart!']);
-///
-/// This example shows how to create a [JsObject] from a JavaScript constructor
-/// and access its properties:
-///
-///     import 'dart:js';
-///
-///     main() {
-///       var object = JsObject(context['Object']);
-///       object['greeting'] = 'Hello';
-///       object['greet'] = (name) => "${object['greeting']} $name";
-///       var message = object.callMethod('greet', ['JavaScript']);
-///       context['console'].callMethod('log', [message]);
-///     }
-///
-/// ## Proxying and automatic conversion
-///
-/// When setting properties on a JsObject or passing arguments to a Javascript
-/// method or function, Dart objects are automatically converted or proxied to
-/// JavaScript objects. When accessing JavaScript properties, or when a Dart
-/// closure is invoked from JavaScript, the JavaScript objects are also
-/// converted to Dart.
-///
-/// Functions and closures are proxied in such a way that they are callable. A
-/// Dart closure assigned to a JavaScript property is proxied by a function in
-/// JavaScript. A JavaScript function accessed from Dart is proxied by a
-/// [JsFunction], which has a [apply] method to invoke it.
-///
-/// The following types are transferred directly and not proxied:
-///
-///   * Basic types: `null`, `bool`, `num`, `String`, `DateTime`
-///   * `TypedData`, including its subclasses like `Int32List`, but _not_
-///     `ByteBuffer`
-///   * When compiling for the web, also: `Blob`, `Event`, `ImageData`,
-///     `KeyRange`, `Node`, and `Window`.
-///
-/// ## Converting collections with JsObject.jsify()
-///
-/// To create a JavaScript collection from a Dart collection use the
-/// [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s
-/// into JavaScript Objects and Arrays.
-///
-/// The following expression creates a new JavaScript object with the properties
-/// `a` and `b` defined:
-///
-///     var jsMap = JsObject.jsify({'a': 1, 'b': 2});
-///
-/// This expression creates a JavaScript array:
-///
-///     var jsArray = JsObject.jsify([1, 2, 3]);
-///
-/// {@category Web}
-library dart.js;
-
+// Patch file for dart:js library.
 import 'dart:collection' show HashMap, ListMixin;
 import 'dart:typed_data' show TypedData;
 
-import 'dart:_foreign_helper' show JS, JS_CONST, DART_CLOSURE_TO_JS;
-import 'dart:_interceptors'
-    show
-        JavaScriptFunction,
-        JavaScriptObject,
-        UnknownJavaScriptObject,
-        DART_CLOSURE_PROPERTY_NAME;
-import 'dart:_js_helper'
-    show Primitives, convertDartClosureToJS, getIsolateAffinityTag;
+import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
+import 'dart:_interceptors' show JavaScriptFunction, DART_CLOSURE_PROPERTY_NAME;
+import 'dart:_js_helper' show patch, Primitives, getIsolateAffinityTag;
 import 'dart:_js' show isBrowserObject, convertFromBrowserObject;
 
-export 'dart:_interceptors' show JavaScriptObject;
+@patch
+JsObject get context => _context;
 
-/// The JavaScript global object, usually `window`.
-final JsObject context = _wrapToDart(JS('', 'self'));
+final JsObject _context = _wrapToDart(JS('', 'self'));
 
 _convertDartFunction(Function f, {bool captureThis: false}) {
   return JS(
@@ -129,10 +42,7 @@
   return _convertToJS(Function.apply(callback, dartArgs));
 }
 
-/// A proxy on a JavaScript object.
-///
-/// The properties of the JavaScript object are accessible via the `[]` and
-/// `[]=` operators. Methods are callable via [callMethod].
+@patch
 class JsObject {
   // The wrapped JS object.
   final dynamic _jsObject;
@@ -142,8 +52,7 @@
     assert(_jsObject != null);
   }
 
-  /// Constructs a JavaScript object from its native [constructor] and returns
-  /// a proxy to it.
+  @patch
   factory JsObject(JsFunction constructor, [List arguments]) {
     var ctor = _convertToJS(constructor);
     if (arguments == null) {
@@ -206,15 +115,7 @@
     //     return _wrapToDart(jsObj);
   }
 
-  /// Constructs a [JsObject] that proxies a native Dart object; _for expert use
-  /// only_.
-  ///
-  /// Use this constructor only if you wish to get access to JavaScript
-  /// properties attached to a browser host object, such as a Node or Blob, that
-  /// is normally automatically converted into a native Dart object.
-  ///
-  /// An exception will be thrown if [object] either is `null` or has the type
-  /// `bool`, `num`, or `String`.
+  @patch
   factory JsObject.fromBrowserObject(object) {
     if (object is num || object is String || object is bool || object == null) {
       throw ArgumentError("object cannot be a num, string, bool, or null");
@@ -222,13 +123,7 @@
     return _wrapToDart(_convertToJS(object));
   }
 
-  /// Recursively converts a JSON-like collection of Dart objects to a
-  /// collection of JavaScript objects and returns a [JsObject] proxy to it.
-  ///
-  /// [object] must be a [Map] or [Iterable], the contents of which are also
-  /// converted. Maps and Iterables are copied to a new JavaScript object.
-  /// Primitives and other transferable values are directly converted to their
-  /// JavaScript type, and all other objects are proxied.
+  @patch
   factory JsObject.jsify(object) {
     if ((object is! Map) && (object is! Iterable)) {
       throw ArgumentError("object must be a Map or Iterable");
@@ -263,10 +158,7 @@
     return _convert(data);
   }
 
-  /// Returns the value associated with [property] from the proxied JavaScript
-  /// object.
-  ///
-  /// The type of [property] must be either [String] or [num].
+  @patch
   dynamic operator [](property) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -274,10 +166,7 @@
     return _convertToDart(JS('', '#[#]', _jsObject, property));
   }
 
-  // Sets the value associated with [property] on the proxied JavaScript
-  // object.
-  //
-  // The type of [property] must be either [String] or [num].
+  @patch
   void operator []=(property, value) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -285,15 +174,11 @@
     JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
   }
 
-  int get hashCode => 0;
-
+  @patch
   bool operator ==(other) =>
       other is JsObject && JS('bool', '# === #', _jsObject, other._jsObject);
 
-  /// Returns `true` if the JavaScript object contains the specified property
-  /// either directly or though its prototype chain.
-  ///
-  /// This is the equivalent of the `in` operator in JavaScript.
+  @patch
   bool hasProperty(property) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -301,9 +186,7 @@
     return JS('bool', '# in #', property, _jsObject);
   }
 
-  /// Removes [property] from the JavaScript object.
-  ///
-  /// This is the equivalent of the `delete` operator in JavaScript.
+  @patch
   void deleteProperty(property) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -311,14 +194,12 @@
     JS('bool', 'delete #[#]', _jsObject, property);
   }
 
-  /// Returns `true` if the JavaScript object has [type] in its prototype chain.
-  ///
-  /// This is the equivalent of the `instanceof` operator in JavaScript.
+  @patch
   bool instanceof(JsFunction type) {
     return JS('bool', '# instanceof #', _jsObject, _convertToJS(type));
   }
 
-  /// Returns the result of the JavaScript objects `toString` method.
+  @patch
   String toString() {
     try {
       return JS('String', 'String(#)', _jsObject);
@@ -327,10 +208,7 @@
     }
   }
 
-  /// Calls [method] on the JavaScript object with the arguments [args] and
-  /// returns the result.
-  ///
-  /// The type of [method] must be either [String] or [num].
+  @patch
   dynamic callMethod(method, [List args]) {
     if (method is! String && method is! num) {
       throw ArgumentError("method is not a String or num");
@@ -340,10 +218,9 @@
   }
 }
 
-/// A proxy on a JavaScript Function object.
+@patch
 class JsFunction extends JsObject {
-  /// Returns a [JsFunction] that captures its 'this' binding and calls [f]
-  /// with the value of JavaScript `this` passed as the first argument.
+  @patch
   factory JsFunction.withThis(Function f) {
     var jsFunc = _convertDartFunction(f, captureThis: true);
     return JsFunction._fromJs(jsFunc);
@@ -351,8 +228,7 @@
 
   JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
 
-  /// Invokes the JavaScript function with arguments [args]. If [thisArg] is
-  /// supplied it is the value of `this` for the invocation.
+  @patch
   dynamic apply(List args, {thisArg}) => _convertToDart(JS(
       '',
       '#.apply(#, #)',
@@ -361,15 +237,14 @@
       args == null ? null : List.from(args.map(_convertToJS))));
 }
 
-/// A [List] that proxies a JavaScript array.
+@patch
 class JsArray<E> extends JsObject with ListMixin<E> {
-  /// Creates an empty JavaScript array.
-  JsArray() : super._fromJs([]);
+  @patch
+  factory JsArray() => JsArray<E>._fromJs([]);
 
-  /// Creates a new JavaScript array and initializes it to the contents of
-  /// [other].
-  JsArray.from(Iterable<E> other)
-      : super._fromJs([]..addAll(other.map(_convertToJS)));
+  @patch
+  factory JsArray.from(Iterable<E> other) =>
+      JsArray<E>._fromJs([]..addAll(other.map(_convertToJS)));
 
   JsArray._fromJs(jsObject) : super._fromJs(jsObject);
 
@@ -396,6 +271,7 @@
 
   // Methods required by ListMixin
 
+  @patch
   E operator [](dynamic index) {
     // TODO(justinfagnani): fix the semantics for non-ints
     // dartbug.com/14605
@@ -405,6 +281,7 @@
     return super[index];
   }
 
+  @patch
   void operator []=(dynamic index, E value) {
     // TODO(justinfagnani): fix the semantics for non-ints
     // dartbug.com/14605
@@ -414,6 +291,7 @@
     super[index] = value;
   }
 
+  @patch
   int get length {
     // Check the length honours the List contract.
     var len = JS('', '#.length', _jsObject);
@@ -424,16 +302,19 @@
     throw StateError('Bad JsArray length');
   }
 
+  @patch
   void set length(int length) {
     super['length'] = length;
   }
 
   // Methods overridden for better performance
 
+  @patch
   void add(E value) {
     callMethod('push', [value]);
   }
 
+  @patch
   void addAll(Iterable<E> iterable) {
     var list = (JS('bool', '# instanceof Array', iterable))
         ? iterable
@@ -441,26 +322,31 @@
     callMethod('push', list);
   }
 
+  @patch
   void insert(int index, E element) {
     _checkInsertIndex(index);
     callMethod('splice', [index, 0, element]);
   }
 
+  @patch
   E removeAt(int index) {
     _checkIndex(index);
     return callMethod('splice', [index, 1])[0];
   }
 
+  @patch
   E removeLast() {
     if (length == 0) throw RangeError(-1);
     return callMethod('pop');
   }
 
+  @patch
   void removeRange(int start, int end) {
     _checkRange(start, end, length);
     callMethod('splice', [start, end - start]);
   }
 
+  @patch
   void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
     _checkRange(start, end, this.length);
     int length = end - start;
@@ -471,6 +357,7 @@
     callMethod('splice', args);
   }
 
+  @patch
   void sort([int compare(E a, E b)]) {
     // Note: arr.sort(null) is a type error in FF
     callMethod('sort', compare == null ? [] : [compare]);
@@ -659,17 +546,7 @@
   return Function.apply(callback, [self]..addAll(arguments));
 }
 
-/// Returns a wrapper around function [f] that can be called from JavaScript
-/// using `package:js` JavaScript interop.
-///
-/// The calling conventions in Dart2Js differ from JavaScript and so, by
-/// default, it is not possible to call a Dart function directly. Wrapping with
-/// `allowInterop` creates a function that can be called from JavaScript or
-/// Dart. The semantics of the wrapped function are still more strict than
-/// JavaScript, and the function will throw if called with too many or too few
-/// arguments.
-///
-/// Calling this method repeatedly on a function will return the same result.
+@patch
 F allowInterop<F extends Function>(F f) {
   if (JS('bool', 'typeof(#) == "function"', f)) {
     // Already supports interop, just use the existing function.
@@ -679,13 +556,7 @@
   }
 }
 
-/// Returns a wrapper around function [f] that can be called from JavaScript
-/// using `package:js` JavaScript interop, passing JavaScript `this` as the first
-/// argument.
-///
-/// See [allowInterop].
-///
-/// When called from Dart, [null] will be passed as the first argument.
+@patch
 Function allowInteropCaptureThis(Function f) {
   if (JS('bool', 'typeof(#) == "function"', f)) {
     // Behavior when the function is already a JS function is unspecified.
diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart
index e8316e1..78c275d 100644
--- a/sdk/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/rti.dart
@@ -16,7 +16,6 @@
         JS_EMBEDDED_GLOBAL,
         JS_GET_FLAG,
         JS_GET_NAME,
-        JS_STRING_CONCAT,
         RAW_DART_FUNCTION_REF,
         TYPE_REF;
 
@@ -789,7 +788,8 @@
 
   var isFn = RAW_DART_FUNCTION_REF(_generalIsTestImplementation);
 
-  if (isTopType(testRti)) {
+  // TODO(fishythefish): Update for NNBD.
+  if (isLegacyTopType(testRti)) {
     isFn = RAW_DART_FUNCTION_REF(_isTop);
     var asFn = RAW_DART_FUNCTION_REF(_asTop);
     Rti._setAsCheckFunction(testRti, asFn);
@@ -811,7 +811,11 @@
       String name = Rti._getInterfaceName(testRti);
       var arguments = Rti._getInterfaceTypeArguments(testRti);
       if (JS(
-          'bool', '#.every(#)', arguments, RAW_DART_FUNCTION_REF(isTopType))) {
+          'bool',
+          '#.every(#)',
+          arguments,
+          // TODO(fishythefish): Update for NNBD.
+          RAW_DART_FUNCTION_REF(isLegacyTopType))) {
         String propertyName =
             '${JS_GET_NAME(JsGetName.OPERATOR_IS_PREFIX)}${name}';
         Rti._setSpecializedTestResource(testRti, propertyName);
@@ -830,7 +834,8 @@
   // method. The Rti object is 'this'.
   Rti testRti = _castToRti(JS('', 'this'));
   Rti objectRti = instanceOrFunctionType(object, testRti);
-  return isSubtype(_theUniverse(), objectRti, testRti);
+  // TODO(fishythefish): Update for NNBD.
+  return isLegacySubtype(_theUniverse(), objectRti, testRti);
 }
 
 /// Called from generated code.
@@ -881,7 +886,8 @@
 
 /// Called from generated code.
 checkTypeBound(Rti type, Rti bound, variable, methodName) {
-  if (isSubtype(_theUniverse(), type, bound)) return type;
+  // TODO(fishythefish): Update for NNBD.
+  if (isLegacySubtype(_theUniverse(), type, bound)) return type;
   String message = "The type argument '${_rtiToString(type, null)}' is not"
       " a subtype of the type variable bound '${_rtiToString(bound, null)}'"
       " of type variable '${_Utils.asString(variable)}' in '$methodName'.";
@@ -1080,7 +1086,8 @@
       typeParametersText += typeSep;
       typeParametersText += genericContext[genericContext.length - 1 - i];
       Rti boundRti = _castToRti(_Utils.arrayAt(bounds, i));
-      if (!isTopType(boundRti)) {
+      // TODO(fishythefish): Update for NNBD.
+      if (!isLegacyTopType(boundRti)) {
         typeParametersText +=
             ' extends ' + _rtiToString(boundRti, genericContext);
       }
@@ -1470,7 +1477,6 @@
       environment = Rti._getBindingBase(environment);
     }
 
-    assert(kind == Rti.kindInterface);
     String interfaceName = Rti._getInterfaceName(environment);
     Object rule = _Universe.findRule(universe, interfaceName);
     assert(rule != null);
@@ -2310,49 +2316,63 @@
 // -------- Subtype tests ------------------------------------------------------
 
 // Future entry point from compiled code.
-bool isSubtype(universe, Rti s, Rti t) {
-  return _isSubtype(universe, s, null, t, null);
+bool isLegacySubtype(universe, Rti s, Rti t) {
+  return _isSubtype(universe, s, null, t, null, true);
 }
 
-bool _isSubtype(universe, Rti s, sEnv, Rti t, tEnv) {
-  // Based on
-  // https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md#rules
-  // and https://github.com/dart-lang/language/pull/388.
-  // In particular, the bulk of the structure is derived from the former
-  // resource, with a few adaptations taken from the latter.
-  // - We freely skip subcases which would have already been handled by a
-  // previous case.
-  // - Some rules are reordered in conjunction with the previous point to reduce
-  // the amount of casework.
-  // - Left Type Variable Bound in particular is split into two pieces: an
-  // optimistic check performed early in the algorithm to reduce the number of
-  // backtracking cases when a union appears on the right, and a pessimistic
-  // check performed at the usual place in order to completely eliminate the
-  // case.
-  // - Function type rules are applied before interface type rules.
+bool isNnbdSubtype(universe, Rti s, Rti t) {
+  return _isSubtype(universe, s, null, t, null, false);
+}
 
+/// Based on
+/// https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md#rules
+/// and https://github.com/dart-lang/language/pull/388.
+/// In particular, the bulk of the structure is derived from the former
+/// resource, with a few adaptations taken from the latter.
+/// - We freely skip subcases which would have already been handled by a
+/// previous case.
+/// - Some rules are reordered in conjunction with the previous point to reduce
+/// the amount of casework.
+/// - Left Type Variable Bound in particular is split into two pieces: an
+/// optimistic check performed early in the algorithm to reduce the number of
+/// backtracking cases when a union appears on the right, and a pessimistic
+/// check performed at the usual place in order to completely eliminate the
+/// case.
+/// - Function type rules are applied before interface type rules.
+///
+/// [s] is considered a legacy subtype of [t] if [s] would be a subtype of [t]
+/// in a modification of the NNBD rules in which `?` on types were ignored, `*`
+/// were added to each time, and `required` parameters were treated as
+/// optional. In effect, `Never` is equivalent to `Null`, `Null` is restored to
+/// the bottom of the type hierarchy, `Object` is treated as nullable, and
+/// `required` is ignored on named parameters. This should provide the same
+/// subtyping results as pre-NNBD Dart.
+bool _isSubtype(universe, Rti s, sEnv, Rti t, tEnv, bool isLegacy) {
   // Reflexivity:
   if (_Utils.isIdentical(s, t)) return true;
 
   // Right Top:
-  if (isTopType(t)) return true;
+  if (isTopType(t, isLegacy)) return true;
 
   int sKind = Rti._getKind(s);
   if (sKind == Rti.kindAny) return true;
 
   // Left Top:
-  if (isTopType(s)) return false;
+  if (isTopType(s, isLegacy)) return false;
 
   // Left Bottom:
-  // TODO(fishythefish): Update for NNBD - check for `Never` instead of `Null`.
-  if (isNullType(s)) return true;
+  if (isLegacy) {
+    if (isNullType(s)) return true;
+  } else {
+    if (sKind == Rti.kindNever) return true;
+  }
 
   // Left Type Variable Bound 1:
   bool leftTypeVariable = sKind == Rti.kindGenericFunctionParameter;
   if (leftTypeVariable) {
     int index = Rti._getGenericFunctionParameterIndex(s);
     Rti bound = _castToRti(_Utils.arrayAt(sEnv, index));
-    if (_isSubtype(universe, bound, sEnv, t, tEnv)) return true;
+    if (_isSubtype(universe, bound, sEnv, t, tEnv, isLegacy)) return true;
   }
 
   int tKind = Rti._getKind(t);
@@ -2360,48 +2380,62 @@
   // Left Null:
   // Note: Interchanging the Left Null and Right Object rules allows us to
   // reduce casework.
-  if (isNullType(s)) {
+  if (!isLegacy && isNullType(s)) {
     if (tKind == Rti.kindFutureOr) {
-      return _isSubtype(universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv);
+      return _isSubtype(
+          universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv, isLegacy);
     }
     return isNullType(t) || tKind == Rti.kindQuestion || tKind == Rti.kindStar;
   }
 
   // Right Object:
-  if (isObjectType(t)) {
+  if (!isLegacy && isObjectType(t)) {
     if (sKind == Rti.kindFutureOr) {
-      return _isSubtype(universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv);
+      return _isSubtype(
+          universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv, isLegacy);
     }
     if (sKind == Rti.kindStar) {
-      return _isSubtype(universe, Rti._getStarArgument(s), sEnv, t, tEnv);
+      return _isSubtype(
+          universe, Rti._getStarArgument(s), sEnv, t, tEnv, isLegacy);
     }
     return sKind != Rti.kindQuestion;
   }
 
   // Left Legacy:
   if (sKind == Rti.kindStar) {
-    return _isSubtype(universe, Rti._getStarArgument(s), sEnv, t, tEnv);
+    return _isSubtype(
+        universe, Rti._getStarArgument(s), sEnv, t, tEnv, isLegacy);
   }
 
   // Right Legacy:
   if (tKind == Rti.kindStar) {
     return _isSubtype(
-        universe, s, sEnv, Rti._getQuestionFromStar(universe, t), tEnv);
+        universe,
+        s,
+        sEnv,
+        isLegacy
+            ? Rti._getStarArgument(t)
+            : Rti._getQuestionFromStar(universe, t),
+        tEnv,
+        isLegacy);
   }
 
   // Left FutureOr:
   if (sKind == Rti.kindFutureOr) {
-    if (!_isSubtype(universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv)) {
+    if (!_isSubtype(
+        universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv, isLegacy)) {
       return false;
     }
-    return _isSubtype(
-        universe, Rti._getFutureFromFutureOr(universe, s), sEnv, t, tEnv);
+    return _isSubtype(universe, Rti._getFutureFromFutureOr(universe, s), sEnv,
+        t, tEnv, isLegacy);
   }
 
   // Left Nullable:
   if (sKind == Rti.kindQuestion) {
-    return _isSubtype(universe, TYPE_REF<Null>(), sEnv, t, tEnv) &&
-        _isSubtype(universe, Rti._getQuestionArgument(s), sEnv, t, tEnv);
+    return (isLegacy ||
+            _isSubtype(universe, TYPE_REF<Null>(), sEnv, t, tEnv, isLegacy)) &&
+        _isSubtype(
+            universe, Rti._getQuestionArgument(s), sEnv, t, tEnv, isLegacy);
   }
 
   // Type Variable Reflexivity 1 is subsumed by Reflexivity and therefore
@@ -2411,17 +2445,20 @@
 
   // Right FutureOr:
   if (tKind == Rti.kindFutureOr) {
-    if (_isSubtype(universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv)) {
+    if (_isSubtype(
+        universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv, isLegacy)) {
       return true;
     }
-    return _isSubtype(
-        universe, s, sEnv, Rti._getFutureFromFutureOr(universe, t), tEnv);
+    return _isSubtype(universe, s, sEnv,
+        Rti._getFutureFromFutureOr(universe, t), tEnv, isLegacy);
   }
 
   // Right Nullable:
   if (tKind == Rti.kindQuestion) {
-    return _isSubtype(universe, s, sEnv, TYPE_REF<Null>(), tEnv) ||
-        _isSubtype(universe, s, sEnv, Rti._getQuestionArgument(t), tEnv);
+    return (!isLegacy &&
+            _isSubtype(universe, s, sEnv, TYPE_REF<Null>(), tEnv, isLegacy)) ||
+        _isSubtype(
+            universe, s, sEnv, Rti._getQuestionArgument(t), tEnv, isLegacy);
   }
 
   // Left Promoted Variable does not apply at runtime.
@@ -2444,37 +2481,39 @@
 
     var sBounds = Rti._getGenericFunctionBounds(s);
     var tBounds = Rti._getGenericFunctionBounds(t);
-    if (!typesEqual(sBounds, tBounds)) return false;
+    if (!typesEqual(sBounds, tBounds, isLegacy)) return false;
 
     sEnv = sEnv == null ? sBounds : _Utils.arrayConcat(sBounds, sEnv);
     tEnv = tEnv == null ? tBounds : _Utils.arrayConcat(tBounds, tEnv);
 
     return _isFunctionSubtype(universe, Rti._getGenericFunctionBase(s), sEnv,
-        Rti._getGenericFunctionBase(t), tEnv);
+        Rti._getGenericFunctionBase(t), tEnv, isLegacy);
   }
   if (tKind == Rti.kindFunction) {
     if (isJsFunctionType(s)) return true;
     if (sKind != Rti.kindFunction) return false;
-    return _isFunctionSubtype(universe, s, sEnv, t, tEnv);
+    return _isFunctionSubtype(universe, s, sEnv, t, tEnv, isLegacy);
   }
 
   // Interface Compositionality + Super-Interface:
   if (sKind == Rti.kindInterface) {
     if (tKind != Rti.kindInterface) return false;
-    return _isInterfaceSubtype(universe, s, sEnv, t, tEnv);
+    return _isInterfaceSubtype(universe, s, sEnv, t, tEnv, isLegacy);
   }
 
   return false;
 }
 
 // TODO(fishythefish): Support required named parameters.
-bool _isFunctionSubtype(universe, Rti s, sEnv, Rti t, tEnv) {
+bool _isFunctionSubtype(universe, Rti s, sEnv, Rti t, tEnv, bool isLegacy) {
   assert(Rti._getKind(s) == Rti.kindFunction);
   assert(Rti._getKind(t) == Rti.kindFunction);
 
   Rti sReturnType = Rti._getReturnType(s);
   Rti tReturnType = Rti._getReturnType(t);
-  if (!_isSubtype(universe, sReturnType, sEnv, tReturnType, tEnv)) return false;
+  if (!_isSubtype(universe, sReturnType, sEnv, tReturnType, tEnv, isLegacy)) {
+    return false;
+  }
 
   _FunctionParameters sParameters = Rti._getFunctionParameters(s);
   _FunctionParameters tParameters = Rti._getFunctionParameters(t);
@@ -2501,21 +2540,27 @@
   for (int i = 0; i < sRequiredPositionalLength; i++) {
     Rti sParameter = _castToRti(_Utils.arrayAt(sRequiredPositional, i));
     Rti tParameter = _castToRti(_Utils.arrayAt(tRequiredPositional, i));
-    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv)) return false;
+    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv, isLegacy)) {
+      return false;
+    }
   }
 
   for (int i = 0; i < requiredPositionalDelta; i++) {
     Rti sParameter = _castToRti(_Utils.arrayAt(sOptionalPositional, i));
     Rti tParameter = _castToRti(
         _Utils.arrayAt(tRequiredPositional, sRequiredPositionalLength + i));
-    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv)) return false;
+    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv, isLegacy)) {
+      return false;
+    }
   }
 
   for (int i = 0; i < tOptionalPositionalLength; i++) {
     Rti sParameter = _castToRti(
         _Utils.arrayAt(sOptionalPositional, requiredPositionalDelta + i));
     Rti tParameter = _castToRti(_Utils.arrayAt(tOptionalPositional, i));
-    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv)) return false;
+    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv, isLegacy)) {
+      return false;
+    }
   }
 
   var sOptionalNamed = _FunctionParameters._getOptionalNamed(sParameters);
@@ -2534,13 +2579,13 @@
     if (_Utils.stringLessThan(tName, sName)) return false;
     Rti sType = _castToRti(_Utils.arrayAt(sOptionalNamed, i - 1));
     Rti tType = _castToRti(_Utils.arrayAt(tOptionalNamed, j + 1));
-    if (!_isSubtype(universe, tType, tEnv, sType, sEnv)) return false;
+    if (!_isSubtype(universe, tType, tEnv, sType, sEnv, isLegacy)) return false;
   }
 
   return true;
 }
 
-bool _isInterfaceSubtype(universe, Rti s, sEnv, Rti t, tEnv) {
+bool _isInterfaceSubtype(universe, Rti s, sEnv, Rti t, tEnv, bool isLegacy) {
   String sName = Rti._getInterfaceName(s);
   String tName = Rti._getInterfaceName(t);
 
@@ -2569,21 +2614,29 @@
         switch (sVariance) {
           case Variance.legacyCovariant:
           case Variance.covariant:
-            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv)) return false;
+            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv, isLegacy)) {
+              return false;
+            }
             break;
           case Variance.contravariant:
-            if (!_isSubtype(universe, tArg, tEnv, sArg, sEnv)) return false;
+            if (!_isSubtype(universe, tArg, tEnv, sArg, sEnv, isLegacy)) {
+              return false;
+            }
             break;
           case Variance.invariant:
-            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv) ||
-                !_isSubtype(universe, tArg, tEnv, sArg, sEnv)) return false;
+            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv, isLegacy) ||
+                !_isSubtype(universe, tArg, tEnv, sArg, sEnv, isLegacy)) {
+              return false;
+            }
             break;
           default:
             throw StateError(
                 "Unknown variance given for subtype check: $sVariance");
         }
       } else {
-        if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv)) return false;
+        if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv, isLegacy)) {
+          return false;
+        }
       }
     }
     return true;
@@ -2610,7 +2663,9 @@
     String recipe = _Utils.asString(_Utils.arrayAt(supertypeArgs, i));
     Rti supertypeArg = _Universe.evalInEnvironment(universe, s, recipe);
     Rti tArg = _castToRti(_Utils.arrayAt(tArgs, i));
-    if (!_isSubtype(universe, supertypeArg, sEnv, tArg, tEnv)) return false;
+    if (!_isSubtype(universe, supertypeArg, sEnv, tArg, tEnv, isLegacy)) {
+      return false;
+    }
   }
   return true;
 }
@@ -2620,10 +2675,10 @@
 ///
 /// We ignore renaming of bound type variables because we operate on de Bruijn
 /// indices, not names.
-bool typeEqual(Rti s, Rti t) {
+bool typeEqual(Rti s, Rti t, bool isLegacy) {
   if (_Utils.isIdentical(s, t)) return true;
 
-  if (isTopType(s)) return isTopType(t);
+  if (isTopType(s, isLegacy)) return isTopType(t, isLegacy);
 
   int sKind = Rti._getKind(s);
   int tKind = Rti._getKind(t);
@@ -2633,46 +2688,49 @@
     case Rti.kindStar:
     case Rti.kindQuestion:
     case Rti.kindFutureOr:
-      return typeEqual(
-          _castToRti(Rti._getPrimary(s)), _castToRti(Rti._getPrimary(t)));
+      return typeEqual(_castToRti(Rti._getPrimary(s)),
+          _castToRti(Rti._getPrimary(t)), isLegacy);
 
     case Rti.kindInterface:
       if (Rti._getInterfaceName(s) != Rti._getInterfaceName(t)) return false;
-      return typesEqual(
-          Rti._getInterfaceTypeArguments(s), Rti._getInterfaceTypeArguments(t));
+      return typesEqual(Rti._getInterfaceTypeArguments(s),
+          Rti._getInterfaceTypeArguments(t), isLegacy);
 
     case Rti.kindBinding:
-      return typeEqual(Rti._getBindingBase(s), Rti._getBindingBase(t)) &&
-          typesEqual(Rti._getBindingArguments(s), Rti._getBindingArguments(t));
+      return typeEqual(
+              Rti._getBindingBase(s), Rti._getBindingBase(t), isLegacy) &&
+          typesEqual(Rti._getBindingArguments(s), Rti._getBindingArguments(t),
+              isLegacy);
 
     case Rti.kindFunction:
-      return typeEqual(Rti._getReturnType(s), Rti._getReturnType(t)) &&
-          functionParametersEqual(
-              Rti._getFunctionParameters(s), Rti._getFunctionParameters(t));
+      return typeEqual(
+              Rti._getReturnType(s), Rti._getReturnType(t), isLegacy) &&
+          functionParametersEqual(Rti._getFunctionParameters(s),
+              Rti._getFunctionParameters(t), isLegacy);
 
     case Rti.kindGenericFunction:
-      return typeEqual(
-              Rti._getGenericFunctionBase(s), Rti._getGenericFunctionBase(t)) &&
+      return typeEqual(Rti._getGenericFunctionBase(s),
+              Rti._getGenericFunctionBase(t), isLegacy) &&
           typesEqual(Rti._getGenericFunctionBounds(s),
-              Rti._getGenericFunctionBounds(t));
+              Rti._getGenericFunctionBounds(t), isLegacy);
 
     default:
       return false;
   }
 }
 
-bool typesEqual(Object sArray, Object tArray) {
+bool typesEqual(Object sArray, Object tArray, isLegacy) {
   int sLength = _Utils.arrayLength(sArray);
   int tLength = _Utils.arrayLength(tArray);
   if (sLength != tLength) return false;
   for (int i = 0; i < sLength; i++) {
     if (!typeEqual(_castToRti(_Utils.arrayAt(sArray, i)),
-        _castToRti(_Utils.arrayAt(tArray, i)))) return false;
+        _castToRti(_Utils.arrayAt(tArray, i)), isLegacy)) return false;
   }
   return true;
 }
 
-bool namedTypesEqual(Object sArray, Object tArray) {
+bool namedTypesEqual(Object sArray, Object tArray, isLegacy) {
   int sLength = _Utils.arrayLength(sArray);
   int tLength = _Utils.arrayLength(tArray);
   assert(sLength.isEven);
@@ -2682,34 +2740,43 @@
     if (_Utils.asString(_Utils.arrayAt(sArray, i)) !=
         _Utils.asString(_Utils.arrayAt(tArray, i))) return false;
     if (!typeEqual(_castToRti(_Utils.arrayAt(sArray, i + 1)),
-        _castToRti(_Utils.arrayAt(tArray, i + 1)))) return false;
+        _castToRti(_Utils.arrayAt(tArray, i + 1)), isLegacy)) return false;
   }
   return true;
 }
 
 // TODO(fishythefish): Support required named parameters.
-bool functionParametersEqual(
-        _FunctionParameters sParameters, _FunctionParameters tParameters) =>
+bool functionParametersEqual(_FunctionParameters sParameters,
+        _FunctionParameters tParameters, isLegacy) =>
     typesEqual(_FunctionParameters._getRequiredPositional(sParameters),
-        _FunctionParameters._getRequiredPositional(tParameters)) &&
+        _FunctionParameters._getRequiredPositional(tParameters), isLegacy) &&
     typesEqual(_FunctionParameters._getOptionalPositional(sParameters),
-        _FunctionParameters._getOptionalPositional(tParameters)) &&
+        _FunctionParameters._getOptionalPositional(tParameters), isLegacy) &&
     namedTypesEqual(_FunctionParameters._getOptionalNamed(sParameters),
-        _FunctionParameters._getOptionalNamed(tParameters));
+        _FunctionParameters._getOptionalNamed(tParameters), isLegacy);
 
-// TODO(fishythefish): Update for NNBD - check for `Object?` instead of
-// `Object`.
-bool isTopType(Rti t) {
-  if (isObjectType(t)) return true;
+bool isLegacyTopType(Rti t) => isTopType(t, true);
+bool isNnbdTopType(Rti t) => isTopType(t, false);
+bool isTopType(Rti t, bool isLegacy) {
+  if (isLegacy) {
+    if (isObjectType(t)) return true;
+  } else {
+    if (isNullableObjectType(t)) return true;
+  }
   int kind = Rti._getKind(t);
   return kind == Rti.kindDynamic ||
       kind == Rti.kindVoid ||
       kind == Rti.kindAny ||
       kind == Rti.kindErased ||
-      kind == Rti.kindFutureOr && isTopType(Rti._getFutureOrArgument(t));
+      kind == Rti.kindFutureOr &&
+          isTopType(Rti._getFutureOrArgument(t), isLegacy);
 }
 
 bool isObjectType(Rti t) => _Utils.isIdentical(t, TYPE_REF<Object>());
+// TODO(fishythefish): Use `TYPE_REF<Object?>()`.
+bool isNullableObjectType(Rti t) =>
+    Rti._getKind(t) == Rti.kindQuestion &&
+    isObjectType(Rti._getQuestionArgument(t));
 bool isNullType(Rti t) => _Utils.isIdentical(t, TYPE_REF<Null>());
 bool isFunctionType(Rti t) => _Utils.isIdentical(t, TYPE_REF<Function>());
 bool isJsFunctionType(Rti t) =>
@@ -2808,8 +2875,8 @@
   _Universe.addTypeParameterVariances(universe, variances);
 }
 
-bool testingIsSubtype(universe, rti1, rti2) {
-  return isSubtype(universe, _castToRti(rti1), _castToRti(rti2));
+bool testingIsLegacySubtype(universe, rti1, rti2) {
+  return isLegacySubtype(universe, _castToRti(rti1), _castToRti(rti2));
 }
 
 Object testingUniverseEval(universe, String recipe) {
diff --git a/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart b/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
index 14b5e23..d86d322 100644
--- a/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
+++ b/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
@@ -91,10 +91,11 @@
       categories: "Client,Server",
       maturity: Maturity.STABLE,
       dart2jsPatchPath: "_internal/js_runtime/lib/isolate_patch.dart"),
-  "js": const LibraryInfo("js/dart2js/js_dart2js.dart",
+  "js": const LibraryInfo("js/js.dart",
       categories: "Client",
       maturity: Maturity.STABLE,
-      platforms: DART2JS_PLATFORM),
+      platforms: DART2JS_PLATFORM,
+      dart2jsPatchPath: "_internal/js_runtime/lib/js_patch.dart"),
   "_js": const LibraryInfo("js/_js.dart",
       categories: "Client",
       dart2jsPatchPath: "js/_js_client.dart",
diff --git a/sdk/lib/js/js.dart b/sdk/lib/js/js.dart
new file mode 100644
index 0000000..139823d
--- /dev/null
+++ b/sdk/lib/js/js.dart
@@ -0,0 +1,235 @@
+// Copyright (c) 2013, 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.6
+
+/// Low-level support for interoperating with JavaScript.
+///
+/// You should usually use `package:js` instead of this library. For more
+/// information, see the [JS interop page](https://dart.dev/web/js-interop).
+///
+/// This library provides access to JavaScript objects from Dart, allowing
+/// Dart code to get and set properties, and call methods of JavaScript objects
+/// and invoke JavaScript functions. The library takes care of converting
+/// between Dart and JavaScript objects where possible, or providing proxies if
+/// conversion isn't possible.
+///
+/// This library does not make Dart objects usable from JavaScript, their
+/// methods and properties are not accessible, though it does allow Dart
+/// functions to be passed into and called from JavaScript.
+///
+/// [JsObject] is the core type and represents a proxy of a JavaScript object.
+/// JsObject gives access to the underlying JavaScript objects properties and
+/// methods. `JsObject`s can be acquired by calls to JavaScript, or they can be
+/// created from proxies to JavaScript constructors.
+///
+/// The top-level getter [context] provides a [JsObject] that represents the
+/// global object in JavaScript, usually `window`.
+///
+/// The following example shows an alert dialog via a JavaScript call to the
+/// global function `alert()`:
+///
+///     import 'dart:js';
+///
+///     main() => context.callMethod('alert', ['Hello from Dart!']);
+///
+/// This example shows how to create a [JsObject] from a JavaScript constructor
+/// and access its properties:
+///
+///     import 'dart:js';
+///
+///     main() {
+///       var object = JsObject(context['Object']);
+///       object['greeting'] = 'Hello';
+///       object['greet'] = (name) => "${object['greeting']} $name";
+///       var message = object.callMethod('greet', ['JavaScript']);
+///       context['console'].callMethod('log', [message]);
+///     }
+///
+/// ## Proxying and automatic conversion
+///
+/// When setting properties on a JsObject or passing arguments to a Javascript
+/// method or function, Dart objects are automatically converted or proxied to
+/// JavaScript objects. When accessing JavaScript properties, or when a Dart
+/// closure is invoked from JavaScript, the JavaScript objects are also
+/// converted to Dart.
+///
+/// Functions and closures are proxied in such a way that they are callable. A
+/// Dart closure assigned to a JavaScript property is proxied by a function in
+/// JavaScript. A JavaScript function accessed from Dart is proxied by a
+/// [JsFunction], which has a [JsFunction.apply] method to invoke it.
+///
+/// The following types are transferred directly and not proxied:
+///
+///   * Basic types: `null`, `bool`, `num`, `String`, `DateTime`
+///   * `TypedData`, including its subclasses like `Int32List`, but _not_
+///     `ByteBuffer`
+///   * When compiling for the web, also: `Blob`, `Event`, `ImageData`,
+///     `KeyRange`, `Node`, and `Window`.
+///
+/// ## Converting collections with JsObject.jsify()
+///
+/// To create a JavaScript collection from a Dart collection use the
+/// [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s
+/// into JavaScript Objects and Arrays.
+///
+/// The following expression creates a new JavaScript object with the properties
+/// `a` and `b` defined:
+///
+///     var jsMap = JsObject.jsify({'a': 1, 'b': 2});
+///
+/// This expression creates a JavaScript array:
+///
+///     var jsArray = JsObject.jsify([1, 2, 3]);
+///
+/// {@category Web}
+library dart.js;
+
+import 'dart:collection' show ListMixin;
+
+/// The JavaScript global object, usually `window`.
+external JsObject get context;
+
+/// A proxy on a JavaScript object.
+///
+/// The properties of the JavaScript object are accessible via the `[]` and
+/// `[]=` operators. Methods are callable via [callMethod].
+class JsObject {
+  /// Constructs a JavaScript object from its native [constructor] and returns
+  /// a proxy to it.
+  external factory JsObject(JsFunction constructor, [List arguments]);
+
+  /// Constructs a [JsObject] that proxies a native Dart object; _for expert use
+  /// only_.
+  ///
+  /// Use this constructor only if you wish to get access to JavaScript
+  /// properties attached to a browser host object, such as a Node or Blob, that
+  /// is normally automatically converted into a native Dart object.
+  ///
+  /// An exception will be thrown if [object] either is `null` or has the type
+  /// `bool`, `num`, or `String`.
+  external factory JsObject.fromBrowserObject(object);
+
+  /// Recursively converts a JSON-like collection of Dart objects to a
+  /// collection of JavaScript objects and returns a [JsObject] proxy to it.
+  ///
+  /// [object] must be a [Map] or [Iterable], the contents of which are also
+  /// converted. Maps and Iterables are copied to a new JavaScript object.
+  /// Primitives and other transferable values are directly converted to their
+  /// JavaScript type, and all other objects are proxied.
+  external factory JsObject.jsify(object);
+
+  /// Returns the value associated with [property] from the proxied JavaScript
+  /// object.
+  ///
+  /// The type of [property] must be either [String] or [num].
+  external dynamic operator [](property);
+
+  // Sets the value associated with [property] on the proxied JavaScript
+  // object.
+  //
+  // The type of [property] must be either [String] or [num].
+  external void operator []=(property, value);
+
+  int get hashCode => 0;
+
+  external bool operator ==(other);
+
+  /// Returns `true` if the JavaScript object contains the specified property
+  /// either directly or though its prototype chain.
+  ///
+  /// This is the equivalent of the `in` operator in JavaScript.
+  external bool hasProperty(property);
+
+  /// Removes [property] from the JavaScript object.
+  ///
+  /// This is the equivalent of the `delete` operator in JavaScript.
+  external void deleteProperty(property);
+
+  /// Returns `true` if the JavaScript object has [type] in its prototype chain.
+  ///
+  /// This is the equivalent of the `instanceof` operator in JavaScript.
+  external bool instanceof(JsFunction type);
+
+  /// Returns the result of the JavaScript objects `toString` method.
+  external String toString();
+
+  /// Calls [method] on the JavaScript object with the arguments [args] and
+  /// returns the result.
+  ///
+  /// The type of [method] must be either [String] or [num].
+  external dynamic callMethod(method, [List args]);
+}
+
+/// A proxy on a JavaScript Function object.
+class JsFunction extends JsObject {
+  /// Returns a [JsFunction] that captures its 'this' binding and calls [f]
+  /// with the value of JavaScript `this` passed as the first argument.
+  external factory JsFunction.withThis(Function f);
+
+  /// Invokes the JavaScript function with arguments [args]. If [thisArg] is
+  /// supplied it is the value of `this` for the invocation.
+  external dynamic apply(List args, {thisArg});
+}
+
+/// A [List] that proxies a JavaScript array.
+class JsArray<E> extends JsObject with ListMixin<E> {
+  /// Creates an empty JavaScript array.
+  external factory JsArray();
+
+  /// Creates a new JavaScript array and initializes it to the contents of
+  /// [other].
+  external factory JsArray.from(Iterable<E> other);
+
+  // Methods required by ListMixin
+
+  external E operator [](dynamic index);
+
+  external void operator []=(dynamic index, E value);
+
+  external int get length;
+
+  external void set length(int length);
+
+  // Methods overridden for better performance
+
+  external void add(E value);
+
+  external void addAll(Iterable<E> iterable);
+
+  external void insert(int index, E element);
+
+  external E removeAt(int index);
+
+  external E removeLast();
+
+  external void removeRange(int start, int end);
+
+  external void setRange(int start, int end, Iterable<E> iterable,
+      [int skipCount = 0]);
+
+  external void sort([int compare(E a, E b)]);
+}
+
+/// Returns a wrapper around function [f] that can be called from JavaScript
+/// using `package:js` JavaScript interop.
+///
+/// The calling conventions in Dart2Js differ from JavaScript and so, by
+/// default, it is not possible to call a Dart function directly. Wrapping with
+/// `allowInterop` creates a function that can be called from JavaScript or
+/// Dart. The semantics of the wrapped function are still more strict than
+/// JavaScript, and the function will throw if called with too many or too few
+/// arguments.
+///
+/// Calling this method repeatedly on a function will return the same result.
+external F allowInterop<F extends Function>(F f);
+
+/// Returns a wrapper around function [f] that can be called from JavaScript
+/// using `package:js` JavaScript interop, passing JavaScript `this` as the first
+/// argument.
+///
+/// See [allowInterop].
+///
+/// When called from Dart, [null] will be passed as the first argument.
+external Function allowInteropCaptureThis(Function f);
diff --git a/sdk/lib/js/js_sources.gni b/sdk/lib/js/js_sources.gni
index 91f6d0c..67d1660 100644
--- a/sdk/lib/js/js_sources.gni
+++ b/sdk/lib/js/js_sources.gni
@@ -2,4 +2,4 @@
 # 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.
 
-js_sdk_sources = [ "dart2js/js_dart2js.dart" ]
+js_sdk_sources = [ "js.dart", "dart2js/js_dart2js.dart" ]
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index 4335766..f31091f 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -1,18 +1,46 @@
 {
   "comment:0": "NOTE: THIS FILE IS GENERATED. DO NOT EDIT.",
   "comment:1": "Instead modify 'sdk/lib/libraries.yaml' and follow the instructions therein.",
+  "none": {
+    "libraries": {}
+  },
   "vm": {
     "libraries": {
       "_builtin": {
         "uri": "_internal/vm/bin/builtin.dart"
       },
-      "cli": {
+      "_internal": {
+        "uri": "internal/internal.dart",
         "patches": [
-          "_internal/vm/bin/cli_patch.dart"
-        ],
-        "uri": "cli/cli.dart"
+          "_internal/vm/lib/internal_patch.dart",
+          "_internal/vm/lib/class_id_fasta.dart",
+          "_internal/vm/lib/print_patch.dart",
+          "_internal/vm/lib/symbol_patch.dart",
+          "internal/patch.dart"
+        ]
+      },
+      "async": {
+        "uri": "async/async.dart",
+        "patches": [
+          "_internal/vm/lib/async_patch.dart",
+          "_internal/vm/lib/deferred_load_patch.dart",
+          "_internal/vm/lib/schedule_microtask_patch.dart",
+          "_internal/vm/lib/timer_patch.dart"
+        ]
+      },
+      "collection": {
+        "uri": "collection/collection.dart",
+        "patches": [
+          "_internal/vm/lib/collection_patch.dart",
+          "_internal/vm/lib/compact_hash.dart"
+        ]
+      },
+      "convert": {
+        "uri": "convert/convert.dart",
+        "patches": "_internal/vm/lib/convert_patch.dart"
       },
       "core": {
+        "uri": "core/core.dart",
         "patches": [
           "_internal/vm/lib/core_patch.dart",
           "_internal/vm/lib/array.dart",
@@ -44,73 +72,35 @@
           "_internal/vm/lib/type_patch.dart",
           "_internal/vm/lib/uri_patch.dart",
           "_internal/vm/lib/weak_property.dart"
-        ],
-        "uri": "core/core.dart"
-      },
-      "async": {
-        "patches": [
-          "_internal/vm/lib/async_patch.dart",
-          "_internal/vm/lib/deferred_load_patch.dart",
-          "_internal/vm/lib/schedule_microtask_patch.dart",
-          "_internal/vm/lib/timer_patch.dart"
-        ],
-        "uri": "async/async.dart"
-      },
-      "collection": {
-        "patches": [
-          "_internal/vm/lib/collection_patch.dart",
-          "_internal/vm/lib/compact_hash.dart"
-        ],
-        "uri": "collection/collection.dart"
-      },
-      "ffi": {
-        "patches": [
-          "_internal/vm/lib/ffi_patch.dart",
-          "_internal/vm/lib/ffi_dynamic_library_patch.dart",
-          "_internal/vm/lib/ffi_native_type_patch.dart"
-        ],
-        "uri": "ffi/ffi.dart"
-      },
-      "typed_data": {
-        "patches": "_internal/vm/lib/typed_data_patch.dart",
-        "uri": "typed_data/typed_data.dart"
-      },
-      "nativewrappers": {
-        "uri": "html/dartium/nativewrappers.dart"
-      },
-      "mirrors": {
-        "patches": [
-          "_internal/vm/lib/mirrors_patch.dart",
-          "_internal/vm/lib/mirrors_impl.dart",
-          "_internal/vm/lib/mirror_reference.dart"
-        ],
-        "uri": "mirrors/mirrors.dart"
+        ]
       },
       "developer": {
+        "uri": "developer/developer.dart",
         "patches": [
           "_internal/vm/lib/developer.dart",
           "_internal/vm/lib/profiler.dart",
           "_internal/vm/lib/timeline.dart"
-        ],
-        "uri": "developer/developer.dart"
+        ]
       },
-      "isolate": {
+      "ffi": {
+        "uri": "ffi/ffi.dart",
         "patches": [
-          "_internal/vm/lib/isolate_patch.dart",
-          "_internal/vm/lib/timer_impl.dart"
-        ],
-        "uri": "isolate/isolate.dart"
-      },
-      "_vmservice": {
-        "uri": "vmservice/vmservice.dart"
+          "_internal/vm/lib/ffi_patch.dart",
+          "_internal/vm/lib/ffi_dynamic_library_patch.dart",
+          "_internal/vm/lib/ffi_native_type_patch.dart"
+        ]
       },
       "wasm": {
+        "uri": "wasm/wasm.dart",
         "patches": [
           "_internal/vm/lib/wasm_patch.dart"
-        ],
-        "uri": "wasm/wasm.dart"
+        ]
+      },
+      "_http": {
+        "uri": "_http/http.dart"
       },
       "io": {
+        "uri": "io/io.dart",
         "patches": [
           "_internal/vm/bin/common_patch.dart",
           "_internal/vm/bin/directory_patch.dart",
@@ -126,358 +116,371 @@
           "_internal/vm/bin/stdio_patch.dart",
           "_internal/vm/bin/secure_socket_patch.dart",
           "_internal/vm/bin/sync_socket_patch.dart"
-        ],
-        "uri": "io/io.dart"
+        ]
       },
-      "_internal": {
+      "isolate": {
+        "uri": "isolate/isolate.dart",
         "patches": [
-          "_internal/vm/lib/internal_patch.dart",
-          "_internal/vm/lib/class_id_fasta.dart",
-          "_internal/vm/lib/print_patch.dart",
-          "_internal/vm/lib/symbol_patch.dart",
-          "internal/patch.dart"
-        ],
-        "uri": "internal/internal.dart"
-      },
-      "convert": {
-        "patches": "_internal/vm/lib/convert_patch.dart",
-        "uri": "convert/convert.dart"
+          "_internal/vm/lib/isolate_patch.dart",
+          "_internal/vm/lib/timer_impl.dart"
+        ]
       },
       "math": {
-        "patches": "_internal/vm/lib/math_patch.dart",
-        "uri": "math/math.dart"
+        "uri": "math/math.dart",
+        "patches": "_internal/vm/lib/math_patch.dart"
       },
-      "_http": {
-        "uri": "_http/http.dart"
+      "mirrors": {
+        "uri": "mirrors/mirrors.dart",
+        "patches": [
+          "_internal/vm/lib/mirrors_patch.dart",
+          "_internal/vm/lib/mirrors_impl.dart",
+          "_internal/vm/lib/mirror_reference.dart"
+        ]
+      },
+      "nativewrappers": {
+        "uri": "html/dartium/nativewrappers.dart"
+      },
+      "cli": {
+        "uri": "cli/cli.dart",
+        "patches": [
+          "_internal/vm/bin/cli_patch.dart"
+        ]
+      },
+      "typed_data": {
+        "uri": "typed_data/typed_data.dart",
+        "patches": "_internal/vm/lib/typed_data_patch.dart"
+      },
+      "_vmservice": {
+        "uri": "vmservice/vmservice.dart"
       },
       "vmservice_io": {
         "uri": "../../runtime/bin/vmservice/vmservice_io.dart"
       }
     }
   },
-  "none": {
-    "libraries": {}
-  },
   "dart2js": {
     "libraries": {
       "async": {
-        "patches": "_internal/js_runtime/lib/async_patch.dart",
-        "uri": "async/async.dart"
+        "uri": "async/async.dart",
+        "patches": "_internal/js_runtime/lib/async_patch.dart"
       },
-      "_interceptors": {
-        "uri": "_internal/js_runtime/lib/interceptors.dart"
+      "collection": {
+        "uri": "collection/collection.dart",
+        "patches": "_internal/js_runtime/lib/collection_patch.dart"
       },
-      "mirrors": {
-        "patches": "_internal/js_runtime/lib/mirrors_patch_cfe.dart",
-        "supported": false,
-        "uri": "mirrors/mirrors.dart"
+      "convert": {
+        "uri": "convert/convert.dart",
+        "patches": "_internal/js_runtime/lib/convert_patch.dart"
       },
-      "_js_embedded_names": {
-        "uri": "_internal/js_runtime/lib/shared/embedded_names.dart"
+      "core": {
+        "uri": "core/core.dart",
+        "patches": "_internal/js_runtime/lib/core_patch.dart"
       },
-      "io": {
-        "patches": "_internal/js_runtime/lib/io_patch.dart",
-        "supported": false,
-        "uri": "io/io.dart"
+      "developer": {
+        "uri": "developer/developer.dart",
+        "patches": "_internal/js_runtime/lib/developer_patch.dart"
       },
-      "_internal": {
-        "patches": "_internal/js_runtime/lib/internal_patch.dart",
-        "uri": "internal/internal.dart"
-      },
-      "_metadata": {
-        "uri": "html/html_common/metadata.dart"
-      },
-      "_async_await_error_codes": {
-        "uri": "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
-      },
-      "_http": {
-        "uri": "_http/http.dart"
-      },
-      "_js_primitives": {
-        "uri": "_internal/js_runtime/lib/js_primitives.dart"
-      },
-      "_js_helper": {
-        "uri": "_internal/js_runtime/lib/js_helper.dart"
-      },
-      "js": {
-        "uri": "js/dart2js/js_dart2js.dart"
+      "html": {
+        "uri": "html/dart2js/html_dart2js.dart"
       },
       "html_common": {
         "uri": "html/html_common/html_common_dart2js.dart"
       },
-      "_recipe_syntax": {
-        "uri": "_internal/js_runtime/lib/shared/recipe_syntax.dart"
+      "indexed_db": {
+        "uri": "indexed_db/dart2js/indexed_db_dart2js.dart"
       },
-      "_native_typed_data": {
-        "uri": "_internal/js_runtime/lib/native_typed_data.dart"
+      "_http": {
+        "uri": "_http/http.dart"
       },
-      "_js_names": {
-        "uri": "_internal/js_runtime/lib/js_names.dart"
+      "io": {
+        "uri": "io/io.dart",
+        "patches": "_internal/js_runtime/lib/io_patch.dart",
+        "supported": false
       },
-      "core": {
-        "patches": "_internal/js_runtime/lib/core_patch.dart",
-        "uri": "core/core.dart"
+      "isolate": {
+        "uri": "isolate/isolate.dart",
+        "patches": "_internal/js_runtime/lib/isolate_patch.dart",
+        "supported": false
       },
-      "collection": {
-        "patches": "_internal/js_runtime/lib/collection_patch.dart",
-        "uri": "collection/collection.dart"
+      "js": {
+        "uri": "js/js.dart",
+        "patches": "_internal/js_runtime/lib/js_patch.dart"
+      },
+      "_js": {
+        "uri": "js/_js.dart",
+        "patches": "js/_js_client.dart"
       },
       "js_util": {
         "uri": "js_util/dart2js/js_util_dart2js.dart"
       },
+      "math": {
+        "uri": "math/math.dart",
+        "patches": "_internal/js_runtime/lib/math_patch.dart"
+      },
+      "mirrors": {
+        "uri": "mirrors/mirrors.dart",
+        "patches": "_internal/js_runtime/lib/mirrors_patch_cfe.dart",
+        "supported": false
+      },
       "typed_data": {
-        "patches": "_internal/js_runtime/lib/typed_data_patch.dart",
-        "uri": "typed_data/typed_data.dart"
+        "uri": "typed_data/typed_data.dart",
+        "patches": "_internal/js_runtime/lib/typed_data_patch.dart"
+      },
+      "_native_typed_data": {
+        "uri": "_internal/js_runtime/lib/native_typed_data.dart"
+      },
+      "svg": {
+        "uri": "svg/dart2js/svg_dart2js.dart"
       },
       "web_audio": {
         "uri": "web_audio/dart2js/web_audio_dart2js.dart"
       },
-      "html": {
-        "uri": "html/dart2js/html_dart2js.dart"
-      },
-      "isolate": {
-        "patches": "_internal/js_runtime/lib/isolate_patch.dart",
-        "supported": false,
-        "uri": "isolate/isolate.dart"
-      },
-      "developer": {
-        "patches": "_internal/js_runtime/lib/developer_patch.dart",
-        "uri": "developer/developer.dart"
-      },
       "web_gl": {
         "uri": "web_gl/dart2js/web_gl_dart2js.dart"
       },
-      "indexed_db": {
-        "uri": "indexed_db/dart2js/indexed_db_dart2js.dart"
-      },
-      "_js": {
-        "patches": "js/_js_client.dart",
-        "uri": "js/_js.dart"
-      },
-      "convert": {
-        "patches": "_internal/js_runtime/lib/convert_patch.dart",
-        "uri": "convert/convert.dart"
-      },
-      "math": {
-        "patches": "_internal/js_runtime/lib/math_patch.dart",
-        "uri": "math/math.dart"
-      },
-      "_foreign_helper": {
-        "uri": "_internal/js_runtime/lib/foreign_helper.dart"
-      },
       "web_sql": {
         "uri": "web_sql/dart2js/web_sql_dart2js.dart"
       },
+      "_internal": {
+        "uri": "internal/internal.dart",
+        "patches": "_internal/js_runtime/lib/internal_patch.dart"
+      },
+      "_js_helper": {
+        "uri": "_internal/js_runtime/lib/js_helper.dart"
+      },
       "_rti": {
         "uri": "_internal/js_runtime/lib/rti.dart"
       },
-      "svg": {
-        "uri": "svg/dart2js/svg_dart2js.dart"
-      }
-    }
-  },
-  "dartdevc": {
-    "libraries": {
-      "async": {
-        "patches": "_internal/js_dev_runtime/patch/async_patch.dart",
-        "uri": "async/async.dart"
-      },
-      "_runtime": {
-        "uri": "_internal/js_dev_runtime/private/ddc_runtime/runtime.dart"
-      },
       "_interceptors": {
-        "uri": "_internal/js_dev_runtime/private/interceptors.dart"
+        "uri": "_internal/js_runtime/lib/interceptors.dart"
       },
-      "mirrors": {
-        "patches": "_internal/js_dev_runtime/patch/mirrors_patch.dart",
-        "supported": false,
-        "uri": "mirrors/mirrors.dart"
+      "_foreign_helper": {
+        "uri": "_internal/js_runtime/lib/foreign_helper.dart"
       },
-      "_debugger": {
-        "uri": "_internal/js_dev_runtime/private/debugger.dart"
+      "_js_names": {
+        "uri": "_internal/js_runtime/lib/js_names.dart"
       },
-      "io": {
-        "patches": "_internal/js_dev_runtime/patch/io_patch.dart",
-        "supported": false,
-        "uri": "io/io.dart"
+      "_js_primitives": {
+        "uri": "_internal/js_runtime/lib/js_primitives.dart"
       },
-      "_internal": {
-        "patches": "_internal/js_dev_runtime/patch/internal_patch.dart",
-        "uri": "internal/internal.dart"
+      "_js_embedded_names": {
+        "uri": "_internal/js_runtime/lib/shared/embedded_names.dart"
+      },
+      "_async_await_error_codes": {
+        "uri": "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
+      },
+      "_recipe_syntax": {
+        "uri": "_internal/js_runtime/lib/shared/recipe_syntax.dart"
       },
       "_metadata": {
         "uri": "html/html_common/metadata.dart"
-      },
-      "_http": {
-        "uri": "_http/http.dart"
-      },
-      "_js_primitives": {
-        "uri": "_internal/js_dev_runtime/private/js_primitives.dart"
-      },
-      "_js_helper": {
-        "uri": "_internal/js_dev_runtime/private/js_helper.dart"
-      },
-      "js": {
-        "uri": "_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart"
-      },
-      "_js_mirrors": {
-        "uri": "_internal/js_dev_runtime/private/js_mirrors.dart"
-      },
-      "html_common": {
-        "uri": "html/html_common/html_common_dart2js.dart"
-      },
-      "_native_typed_data": {
-        "uri": "_internal/js_dev_runtime/private/native_typed_data.dart"
-      },
-      "core": {
-        "patches": "_internal/js_dev_runtime/patch/core_patch.dart",
-        "uri": "core/core.dart"
-      },
-      "js_util": {
-        "uri": "_internal/js_dev_runtime/lib/js_util/dart2js/js_util_dart2js.dart"
-      },
-      "collection": {
-        "patches": "_internal/js_dev_runtime/patch/collection_patch.dart",
-        "uri": "collection/collection.dart"
-      },
-      "typed_data": {
-        "patches": "_internal/js_dev_runtime/patch/typed_data_patch.dart",
-        "uri": "typed_data/typed_data.dart"
-      },
-      "web_audio": {
-        "uri": "web_audio/dart2js/web_audio_dart2js.dart"
-      },
-      "html": {
-        "uri": "html/dart2js/html_dart2js.dart"
-      },
-      "developer": {
-        "patches": "_internal/js_dev_runtime/patch/developer_patch.dart",
-        "uri": "developer/developer.dart"
-      },
-      "isolate": {
-        "patches": "_internal/js_dev_runtime/patch/isolate_patch.dart",
-        "supported": false,
-        "uri": "isolate/isolate.dart"
-      },
-      "web_gl": {
-        "uri": "web_gl/dart2js/web_gl_dart2js.dart"
-      },
-      "indexed_db": {
-        "uri": "indexed_db/dart2js/indexed_db_dart2js.dart"
-      },
-      "convert": {
-        "patches": "_internal/js_dev_runtime/patch/convert_patch.dart",
-        "uri": "convert/convert.dart"
-      },
-      "_isolate_helper": {
-        "uri": "_internal/js_dev_runtime/private/isolate_helper.dart"
-      },
-      "math": {
-        "patches": "_internal/js_dev_runtime/patch/math_patch.dart",
-        "uri": "math/math.dart"
-      },
-      "_foreign_helper": {
-        "uri": "_internal/js_dev_runtime/private/foreign_helper.dart"
-      },
-      "web_sql": {
-        "uri": "web_sql/dart2js/web_sql_dart2js.dart"
-      },
-      "svg": {
-        "uri": "svg/dart2js/svg_dart2js.dart"
       }
     }
   },
   "dart2js_server": {
     "libraries": {
       "async": {
-        "patches": "_internal/js_runtime/lib/async_patch.dart",
-        "uri": "async/async.dart"
+        "uri": "async/async.dart",
+        "patches": "_internal/js_runtime/lib/async_patch.dart"
       },
-      "mirrors": {
-        "patches": "_internal/js_runtime/lib/mirrors_patch_cfe.dart",
-        "supported": false,
-        "uri": "mirrors/mirrors.dart"
+      "collection": {
+        "uri": "collection/collection.dart",
+        "patches": "_internal/js_runtime/lib/collection_patch.dart"
       },
-      "_interceptors": {
-        "uri": "_internal/js_runtime/lib/interceptors.dart"
+      "convert": {
+        "uri": "convert/convert.dart",
+        "patches": "_internal/js_runtime/lib/convert_patch.dart"
       },
-      "_js_embedded_names": {
-        "uri": "_internal/js_runtime/lib/shared/embedded_names.dart"
+      "core": {
+        "uri": "core/core.dart",
+        "patches": "_internal/js_runtime/lib/core_patch.dart"
       },
-      "io": {
-        "patches": "_internal/js_runtime/lib/io_patch.dart",
-        "supported": false,
-        "uri": "io/io.dart"
-      },
-      "_internal": {
-        "patches": "_internal/js_runtime/lib/internal_patch.dart",
-        "uri": "internal/internal.dart"
-      },
-      "_async_await_error_codes": {
-        "uri": "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
+      "developer": {
+        "uri": "developer/developer.dart",
+        "patches": "_internal/js_runtime/lib/developer_patch.dart"
       },
       "_http": {
         "uri": "_http/http.dart"
       },
-      "_js_helper": {
-        "uri": "_internal/js_runtime/lib/js_helper.dart"
+      "io": {
+        "uri": "io/io.dart",
+        "patches": "_internal/js_runtime/lib/io_patch.dart",
+        "supported": false
       },
-      "_js_primitives": {
-        "uri": "_internal/js_runtime/lib/js_primitives.dart"
+      "isolate": {
+        "uri": "isolate/isolate.dart",
+        "patches": "_internal/js_runtime/lib/isolate_patch.dart",
+        "supported": false
       },
       "js": {
-        "uri": "js/dart2js/js_dart2js.dart"
+        "uri": "js/js.dart",
+        "patches": "_internal/js_runtime/lib/js_patch.dart"
       },
-      "_recipe_syntax": {
-        "uri": "_internal/js_runtime/lib/shared/recipe_syntax.dart"
-      },
-      "_native_typed_data": {
-        "uri": "_internal/js_runtime/lib/native_typed_data.dart"
-      },
-      "core": {
-        "patches": "_internal/js_runtime/lib/core_patch.dart",
-        "uri": "core/core.dart"
-      },
-      "_js_names": {
-        "uri": "_internal/js_runtime/lib/js_names.dart"
+      "_js": {
+        "uri": "js/_js.dart",
+        "patches": "js/_js_server.dart"
       },
       "js_util": {
         "uri": "js_util/dart2js/js_util_dart2js.dart"
       },
-      "collection": {
-        "patches": "_internal/js_runtime/lib/collection_patch.dart",
-        "uri": "collection/collection.dart"
+      "math": {
+        "uri": "math/math.dart",
+        "patches": "_internal/js_runtime/lib/math_patch.dart"
+      },
+      "mirrors": {
+        "uri": "mirrors/mirrors.dart",
+        "patches": "_internal/js_runtime/lib/mirrors_patch_cfe.dart",
+        "supported": false
       },
       "typed_data": {
-        "patches": "_internal/js_runtime/lib/typed_data_patch.dart",
-        "uri": "typed_data/typed_data.dart"
+        "uri": "typed_data/typed_data.dart",
+        "patches": "_internal/js_runtime/lib/typed_data_patch.dart"
       },
-      "isolate": {
-        "patches": "_internal/js_runtime/lib/isolate_patch.dart",
-        "supported": false,
-        "uri": "isolate/isolate.dart"
+      "_native_typed_data": {
+        "uri": "_internal/js_runtime/lib/native_typed_data.dart"
       },
-      "developer": {
-        "patches": "_internal/js_runtime/lib/developer_patch.dart",
-        "uri": "developer/developer.dart"
+      "_internal": {
+        "uri": "internal/internal.dart",
+        "patches": "_internal/js_runtime/lib/internal_patch.dart"
       },
-      "_js": {
-        "patches": "js/_js_server.dart",
-        "uri": "js/_js.dart"
+      "_js_helper": {
+        "uri": "_internal/js_runtime/lib/js_helper.dart"
       },
-      "convert": {
-        "patches": "_internal/js_runtime/lib/convert_patch.dart",
-        "uri": "convert/convert.dart"
+      "_rti": {
+        "uri": "_internal/js_runtime/lib/rti.dart"
       },
-      "math": {
-        "patches": "_internal/js_runtime/lib/math_patch.dart",
-        "uri": "math/math.dart"
+      "_interceptors": {
+        "uri": "_internal/js_runtime/lib/interceptors.dart"
       },
       "_foreign_helper": {
         "uri": "_internal/js_runtime/lib/foreign_helper.dart"
       },
-      "_rti": {
-        "uri": "_internal/js_runtime/lib/rti.dart"
+      "_js_names": {
+        "uri": "_internal/js_runtime/lib/js_names.dart"
+      },
+      "_js_primitives": {
+        "uri": "_internal/js_runtime/lib/js_primitives.dart"
+      },
+      "_js_embedded_names": {
+        "uri": "_internal/js_runtime/lib/shared/embedded_names.dart"
+      },
+      "_async_await_error_codes": {
+        "uri": "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
+      },
+      "_recipe_syntax": {
+        "uri": "_internal/js_runtime/lib/shared/recipe_syntax.dart"
+      }
+    }
+  },
+  "dartdevc": {
+    "libraries": {
+      "_runtime": {
+        "uri": "_internal/js_dev_runtime/private/ddc_runtime/runtime.dart"
+      },
+      "_debugger": {
+        "uri": "_internal/js_dev_runtime/private/debugger.dart"
+      },
+      "_foreign_helper": {
+        "uri": "_internal/js_dev_runtime/private/foreign_helper.dart"
+      },
+      "_http": {
+        "uri": "_http/http.dart"
+      },
+      "_interceptors": {
+        "uri": "_internal/js_dev_runtime/private/interceptors.dart"
+      },
+      "_internal": {
+        "uri": "internal/internal.dart",
+        "patches": "_internal/js_dev_runtime/patch/internal_patch.dart"
+      },
+      "_isolate_helper": {
+        "uri": "_internal/js_dev_runtime/private/isolate_helper.dart"
+      },
+      "_js_helper": {
+        "uri": "_internal/js_dev_runtime/private/js_helper.dart"
+      },
+      "_js_mirrors": {
+        "uri": "_internal/js_dev_runtime/private/js_mirrors.dart"
+      },
+      "_js_primitives": {
+        "uri": "_internal/js_dev_runtime/private/js_primitives.dart"
+      },
+      "_metadata": {
+        "uri": "html/html_common/metadata.dart"
+      },
+      "_native_typed_data": {
+        "uri": "_internal/js_dev_runtime/private/native_typed_data.dart"
+      },
+      "async": {
+        "uri": "async/async.dart",
+        "patches": "_internal/js_dev_runtime/patch/async_patch.dart"
+      },
+      "collection": {
+        "uri": "collection/collection.dart",
+        "patches": "_internal/js_dev_runtime/patch/collection_patch.dart"
+      },
+      "convert": {
+        "uri": "convert/convert.dart",
+        "patches": "_internal/js_dev_runtime/patch/convert_patch.dart"
+      },
+      "core": {
+        "uri": "core/core.dart",
+        "patches": "_internal/js_dev_runtime/patch/core_patch.dart"
+      },
+      "developer": {
+        "uri": "developer/developer.dart",
+        "patches": "_internal/js_dev_runtime/patch/developer_patch.dart"
+      },
+      "io": {
+        "uri": "io/io.dart",
+        "patches": "_internal/js_dev_runtime/patch/io_patch.dart",
+        "supported": false
+      },
+      "isolate": {
+        "uri": "isolate/isolate.dart",
+        "patches": "_internal/js_dev_runtime/patch/isolate_patch.dart",
+        "supported": false
+      },
+      "mirrors": {
+        "uri": "mirrors/mirrors.dart",
+        "patches": "_internal/js_dev_runtime/patch/mirrors_patch.dart",
+        "supported": false
+      },
+      "math": {
+        "uri": "math/math.dart",
+        "patches": "_internal/js_dev_runtime/patch/math_patch.dart"
+      },
+      "typed_data": {
+        "uri": "typed_data/typed_data.dart",
+        "patches": "_internal/js_dev_runtime/patch/typed_data_patch.dart"
+      },
+      "html": {
+        "uri": "html/dart2js/html_dart2js.dart"
+      },
+      "html_common": {
+        "uri": "html/html_common/html_common_dart2js.dart"
+      },
+      "indexed_db": {
+        "uri": "indexed_db/dart2js/indexed_db_dart2js.dart"
+      },
+      "js": {
+        "uri": "js/js.dart",
+        "patches": "_internal/js_dev_runtime/patch/js_patch.dart"
+      },
+      "js_util": {
+        "uri": "_internal/js_dev_runtime/lib/js_util/dart2js/js_util_dart2js.dart"
+      },
+      "svg": {
+        "uri": "svg/dart2js/svg_dart2js.dart"
+      },
+      "web_audio": {
+        "uri": "web_audio/dart2js/web_audio_dart2js.dart"
+      },
+      "web_gl": {
+        "uri": "web_gl/dart2js/web_gl_dart2js.dart"
+      },
+      "web_sql": {
+        "uri": "web_sql/dart2js/web_sql_dart2js.dart"
       }
     }
   }
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index 78fa02d..d4a3d8b 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -200,7 +200,8 @@
       supported: false
 
     js:
-      uri: "js/dart2js/js_dart2js.dart"
+      uri: "js/js.dart"
+      patches: "_internal/js_runtime/lib/js_patch.dart"
 
     _js:
       uri: "js/_js.dart"
@@ -307,7 +308,8 @@
       supported: false
 
     js:
-      uri: "js/dart2js/js_dart2js.dart"
+      uri: "js/js.dart"
+      patches: "_internal/js_runtime/lib/js_patch.dart"
 
     _js:
       uri: "js/_js.dart"
@@ -455,7 +457,8 @@
         uri: "indexed_db/dart2js/indexed_db_dart2js.dart"
 
       js:
-        uri: "_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart"
+        uri: "js/js.dart"
+        patches: "_internal/js_dev_runtime/patch/js_patch.dart"
 
       js_util:
         uri: "_internal/js_dev_runtime/lib/js_util/dart2js/js_util_dart2js.dart"
diff --git a/sdk_nnbd/lib/_http/http_impl.dart b/sdk_nnbd/lib/_http/http_impl.dart
index a119a93..c18d12d 100644
--- a/sdk_nnbd/lib/_http/http_impl.dart
+++ b/sdk_nnbd/lib/_http/http_impl.dart
@@ -961,7 +961,7 @@
   }
 
   String _findReasonPhrase(int statusCode) {
-    var resonPhrase = _reasonPhrase;
+    var reasonPhrase = _reasonPhrase;
     if (reasonPhrase != null) {
       return reasonPhrase;
     }
diff --git a/sdk_nnbd/lib/_http/http_parser.dart b/sdk_nnbd/lib/_http/http_parser.dart
index a3ae3c2f..1561ce8 100644
--- a/sdk_nnbd/lib/_http/http_parser.dart
+++ b/sdk_nnbd/lib/_http/http_parser.dart
@@ -975,7 +975,7 @@
     return true;
   }
 
-  int _expect(int val1, int val2) {
+  void _expect(int val1, int val2) {
     if (val1 != val2) {
       throw new HttpException("Failed to parse HTTP");
     }
diff --git a/sdk_nnbd/lib/_http/websocket.dart b/sdk_nnbd/lib/_http/websocket.dart
index 6dd55b2..9c670a6 100644
--- a/sdk_nnbd/lib/_http/websocket.dart
+++ b/sdk_nnbd/lib/_http/websocket.dart
@@ -445,7 +445,7 @@
   /**
    * Closes the WebSocket connection. Set the optional [code] and [reason]
    * arguments to send close information to the remote peer. If they are
-   * omitted, the peer will see [WebSocketStatus.NO_STATUS_RECEIVED] code
+   * omitted, the peer will see [WebSocketStatus.noStatusReceived] code
    * with no reason.
    */
   Future close([int? code, String? reason]);
diff --git a/sdk_nnbd/lib/_http/websocket_impl.dart b/sdk_nnbd/lib/_http/websocket_impl.dart
index af7c1bb..22befce 100644
--- a/sdk_nnbd/lib/_http/websocket_impl.dart
+++ b/sdk_nnbd/lib/_http/websocket_impl.dart
@@ -347,14 +347,14 @@
   void _controlFrameEnd() {
     switch (_opcode) {
       case _WebSocketOpcode.CLOSE:
-        closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
+        closeCode = WebSocketStatus.noStatusReceived;
         var payload = _payload.takeBytes();
         if (payload.length > 0) {
           if (payload.length == 1) {
             throw new WebSocketException("Protocol error");
           }
           closeCode = payload[0] << 8 | payload[1];
-          if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) {
+          if (closeCode == WebSocketStatus.noStatusReceived) {
             throw new WebSocketException("Protocol error");
           }
           if (payload.length > 2) {
@@ -1138,9 +1138,9 @@
     }, onError: (Object error, StackTrace? stackTrace) {
       _closeTimer?.cancel();
       if (error is FormatException) {
-        _close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA);
+        _close(WebSocketStatus.invalidFramePayloadData);
       } else {
-        _close(WebSocketStatus.PROTOCOL_ERROR);
+        _close(WebSocketStatus.protocolError);
       }
       // An error happened, set the close code set above.
       _closeCode = _outCloseCode;
@@ -1195,7 +1195,7 @@
       _consumer.add(new _WebSocketPing());
       _pingTimer = new Timer(interval, () {
         // No pong received.
-        _close(WebSocketStatus.GOING_AWAY);
+        _close(WebSocketStatus.goingAway);
       });
     });
   }
@@ -1300,12 +1300,12 @@
 
   static bool _isReservedStatusCode(int? code) {
     return code != null &&
-        (code < WebSocketStatus.NORMAL_CLOSURE ||
-            code == WebSocketStatus.RESERVED_1004 ||
-            code == WebSocketStatus.NO_STATUS_RECEIVED ||
-            code == WebSocketStatus.ABNORMAL_CLOSURE ||
-            (code > WebSocketStatus.INTERNAL_SERVER_ERROR &&
-                code < WebSocketStatus.RESERVED_1015) ||
-            (code >= WebSocketStatus.RESERVED_1015 && code < 3000));
+        (code < WebSocketStatus.normalClosure ||
+            code == WebSocketStatus.reserved1004 ||
+            code == WebSocketStatus.noStatusReceived ||
+            code == WebSocketStatus.abnormalClosure ||
+            (code > WebSocketStatus.internalServerError &&
+                code < WebSocketStatus.reserved1015) ||
+            (code >= WebSocketStatus.reserved1015 && code < 3000));
   }
 }
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart
deleted file mode 100644
index 01eed1e..0000000
--- a/sdk_nnbd/lib/_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart
+++ /dev/null
@@ -1,568 +0,0 @@
-// Copyright (c) 2015, 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.5
-
-// DDC version of sdk/lib/js/dart2js/js_dart2js.dart
-
-/// Low-level support for interoperating with JavaScript.
-///
-/// You should usually use `package:js` instead of this library. For more
-/// information, see the [JS interop page](https://dart.dev/web/js-interop).
-///
-/// This library provides access to JavaScript objects from Dart, allowing
-/// Dart code to get and set properties, and call methods of JavaScript objects
-/// and invoke JavaScript functions. The library takes care of converting
-/// between Dart and JavaScript objects where possible, or providing proxies if
-/// conversion isn't possible.
-///
-/// This library does not make Dart objects usable from JavaScript, their
-/// methods and properties are not accessible, though it does allow Dart
-/// functions to be passed into and called from JavaScript.
-///
-/// [JsObject] is the core type and represents a proxy of a JavaScript object.
-/// JsObject gives access to the underlying JavaScript objects properties and
-/// methods. `JsObject`s can be acquired by calls to JavaScript, or they can be
-/// created from proxies to JavaScript constructors.
-///
-/// The top-level getter [context] provides a [JsObject] that represents the
-/// global object in JavaScript, usually `window`.
-///
-/// The following example shows an alert dialog via a JavaScript call to the
-/// global function `alert()`:
-///
-///     import 'dart:js';
-///
-///     main() => context.callMethod('alert', ['Hello from Dart!']);
-///
-/// This example shows how to create a [JsObject] from a JavaScript constructor
-/// and access its properties:
-///
-///     import 'dart:js';
-///
-///     main() {
-///       var object = JsObject(context['Object']);
-///       object['greeting'] = 'Hello';
-///       object['greet'] = (name) => "${object['greeting']} $name";
-///       var message = object.callMethod('greet', ['JavaScript']);
-///       context['console'].callMethod('log', [message]);
-///     }
-///
-/// ## Proxying and automatic conversion
-///
-/// When setting properties on a JsObject or passing arguments to a Javascript
-/// method or function, Dart objects are automatically converted or proxied to
-/// JavaScript objects. When accessing JavaScript properties, or when a Dart
-/// closure is invoked from JavaScript, the JavaScript objects are also
-/// converted to Dart.
-///
-/// Functions and closures are proxied in such a way that they are callable. A
-/// Dart closure assigned to a JavaScript property is proxied by a function in
-/// JavaScript. A JavaScript function accessed from Dart is proxied by a
-/// [JsFunction], which has a [apply] method to invoke it.
-///
-/// The following types are transferred directly and not proxied:
-///
-///   * Basic types: `null`, `bool`, `num`, `String`, `DateTime`
-///   * `TypedData`, including its subclasses like `Int32List`, but _not_
-///     `ByteBuffer`
-///   * When compiling for the web, also: `Blob`, `Event`, `ImageData`,
-///     `KeyRange`, `Node`, and `Window`.
-///
-/// ## Converting collections with JsObject.jsify()
-///
-/// To create a JavaScript collection from a Dart collection use the
-/// [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s
-/// into JavaScript Objects and Arrays.
-///
-/// The following expression creates a new JavaScript object with the properties
-/// `a` and `b` defined:
-///
-///     var jsMap = JsObject.jsify({'a': 1, 'b': 2});
-///
-/// This expression creates a JavaScript array:
-///
-///     var jsArray = JsObject.jsify([1, 2, 3]);
-///
-/// {@category Web}
-library dart.js;
-
-import 'dart:collection' show HashMap, ListMixin;
-
-import 'dart:_js_helper' show Primitives;
-import 'dart:_foreign_helper' show JS;
-import 'dart:_runtime' as dart;
-
-/// The JavaScript global object, usually `window`.
-final JsObject context = _wrapToDart(dart.global_);
-
-/// A proxy on a JavaScript object.
-///
-/// The properties of the JavaScript object are accessible via the `[]` and
-/// `[]=` operators. Methods are callable via [callMethod].
-class JsObject {
-  // The wrapped JS object.
-  final dynamic _jsObject;
-
-  // This should only be called from _wrapToDart
-  JsObject._fromJs(this._jsObject) {
-    assert(_jsObject != null);
-  }
-
-  /// Constructs a JavaScript object from its native [constructor] and returns
-  /// a proxy to it.
-  factory JsObject(JsFunction constructor, [List arguments]) {
-    var ctor = constructor._jsObject;
-    if (arguments == null) {
-      return _wrapToDart(JS('', 'new #()', ctor));
-    }
-    var unwrapped = List.from(arguments.map(_convertToJS));
-    return _wrapToDart(JS('', 'new #(...#)', ctor, unwrapped));
-  }
-
-  /// Constructs a [JsObject] that proxies a native Dart object; _for expert use
-  /// only_.
-  ///
-  /// Use this constructor only if you wish to get access to JavaScript
-  /// properties attached to a browser host object, such as a Node or Blob, that
-  /// is normally automatically converted into a native Dart object.
-  ///
-  /// An exception will be thrown if [object] either is `null` or has the type
-  /// `bool`, `num`, or `String`.
-  factory JsObject.fromBrowserObject(object) {
-    if (object is num || object is String || object is bool || object == null) {
-      throw ArgumentError("object cannot be a num, string, bool, or null");
-    }
-    return _wrapToDart(_convertToJS(object));
-  }
-
-  /// Recursively converts a JSON-like collection of Dart objects to a
-  /// collection of JavaScript objects and returns a [JsObject] proxy to it.
-  ///
-  /// [object] must be a [Map] or [Iterable], the contents of which are also
-  /// converted. Maps and Iterables are copied to a new JavaScript object.
-  /// Primitives and other transferable values are directly converted to their
-  /// JavaScript type, and all other objects are proxied.
-  factory JsObject.jsify(object) {
-    if ((object is! Map) && (object is! Iterable)) {
-      throw ArgumentError("object must be a Map or Iterable");
-    }
-    return _wrapToDart(_convertDataTree(object));
-  }
-
-  static _convertDataTree(data) {
-    var _convertedObjects = HashMap.identity();
-
-    _convert(o) {
-      if (_convertedObjects.containsKey(o)) {
-        return _convertedObjects[o];
-      }
-      if (o is Map) {
-        final convertedMap = JS('', '{}');
-        _convertedObjects[o] = convertedMap;
-        for (var key in o.keys) {
-          JS('', '#[#] = #', convertedMap, key, _convert(o[key]));
-        }
-        return convertedMap;
-      } else if (o is Iterable) {
-        var convertedList = [];
-        _convertedObjects[o] = convertedList;
-        convertedList.addAll(o.map(_convert));
-        return convertedList;
-      } else {
-        return _convertToJS(o);
-      }
-    }
-
-    return _convert(data);
-  }
-
-  /// Returns the value associated with [property] from the proxied JavaScript
-  /// object.
-  ///
-  /// The type of [property] must be either [String] or [num].
-  dynamic operator [](Object property) {
-    if (property is! String && property is! num) {
-      throw ArgumentError("property is not a String or num");
-    }
-    return _convertToDart(JS('', '#[#]', _jsObject, property));
-  }
-
-  // Sets the value associated with [property] on the proxied JavaScript
-  // object.
-  //
-  // The type of [property] must be either [String] or [num].
-  void operator []=(Object property, value) {
-    if (property is! String && property is! num) {
-      throw ArgumentError("property is not a String or num");
-    }
-    JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
-  }
-
-  int get hashCode => 0;
-
-  bool operator ==(other) =>
-      other is JsObject && JS<bool>('!', '# === #', _jsObject, other._jsObject);
-
-  /// Returns `true` if the JavaScript object contains the specified property
-  /// either directly or though its prototype chain.
-  ///
-  /// This is the equivalent of the `in` operator in JavaScript.
-  bool hasProperty(property) {
-    if (property is! String && property is! num) {
-      throw ArgumentError("property is not a String or num");
-    }
-    return JS<bool>('!', '# in #', property, _jsObject);
-  }
-
-  /// Removes [property] from the JavaScript object.
-  ///
-  /// This is the equivalent of the `delete` operator in JavaScript.
-  void deleteProperty(property) {
-    if (property is! String && property is! num) {
-      throw ArgumentError("property is not a String or num");
-    }
-    JS<bool>('!', 'delete #[#]', _jsObject, property);
-  }
-
-  /// Returns `true` if the JavaScript object has [type] in its prototype chain.
-  ///
-  /// This is the equivalent of the `instanceof` operator in JavaScript.
-  bool instanceof(JsFunction type) {
-    return JS<bool>('!', '# instanceof #', _jsObject, _convertToJS(type));
-  }
-
-  /// Returns the result of the JavaScript objects `toString` method.
-  String toString() {
-    try {
-      return JS<String>('!', 'String(#)', _jsObject);
-    } catch (e) {
-      return super.toString();
-    }
-  }
-
-  /// Calls [method] on the JavaScript object with the arguments [args] and
-  /// returns the result.
-  ///
-  /// The type of [method] must be either [String] or [num].
-  dynamic callMethod(method, [List args]) {
-    if (method is! String && method is! num) {
-      throw ArgumentError("method is not a String or num");
-    }
-    if (args != null) args = List.from(args.map(_convertToJS));
-    var fn = JS('', '#[#]', _jsObject, method);
-    if (JS<bool>('!', 'typeof(#) !== "function"', fn)) {
-      throw NoSuchMethodError(_jsObject, Symbol(method), args, {});
-    }
-    return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args));
-  }
-}
-
-/// A proxy on a JavaScript Function object.
-class JsFunction extends JsObject {
-  /// Returns a [JsFunction] that captures its 'this' binding and calls [f]
-  /// with the value of JavaScript `this` passed as the first argument.
-  factory JsFunction.withThis(Function f) {
-    return JsFunction._fromJs(JS(
-        '',
-        'function(/*...arguments*/) {'
-            '  let args = [#(this)];'
-            '  for (let arg of arguments) {'
-            '    args.push(#(arg));'
-            '  }'
-            '  return #(#(...args));'
-            '}',
-        _convertToDart,
-        _convertToDart,
-        _convertToJS,
-        f));
-  }
-
-  JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
-
-  /// Invokes the JavaScript function with arguments [args]. If [thisArg] is
-  /// supplied it is the value of `this` for the invocation.
-  dynamic apply(List args, {thisArg}) => _convertToDart(JS(
-      '',
-      '#.apply(#, #)',
-      _jsObject,
-      _convertToJS(thisArg),
-      args == null ? null : List.from(args.map(_convertToJS))));
-}
-
-// TODO(jmesserly): this is totally unnecessary in dev_compiler.
-/// A [List] that proxies a JavaScript array.
-class JsArray<E> extends JsObject with ListMixin<E> {
-  /// Creates an empty JavaScript array.
-  JsArray() : super._fromJs([]);
-
-  /// Creates a new JavaScript array and initializes it to the contents of
-  /// [other].
-  JsArray.from(Iterable<E> other)
-      : super._fromJs([]..addAll(other.map(_convertToJS)));
-
-  JsArray._fromJs(jsObject) : super._fromJs(jsObject);
-
-  _checkIndex(int index) {
-    if (index is int && (index < 0 || index >= length)) {
-      throw RangeError.range(index, 0, length);
-    }
-  }
-
-  _checkInsertIndex(int index) {
-    if (index is int && (index < 0 || index >= length + 1)) {
-      throw RangeError.range(index, 0, length);
-    }
-  }
-
-  static _checkRange(int start, int end, int length) {
-    if (start < 0 || start > length) {
-      throw RangeError.range(start, 0, length);
-    }
-    if (end < start || end > length) {
-      throw RangeError.range(end, start, length);
-    }
-  }
-
-  // Methods required by ListMixin
-
-  E operator [](Object index) {
-    // TODO(justinfagnani): fix the semantics for non-ints
-    // dartbug.com/14605
-    if (index is num && index == index.toInt()) {
-      _checkIndex(index);
-    }
-    return super[index] as E;
-  }
-
-  void operator []=(Object index, value) {
-    // TODO(justinfagnani): fix the semantics for non-ints
-    // dartbug.com/14605
-    if (index is num && index == index.toInt()) {
-      _checkIndex(index);
-    }
-    super[index] = value;
-  }
-
-  int get length {
-    // Check the length honours the List contract.
-    var len = JS('', '#.length', _jsObject);
-    // JavaScript arrays have lengths which are unsigned 32-bit integers.
-    if (JS<bool>(
-        '!', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) {
-      return JS<int>('!', '#', len);
-    }
-    throw StateError('Bad JsArray length');
-  }
-
-  void set length(int length) {
-    super['length'] = length;
-  }
-
-  // Methods overridden for better performance
-
-  void add(E value) {
-    callMethod('push', [value]);
-  }
-
-  void addAll(Iterable<E> iterable) {
-    var list = (JS<bool>('!', '# instanceof Array', iterable))
-        ? iterable
-        : List.from(iterable);
-    callMethod('push', list);
-  }
-
-  void insert(int index, E element) {
-    _checkInsertIndex(index);
-    callMethod('splice', [index, 0, element]);
-  }
-
-  E removeAt(int index) {
-    _checkIndex(index);
-    return callMethod('splice', [index, 1])[0] as E;
-  }
-
-  E removeLast() {
-    if (length == 0) throw RangeError(-1);
-    return callMethod('pop') as E;
-  }
-
-  void removeRange(int start, int end) {
-    _checkRange(start, end, length);
-    callMethod('splice', [start, end - start]);
-  }
-
-  void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
-    _checkRange(start, end, this.length);
-    int length = end - start;
-    if (length == 0) return;
-    if (skipCount < 0) throw ArgumentError(skipCount);
-    var args = <Object>[start, length]
-      ..addAll(iterable.skip(skipCount).take(length));
-    callMethod('splice', args);
-  }
-
-  void sort([int compare(E a, E b)]) {
-    // Note: arr.sort(null) is a type error in FF
-    callMethod('sort', compare == null ? [] : [compare]);
-  }
-}
-
-// Cross frame objects should not be considered browser types.
-// We include the instanceof Object test to filter out cross frame objects
-// on FireFox. Surprisingly on FireFox the instanceof Window test succeeds for
-// cross frame windows while the instanceof Object test fails.
-bool _isBrowserType(o) => JS(
-    'bool',
-    '# instanceof Object && ('
-        '# instanceof Blob || '
-        '# instanceof Event || '
-        '(window.KeyRange && # instanceof KeyRange) || '
-        '(window.IDBKeyRange && # instanceof IDBKeyRange) || '
-        '# instanceof ImageData || '
-        '# instanceof Node || '
-        // Int8Array.__proto__ is TypedArray.
-        '(window.Int8Array && # instanceof Int8Array.__proto__) || '
-        '# instanceof Window)',
-    o,
-    o,
-    o,
-    o,
-    o,
-    o,
-    o,
-    o,
-    o);
-
-class _DartObject {
-  final _dartObj;
-  _DartObject(this._dartObj);
-}
-
-dynamic _convertToJS(dynamic o) {
-  if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) {
-    return o;
-  } else if (o is DateTime) {
-    return Primitives.lazyAsJsDate(o);
-  } else if (o is JsObject) {
-    return o._jsObject;
-  } else if (o is Function) {
-    return _putIfAbsent(_jsProxies, o, _wrapDartFunction);
-  } else {
-    // TODO(jmesserly): for now, we wrap other objects, to keep compatibility
-    // with the original dart:js behavior.
-    return _putIfAbsent(_jsProxies, o, (o) => _DartObject(o));
-  }
-}
-
-dynamic _wrapDartFunction(f) {
-  var wrapper = JS(
-      '',
-      'function(/*...arguments*/) {'
-          '  let args = Array.prototype.map.call(arguments, #);'
-          '  return #(#(...args));'
-          '}',
-      _convertToDart,
-      _convertToJS,
-      f);
-  JS('', '#.set(#, #)', _dartProxies, wrapper, f);
-
-  return wrapper;
-}
-
-// converts a Dart object to a reference to a native JS object
-// which might be a DartObject JS->Dart proxy
-Object _convertToDart(o) {
-  if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) {
-    return o;
-  } else if (JS('!', '# instanceof Date', o)) {
-    num ms = JS('!', '#.getTime()', o);
-    return DateTime.fromMillisecondsSinceEpoch(ms);
-  } else if (o is _DartObject &&
-      !identical(dart.getReifiedType(o), dart.jsobject)) {
-    return o._dartObj;
-  } else {
-    return _wrapToDart(o);
-  }
-}
-
-Object _wrapToDart(o) => _putIfAbsent(_dartProxies, o, _wrapToDartHelper);
-
-Object _wrapToDartHelper(o) {
-  if (JS<bool>('!', 'typeof # == "function"', o)) {
-    return JsFunction._fromJs(o);
-  }
-  if (JS<bool>('!', '# instanceof Array', o)) {
-    return JsArray._fromJs(o);
-  }
-  return JsObject._fromJs(o);
-}
-
-final _dartProxies = JS('', 'new WeakMap()');
-final _jsProxies = JS('', 'new WeakMap()');
-
-Object _putIfAbsent(weakMap, o, getValue(o)) {
-  var value = JS('', '#.get(#)', weakMap, o);
-  if (value == null) {
-    value = getValue(o);
-    JS('', '#.set(#, #)', weakMap, o, value);
-  }
-  return value;
-}
-
-Expando<dynamic> _interopExpando = Expando<dynamic>();
-
-/// Returns a wrapper around function [f] that can be called from JavaScript
-/// using `package:js` JavaScript interop.
-///
-/// The calling conventions in Dart2Js differ from JavaScript and so, by
-/// default, it is not possible to call a Dart function directly. Wrapping with
-/// `allowInterop` creates a function that can be called from JavaScript or
-/// Dart. The semantics of the wrapped function are still more strict than
-/// JavaScript, and the function will throw if called with too many or too few
-/// arguments.
-///
-/// Calling this method repeatedly on a function will return the same result.
-F allowInterop<F extends Function>(F f) {
-  if (!dart.isDartFunction(f)) return f;
-  var ret = _interopExpando[f];
-  if (ret == null) {
-    ret = JS(
-        '',
-        'function (...args) {'
-            ' return #(#, args);'
-            '}',
-        dart.dcall,
-        f);
-    _interopExpando[f] = ret;
-  }
-  return ret;
-}
-
-Expando<Function> _interopCaptureThisExpando = Expando<Function>();
-
-/// Returns a wrapper around function [f] that can be called from JavaScript
-/// using `package:js` JavaScript interop, passing JavaScript `this` as the first
-/// argument.
-///
-/// See [allowInterop].
-///
-/// When called from Dart, [null] will be passed as the first argument.
-Function allowInteropCaptureThis(Function f) {
-  if (!dart.isDartFunction(f)) return f;
-  var ret = _interopCaptureThisExpando[f];
-  if (ret == null) {
-    ret = JS(
-        '',
-        'function(...arguments) {'
-            '  let args = [this];'
-            '  args.push.apply(args, arguments);'
-            '  return #(#, args);'
-            '}',
-        dart.dcall,
-        f);
-    _interopCaptureThisExpando[f] = ret;
-  }
-  return ret;
-}
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart
new file mode 100644
index 0000000..e0c10e8
--- /dev/null
+++ b/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart
@@ -0,0 +1,443 @@
+// Copyright (c) 2020, 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.6
+
+// Patch file for dart:js library.
+library dart.js;
+
+import 'dart:collection' show HashMap, ListMixin;
+
+import 'dart:_js_helper' show patch, Primitives;
+import 'dart:_foreign_helper' show JS;
+import 'dart:_runtime' as dart;
+
+@patch
+JsObject get context => _context;
+
+final JsObject _context = _wrapToDart(dart.global_);
+
+@patch
+class JsObject {
+  // The wrapped JS object.
+  final dynamic _jsObject;
+
+  // This should only be called from _wrapToDart
+  JsObject._fromJs(this._jsObject) {
+    assert(_jsObject != null);
+  }
+
+  @patch
+  factory JsObject(JsFunction constructor, [List arguments]) {
+    var ctor = constructor._jsObject;
+    if (arguments == null) {
+      return _wrapToDart(JS('', 'new #()', ctor));
+    }
+    var unwrapped = List.from(arguments.map(_convertToJS));
+    return _wrapToDart(JS('', 'new #(...#)', ctor, unwrapped));
+  }
+
+  @patch
+  factory JsObject.fromBrowserObject(object) {
+    if (object is num || object is String || object is bool || object == null) {
+      throw ArgumentError("object cannot be a num, string, bool, or null");
+    }
+    return _wrapToDart(_convertToJS(object));
+  }
+
+  @patch
+  factory JsObject.jsify(object) {
+    if ((object is! Map) && (object is! Iterable)) {
+      throw ArgumentError("object must be a Map or Iterable");
+    }
+    return _wrapToDart(_convertDataTree(object));
+  }
+
+  static _convertDataTree(data) {
+    var _convertedObjects = HashMap.identity();
+
+    _convert(o) {
+      if (_convertedObjects.containsKey(o)) {
+        return _convertedObjects[o];
+      }
+      if (o is Map) {
+        final convertedMap = JS('', '{}');
+        _convertedObjects[o] = convertedMap;
+        for (var key in o.keys) {
+          JS('', '#[#] = #', convertedMap, key, _convert(o[key]));
+        }
+        return convertedMap;
+      } else if (o is Iterable) {
+        var convertedList = [];
+        _convertedObjects[o] = convertedList;
+        convertedList.addAll(o.map(_convert));
+        return convertedList;
+      } else {
+        return _convertToJS(o);
+      }
+    }
+
+    return _convert(data);
+  }
+
+  @patch
+  dynamic operator [](Object property) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    return _convertToDart(JS('', '#[#]', _jsObject, property));
+  }
+
+  @patch
+  void operator []=(Object property, value) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
+  }
+
+  @patch
+  bool operator ==(other) =>
+      other is JsObject && JS<bool>('!', '# === #', _jsObject, other._jsObject);
+
+  @patch
+  bool hasProperty(property) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    return JS<bool>('!', '# in #', property, _jsObject);
+  }
+
+  @patch
+  void deleteProperty(property) {
+    if (property is! String && property is! num) {
+      throw ArgumentError("property is not a String or num");
+    }
+    JS<bool>('!', 'delete #[#]', _jsObject, property);
+  }
+
+  @patch
+  bool instanceof(JsFunction type) {
+    return JS<bool>('!', '# instanceof #', _jsObject, _convertToJS(type));
+  }
+
+  @patch
+  String toString() {
+    try {
+      return JS<String>('!', 'String(#)', _jsObject);
+    } catch (e) {
+      return super.toString();
+    }
+  }
+
+  @patch
+  dynamic callMethod(method, [List args]) {
+    if (method is! String && method is! num) {
+      throw ArgumentError("method is not a String or num");
+    }
+    if (args != null) args = List.from(args.map(_convertToJS));
+    var fn = JS('', '#[#]', _jsObject, method);
+    if (JS<bool>('!', 'typeof(#) !== "function"', fn)) {
+      throw NoSuchMethodError(_jsObject, Symbol(method), args, {});
+    }
+    return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args));
+  }
+}
+
+@patch
+class JsFunction extends JsObject {
+  @patch
+  factory JsFunction.withThis(Function f) {
+    return JsFunction._fromJs(JS(
+        '',
+        'function(/*...arguments*/) {'
+            '  let args = [#(this)];'
+            '  for (let arg of arguments) {'
+            '    args.push(#(arg));'
+            '  }'
+            '  return #(#(...args));'
+            '}',
+        _convertToDart,
+        _convertToDart,
+        _convertToJS,
+        f));
+  }
+
+  JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
+
+  @patch
+  dynamic apply(List args, {thisArg}) => _convertToDart(JS(
+      '',
+      '#.apply(#, #)',
+      _jsObject,
+      _convertToJS(thisArg),
+      args == null ? null : List.from(args.map(_convertToJS))));
+}
+
+// TODO(jmesserly): this is totally unnecessary in dev_compiler.
+@patch
+class JsArray<E> extends JsObject with ListMixin<E> {
+  @patch
+  factory JsArray() => JsArray<E>._fromJs([]);
+
+  @patch
+  factory JsArray.from(Iterable<E> other) =>
+      JsArray<E>._fromJs([]..addAll(other.map(_convertToJS)));
+
+  JsArray._fromJs(jsObject) : super._fromJs(jsObject);
+
+  _checkIndex(int index) {
+    if (index is int && (index < 0 || index >= length)) {
+      throw RangeError.range(index, 0, length);
+    }
+  }
+
+  _checkInsertIndex(int index) {
+    if (index is int && (index < 0 || index >= length + 1)) {
+      throw RangeError.range(index, 0, length);
+    }
+  }
+
+  static _checkRange(int start, int end, int length) {
+    if (start < 0 || start > length) {
+      throw RangeError.range(start, 0, length);
+    }
+    if (end < start || end > length) {
+      throw RangeError.range(end, start, length);
+    }
+  }
+
+  @patch
+  E operator [](Object index) {
+    // TODO(justinfagnani): fix the semantics for non-ints
+    // dartbug.com/14605
+    if (index is num && index == index.toInt()) {
+      _checkIndex(index);
+    }
+    return super[index] as E;
+  }
+
+  @patch
+  void operator []=(Object index, value) {
+    // TODO(justinfagnani): fix the semantics for non-ints
+    // dartbug.com/14605
+    if (index is num && index == index.toInt()) {
+      _checkIndex(index);
+    }
+    super[index] = value;
+  }
+
+  @patch
+  int get length {
+    // Check the length honours the List contract.
+    var len = JS('', '#.length', _jsObject);
+    // JavaScript arrays have lengths which are unsigned 32-bit integers.
+    if (JS<bool>(
+        '!', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) {
+      return JS<int>('!', '#', len);
+    }
+    throw StateError('Bad JsArray length');
+  }
+
+  @patch
+  void set length(int length) {
+    super['length'] = length;
+  }
+
+  @patch
+  void add(E value) {
+    callMethod('push', [value]);
+  }
+
+  @patch
+  void addAll(Iterable<E> iterable) {
+    var list = (JS<bool>('!', '# instanceof Array', iterable))
+        ? iterable
+        : List.from(iterable);
+    callMethod('push', list);
+  }
+
+  @patch
+  void insert(int index, E element) {
+    _checkInsertIndex(index);
+    callMethod('splice', [index, 0, element]);
+  }
+
+  @patch
+  E removeAt(int index) {
+    _checkIndex(index);
+    return callMethod('splice', [index, 1])[0] as E;
+  }
+
+  @patch
+  E removeLast() {
+    if (length == 0) throw RangeError(-1);
+    return callMethod('pop') as E;
+  }
+
+  @patch
+  void removeRange(int start, int end) {
+    _checkRange(start, end, length);
+    callMethod('splice', [start, end - start]);
+  }
+
+  @patch
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
+    _checkRange(start, end, this.length);
+    int length = end - start;
+    if (length == 0) return;
+    if (skipCount < 0) throw ArgumentError(skipCount);
+    var args = <Object>[start, length]
+      ..addAll(iterable.skip(skipCount).take(length));
+    callMethod('splice', args);
+  }
+
+  @patch
+  void sort([int compare(E a, E b)]) {
+    // Note: arr.sort(null) is a type error in FF
+    callMethod('sort', compare == null ? [] : [compare]);
+  }
+}
+
+// Cross frame objects should not be considered browser types.
+// We include the instanceof Object test to filter out cross frame objects
+// on FireFox. Surprisingly on FireFox the instanceof Window test succeeds for
+// cross frame windows while the instanceof Object test fails.
+bool _isBrowserType(o) => JS(
+    'bool',
+    '# instanceof Object && ('
+        '# instanceof Blob || '
+        '# instanceof Event || '
+        '(window.KeyRange && # instanceof KeyRange) || '
+        '(window.IDBKeyRange && # instanceof IDBKeyRange) || '
+        '# instanceof ImageData || '
+        '# instanceof Node || '
+        // Int8Array.__proto__ is TypedArray.
+        '(window.Int8Array && # instanceof Int8Array.__proto__) || '
+        '# instanceof Window)',
+    o,
+    o,
+    o,
+    o,
+    o,
+    o,
+    o,
+    o,
+    o);
+
+class _DartObject {
+  final _dartObj;
+  _DartObject(this._dartObj);
+}
+
+dynamic _convertToJS(dynamic o) {
+  if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) {
+    return o;
+  } else if (o is DateTime) {
+    return Primitives.lazyAsJsDate(o);
+  } else if (o is JsObject) {
+    return o._jsObject;
+  } else if (o is Function) {
+    return _putIfAbsent(_jsProxies, o, _wrapDartFunction);
+  } else {
+    // TODO(jmesserly): for now, we wrap other objects, to keep compatibility
+    // with the original dart:js behavior.
+    return _putIfAbsent(_jsProxies, o, (o) => _DartObject(o));
+  }
+}
+
+dynamic _wrapDartFunction(f) {
+  var wrapper = JS(
+      '',
+      'function(/*...arguments*/) {'
+          '  let args = Array.prototype.map.call(arguments, #);'
+          '  return #(#(...args));'
+          '}',
+      _convertToDart,
+      _convertToJS,
+      f);
+  JS('', '#.set(#, #)', _dartProxies, wrapper, f);
+
+  return wrapper;
+}
+
+// converts a Dart object to a reference to a native JS object
+// which might be a DartObject JS->Dart proxy
+Object _convertToDart(o) {
+  if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) {
+    return o;
+  } else if (JS('!', '# instanceof Date', o)) {
+    num ms = JS('!', '#.getTime()', o);
+    return DateTime.fromMillisecondsSinceEpoch(ms);
+  } else if (o is _DartObject &&
+      !identical(dart.getReifiedType(o), dart.jsobject)) {
+    return o._dartObj;
+  } else {
+    return _wrapToDart(o);
+  }
+}
+
+Object _wrapToDart(o) => _putIfAbsent(_dartProxies, o, _wrapToDartHelper);
+
+Object _wrapToDartHelper(o) {
+  if (JS<bool>('!', 'typeof # == "function"', o)) {
+    return JsFunction._fromJs(o);
+  }
+  if (JS<bool>('!', '# instanceof Array', o)) {
+    return JsArray._fromJs(o);
+  }
+  return JsObject._fromJs(o);
+}
+
+final _dartProxies = JS('', 'new WeakMap()');
+final _jsProxies = JS('', 'new WeakMap()');
+
+Object _putIfAbsent(weakMap, o, getValue(o)) {
+  var value = JS('', '#.get(#)', weakMap, o);
+  if (value == null) {
+    value = getValue(o);
+    JS('', '#.set(#, #)', weakMap, o, value);
+  }
+  return value;
+}
+
+Expando<Function> _interopExpando = Expando<Function>();
+
+@patch
+F allowInterop<F extends Function>(F f) {
+  if (!dart.isDartFunction(f)) return f;
+  var ret = _interopExpando[f];
+  if (ret == null) {
+    ret = JS(
+        '',
+        'function (...args) {'
+            ' return #(#, args);'
+            '}',
+        dart.dcall,
+        f);
+    _interopExpando[f] = ret;
+  }
+  return ret;
+}
+
+Expando<Function> _interopCaptureThisExpando = Expando<Function>();
+
+@patch
+Function allowInteropCaptureThis(Function f) {
+  if (!dart.isDartFunction(f)) return f;
+  var ret = _interopCaptureThisExpando[f];
+  if (ret == null) {
+    ret = JS(
+        '',
+        'function(...arguments) {'
+            '  let args = [this];'
+            '  args.push.apply(args, arguments);'
+            '  return #(#, args);'
+            '}',
+        dart.dcall,
+        f);
+    _interopCaptureThisExpando[f] = ret;
+  }
+  return ret;
+}
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/private/foreign_helper.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/private/foreign_helper.dart
index adfae9f..357047e 100644
--- a/sdk_nnbd/lib/_internal/js_dev_runtime/private/foreign_helper.dart
+++ b/sdk_nnbd/lib/_internal/js_dev_runtime/private/foreign_helper.dart
@@ -106,9 +106,7 @@
  */
 // Add additional optional arguments if needed. The method is treated internally
 // as a variable argument method.
-// TODO(vsm): Make this `T extends Object?` when
-// https://github.com/dart-lang/sdk/issues/40022 is fixed.
-T JS<T>(String typeDescription, String codeTemplate,
+T JS<T extends Object?>(String typeDescription, String codeTemplate,
     [arg0,
     arg1,
     arg2,
diff --git a/sdk_nnbd/lib/js/dart2js/js_dart2js.dart b/sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart
similarity index 67%
copy from sdk_nnbd/lib/js/dart2js/js_dart2js.dart
copy to sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart
index 4155113..ab4a332 100644
--- a/sdk_nnbd/lib/js/dart2js/js_dart2js.dart
+++ b/sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart
@@ -1,109 +1,22 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2020, 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.5
+// @dart = 2.6
 
-/// Low-level support for interoperating with JavaScript.
-///
-/// You should usually use `package:js` instead of this library. For more
-/// information, see the [JS interop page](https://dart.dev/web/js-interop).
-///
-/// This library provides access to JavaScript objects from Dart, allowing
-/// Dart code to get and set properties, and call methods of JavaScript objects
-/// and invoke JavaScript functions. The library takes care of converting
-/// between Dart and JavaScript objects where possible, or providing proxies if
-/// conversion isn't possible.
-///
-/// This library does not make Dart objects usable from JavaScript, their
-/// methods and properties are not accessible, though it does allow Dart
-/// functions to be passed into and called from JavaScript.
-///
-/// [JsObject] is the core type and represents a proxy of a JavaScript object.
-/// JsObject gives access to the underlying JavaScript objects properties and
-/// methods. `JsObject`s can be acquired by calls to JavaScript, or they can be
-/// created from proxies to JavaScript constructors.
-///
-/// The top-level getter [context] provides a [JsObject] that represents the
-/// global object in JavaScript, usually `window`.
-///
-/// The following example shows an alert dialog via a JavaScript call to the
-/// global function `alert()`:
-///
-///     import 'dart:js';
-///
-///     main() => context.callMethod('alert', ['Hello from Dart!']);
-///
-/// This example shows how to create a [JsObject] from a JavaScript constructor
-/// and access its properties:
-///
-///     import 'dart:js';
-///
-///     main() {
-///       var object = JsObject(context['Object']);
-///       object['greeting'] = 'Hello';
-///       object['greet'] = (name) => "${object['greeting']} $name";
-///       var message = object.callMethod('greet', ['JavaScript']);
-///       context['console'].callMethod('log', [message]);
-///     }
-///
-/// ## Proxying and automatic conversion
-///
-/// When setting properties on a JsObject or passing arguments to a Javascript
-/// method or function, Dart objects are automatically converted or proxied to
-/// JavaScript objects. When accessing JavaScript properties, or when a Dart
-/// closure is invoked from JavaScript, the JavaScript objects are also
-/// converted to Dart.
-///
-/// Functions and closures are proxied in such a way that they are callable. A
-/// Dart closure assigned to a JavaScript property is proxied by a function in
-/// JavaScript. A JavaScript function accessed from Dart is proxied by a
-/// [JsFunction], which has a [apply] method to invoke it.
-///
-/// The following types are transferred directly and not proxied:
-///
-///   * Basic types: `null`, `bool`, `num`, `String`, `DateTime`
-///   * `TypedData`, including its subclasses like `Int32List`, but _not_
-///     `ByteBuffer`
-///   * When compiling for the web, also: `Blob`, `Event`, `ImageData`,
-///     `KeyRange`, `Node`, and `Window`.
-///
-/// ## Converting collections with JsObject.jsify()
-///
-/// To create a JavaScript collection from a Dart collection use the
-/// [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s
-/// into JavaScript Objects and Arrays.
-///
-/// The following expression creates a new JavaScript object with the properties
-/// `a` and `b` defined:
-///
-///     var jsMap = JsObject.jsify({'a': 1, 'b': 2});
-///
-/// This expression creates a JavaScript array:
-///
-///     var jsArray = JsObject.jsify([1, 2, 3]);
-///
-/// {@category Web}
-library dart.js;
-
+// Patch file for dart:js library.
 import 'dart:collection' show HashMap, ListMixin;
 import 'dart:typed_data' show TypedData;
 
-import 'dart:_foreign_helper' show JS, JS_CONST, DART_CLOSURE_TO_JS;
-import 'dart:_interceptors'
-    show
-        JavaScriptFunction,
-        JavaScriptObject,
-        UnknownJavaScriptObject,
-        DART_CLOSURE_PROPERTY_NAME;
-import 'dart:_js_helper'
-    show Primitives, convertDartClosureToJS, getIsolateAffinityTag;
+import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
+import 'dart:_interceptors' show JavaScriptFunction, DART_CLOSURE_PROPERTY_NAME;
+import 'dart:_js_helper' show patch, Primitives, getIsolateAffinityTag;
 import 'dart:_js' show isBrowserObject, convertFromBrowserObject;
 
-export 'dart:_interceptors' show JavaScriptObject;
+@patch
+JsObject get context => _context;
 
-/// The JavaScript global object, usually `window`.
-final JsObject context = _wrapToDart(JS('', 'self'));
+final JsObject _context = _wrapToDart(JS('', 'self'));
 
 _convertDartFunction(Function f, {bool captureThis: false}) {
   return JS(
@@ -129,10 +42,7 @@
   return _convertToJS(Function.apply(callback, dartArgs));
 }
 
-/// A proxy on a JavaScript object.
-///
-/// The properties of the JavaScript object are accessible via the `[]` and
-/// `[]=` operators. Methods are callable via [callMethod].
+@patch
 class JsObject {
   // The wrapped JS object.
   final dynamic _jsObject;
@@ -142,8 +52,7 @@
     assert(_jsObject != null);
   }
 
-  /// Constructs a JavaScript object from its native [constructor] and returns
-  /// a proxy to it.
+  @patch
   factory JsObject(JsFunction constructor, [List arguments]) {
     var ctor = _convertToJS(constructor);
     if (arguments == null) {
@@ -206,15 +115,7 @@
     //     return _wrapToDart(jsObj);
   }
 
-  /// Constructs a [JsObject] that proxies a native Dart object; _for expert use
-  /// only_.
-  ///
-  /// Use this constructor only if you wish to get access to JavaScript
-  /// properties attached to a browser host object, such as a Node or Blob, that
-  /// is normally automatically converted into a native Dart object.
-  ///
-  /// An exception will be thrown if [object] either is `null` or has the type
-  /// `bool`, `num`, or `String`.
+  @patch
   factory JsObject.fromBrowserObject(object) {
     if (object is num || object is String || object is bool || object == null) {
       throw ArgumentError("object cannot be a num, string, bool, or null");
@@ -222,13 +123,7 @@
     return _wrapToDart(_convertToJS(object));
   }
 
-  /// Recursively converts a JSON-like collection of Dart objects to a
-  /// collection of JavaScript objects and returns a [JsObject] proxy to it.
-  ///
-  /// [object] must be a [Map] or [Iterable], the contents of which are also
-  /// converted. Maps and Iterables are copied to a new JavaScript object.
-  /// Primitives and other transferable values are directly converted to their
-  /// JavaScript type, and all other objects are proxied.
+  @patch
   factory JsObject.jsify(object) {
     if ((object is! Map) && (object is! Iterable)) {
       throw ArgumentError("object must be a Map or Iterable");
@@ -263,10 +158,7 @@
     return _convert(data);
   }
 
-  /// Returns the value associated with [property] from the proxied JavaScript
-  /// object.
-  ///
-  /// The type of [property] must be either [String] or [num].
+  @patch
   dynamic operator [](property) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -274,10 +166,7 @@
     return _convertToDart(JS('', '#[#]', _jsObject, property));
   }
 
-  // Sets the value associated with [property] on the proxied JavaScript
-  // object.
-  //
-  // The type of [property] must be either [String] or [num].
+  @patch
   void operator []=(property, value) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -285,15 +174,11 @@
     JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
   }
 
-  int get hashCode => 0;
-
+  @patch
   bool operator ==(other) =>
       other is JsObject && JS('bool', '# === #', _jsObject, other._jsObject);
 
-  /// Returns `true` if the JavaScript object contains the specified property
-  /// either directly or though its prototype chain.
-  ///
-  /// This is the equivalent of the `in` operator in JavaScript.
+  @patch
   bool hasProperty(property) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -301,9 +186,7 @@
     return JS('bool', '# in #', property, _jsObject);
   }
 
-  /// Removes [property] from the JavaScript object.
-  ///
-  /// This is the equivalent of the `delete` operator in JavaScript.
+  @patch
   void deleteProperty(property) {
     if (property is! String && property is! num) {
       throw ArgumentError("property is not a String or num");
@@ -311,14 +194,12 @@
     JS('bool', 'delete #[#]', _jsObject, property);
   }
 
-  /// Returns `true` if the JavaScript object has [type] in its prototype chain.
-  ///
-  /// This is the equivalent of the `instanceof` operator in JavaScript.
+  @patch
   bool instanceof(JsFunction type) {
     return JS('bool', '# instanceof #', _jsObject, _convertToJS(type));
   }
 
-  /// Returns the result of the JavaScript objects `toString` method.
+  @patch
   String toString() {
     try {
       return JS('String', 'String(#)', _jsObject);
@@ -327,10 +208,7 @@
     }
   }
 
-  /// Calls [method] on the JavaScript object with the arguments [args] and
-  /// returns the result.
-  ///
-  /// The type of [method] must be either [String] or [num].
+  @patch
   dynamic callMethod(method, [List args]) {
     if (method is! String && method is! num) {
       throw ArgumentError("method is not a String or num");
@@ -340,10 +218,9 @@
   }
 }
 
-/// A proxy on a JavaScript Function object.
+@patch
 class JsFunction extends JsObject {
-  /// Returns a [JsFunction] that captures its 'this' binding and calls [f]
-  /// with the value of JavaScript `this` passed as the first argument.
+  @patch
   factory JsFunction.withThis(Function f) {
     var jsFunc = _convertDartFunction(f, captureThis: true);
     return JsFunction._fromJs(jsFunc);
@@ -351,8 +228,7 @@
 
   JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
 
-  /// Invokes the JavaScript function with arguments [args]. If [thisArg] is
-  /// supplied it is the value of `this` for the invocation.
+  @patch
   dynamic apply(List args, {thisArg}) => _convertToDart(JS(
       '',
       '#.apply(#, #)',
@@ -361,15 +237,14 @@
       args == null ? null : List.from(args.map(_convertToJS))));
 }
 
-/// A [List] that proxies a JavaScript array.
+@patch
 class JsArray<E> extends JsObject with ListMixin<E> {
-  /// Creates an empty JavaScript array.
-  JsArray() : super._fromJs([]);
+  @patch
+  factory JsArray() => JsArray<E>._fromJs([]);
 
-  /// Creates a new JavaScript array and initializes it to the contents of
-  /// [other].
-  JsArray.from(Iterable<E> other)
-      : super._fromJs([]..addAll(other.map(_convertToJS)));
+  @patch
+  factory JsArray.from(Iterable<E> other) =>
+      JsArray<E>._fromJs([]..addAll(other.map(_convertToJS)));
 
   JsArray._fromJs(jsObject) : super._fromJs(jsObject);
 
@@ -396,6 +271,7 @@
 
   // Methods required by ListMixin
 
+  @patch
   E operator [](dynamic index) {
     // TODO(justinfagnani): fix the semantics for non-ints
     // dartbug.com/14605
@@ -405,6 +281,7 @@
     return super[index];
   }
 
+  @patch
   void operator []=(dynamic index, E value) {
     // TODO(justinfagnani): fix the semantics for non-ints
     // dartbug.com/14605
@@ -414,6 +291,7 @@
     super[index] = value;
   }
 
+  @patch
   int get length {
     // Check the length honours the List contract.
     var len = JS('', '#.length', _jsObject);
@@ -424,16 +302,19 @@
     throw StateError('Bad JsArray length');
   }
 
+  @patch
   void set length(int length) {
     super['length'] = length;
   }
 
   // Methods overridden for better performance
 
+  @patch
   void add(E value) {
     callMethod('push', [value]);
   }
 
+  @patch
   void addAll(Iterable<E> iterable) {
     var list = (JS('bool', '# instanceof Array', iterable))
         ? iterable
@@ -441,26 +322,31 @@
     callMethod('push', list);
   }
 
+  @patch
   void insert(int index, E element) {
     _checkInsertIndex(index);
     callMethod('splice', [index, 0, element]);
   }
 
+  @patch
   E removeAt(int index) {
     _checkIndex(index);
     return callMethod('splice', [index, 1])[0];
   }
 
+  @patch
   E removeLast() {
     if (length == 0) throw RangeError(-1);
     return callMethod('pop');
   }
 
+  @patch
   void removeRange(int start, int end) {
     _checkRange(start, end, length);
     callMethod('splice', [start, end - start]);
   }
 
+  @patch
   void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
     _checkRange(start, end, this.length);
     int length = end - start;
@@ -471,6 +357,7 @@
     callMethod('splice', args);
   }
 
+  @patch
   void sort([int compare(E a, E b)]) {
     // Note: arr.sort(null) is a type error in FF
     callMethod('sort', compare == null ? [] : [compare]);
@@ -659,17 +546,7 @@
   return Function.apply(callback, [self]..addAll(arguments));
 }
 
-/// Returns a wrapper around function [f] that can be called from JavaScript
-/// using `package:js` JavaScript interop.
-///
-/// The calling conventions in Dart2Js differ from JavaScript and so, by
-/// default, it is not possible to call a Dart function directly. Wrapping with
-/// `allowInterop` creates a function that can be called from JavaScript or
-/// Dart. The semantics of the wrapped function are still more strict than
-/// JavaScript, and the function will throw if called with too many or too few
-/// arguments.
-///
-/// Calling this method repeatedly on a function will return the same result.
+@patch
 F allowInterop<F extends Function>(F f) {
   if (JS('bool', 'typeof(#) == "function"', f)) {
     // Already supports interop, just use the existing function.
@@ -679,13 +556,7 @@
   }
 }
 
-/// Returns a wrapper around function [f] that can be called from JavaScript
-/// using `package:js` JavaScript interop, passing JavaScript `this` as the first
-/// argument.
-///
-/// See [allowInterop].
-///
-/// When called from Dart, [null] will be passed as the first argument.
+@patch
 Function allowInteropCaptureThis(Function f) {
   if (JS('bool', 'typeof(#) == "function"', f)) {
     // Behavior when the function is already a JS function is unspecified.
diff --git a/sdk_nnbd/lib/_internal/js_runtime/lib/rti.dart b/sdk_nnbd/lib/_internal/js_runtime/lib/rti.dart
index ef985e2..ef4654b 100644
--- a/sdk_nnbd/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk_nnbd/lib/_internal/js_runtime/lib/rti.dart
@@ -16,7 +16,6 @@
         JS_EMBEDDED_GLOBAL,
         JS_GET_FLAG,
         JS_GET_NAME,
-        JS_STRING_CONCAT,
         RAW_DART_FUNCTION_REF,
         TYPE_REF;
 
@@ -789,7 +788,8 @@
 
   var isFn = RAW_DART_FUNCTION_REF(_generalIsTestImplementation);
 
-  if (isTopType(testRti)) {
+  // TODO(fishythefish): Update for NNBD.
+  if (isLegacyTopType(testRti)) {
     isFn = RAW_DART_FUNCTION_REF(_isTop);
     var asFn = RAW_DART_FUNCTION_REF(_asTop);
     Rti._setAsCheckFunction(testRti, asFn);
@@ -811,7 +811,11 @@
       String name = Rti._getInterfaceName(testRti);
       var arguments = Rti._getInterfaceTypeArguments(testRti);
       if (JS(
-          'bool', '#.every(#)', arguments, RAW_DART_FUNCTION_REF(isTopType))) {
+          'bool',
+          '#.every(#)',
+          arguments,
+          // TODO(fishythefish): Update for NNBD.
+          RAW_DART_FUNCTION_REF(isLegacyTopType))) {
         String propertyName =
             '${JS_GET_NAME(JsGetName.OPERATOR_IS_PREFIX)}${name}';
         Rti._setSpecializedTestResource(testRti, propertyName);
@@ -830,7 +834,8 @@
   // method. The Rti object is 'this'.
   Rti testRti = _castToRti(JS('', 'this'));
   Rti objectRti = instanceOrFunctionType(object, testRti);
-  return isSubtype(_theUniverse(), objectRti, testRti);
+  // TODO(fishythefish): Update for NNBD.
+  return isLegacySubtype(_theUniverse(), objectRti, testRti);
 }
 
 /// Called from generated code.
@@ -881,7 +886,8 @@
 
 /// Called from generated code.
 checkTypeBound(Rti type, Rti bound, variable, methodName) {
-  if (isSubtype(_theUniverse(), type, bound)) return type;
+  // TODO(fishythefish): Update for NNBD.
+  if (isLegacySubtype(_theUniverse(), type, bound)) return type;
   String message = "The type argument '${_rtiToString(type, null)}' is not"
       " a subtype of the type variable bound '${_rtiToString(bound, null)}'"
       " of type variable '${_Utils.asString(variable)}' in '$methodName'.";
@@ -1080,7 +1086,8 @@
       typeParametersText += typeSep;
       typeParametersText += genericContext[genericContext.length - 1 - i];
       Rti boundRti = _castToRti(_Utils.arrayAt(bounds, i));
-      if (!isTopType(boundRti)) {
+      // TODO(fishythefish): Update for NNBD.
+      if (!isLegacyTopType(boundRti)) {
         typeParametersText +=
             ' extends ' + _rtiToString(boundRti, genericContext);
       }
@@ -1470,7 +1477,6 @@
       environment = Rti._getBindingBase(environment);
     }
 
-    assert(kind == Rti.kindInterface);
     String interfaceName = Rti._getInterfaceName(environment);
     Object rule = _Universe.findRule(universe, interfaceName);
     assert(rule != null);
@@ -2310,49 +2316,63 @@
 // -------- Subtype tests ------------------------------------------------------
 
 // Future entry point from compiled code.
-bool isSubtype(universe, Rti s, Rti t) {
-  return _isSubtype(universe, s, null, t, null);
+bool isLegacySubtype(universe, Rti s, Rti t) {
+  return _isSubtype(universe, s, null, t, null, true);
 }
 
-bool _isSubtype(universe, Rti s, sEnv, Rti t, tEnv) {
-  // Based on
-  // https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md#rules
-  // and https://github.com/dart-lang/language/pull/388.
-  // In particular, the bulk of the structure is derived from the former
-  // resource, with a few adaptations taken from the latter.
-  // - We freely skip subcases which would have already been handled by a
-  // previous case.
-  // - Some rules are reordered in conjunction with the previous point to reduce
-  // the amount of casework.
-  // - Left Type Variable Bound in particular is split into two pieces: an
-  // optimistic check performed early in the algorithm to reduce the number of
-  // backtracking cases when a union appears on the right, and a pessimistic
-  // check performed at the usual place in order to completely eliminate the
-  // case.
-  // - Function type rules are applied before interface type rules.
+bool isNnbdSubtype(universe, Rti s, Rti t) {
+  return _isSubtype(universe, s, null, t, null, false);
+}
 
+/// Based on
+/// https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md#rules
+/// and https://github.com/dart-lang/language/pull/388.
+/// In particular, the bulk of the structure is derived from the former
+/// resource, with a few adaptations taken from the latter.
+/// - We freely skip subcases which would have already been handled by a
+/// previous case.
+/// - Some rules are reordered in conjunction with the previous point to reduce
+/// the amount of casework.
+/// - Left Type Variable Bound in particular is split into two pieces: an
+/// optimistic check performed early in the algorithm to reduce the number of
+/// backtracking cases when a union appears on the right, and a pessimistic
+/// check performed at the usual place in order to completely eliminate the
+/// case.
+/// - Function type rules are applied before interface type rules.
+///
+/// [s] is considered a legacy subtype of [t] if [s] would be a subtype of [t]
+/// in a modification of the NNBD rules in which `?` on types were ignored, `*`
+/// were added to each time, and `required` parameters were treated as
+/// optional. In effect, `Never` is equivalent to `Null`, `Null` is restored to
+/// the bottom of the type hierarchy, `Object` is treated as nullable, and
+/// `required` is ignored on named parameters. This should provide the same
+/// subtyping results as pre-NNBD Dart.
+bool _isSubtype(universe, Rti s, sEnv, Rti t, tEnv, bool isLegacy) {
   // Reflexivity:
   if (_Utils.isIdentical(s, t)) return true;
 
   // Right Top:
-  if (isTopType(t)) return true;
+  if (isTopType(t, isLegacy)) return true;
 
   int sKind = Rti._getKind(s);
   if (sKind == Rti.kindAny) return true;
 
   // Left Top:
-  if (isTopType(s)) return false;
+  if (isTopType(s, isLegacy)) return false;
 
   // Left Bottom:
-  // TODO(fishythefish): Update for NNBD - check for `Never` instead of `Null`.
-  if (isNullType(s)) return true;
+  if (isLegacy) {
+    if (isNullType(s)) return true;
+  } else {
+    if (sKind == Rti.kindNever) return true;
+  }
 
   // Left Type Variable Bound 1:
   bool leftTypeVariable = sKind == Rti.kindGenericFunctionParameter;
   if (leftTypeVariable) {
     int index = Rti._getGenericFunctionParameterIndex(s);
     Rti bound = _castToRti(_Utils.arrayAt(sEnv, index));
-    if (_isSubtype(universe, bound, sEnv, t, tEnv)) return true;
+    if (_isSubtype(universe, bound, sEnv, t, tEnv, isLegacy)) return true;
   }
 
   int tKind = Rti._getKind(t);
@@ -2360,48 +2380,62 @@
   // Left Null:
   // Note: Interchanging the Left Null and Right Object rules allows us to
   // reduce casework.
-  if (isNullType(s)) {
+  if (!isLegacy && isNullType(s)) {
     if (tKind == Rti.kindFutureOr) {
-      return _isSubtype(universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv);
+      return _isSubtype(
+          universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv, isLegacy);
     }
     return isNullType(t) || tKind == Rti.kindQuestion || tKind == Rti.kindStar;
   }
 
   // Right Object:
-  if (isObjectType(t)) {
+  if (!isLegacy && isObjectType(t)) {
     if (sKind == Rti.kindFutureOr) {
-      return _isSubtype(universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv);
+      return _isSubtype(
+          universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv, isLegacy);
     }
     if (sKind == Rti.kindStar) {
-      return _isSubtype(universe, Rti._getStarArgument(s), sEnv, t, tEnv);
+      return _isSubtype(
+          universe, Rti._getStarArgument(s), sEnv, t, tEnv, isLegacy);
     }
     return sKind != Rti.kindQuestion;
   }
 
   // Left Legacy:
   if (sKind == Rti.kindStar) {
-    return _isSubtype(universe, Rti._getStarArgument(s), sEnv, t, tEnv);
+    return _isSubtype(
+        universe, Rti._getStarArgument(s), sEnv, t, tEnv, isLegacy);
   }
 
   // Right Legacy:
   if (tKind == Rti.kindStar) {
     return _isSubtype(
-        universe, s, sEnv, Rti._getQuestionFromStar(universe, t), tEnv);
+        universe,
+        s,
+        sEnv,
+        isLegacy
+            ? Rti._getStarArgument(t)
+            : Rti._getQuestionFromStar(universe, t),
+        tEnv,
+        isLegacy);
   }
 
   // Left FutureOr:
   if (sKind == Rti.kindFutureOr) {
-    if (!_isSubtype(universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv)) {
+    if (!_isSubtype(
+        universe, Rti._getFutureOrArgument(s), sEnv, t, tEnv, isLegacy)) {
       return false;
     }
-    return _isSubtype(
-        universe, Rti._getFutureFromFutureOr(universe, s), sEnv, t, tEnv);
+    return _isSubtype(universe, Rti._getFutureFromFutureOr(universe, s), sEnv,
+        t, tEnv, isLegacy);
   }
 
   // Left Nullable:
   if (sKind == Rti.kindQuestion) {
-    return _isSubtype(universe, TYPE_REF<Null>(), sEnv, t, tEnv) &&
-        _isSubtype(universe, Rti._getQuestionArgument(s), sEnv, t, tEnv);
+    return (isLegacy ||
+            _isSubtype(universe, TYPE_REF<Null>(), sEnv, t, tEnv, isLegacy)) &&
+        _isSubtype(
+            universe, Rti._getQuestionArgument(s), sEnv, t, tEnv, isLegacy);
   }
 
   // Type Variable Reflexivity 1 is subsumed by Reflexivity and therefore
@@ -2411,17 +2445,20 @@
 
   // Right FutureOr:
   if (tKind == Rti.kindFutureOr) {
-    if (_isSubtype(universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv)) {
+    if (_isSubtype(
+        universe, s, sEnv, Rti._getFutureOrArgument(t), tEnv, isLegacy)) {
       return true;
     }
-    return _isSubtype(
-        universe, s, sEnv, Rti._getFutureFromFutureOr(universe, t), tEnv);
+    return _isSubtype(universe, s, sEnv,
+        Rti._getFutureFromFutureOr(universe, t), tEnv, isLegacy);
   }
 
   // Right Nullable:
   if (tKind == Rti.kindQuestion) {
-    return _isSubtype(universe, s, sEnv, TYPE_REF<Null>(), tEnv) ||
-        _isSubtype(universe, s, sEnv, Rti._getQuestionArgument(t), tEnv);
+    return (!isLegacy &&
+            _isSubtype(universe, s, sEnv, TYPE_REF<Null>(), tEnv, isLegacy)) ||
+        _isSubtype(
+            universe, s, sEnv, Rti._getQuestionArgument(t), tEnv, isLegacy);
   }
 
   // Left Promoted Variable does not apply at runtime.
@@ -2444,37 +2481,39 @@
 
     var sBounds = Rti._getGenericFunctionBounds(s);
     var tBounds = Rti._getGenericFunctionBounds(t);
-    if (!typesEqual(sBounds, tBounds)) return false;
+    if (!typesEqual(sBounds, tBounds, isLegacy)) return false;
 
     sEnv = sEnv == null ? sBounds : _Utils.arrayConcat(sBounds, sEnv);
     tEnv = tEnv == null ? tBounds : _Utils.arrayConcat(tBounds, tEnv);
 
     return _isFunctionSubtype(universe, Rti._getGenericFunctionBase(s), sEnv,
-        Rti._getGenericFunctionBase(t), tEnv);
+        Rti._getGenericFunctionBase(t), tEnv, isLegacy);
   }
   if (tKind == Rti.kindFunction) {
     if (isJsFunctionType(s)) return true;
     if (sKind != Rti.kindFunction) return false;
-    return _isFunctionSubtype(universe, s, sEnv, t, tEnv);
+    return _isFunctionSubtype(universe, s, sEnv, t, tEnv, isLegacy);
   }
 
   // Interface Compositionality + Super-Interface:
   if (sKind == Rti.kindInterface) {
     if (tKind != Rti.kindInterface) return false;
-    return _isInterfaceSubtype(universe, s, sEnv, t, tEnv);
+    return _isInterfaceSubtype(universe, s, sEnv, t, tEnv, isLegacy);
   }
 
   return false;
 }
 
 // TODO(fishythefish): Support required named parameters.
-bool _isFunctionSubtype(universe, Rti s, sEnv, Rti t, tEnv) {
+bool _isFunctionSubtype(universe, Rti s, sEnv, Rti t, tEnv, bool isLegacy) {
   assert(Rti._getKind(s) == Rti.kindFunction);
   assert(Rti._getKind(t) == Rti.kindFunction);
 
   Rti sReturnType = Rti._getReturnType(s);
   Rti tReturnType = Rti._getReturnType(t);
-  if (!_isSubtype(universe, sReturnType, sEnv, tReturnType, tEnv)) return false;
+  if (!_isSubtype(universe, sReturnType, sEnv, tReturnType, tEnv, isLegacy)) {
+    return false;
+  }
 
   _FunctionParameters sParameters = Rti._getFunctionParameters(s);
   _FunctionParameters tParameters = Rti._getFunctionParameters(t);
@@ -2501,21 +2540,27 @@
   for (int i = 0; i < sRequiredPositionalLength; i++) {
     Rti sParameter = _castToRti(_Utils.arrayAt(sRequiredPositional, i));
     Rti tParameter = _castToRti(_Utils.arrayAt(tRequiredPositional, i));
-    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv)) return false;
+    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv, isLegacy)) {
+      return false;
+    }
   }
 
   for (int i = 0; i < requiredPositionalDelta; i++) {
     Rti sParameter = _castToRti(_Utils.arrayAt(sOptionalPositional, i));
     Rti tParameter = _castToRti(
         _Utils.arrayAt(tRequiredPositional, sRequiredPositionalLength + i));
-    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv)) return false;
+    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv, isLegacy)) {
+      return false;
+    }
   }
 
   for (int i = 0; i < tOptionalPositionalLength; i++) {
     Rti sParameter = _castToRti(
         _Utils.arrayAt(sOptionalPositional, requiredPositionalDelta + i));
     Rti tParameter = _castToRti(_Utils.arrayAt(tOptionalPositional, i));
-    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv)) return false;
+    if (!_isSubtype(universe, tParameter, tEnv, sParameter, sEnv, isLegacy)) {
+      return false;
+    }
   }
 
   var sOptionalNamed = _FunctionParameters._getOptionalNamed(sParameters);
@@ -2534,13 +2579,13 @@
     if (_Utils.stringLessThan(tName, sName)) return false;
     Rti sType = _castToRti(_Utils.arrayAt(sOptionalNamed, i - 1));
     Rti tType = _castToRti(_Utils.arrayAt(tOptionalNamed, j + 1));
-    if (!_isSubtype(universe, tType, tEnv, sType, sEnv)) return false;
+    if (!_isSubtype(universe, tType, tEnv, sType, sEnv, isLegacy)) return false;
   }
 
   return true;
 }
 
-bool _isInterfaceSubtype(universe, Rti s, sEnv, Rti t, tEnv) {
+bool _isInterfaceSubtype(universe, Rti s, sEnv, Rti t, tEnv, bool isLegacy) {
   String sName = Rti._getInterfaceName(s);
   String tName = Rti._getInterfaceName(t);
 
@@ -2569,21 +2614,29 @@
         switch (sVariance) {
           case Variance.legacyCovariant:
           case Variance.covariant:
-            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv)) return false;
+            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv, isLegacy)) {
+              return false;
+            }
             break;
           case Variance.contravariant:
-            if (!_isSubtype(universe, tArg, tEnv, sArg, sEnv)) return false;
+            if (!_isSubtype(universe, tArg, tEnv, sArg, sEnv, isLegacy)) {
+              return false;
+            }
             break;
           case Variance.invariant:
-            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv) ||
-                !_isSubtype(universe, tArg, tEnv, sArg, sEnv)) return false;
+            if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv, isLegacy) ||
+                !_isSubtype(universe, tArg, tEnv, sArg, sEnv, isLegacy)) {
+              return false;
+            }
             break;
           default:
             throw StateError(
                 "Unknown variance given for subtype check: $sVariance");
         }
       } else {
-        if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv)) return false;
+        if (!_isSubtype(universe, sArg, sEnv, tArg, tEnv, isLegacy)) {
+          return false;
+        }
       }
     }
     return true;
@@ -2610,7 +2663,9 @@
     String recipe = _Utils.asString(_Utils.arrayAt(supertypeArgs, i));
     Rti supertypeArg = _Universe.evalInEnvironment(universe, s, recipe);
     Rti tArg = _castToRti(_Utils.arrayAt(tArgs, i));
-    if (!_isSubtype(universe, supertypeArg, sEnv, tArg, tEnv)) return false;
+    if (!_isSubtype(universe, supertypeArg, sEnv, tArg, tEnv, isLegacy)) {
+      return false;
+    }
   }
   return true;
 }
@@ -2620,10 +2675,10 @@
 ///
 /// We ignore renaming of bound type variables because we operate on de Bruijn
 /// indices, not names.
-bool typeEqual(Rti s, Rti t) {
+bool typeEqual(Rti s, Rti t, bool isLegacy) {
   if (_Utils.isIdentical(s, t)) return true;
 
-  if (isTopType(s)) return isTopType(t);
+  if (isTopType(s, isLegacy)) return isTopType(t, isLegacy);
 
   int sKind = Rti._getKind(s);
   int tKind = Rti._getKind(t);
@@ -2633,46 +2688,49 @@
     case Rti.kindStar:
     case Rti.kindQuestion:
     case Rti.kindFutureOr:
-      return typeEqual(
-          _castToRti(Rti._getPrimary(s)), _castToRti(Rti._getPrimary(t)));
+      return typeEqual(_castToRti(Rti._getPrimary(s)),
+          _castToRti(Rti._getPrimary(t)), isLegacy);
 
     case Rti.kindInterface:
       if (Rti._getInterfaceName(s) != Rti._getInterfaceName(t)) return false;
-      return typesEqual(
-          Rti._getInterfaceTypeArguments(s), Rti._getInterfaceTypeArguments(t));
+      return typesEqual(Rti._getInterfaceTypeArguments(s),
+          Rti._getInterfaceTypeArguments(t), isLegacy);
 
     case Rti.kindBinding:
-      return typeEqual(Rti._getBindingBase(s), Rti._getBindingBase(t)) &&
-          typesEqual(Rti._getBindingArguments(s), Rti._getBindingArguments(t));
+      return typeEqual(
+              Rti._getBindingBase(s), Rti._getBindingBase(t), isLegacy) &&
+          typesEqual(Rti._getBindingArguments(s), Rti._getBindingArguments(t),
+              isLegacy);
 
     case Rti.kindFunction:
-      return typeEqual(Rti._getReturnType(s), Rti._getReturnType(t)) &&
-          functionParametersEqual(
-              Rti._getFunctionParameters(s), Rti._getFunctionParameters(t));
+      return typeEqual(
+              Rti._getReturnType(s), Rti._getReturnType(t), isLegacy) &&
+          functionParametersEqual(Rti._getFunctionParameters(s),
+              Rti._getFunctionParameters(t), isLegacy);
 
     case Rti.kindGenericFunction:
-      return typeEqual(
-              Rti._getGenericFunctionBase(s), Rti._getGenericFunctionBase(t)) &&
+      return typeEqual(Rti._getGenericFunctionBase(s),
+              Rti._getGenericFunctionBase(t), isLegacy) &&
           typesEqual(Rti._getGenericFunctionBounds(s),
-              Rti._getGenericFunctionBounds(t));
+              Rti._getGenericFunctionBounds(t), isLegacy);
 
     default:
       return false;
   }
 }
 
-bool typesEqual(Object sArray, Object tArray) {
+bool typesEqual(Object sArray, Object tArray, isLegacy) {
   int sLength = _Utils.arrayLength(sArray);
   int tLength = _Utils.arrayLength(tArray);
   if (sLength != tLength) return false;
   for (int i = 0; i < sLength; i++) {
     if (!typeEqual(_castToRti(_Utils.arrayAt(sArray, i)),
-        _castToRti(_Utils.arrayAt(tArray, i)))) return false;
+        _castToRti(_Utils.arrayAt(tArray, i)), isLegacy)) return false;
   }
   return true;
 }
 
-bool namedTypesEqual(Object sArray, Object tArray) {
+bool namedTypesEqual(Object sArray, Object tArray, isLegacy) {
   int sLength = _Utils.arrayLength(sArray);
   int tLength = _Utils.arrayLength(tArray);
   assert(sLength.isEven);
@@ -2682,34 +2740,43 @@
     if (_Utils.asString(_Utils.arrayAt(sArray, i)) !=
         _Utils.asString(_Utils.arrayAt(tArray, i))) return false;
     if (!typeEqual(_castToRti(_Utils.arrayAt(sArray, i + 1)),
-        _castToRti(_Utils.arrayAt(tArray, i + 1)))) return false;
+        _castToRti(_Utils.arrayAt(tArray, i + 1)), isLegacy)) return false;
   }
   return true;
 }
 
 // TODO(fishythefish): Support required named parameters.
-bool functionParametersEqual(
-        _FunctionParameters sParameters, _FunctionParameters tParameters) =>
+bool functionParametersEqual(_FunctionParameters sParameters,
+        _FunctionParameters tParameters, isLegacy) =>
     typesEqual(_FunctionParameters._getRequiredPositional(sParameters),
-        _FunctionParameters._getRequiredPositional(tParameters)) &&
+        _FunctionParameters._getRequiredPositional(tParameters), isLegacy) &&
     typesEqual(_FunctionParameters._getOptionalPositional(sParameters),
-        _FunctionParameters._getOptionalPositional(tParameters)) &&
+        _FunctionParameters._getOptionalPositional(tParameters), isLegacy) &&
     namedTypesEqual(_FunctionParameters._getOptionalNamed(sParameters),
-        _FunctionParameters._getOptionalNamed(tParameters));
+        _FunctionParameters._getOptionalNamed(tParameters), isLegacy);
 
-// TODO(fishythefish): Update for NNBD - check for `Object?` instead of
-// `Object`.
-bool isTopType(Rti t) {
-  if (isObjectType(t)) return true;
+bool isLegacyTopType(Rti t) => isTopType(t, true);
+bool isNnbdTopType(Rti t) => isTopType(t, false);
+bool isTopType(Rti t, bool isLegacy) {
+  if (isLegacy) {
+    if (isObjectType(t)) return true;
+  } else {
+    if (isNullableObjectType(t)) return true;
+  }
   int kind = Rti._getKind(t);
   return kind == Rti.kindDynamic ||
       kind == Rti.kindVoid ||
       kind == Rti.kindAny ||
       kind == Rti.kindErased ||
-      kind == Rti.kindFutureOr && isTopType(Rti._getFutureOrArgument(t));
+      kind == Rti.kindFutureOr &&
+          isTopType(Rti._getFutureOrArgument(t), isLegacy);
 }
 
 bool isObjectType(Rti t) => _Utils.isIdentical(t, TYPE_REF<Object>());
+// TODO(fishythefish): Use `TYPE_REF<Object?>()`.
+bool isNullableObjectType(Rti t) =>
+    Rti._getKind(t) == Rti.kindQuestion &&
+    isObjectType(Rti._getQuestionArgument(t));
 bool isNullType(Rti t) => _Utils.isIdentical(t, TYPE_REF<Null>());
 bool isFunctionType(Rti t) => _Utils.isIdentical(t, TYPE_REF<Function>());
 bool isJsFunctionType(Rti t) =>
@@ -2808,8 +2875,8 @@
   _Universe.addTypeParameterVariances(universe, variances);
 }
 
-bool testingIsSubtype(universe, rti1, rti2) {
-  return isSubtype(universe, _castToRti(rti1), _castToRti(rti2));
+bool testingIsLegacySubtype(universe, rti1, rti2) {
+  return isLegacySubtype(universe, _castToRti(rti1), _castToRti(rti2));
 }
 
 Object testingUniverseEval(universe, String recipe) {
diff --git a/sdk_nnbd/lib/_internal/sdk_library_metadata/lib/libraries.dart b/sdk_nnbd/lib/_internal/sdk_library_metadata/lib/libraries.dart
index 523a0c1..b8e584b 100644
--- a/sdk_nnbd/lib/_internal/sdk_library_metadata/lib/libraries.dart
+++ b/sdk_nnbd/lib/_internal/sdk_library_metadata/lib/libraries.dart
@@ -91,10 +91,11 @@
       categories: "Client,Server",
       maturity: Maturity.STABLE,
       dart2jsPatchPath: "_internal/js_runtime/lib/isolate_patch.dart"),
-  "js": const LibraryInfo("js/dart2js/js_dart2js.dart",
+  "js": const LibraryInfo("js/js.dart",
       categories: "Client",
       maturity: Maturity.STABLE,
-      platforms: DART2JS_PLATFORM),
+      platforms: DART2JS_PLATFORM,
+      dart2jsPatchPath: "_internal/js_runtime/lib/js_patch.dart"),
   "_js": const LibraryInfo("js/_js.dart",
       categories: "Client",
       dart2jsPatchPath: "js/_js_client.dart",
diff --git a/sdk_nnbd/lib/async/stream_controller.dart b/sdk_nnbd/lib/async/stream_controller.dart
index 7861fb8..31fc5c3 100644
--- a/sdk_nnbd/lib/async/stream_controller.dart
+++ b/sdk_nnbd/lib/async/stream_controller.dart
@@ -515,7 +515,7 @@
   _PendingEvents<T>? get _pendingEvents {
     assert(_isInitialState);
     if (!_isAddingStream) {
-      return _varData;
+      return _varData as _PendingEvents<T>?;
     }
     var state = _varData as _StreamControllerAddStreamState<T>;
     return state.varData;
@@ -679,7 +679,7 @@
     _ControllerSubscription<T> subscription = _ControllerSubscription<T>(
         this, onData, onError, onDone, cancelOnError);
 
-    _PendingEvents<T> pendingEvents = _pendingEvents;
+    _PendingEvents<T>? pendingEvents = _pendingEvents;
     _state |= _STATE_SUBSCRIBED;
     if (_isAddingStream) {
       var addState = _varData as _StreamControllerAddStreamState<T>;
diff --git a/sdk_nnbd/lib/async/zone.dart b/sdk_nnbd/lib/async/zone.dart
index 8be575d1..31644f2 100644
--- a/sdk_nnbd/lib/async/zone.dart
+++ b/sdk_nnbd/lib/async/zone.dart
@@ -110,16 +110,14 @@
       ForkHandler? fork}) {
     return new ZoneSpecification(
         handleUncaughtError: handleUncaughtError ?? other.handleUncaughtError,
-        // TODO(#39534) Cleanup casts to dynamic when CFE can find the LUB.
-        run: (run ?? other.run) as dynamic,
-        runUnary: (runUnary ?? other.runUnary) as dynamic,
-        runBinary: (runBinary ?? other.runBinary) as dynamic,
-        registerCallback:
-            (registerCallback ?? other.registerCallback) as dynamic,
+        run: run ?? other.run,
+        runUnary: runUnary ?? other.runUnary,
+        runBinary: runBinary ?? other.runBinary,
+        registerCallback: registerCallback ?? other.registerCallback,
         registerUnaryCallback:
-            (registerUnaryCallback ?? other.registerUnaryCallback) as dynamic,
+            registerUnaryCallback ?? other.registerUnaryCallback,
         registerBinaryCallback:
-            (registerBinaryCallback ?? other.registerBinaryCallback) as dynamic,
+            registerBinaryCallback ?? other.registerBinaryCallback,
         errorCallback: errorCallback ?? other.errorCallback,
         scheduleMicrotask: scheduleMicrotask ?? other.scheduleMicrotask,
         createTimer: createTimer ?? other.createTimer,
diff --git a/sdk_nnbd/lib/js/js.dart b/sdk_nnbd/lib/js/js.dart
new file mode 100644
index 0000000..139823d
--- /dev/null
+++ b/sdk_nnbd/lib/js/js.dart
@@ -0,0 +1,235 @@
+// Copyright (c) 2013, 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.6
+
+/// Low-level support for interoperating with JavaScript.
+///
+/// You should usually use `package:js` instead of this library. For more
+/// information, see the [JS interop page](https://dart.dev/web/js-interop).
+///
+/// This library provides access to JavaScript objects from Dart, allowing
+/// Dart code to get and set properties, and call methods of JavaScript objects
+/// and invoke JavaScript functions. The library takes care of converting
+/// between Dart and JavaScript objects where possible, or providing proxies if
+/// conversion isn't possible.
+///
+/// This library does not make Dart objects usable from JavaScript, their
+/// methods and properties are not accessible, though it does allow Dart
+/// functions to be passed into and called from JavaScript.
+///
+/// [JsObject] is the core type and represents a proxy of a JavaScript object.
+/// JsObject gives access to the underlying JavaScript objects properties and
+/// methods. `JsObject`s can be acquired by calls to JavaScript, or they can be
+/// created from proxies to JavaScript constructors.
+///
+/// The top-level getter [context] provides a [JsObject] that represents the
+/// global object in JavaScript, usually `window`.
+///
+/// The following example shows an alert dialog via a JavaScript call to the
+/// global function `alert()`:
+///
+///     import 'dart:js';
+///
+///     main() => context.callMethod('alert', ['Hello from Dart!']);
+///
+/// This example shows how to create a [JsObject] from a JavaScript constructor
+/// and access its properties:
+///
+///     import 'dart:js';
+///
+///     main() {
+///       var object = JsObject(context['Object']);
+///       object['greeting'] = 'Hello';
+///       object['greet'] = (name) => "${object['greeting']} $name";
+///       var message = object.callMethod('greet', ['JavaScript']);
+///       context['console'].callMethod('log', [message]);
+///     }
+///
+/// ## Proxying and automatic conversion
+///
+/// When setting properties on a JsObject or passing arguments to a Javascript
+/// method or function, Dart objects are automatically converted or proxied to
+/// JavaScript objects. When accessing JavaScript properties, or when a Dart
+/// closure is invoked from JavaScript, the JavaScript objects are also
+/// converted to Dart.
+///
+/// Functions and closures are proxied in such a way that they are callable. A
+/// Dart closure assigned to a JavaScript property is proxied by a function in
+/// JavaScript. A JavaScript function accessed from Dart is proxied by a
+/// [JsFunction], which has a [JsFunction.apply] method to invoke it.
+///
+/// The following types are transferred directly and not proxied:
+///
+///   * Basic types: `null`, `bool`, `num`, `String`, `DateTime`
+///   * `TypedData`, including its subclasses like `Int32List`, but _not_
+///     `ByteBuffer`
+///   * When compiling for the web, also: `Blob`, `Event`, `ImageData`,
+///     `KeyRange`, `Node`, and `Window`.
+///
+/// ## Converting collections with JsObject.jsify()
+///
+/// To create a JavaScript collection from a Dart collection use the
+/// [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s
+/// into JavaScript Objects and Arrays.
+///
+/// The following expression creates a new JavaScript object with the properties
+/// `a` and `b` defined:
+///
+///     var jsMap = JsObject.jsify({'a': 1, 'b': 2});
+///
+/// This expression creates a JavaScript array:
+///
+///     var jsArray = JsObject.jsify([1, 2, 3]);
+///
+/// {@category Web}
+library dart.js;
+
+import 'dart:collection' show ListMixin;
+
+/// The JavaScript global object, usually `window`.
+external JsObject get context;
+
+/// A proxy on a JavaScript object.
+///
+/// The properties of the JavaScript object are accessible via the `[]` and
+/// `[]=` operators. Methods are callable via [callMethod].
+class JsObject {
+  /// Constructs a JavaScript object from its native [constructor] and returns
+  /// a proxy to it.
+  external factory JsObject(JsFunction constructor, [List arguments]);
+
+  /// Constructs a [JsObject] that proxies a native Dart object; _for expert use
+  /// only_.
+  ///
+  /// Use this constructor only if you wish to get access to JavaScript
+  /// properties attached to a browser host object, such as a Node or Blob, that
+  /// is normally automatically converted into a native Dart object.
+  ///
+  /// An exception will be thrown if [object] either is `null` or has the type
+  /// `bool`, `num`, or `String`.
+  external factory JsObject.fromBrowserObject(object);
+
+  /// Recursively converts a JSON-like collection of Dart objects to a
+  /// collection of JavaScript objects and returns a [JsObject] proxy to it.
+  ///
+  /// [object] must be a [Map] or [Iterable], the contents of which are also
+  /// converted. Maps and Iterables are copied to a new JavaScript object.
+  /// Primitives and other transferable values are directly converted to their
+  /// JavaScript type, and all other objects are proxied.
+  external factory JsObject.jsify(object);
+
+  /// Returns the value associated with [property] from the proxied JavaScript
+  /// object.
+  ///
+  /// The type of [property] must be either [String] or [num].
+  external dynamic operator [](property);
+
+  // Sets the value associated with [property] on the proxied JavaScript
+  // object.
+  //
+  // The type of [property] must be either [String] or [num].
+  external void operator []=(property, value);
+
+  int get hashCode => 0;
+
+  external bool operator ==(other);
+
+  /// Returns `true` if the JavaScript object contains the specified property
+  /// either directly or though its prototype chain.
+  ///
+  /// This is the equivalent of the `in` operator in JavaScript.
+  external bool hasProperty(property);
+
+  /// Removes [property] from the JavaScript object.
+  ///
+  /// This is the equivalent of the `delete` operator in JavaScript.
+  external void deleteProperty(property);
+
+  /// Returns `true` if the JavaScript object has [type] in its prototype chain.
+  ///
+  /// This is the equivalent of the `instanceof` operator in JavaScript.
+  external bool instanceof(JsFunction type);
+
+  /// Returns the result of the JavaScript objects `toString` method.
+  external String toString();
+
+  /// Calls [method] on the JavaScript object with the arguments [args] and
+  /// returns the result.
+  ///
+  /// The type of [method] must be either [String] or [num].
+  external dynamic callMethod(method, [List args]);
+}
+
+/// A proxy on a JavaScript Function object.
+class JsFunction extends JsObject {
+  /// Returns a [JsFunction] that captures its 'this' binding and calls [f]
+  /// with the value of JavaScript `this` passed as the first argument.
+  external factory JsFunction.withThis(Function f);
+
+  /// Invokes the JavaScript function with arguments [args]. If [thisArg] is
+  /// supplied it is the value of `this` for the invocation.
+  external dynamic apply(List args, {thisArg});
+}
+
+/// A [List] that proxies a JavaScript array.
+class JsArray<E> extends JsObject with ListMixin<E> {
+  /// Creates an empty JavaScript array.
+  external factory JsArray();
+
+  /// Creates a new JavaScript array and initializes it to the contents of
+  /// [other].
+  external factory JsArray.from(Iterable<E> other);
+
+  // Methods required by ListMixin
+
+  external E operator [](dynamic index);
+
+  external void operator []=(dynamic index, E value);
+
+  external int get length;
+
+  external void set length(int length);
+
+  // Methods overridden for better performance
+
+  external void add(E value);
+
+  external void addAll(Iterable<E> iterable);
+
+  external void insert(int index, E element);
+
+  external E removeAt(int index);
+
+  external E removeLast();
+
+  external void removeRange(int start, int end);
+
+  external void setRange(int start, int end, Iterable<E> iterable,
+      [int skipCount = 0]);
+
+  external void sort([int compare(E a, E b)]);
+}
+
+/// Returns a wrapper around function [f] that can be called from JavaScript
+/// using `package:js` JavaScript interop.
+///
+/// The calling conventions in Dart2Js differ from JavaScript and so, by
+/// default, it is not possible to call a Dart function directly. Wrapping with
+/// `allowInterop` creates a function that can be called from JavaScript or
+/// Dart. The semantics of the wrapped function are still more strict than
+/// JavaScript, and the function will throw if called with too many or too few
+/// arguments.
+///
+/// Calling this method repeatedly on a function will return the same result.
+external F allowInterop<F extends Function>(F f);
+
+/// Returns a wrapper around function [f] that can be called from JavaScript
+/// using `package:js` JavaScript interop, passing JavaScript `this` as the first
+/// argument.
+///
+/// See [allowInterop].
+///
+/// When called from Dart, [null] will be passed as the first argument.
+external Function allowInteropCaptureThis(Function f);
diff --git a/sdk_nnbd/lib/js/js_sources.gni b/sdk_nnbd/lib/js/js_sources.gni
index 91f6d0c..6308b38 100644
--- a/sdk_nnbd/lib/js/js_sources.gni
+++ b/sdk_nnbd/lib/js/js_sources.gni
@@ -2,4 +2,4 @@
 # 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.
 
-js_sdk_sources = [ "dart2js/js_dart2js.dart" ]
+js_sdk_sources = [ "js.dart" ]
diff --git a/sdk_nnbd/lib/libraries.json b/sdk_nnbd/lib/libraries.json
index e8107ef..3391292 100644
--- a/sdk_nnbd/lib/libraries.json
+++ b/sdk_nnbd/lib/libraries.json
@@ -203,7 +203,8 @@
         "supported": false
       },
       "js": {
-        "uri": "../../sdk/lib/js/dart2js/js_dart2js.dart"
+        "uri": "../../sdk/lib/js/js.dart",
+        "patches": "../../sdk/lib/_internal/js_runtime/lib/js_patch.dart"
       },
       "_js": {
         "uri": "../../sdk/lib/js/_js.dart",
@@ -312,7 +313,8 @@
         "supported": false
       },
       "js": {
-        "uri": "../../sdk/lib/js/dart2js/js_dart2js.dart"
+        "uri": "../../sdk/lib/js/js.dart",
+        "patches": "../../sdk/lib/_internal/js_runtime/lib/js_patch.dart"
       },
       "_js": {
         "uri": "../../sdk/lib/js/_js.dart",
@@ -462,7 +464,8 @@
         "uri": "indexed_db/dart2js/indexed_db_dart2js.dart"
       },
       "js": {
-        "uri": "_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart"
+        "uri": "js/js.dart",
+        "patches": "_internal/js_dev_runtime/patch/js_patch.dart"
       },
       "js_util": {
         "uri": "_internal/js_dev_runtime/lib/js_util/dart2js/js_util_dart2js.dart"
diff --git a/sdk_nnbd/lib/libraries.yaml b/sdk_nnbd/lib/libraries.yaml
index 6fbbdff..1aa29b8 100644
--- a/sdk_nnbd/lib/libraries.yaml
+++ b/sdk_nnbd/lib/libraries.yaml
@@ -200,7 +200,8 @@
       supported: false
 
     js:
-      uri: "../../sdk/lib/js/dart2js/js_dart2js.dart"
+      uri: "../../sdk/lib/js/js.dart"
+      patches: "../../sdk/lib/_internal/js_runtime/lib/js_patch.dart"
 
     _js:
       uri: "../../sdk/lib/js/_js.dart"
@@ -307,7 +308,8 @@
       supported: false
 
     js:
-      uri: "../../sdk/lib/js/dart2js/js_dart2js.dart"
+      uri: "../../sdk/lib/js/js.dart"
+      patches: "../../sdk/lib/_internal/js_runtime/lib/js_patch.dart"
 
     _js:
       uri: "../../sdk/lib/js/_js.dart"
@@ -455,7 +457,8 @@
         uri: "indexed_db/dart2js/indexed_db_dart2js.dart"
 
       js:
-        uri: "_internal/js_dev_runtime/lib/js/dart2js/js_dart2js.dart"
+        uri: "js/js.dart"
+        patches: "_internal/js_dev_runtime/patch/js_patch.dart"
 
       js_util:
         uri: "_internal/js_dev_runtime/lib/js_util/dart2js/js_util_dart2js.dart"
diff --git a/tests/compiler/dart2js/analyses/api_allowed.json b/tests/compiler/dart2js/analyses/api_allowed.json
index b988fc0..062db80 100644
--- a/tests/compiler/dart2js/analyses/api_allowed.json
+++ b/tests/compiler/dart2js/analyses/api_allowed.json
@@ -169,7 +169,7 @@
   "org-dartlang-sdk:///sdk/lib/io/common.dart": {
     "Dynamic invocation of '[]'.": 3
   },
-  "org-dartlang-sdk:///sdk/lib/js/dart2js/js_dart2js.dart": {
+  "org-dartlang-sdk:///sdk/lib/_internal/js_runtime/lib/js_patch.dart": {
     "Dynamic invocation of '[]'.": 1
   },
   "org-dartlang-sdk:///sdk/lib/svg/dart2js/svg_dart2js.dart": {
@@ -225,4 +225,4 @@
     "Dynamic access of 'port'.": 1,
     "Dynamic invocation of 'dart._http::_toJSON'.": 1
   }
-}
+}
\ No newline at end of file
diff --git a/tests/compiler/dart2js/inference/data/closurization_instance_call.dart b/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
index 4f785bf..d5eabac 100644
--- a/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
@@ -24,7 +24,7 @@
 /*member: closurizedCallToString:[exact=JSString]*/
 closurizedCallToString() {
   var c = new Class();
-  c.call(); // Make `Class.call` live.
+  c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
   var local = c. /*[exact=Class]*/ method;
   local. /*invoke: [subclass=Closure]*/ toString();
   local();
diff --git a/tests/compiler/dart2js/inference/data/closurization_local_call.dart b/tests/compiler/dart2js/inference/data/closurization_local_call.dart
index 65439e1..19c5758 100644
--- a/tests/compiler/dart2js/inference/data/closurization_local_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_local_call.dart
@@ -21,7 +21,7 @@
 /*member: closurizedCallToString:[exact=JSString]*/
 closurizedCallToString() {
   var c = new Class();
-  c.call(); // Make `Class.call` live.
+  c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
   var local = /*[exact=JSUInt31]*/ () => 42;
   local. /*invoke: [subclass=Closure]*/ toString();
   local();
diff --git a/tests/compiler/dart2js/inference/data/closurization_static_call.dart b/tests/compiler/dart2js/inference/data/closurization_static_call.dart
index a29e594..575f68d 100644
--- a/tests/compiler/dart2js/inference/data/closurization_static_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_static_call.dart
@@ -24,7 +24,7 @@
 /*member: closurizedCallToString:[exact=JSString]*/
 closurizedCallToString() {
   var c = new Class();
-  c.call(); // Make `Class.call` live.
+  c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
   var local = method;
   local. /*invoke: [subclass=Closure]*/ toString();
   local();
diff --git a/tests/compiler/dart2js/inference/data/do.dart b/tests/compiler/dart2js/inference/data/do.dart
index f900e4b..5a7ffac 100644
--- a/tests/compiler/dart2js/inference/data/do.dart
+++ b/tests/compiler/dart2js/inference/data/do.dart
@@ -47,7 +47,7 @@
   var o = '';
   do {
     o = o. /*invoke: [exact=JSString]*/ toString();
-  } while (o != null);
+  } while (o /*invoke: [null|exact=JSString]*/ != null);
   return o;
 }
 
@@ -60,7 +60,7 @@
   var o = '';
   do {
     o = o. /*invoke: [exact=JSString]*/ toString();
-  } while (o == null);
+  } while (o /*invoke: [null|exact=JSString]*/ == null);
   return o;
 }
 
diff --git a/tests/compiler/dart2js/inference/data/for.dart b/tests/compiler/dart2js/inference/data/for.dart
index 72cc8bb..5af242e 100644
--- a/tests/compiler/dart2js/inference/data/for.dart
+++ b/tests/compiler/dart2js/inference/data/for.dart
@@ -47,7 +47,9 @@
 /*member: forNotNull:[null|exact=JSString]*/
 forNotNull() {
   var local;
-  for (var o = ''; o != null; o = o. /*invoke: [exact=JSString]*/ toString()) {
+  for (var o = '';
+      o /*invoke: [null|exact=JSString]*/ != null;
+      o = o. /*invoke: [exact=JSString]*/ toString()) {
     local = o;
   }
   return local;
@@ -60,7 +62,9 @@
 /*member: forNullFalse:[null]*/
 forNullFalse() {
   var local;
-  for (var o = ''; o == null; o = o. /*invoke: [null]*/ toString()) {
+  for (var o = '';
+      o /*invoke: [null|exact=JSString]*/ == null;
+      o = o. /*invoke: [null]*/ toString()) {
     local = o;
   }
   return local;
diff --git a/tests/compiler/dart2js/inference/data/general.dart b/tests/compiler/dart2js/inference/data/general.dart
index b73d97f..f858024 100644
--- a/tests/compiler/dart2js/inference/data/general.dart
+++ b/tests/compiler/dart2js/inference/data/general.dart
@@ -675,7 +675,8 @@
   A.generative();
 
   /*member: A.==:[exact=JSBool]*/
-  operator ==(/*[null|subclass=Object]*/ other) => 42 as dynamic;
+  operator ==(/*Union([exact=JSString], [exact=JSUInt31])*/ other) =>
+      42 as dynamic;
 
   /*member: A.myField:[exact=JSUInt31]*/
   get myField => 42;
@@ -892,7 +893,7 @@
   testDoWhile2();
   testDoWhile3();
   testDoWhile4();
-  new A() == null;
+  new A() /*invoke: [null|subclass=A]*/ == null;
   new A()
     .. /*invoke: [exact=A]*/ returnInt1()
     .. /*invoke: [exact=A]*/ returnInt2()
diff --git a/tests/compiler/dart2js/inference/data/narrowing.dart b/tests/compiler/dart2js/inference/data/narrowing.dart
index ba26c79..27c041d 100644
--- a/tests/compiler/dart2js/inference/data/narrowing.dart
+++ b/tests/compiler/dart2js/inference/data/narrowing.dart
@@ -13,7 +13,7 @@
 /*member: nonNull1:[null]*/
 void nonNull1() {
   var x = 1;
-  if (x == null) return;
+  if (x /*invoke: [null|subclass=JSInt]*/ == null) return;
   argIsNonNull1(x);
 }
 
@@ -25,7 +25,9 @@
 /*member: nonNull2:[null]*/
 void nonNull2() {
   var x = 1;
-  if ((x == null) /*invoke: [exact=JSBool]*/ == true) return;
+  if ((x /*invoke: [null|subclass=JSInt]*/ ==
+          null) /*invoke: [exact=JSBool]*/ ==
+      true) return;
   argIsNonNull2(x);
 }
 
@@ -37,7 +39,9 @@
 /*member: nonNull3:[null]*/
 void nonNull3() {
   var x = 1;
-  if ((x == null) /*invoke: [exact=JSBool]*/ != false) return;
+  if ((x /*invoke: [null|subclass=JSInt]*/ ==
+          null) /*invoke: [exact=JSBool]*/ !=
+      false) return;
   argIsNonNull3(x);
 }
 
@@ -52,7 +56,7 @@
 /*member: nonNull4:[null]*/
 void nonNull4() {
   var x = 1;
-  if (discard(x != null)) return;
+  if (discard(x /*invoke: [null|subclass=JSInt]*/ != null)) return;
   argIsNonNull4(x);
 }
 
@@ -64,7 +68,7 @@
 /*member: nonNull5:[null]*/
 void nonNull5() {
   var x = 1;
-  if (x != null ? false : false) return;
+  if (x /*invoke: [null|subclass=JSInt]*/ != null ? false : false) return;
   argIsNonNull5(x);
 }
 
@@ -76,8 +80,8 @@
 /*member: nonNull6:[null]*/
 void nonNull6() {
   var x = 1;
-  if ((/*[exact=JSBool]*/ (/*[exact=JSBool]*/ y) => y && false)(x != null))
-    return;
+  if ((/*[exact=JSBool]*/ (/*[exact=JSBool]*/ y) =>
+      y && false)(x /*invoke: [null|subclass=JSInt]*/ != null)) return;
   argIsNonNull6(x);
 }
 
@@ -90,7 +94,7 @@
 void nonNull7() {
   var f = false;
   var x = 1;
-  if (f ? (throw x != null) : false) return;
+  if (f ? (throw x /*invoke: [null|subclass=JSInt]*/ != null) : false) return;
   argIsNonNull7(x);
 }
 
@@ -103,7 +107,7 @@
 void nonNull8() {
   var f = false;
   var x = 1;
-  if (f ?? (x != null)) return;
+  if (f ?? (x /*invoke: [null|subclass=JSInt]*/ != null)) return;
   argIsNonNull8(x);
 }
 
diff --git a/tests/compiler/dart2js/inference/data/null.dart b/tests/compiler/dart2js/inference/data/null.dart
index 9e53378..05fd76b 100644
--- a/tests/compiler/dart2js/inference/data/null.dart
+++ b/tests/compiler/dart2js/inference/data/null.dart
@@ -24,7 +24,7 @@
 
 /*member: ifThenNullCheck:[exact=JSUInt31]*/
 ifThenNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value == null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ == null) {
     return 0;
   }
   return value;
@@ -32,7 +32,7 @@
 
 /*member: ifThenElseNullCheck:[exact=JSUInt31]*/
 ifThenElseNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value == null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ == null) {
     return 0;
   } else {
     return value;
@@ -41,7 +41,7 @@
 
 /*member: ifNotThenNullCheck:[exact=JSUInt31]*/
 ifNotThenNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value != null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ != null) {
     return value;
   }
   return 0;
@@ -49,7 +49,7 @@
 
 /*member: ifNotThenElseNullCheck:[exact=JSUInt31]*/
 ifNotThenElseNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value != null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ != null) {
     return value;
   } else {
     return 0;
@@ -59,7 +59,8 @@
 /*member: ifThenNotNullComplexCheck:[exact=JSUInt31]*/
 ifThenNotNullComplexCheck(
     int /*[null|exact=JSUInt31]*/ a, int /*[null|exact=JSUInt31]*/ b) {
-  if (a != null && a /*invoke: [exact=JSUInt31]*/ != b) {
+  if (a /*invoke: [null|subclass=JSInt]*/ != null &&
+      a /*invoke: [exact=JSUInt31]*/ != b) {
     return a;
   }
   return 0;
@@ -68,7 +69,8 @@
 /*member: ifThenElseNotNullComplexCheck:[null|exact=JSUInt31]*/
 ifThenElseNotNullComplexCheck(
     int /*[null|exact=JSUInt31]*/ a, int /*[null|exact=JSUInt31]*/ b) {
-  if (a != null && a /*invoke: [exact=JSUInt31]*/ != b) {
+  if (a /*invoke: [null|subclass=JSInt]*/ != null &&
+      a /*invoke: [exact=JSUInt31]*/ != b) {
     return a;
   }
   return a;
@@ -78,7 +80,7 @@
 ifThenNotNullGradualCheck1(
     int /*[null|exact=JSUInt31]*/ a, int /*[exact=JSUInt31]*/ b) {
   if (a /*invoke: [null|exact=JSUInt31]*/ != b) {
-    if (a != null) {
+    if (a /*invoke: [null|subclass=JSInt]*/ != null) {
       return a;
     }
   }
@@ -88,7 +90,7 @@
 /*member: ifThenNotNullGradualCheck2:[exact=JSUInt31]*/
 ifThenNotNullGradualCheck2(
     int /*[null|exact=JSUInt31]*/ a, int /*[exact=JSUInt31]*/ b) {
-  if (a != null) {
+  if (a /*invoke: [null|subclass=JSInt]*/ != null) {
     if (a /*invoke: [exact=JSUInt31]*/ != b) {
       return a;
     }
diff --git a/tests/compiler/dart2js/inference/data/while.dart b/tests/compiler/dart2js/inference/data/while.dart
index 4a711e2..73b214e 100644
--- a/tests/compiler/dart2js/inference/data/while.dart
+++ b/tests/compiler/dart2js/inference/data/while.dart
@@ -49,7 +49,7 @@
 /*member: whileNotNull:[exact=JSString]*/
 whileNotNull() {
   var o = '';
-  while (o != null) {
+  while (o /*invoke: [null|exact=JSString]*/ != null) {
     o = o. /*invoke: [exact=JSString]*/ toString();
   }
   return o;
@@ -62,7 +62,7 @@
 /*member: whileNullUnreachable:[exact=JSString]*/
 whileNullUnreachable() {
   var o = '';
-  while (o == null) {
+  while (o /*invoke: [null|exact=JSString]*/ == null) {
     o = o. /*invoke: [null]*/ toString();
   }
   return o;
diff --git a/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart b/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
index d984418..884773e 100644
--- a/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
+++ b/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
@@ -84,7 +84,7 @@
   Class.constructorNotCalledInForLoop();
 
   // TODO(johnniwinther): Should we track instance calls in loops?
-  /*member: Class.instanceCalledInForLoop:*/
+  /*member: Class.instanceCalledInForLoop:loop*/
   instanceCalledInForLoop() {}
 
   /*member: Class.instanceNotCalledInForLoop:*/
diff --git a/tests/compiler/dart2js/inference/inference_data_test.dart b/tests/compiler/dart2js/inference/inference_data_test.dart
index e89e33e..c9aad1d 100644
--- a/tests/compiler/dart2js/inference/inference_data_test.dart
+++ b/tests/compiler/dart2js/inference/inference_data_test.dart
@@ -24,6 +24,7 @@
     await checkTests(dataDir, const InferenceDataComputer(),
         args: args,
         testedConfigs: [strongConfig],
+        supportedMarkers: [strongMarker],
         options: [stopAfterTypeInference]);
   });
 }
diff --git a/tests/compiler/dart2js/inference/inference_test_helper.dart b/tests/compiler/dart2js/inference/inference_test_helper.dart
index 06da02c..d0b6caa 100644
--- a/tests/compiler/dart2js/inference/inference_test_helper.dart
+++ b/tests/compiler/dart2js/inference/inference_test_helper.dart
@@ -146,11 +146,11 @@
       ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
       return getMemberValue(info.callMethod);
     } else if (node is ir.MethodInvocation) {
-      return getTypeMaskValue(result.typeOfSend(node));
+      return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.PropertyGet) {
-      return getTypeMaskValue(result.typeOfGetter(node));
+      return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.PropertySet) {
-      return getTypeMaskValue(result.typeOfSend(node));
+      return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.ForInStatement) {
       if (id.kind == IdKind.iterator) {
         return getTypeMaskValue(result.typeOfIterator(node));
diff --git a/tests/compiler/dart2js_extra/rti/subtype_utils.dart b/tests/compiler/dart2js_extra/rti/subtype_utils.dart
index dc4f608..f147dc7 100644
--- a/tests/compiler/dart2js_extra/rti/subtype_utils.dart
+++ b/tests/compiler/dart2js_extra/rti/subtype_utils.dart
@@ -12,20 +12,23 @@
 void strictSubtype(String s, String t) {
   var sRti = rti.testingUniverseEval(universe, s);
   var tRti = rti.testingUniverseEval(universe, t);
-  Expect.isTrue(rti.testingIsSubtype(universe, sRti, tRti), reason(s, t));
-  Expect.isFalse(rti.testingIsSubtype(universe, tRti, sRti), reason(t, s));
+  Expect.isTrue(rti.testingIsLegacySubtype(universe, sRti, tRti), reason(s, t));
+  Expect.isFalse(
+      rti.testingIsLegacySubtype(universe, tRti, sRti), reason(t, s));
 }
 
 void unrelated(String s, String t) {
   var sRti = rti.testingUniverseEval(universe, s);
   var tRti = rti.testingUniverseEval(universe, t);
-  Expect.isFalse(rti.testingIsSubtype(universe, sRti, tRti), reason(s, t));
-  Expect.isFalse(rti.testingIsSubtype(universe, tRti, sRti), reason(t, s));
+  Expect.isFalse(
+      rti.testingIsLegacySubtype(universe, sRti, tRti), reason(s, t));
+  Expect.isFalse(
+      rti.testingIsLegacySubtype(universe, tRti, sRti), reason(t, s));
 }
 
 void equivalent(String s, String t) {
   var sRti = rti.testingUniverseEval(universe, s);
   var tRti = rti.testingUniverseEval(universe, t);
-  Expect.isTrue(rti.testingIsSubtype(universe, sRti, tRti), reason(s, t));
-  Expect.isTrue(rti.testingIsSubtype(universe, tRti, sRti), reason(t, s));
+  Expect.isTrue(rti.testingIsLegacySubtype(universe, sRti, tRti), reason(s, t));
+  Expect.isTrue(rti.testingIsLegacySubtype(universe, tRti, sRti), reason(t, s));
 }
diff --git a/tests/language_2/extension_methods/static_extension_internal_basename_shadowing_error_test.dart b/tests/language_2/extension_methods/static_extension_internal_basename_shadowing_error_test.dart
index 297cfcd..04d90b6 100644
--- a/tests/language_2/extension_methods/static_extension_internal_basename_shadowing_error_test.dart
+++ b/tests/language_2/extension_methods/static_extension_internal_basename_shadowing_error_test.dart
@@ -53,7 +53,7 @@
     // The instance getter shadows the global method
     topLevelMethod(4);
 //  ^^^^^^^^^^^^^^
-// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
 //                ^
 // [cfe] The method 'call' isn't defined for the class 'int'.
   }
@@ -145,7 +145,7 @@
     // The static getter shadows the global method
     topLevelMethod(4);
 //  ^^^^^^^^^^^^^^
-// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
 //                   ^
 // [cfe] The method 'call' isn't defined for the class 'int'.
   }
@@ -227,7 +227,7 @@
     // The instance getter shadows the other extensions method
     extensionMethod(4);
 //  ^^^^^^^^^^^^^^^
-// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
 //                 ^
 // [cfe] The method 'call' isn't defined for the class 'int'.
   }
@@ -258,7 +258,7 @@
     // The instance getter shadows the other extensions method
     extensionMethod(4);
 //  ^^^^^^^^^^^^^^^
-// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
 // [cfe] 'extensionMethod' isn't a function or method and can't be invoked.
   }
 }
@@ -372,7 +372,7 @@
     // The static getter shadows the other extensions method
     extensionMethod(4);
 //  ^^^^^^^^^^^^^^^
-// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
 //                     ^
 // [cfe] The method 'call' isn't defined for the class 'int'.
   }
@@ -418,7 +418,7 @@
     // The static getter shadows the other extensions method
     extensionMethod(4);
 //  ^^^^^^^^^^^^^^^
-// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION
+// [analyzer] STATIC_TYPE_WARNING.INVOCATION_OF_NON_FUNCTION_EXPRESSION
 //                 ^
 // [cfe] The method 'call' isn't defined for the class 'int'.
   }
diff --git a/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart b/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart
index 6842e6b..dcb362a 100644
--- a/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart
+++ b/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart
@@ -259,8 +259,6 @@
       //        ^^^^^^^^^^^^^^^^^^^^^^
       // [analyzer] COMPILE_TIME_ERROR.AMBIGUOUS_EXTENSION_MEMBER_ACCESS
       // [cfe] The method 'methodInExtensionScope' isn't defined for the class 'B'.
-      //        ^^^^^^^^^^^^^^^^^^^^^^
-      // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
       checkExtensionValue(t2);
     }
 
diff --git a/tests/language_2/unsorted/mint_compares_test.dart b/tests/language_2/unsorted/mint_compares_test.dart
index aff8bd6..ef37d9a 100644
--- a/tests/language_2/unsorted/mint_compares_test.dart
+++ b/tests/language_2/unsorted/mint_compares_test.dart
@@ -80,6 +80,43 @@
   Expect.isTrue(gt(4294967296, -1));
 }
 
+compareTestWithZero(lt, lte, gt, gte, eq, ne) {
+  Expect.isFalse(lt(4294967296));
+  Expect.isFalse(lte(4294967296));
+  Expect.isTrue(gt(4294967296));
+  Expect.isTrue(gte(4294967296));
+  Expect.isFalse(eq(4294967296));
+  Expect.isTrue(ne(4294967296));
+
+  Expect.isTrue(lt(-1));
+  Expect.isTrue(lte(-1));
+  Expect.isFalse(gt(-1));
+  Expect.isFalse(gte(-1));
+  Expect.isFalse(eq(-1));
+  Expect.isTrue(ne(-1));
+
+  Expect.isTrue(lt(-2));
+  Expect.isTrue(lte(-2));
+  Expect.isFalse(gt(-2));
+  Expect.isFalse(gte(-2));
+  Expect.isFalse(eq(-2));
+  Expect.isTrue(ne(-2));
+
+  Expect.isTrue(lt(-4294967296));
+  Expect.isTrue(lte(-4294967296));
+  Expect.isFalse(gt(-4294967296));
+  Expect.isFalse(gte(-4294967296));
+  Expect.isFalse(eq(-4294967296));
+  Expect.isTrue(ne(-4294967296));
+
+  Expect.isFalse(lt(0));
+  Expect.isTrue(lte(0));
+  Expect.isFalse(gt(0));
+  Expect.isTrue(gte(0));
+  Expect.isTrue(eq(0));
+  Expect.isFalse(ne(0));
+}
+
 bool lt1(a, b) => a < b;
 bool lte1(a, b) => a <= b;
 bool gt1(a, b) => a > b;
@@ -90,10 +127,27 @@
 bool gt2(a, b) => a > b ? true : false;
 bool gte2(a, b) => a >= b ? true : false;
 
+bool int_lt1(int a) => a < 0;
+bool int_lte1(int a) => a <= 0;
+bool int_gt1(int a) => a > 0;
+bool int_gte1(int a) => a >= 0;
+bool int_eq1(int a) => a == 0;
+bool int_ne1(int a) => a != 0;
+
+bool int_lt2(int a) => a < 0 ? true : false;
+bool int_lte2(int a) => a <= 0 ? true : false;
+bool int_gt2(int a) => a > 0 ? true : false;
+bool int_gte2(int a) => a >= 0 ? true : false;
+bool int_eq2(int a) => a == 0 ? true : false;
+bool int_ne2(int a) => a != 0 ? true : false;
+
 main() {
   for (var i = 0; i < 20; i++) {
     compareTest();
     compareTest2(lt1, lte1, gt1, gte1);
     compareTest2(lt2, lte2, gt2, gte2);
+
+    compareTestWithZero(int_lt1, int_lte1, int_gt1, int_gte1, int_eq1, int_ne1);
+    compareTestWithZero(int_lt2, int_lte2, int_gt2, int_gte2, int_eq2, int_ne2);
   }
 }
diff --git a/tools/VERSION b/tools/VERSION
index 28a2d71..bc4e037 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -33,7 +33,7 @@
 MAJOR 2
 MINOR 8
 PATCH 0
-PRERELEASE 1
+PRERELEASE 2
 PRERELEASE_PATCH 0
 ABI_VERSION 27
 OLDEST_SUPPORTED_ABI_VERSION 27
diff --git a/tools/migration/bin/progress.dart b/tools/migration/bin/progress.dart
index 6b3b180..4a1c3dc 100644
--- a/tools/migration/bin/progress.dart
+++ b/tools/migration/bin/progress.dart
@@ -2,6 +2,10 @@
 // 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 'dart:io';
+
+import 'package:path/path.dart' as p;
+
 import 'package:migration/src/io.dart';
 import 'package:migration/src/test_directories.dart';
 
@@ -37,34 +41,46 @@
   var totalMigratedFiles = 0;
   var totalMigratedLines = 0;
 
-  for (var dir in legacyRootDirs) {
-    var files = 0;
-    var lines = 0;
-    var migratedFiles = 0;
-    var migratedLines = 0;
+  for (var rootDir in legacyRootDirs) {
+    var subdirs = Directory(p.join(testRoot, rootDir))
+        .listSync()
+        .where((subdir) => subdir is Directory)
+        .map((subdir) => p.relative(subdir.path, from: testRoot))
+        .toList();
+    subdirs.add(rootDir);
+    subdirs.sort();
 
-    for (var legacyPath in listFiles(dir)) {
-      if (!_includeNonCoreLibs && _nonCoreLibs.any(legacyPath.startsWith)) {
-        continue;
+    for (var dir in subdirs) {
+      var files = 0;
+      var lines = 0;
+      var migratedFiles = 0;
+      var migratedLines = 0;
+
+      for (var legacyPath in listFiles(dir)) {
+        if (!_includeNonCoreLibs && _nonCoreLibs.any(legacyPath.startsWith)) {
+          continue;
+        }
+
+        files++;
+        var sourceLines = readFileLines(legacyPath);
+        lines += sourceLines.length;
+
+        var nnbdPath = toNnbdPath(legacyPath);
+        if (fileExists(nnbdPath) ||
+            sourceLines.any((line) => line.contains(_nonMigratedMarker))) {
+          migratedFiles++;
+          migratedLines += sourceLines.length;
+        }
       }
 
-      files++;
-      var sourceLines = readFileLines(legacyPath);
-      lines += sourceLines.length;
+      if (files == 0) continue;
 
-      var nnbdPath = toNnbdPath(legacyPath);
-      if (fileExists(nnbdPath) ||
-          sourceLines.any((line) => line.contains(_nonMigratedMarker))) {
-        migratedFiles++;
-        migratedLines += sourceLines.length;
-      }
+      _show(dir, migratedFiles, files, migratedLines, lines);
+      totalFiles += files;
+      totalLines += lines;
+      totalMigratedFiles += migratedFiles;
+      totalMigratedLines += migratedLines;
     }
-
-    _show(dir, migratedFiles, files, migratedLines, lines);
-    totalFiles += files;
-    totalLines += lines;
-    totalMigratedFiles += migratedFiles;
-    totalMigratedLines += migratedLines;
   }
 
   print("");
@@ -82,12 +98,17 @@
   var migratedDays = migratedLines / _linesPerDay;
   var daysLeft = days - migratedDays;
 
-  print("${label.padRight(12)} ${pad(migratedFiles, 4)}/${pad(files, 4)} "
+  var daysLeftString = ", ${pad(daysLeft.toStringAsFixed(2), 6)}/"
+      "${pad(days.toStringAsFixed(2), 5)} days left";
+  if (migratedLines == 0) {
+    daysLeftString = ", ${pad(daysLeft.toStringAsFixed(2), 6)} days left";
+  } else if (migratedLines == lines) {
+    daysLeftString = "";
+  }
+
+  print("${label.padRight(40)} ${pad(migratedFiles, 4)}/${pad(files, 4)} "
       "files (${percent(migratedFiles, files)}%), "
       "${pad(migratedLines, 6)}/${pad(lines, 6)} "
-      "lines (${percent(migratedLines, lines)}%), "
-      "${pad(migratedDays.toStringAsFixed(2), 6)}/"
-      "${pad(days.toStringAsFixed(2), 5)} "
-      "days (${percent(migratedDays, days)}%), "
-      "${pad(daysLeft.toStringAsFixed(2), 5)} days left");
+      "lines (${percent(migratedLines, lines)}%)"
+      "$daysLeftString");
 }