Version 2.13.0-13.0.dev

Merge commit '63737581f88f64e56f62d555ae8e5690cc59d4be' into 'dev'
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 9527cd2..28c7cfd 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -1009,7 +1009,7 @@
     ir.DartType operandType = visitNode(node.operand);
     handleNullCheck(node, operandType);
     ir.DartType resultType = operandType is ir.NullType
-        ? const ir.NeverType(ir.Nullability.nonNullable)
+        ? const ir.NeverType.nonNullable()
         : operandType.withDeclaredNullability(ir.Nullability.nonNullable);
     _staticTypeCache._expressionTypes[node] = resultType;
     return resultType;
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index f053835..8037da0 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -190,7 +190,7 @@
         return const DoesNotCompleteType();
       case DartTypeNodeKind.neverType:
         ir.Nullability nullability = readEnum(ir.Nullability.values);
-        return ir.NeverType(nullability);
+        return ir.NeverType.fromNullability(nullability);
       case DartTypeNodeKind.typeParameterType:
         ir.TypeParameter typeParameter = readTypeParameterNode();
         ir.Nullability typeParameterTypeNullability =
diff --git a/pkg/front_end/lib/src/compute_platform_binaries_location.dart b/pkg/front_end/lib/src/compute_platform_binaries_location.dart
index 3060afb..dce1614 100644
--- a/pkg/front_end/lib/src/compute_platform_binaries_location.dart
+++ b/pkg/front_end/lib/src/compute_platform_binaries_location.dart
@@ -7,11 +7,64 @@
 import "dart:io" show File, Platform;
 
 import 'package:kernel/ast.dart' show Source;
+import 'package:kernel/target/targets.dart';
 
+import 'base/nnbd_mode.dart' show NnbdMode;
 import 'base/processed_options.dart' show ProcessedOptions;
 
 import 'fasta/compiler_context.dart' show CompilerContext;
 
+/// Returns the name of the default platform dill file name for the [target]
+/// with the given [nnbdMode].
+///
+/// If the target doesn't have a default platform dill file for the nnbd mode,
+/// [onError] is called.
+String computePlatformDillName(
+    Target target, NnbdMode nnbdMode, void Function() onError) {
+  switch (target.name) {
+    case 'dartdevc':
+      switch (nnbdMode) {
+        case NnbdMode.Strong:
+          return 'ddc_platform_sound.dill';
+        case NnbdMode.Weak:
+          return 'ddc_platform.dill';
+        case NnbdMode.Agnostic:
+          break;
+      }
+      break;
+    case 'dart2js':
+      switch (nnbdMode) {
+        case NnbdMode.Strong:
+          return 'dart2js_nnbd_strong_platform.dill';
+        case NnbdMode.Weak:
+          return 'dart2js_platform.dill';
+        case NnbdMode.Agnostic:
+          break;
+      }
+      break;
+    case 'dart2js_server':
+      switch (nnbdMode) {
+        case NnbdMode.Strong:
+          return 'dart2js_server_nnbd_strong_platform.dill';
+        case NnbdMode.Weak:
+          return 'dart2js_server_platform.dill';
+        case NnbdMode.Agnostic:
+          break;
+      }
+      break;
+    case 'vm':
+      // TODO(johnniwinther): Stop generating 'vm_platform.dill' and rename
+      // 'vm_platform_strong.dill' to 'vm_platform.dill'.
+      return "vm_platform_strong.dill";
+    case 'none':
+      return "vm_platform_strong.dill";
+    default:
+      break;
+  }
+  onError();
+  return null;
+}
+
 /// Computes the location of platform binaries, that is, compiled `.dill` files
 /// of the platform libraries that are used to avoid recompiling those
 /// libraries.
diff --git a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
index 7101cf2..ce76627 100644
--- a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
@@ -41,8 +41,6 @@
         templateDuplicatedDeclarationSyntheticCause,
         templateEnumConstantSameNameAsEnclosing;
 
-import '../kernel/metadata_collector.dart';
-
 import '../modifier.dart'
     show
         constMask,
@@ -118,7 +116,6 @@
             cls: cls);
 
   factory EnumBuilder(
-      MetadataCollector metadataCollector,
       List<MetadataBuilder> metadata,
       String name,
       List<EnumConstantInfo> enumConstantInfos,
@@ -285,7 +282,6 @@
         EnumConstantInfo enumConstantInfo = enumConstantInfos[i];
         List<MetadataBuilder> metadata = enumConstantInfo.metadata;
         String name = enumConstantInfo.name;
-        String documentationComment = enumConstantInfo.documentationComment;
         MemberBuilder existing = members[name];
         if (existing != null) {
           // The existing declaration is synthetic if it has the same
@@ -335,8 +331,6 @@
             enumConstantInfo.charOffset,
             fieldGetterReference: getterReference,
             fieldSetterReference: setterReference);
-        metadataCollector?.setDocumentationComment(
-            fieldBuilder.field, documentationComment);
         members[name] = fieldBuilder..next = existing;
       }
     }
@@ -488,7 +482,5 @@
   final List<MetadataBuilder> metadata;
   final String name;
   final int charOffset;
-  final String documentationComment;
-  const EnumConstantInfo(
-      this.metadata, this.name, this.charOffset, this.documentationComment);
+  const EnumConstantInfo(this.metadata, this.name, this.charOffset);
 }
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart
index f4ddeff..c3813c3 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart
@@ -22,7 +22,6 @@
         Member,
         NamedNode,
         NeverType,
-        Nullability,
         Procedure,
         ProcedureKind,
         Reference,
@@ -162,7 +161,7 @@
     addBuilder(
         "Never",
         new NeverTypeDeclarationBuilder(
-            const NeverType(Nullability.nonNullable), this, -1),
+            const NeverType.nonNullable(), this, -1),
         -1);
   }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index c2ff740..c97c809 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1333,7 +1333,7 @@
       DartType spreadType, DartType spreadTypeBound, bool isNullAware) {
     if (inferrer.coreTypes.isNull(spreadTypeBound)) {
       if (inferrer.isNonNullableByDefault) {
-        return isNullAware ? const NeverType(Nullability.nonNullable) : null;
+        return isNullAware ? const NeverType.nonNullable() : null;
       } else {
         return isNullAware ? const NullType() : null;
       }
@@ -1349,7 +1349,7 @@
     } else if (spreadType is DynamicType) {
       return const DynamicType();
     } else if (inferrer.coreTypes.isBottom(spreadType)) {
-      return const NeverType(Nullability.nonNullable);
+      return const NeverType.nonNullable();
     }
     return null;
   }
@@ -1802,8 +1802,7 @@
     if (inferrer.coreTypes.isNull(typeBound)) {
       if (isNullAware) {
         if (inferrer.isNonNullableByDefault) {
-          output[offset] =
-              output[offset + 1] = const NeverType(Nullability.nonNullable);
+          output[offset] = output[offset + 1] = const NeverType.nonNullable();
         } else {
           output[offset] = output[offset + 1] = const NullType();
         }
@@ -1818,8 +1817,7 @@
     } else if (spreadMapEntryType is DynamicType) {
       output[offset] = output[offset + 1] = const DynamicType();
     } else if (inferrer.coreTypes.isBottom(spreadMapEntryType)) {
-      output[offset] =
-          output[offset + 1] = const NeverType(Nullability.nonNullable);
+      output[offset] = output[offset + 1] = const NeverType.nonNullable();
     }
   }
 
@@ -3809,10 +3807,8 @@
           ..fileOffset = fileOffset;
       } else {
         assert(equalsTarget.isNever);
-        FunctionType functionType = new FunctionType(
-            [const DynamicType()],
-            const NeverType(Nullability.nonNullable),
-            inferrer.library.nonNullable);
+        FunctionType functionType = new FunctionType([const DynamicType()],
+            const NeverType.nonNullable(), inferrer.library.nonNullable);
         // Ensure operator == member even for `Never`.
         Member target = inferrer
             .findInterfaceMember(const DynamicType(), equalsName, -1,
@@ -3841,7 +3837,7 @@
         notEqual: isNot);
     return new ExpressionInferenceResult(
         equalsTarget.isNever
-            ? const NeverType(Nullability.nonNullable)
+            ? const NeverType.nonNullable()
             : inferrer.coreTypes.boolRawType(inferrer.library.nonNullable),
         equals);
   }
@@ -5768,7 +5764,7 @@
     inferrer.flowAnalysis.handleExit();
     return new ExpressionInferenceResult(
         inferrer.isNonNullableByDefault
-            ? const NeverType(Nullability.nonNullable)
+            ? const NeverType.nonNullable()
             : const BottomType(),
         node);
   }
@@ -6215,7 +6211,7 @@
     // Return BottomType in legacy mode for compatibility.
     return new ExpressionInferenceResult(
         inferrer.isNonNullableByDefault
-            ? const NeverType(Nullability.nonNullable)
+            ? const NeverType.nonNullable()
             : const BottomType(),
         node);
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 5135011..513dac3 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -113,7 +113,6 @@
         transformProcedure,
         ConstantCoverage;
 import 'kernel_constants.dart' show KernelConstantErrorReporter;
-import 'metadata_collector.dart' show MetadataCollector;
 import 'verifier.dart' show verifyComponent, verifyGetStaticType;
 
 class KernelTarget extends TargetImplementation {
@@ -125,9 +124,6 @@
 
   final DillTarget dillTarget;
 
-  /// The [MetadataCollector] to write metadata to.
-  final MetadataCollector metadataCollector;
-
   SourceLoader loader;
 
   Component component;
@@ -177,10 +173,8 @@
   final List<ClonedFunctionNode> clonedFunctionNodes = <ClonedFunctionNode>[];
 
   KernelTarget(this.fileSystem, this.includeComments, DillTarget dillTarget,
-      UriTranslator uriTranslator,
-      {MetadataCollector metadataCollector})
+      UriTranslator uriTranslator)
       : dillTarget = dillTarget,
-        metadataCollector = metadataCollector,
         super(dillTarget.ticker, uriTranslator, dillTarget.backendTarget) {
     loader = createLoader();
   }
@@ -479,9 +473,6 @@
     }
     component.setMainMethodAndMode(mainReference, true, compiledMode);
 
-    if (metadataCollector != null) {
-      component.addMetadataRepository(metadataCollector.repository);
-    }
     assert(_getLibraryNnbdModeError(component) == null,
         "Got error: ${_getLibraryNnbdModeError(component)}");
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/metadata_collector.dart b/pkg/front_end/lib/src/fasta/kernel/metadata_collector.dart
deleted file mode 100644
index f77e230..0000000
--- a/pkg/front_end/lib/src/fasta/kernel/metadata_collector.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2017, 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.9
-
-import 'package:kernel/kernel.dart' show Member, MetadataRepository, NamedNode;
-
-/// The collector to add target specific metadata to.
-abstract class MetadataCollector {
-  /// Metadata is remembered in this repository, so that when it is added
-  /// to a component, metadata is serialized with the component.
-  MetadataRepository<dynamic> get repository;
-
-  void setConstructorNameOffset(Member node, Object name);
-
-  void setDocumentationComment(NamedNode node, String comment);
-}
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index c8aa4b5..990f491 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -329,8 +329,7 @@
       if (token.isSynthetic) {
         push(new ParserRecovery(token.charOffset));
       } else {
-        push(new EnumConstantInfo(metadata, token.lexeme, token.charOffset,
-            getDocumentationComment(token)));
+        push(new EnumConstantInfo(metadata, token.lexeme, token.charOffset));
       }
     } else {
       super.handleIdentifier(token, context);
@@ -441,10 +440,8 @@
   void endLibraryName(Token libraryKeyword, Token semicolon) {
     debugEvent("endLibraryName");
     popCharOffset();
-    String documentationComment = getDocumentationComment(libraryKeyword);
     Object name = pop();
     List<MetadataBuilder> metadata = pop();
-    libraryBuilder.documentationComment = documentationComment;
     if (name is! ParserRecovery) {
       libraryBuilder.name =
           flattenName(name, offsetForToken(libraryKeyword), uri);
@@ -565,7 +562,6 @@
   @override
   void endClassDeclaration(Token beginToken, Token endToken) {
     debugEvent("endClassDeclaration");
-    String documentationComment = getDocumentationComment(beginToken);
     List<TypeBuilder> interfaces = pop(NullValue.TypeBuilderList);
     int supertypeOffset = pop();
     TypeBuilder supertype = nullIfParserRecovery(pop());
@@ -634,7 +630,6 @@
     }
 
     libraryBuilder.addClass(
-        documentationComment,
         metadata,
         modifiers,
         name,
@@ -656,7 +651,6 @@
   @override
   void endMixinDeclaration(Token mixinToken, Token endToken) {
     debugEvent("endMixinDeclaration");
-    String documentationComment = getDocumentationComment(mixinToken);
     List<TypeBuilder> interfaces = pop(NullValue.TypeBuilderList);
     List<TypeBuilder> supertypeConstraints = nullIfParserRecovery(pop());
     List<TypeVariableBuilder> typeVariables = pop(NullValue.TypeVariables);
@@ -715,7 +709,6 @@
     }
 
     libraryBuilder.addMixinDeclaration(
-        documentationComment,
         metadata,
         mixinDeclarationMask,
         name,
@@ -765,7 +758,6 @@
       ValueKinds.MetadataListOrNull
     ]));
     debugEvent("endExtensionDeclaration");
-    String documentationComment = getDocumentationComment(extensionKeyword);
     Object onType = pop();
     if (onType is ParserRecovery) {
       ParserRecovery parserRecovery = onType;
@@ -785,7 +777,6 @@
         ? extensionKeyword.charOffset
         : metadata.first.charOffset;
     libraryBuilder.addExtensionDeclaration(
-        documentationComment,
         metadata,
         // TODO(johnniwinther): Support modifiers on extensions?
         0,
@@ -850,9 +841,7 @@
     if (name is ParserRecovery) return;
     final int startCharOffset =
         metadata == null ? beginToken.charOffset : metadata.first.charOffset;
-    String documentationComment = getDocumentationComment(beginToken);
     libraryBuilder.addProcedure(
-        documentationComment,
         metadata,
         modifiers,
         returnType,
@@ -1105,7 +1094,6 @@
     bool isConst = (modifiers & constMask) != 0;
     int varFinalOrConstOffset = pop();
     List<MetadataBuilder> metadata = pop();
-    String documentationComment = getDocumentationComment(beginToken);
 
     TypeParameterScopeBuilder declarationBuilder =
         libraryBuilder.endNestedDeclaration(
@@ -1196,7 +1184,6 @@
       final int startCharOffset =
           metadata == null ? beginToken.charOffset : metadata.first.charOffset;
       libraryBuilder.addConstructor(
-          documentationComment,
           metadata,
           modifiers,
           returnType,
@@ -1219,7 +1206,6 @@
       final int startCharOffset =
           metadata == null ? beginToken.charOffset : metadata.first.charOffset;
       libraryBuilder.addProcedure(
-          documentationComment,
           metadata,
           modifiers,
           returnType,
@@ -1260,7 +1246,6 @@
   void endNamedMixinApplication(Token beginToken, Token classKeyword,
       Token equals, Token implementsKeyword, Token endToken) {
     debugEvent("endNamedMixinApplication");
-    String documentationComment = getDocumentationComment(beginToken);
     List<TypeBuilder> interfaces = popIfNotNull(implementsKeyword);
     Object mixinApplication = pop();
     int modifiers = pop();
@@ -1319,7 +1304,6 @@
     int startCharOffset = beginToken.charOffset;
     int charEndOffset = endToken.charOffset;
     libraryBuilder.addNamedMixinApplication(
-        documentationComment,
         metadata,
         name,
         typeVariables,
@@ -1574,7 +1558,6 @@
   @override
   void endEnum(Token enumKeyword, Token leftBrace, int count) {
     debugEvent("Enum");
-    String documentationComment = getDocumentationComment(enumKeyword);
     List<EnumConstantInfo> enumConstantInfos =
         const FixedNullableList<EnumConstantInfo>().pop(stack, count);
     int charOffset = pop(); // identifier char offset.
@@ -1583,14 +1566,8 @@
     List<MetadataBuilder> metadata = pop();
     checkEmpty(enumKeyword.charOffset);
     if (name is ParserRecovery) return;
-    libraryBuilder.addEnum(
-        documentationComment,
-        metadata,
-        name,
-        enumConstantInfos,
-        startCharOffset,
-        charOffset,
-        leftBrace?.endGroup?.charOffset);
+    libraryBuilder.addEnum(metadata, name, enumConstantInfos, startCharOffset,
+        charOffset, leftBrace?.endGroup?.charOffset);
   }
 
   @override
@@ -1652,7 +1629,6 @@
   void endFunctionTypeAlias(
       Token typedefKeyword, Token equals, Token endToken) {
     debugEvent("endFunctionTypeAlias");
-    String documentationComment = getDocumentationComment(typedefKeyword);
     List<TypeVariableBuilder> typeVariables;
     Object name;
     int charOffset;
@@ -1720,8 +1696,8 @@
     }
     List<MetadataBuilder> metadata = pop();
     checkEmpty(typedefKeyword.charOffset);
-    libraryBuilder.addFunctionTypeAlias(documentationComment, metadata, name,
-        typeVariables, aliasedType, charOffset);
+    libraryBuilder.addFunctionTypeAlias(
+        metadata, name, typeVariables, aliasedType, charOffset);
   }
 
   @override
@@ -1759,9 +1735,7 @@
     List<MetadataBuilder> metadata = pop();
     checkEmpty(beginToken.charOffset);
     if (fieldInfos == null) return;
-    String documentationComment = getDocumentationComment(beginToken);
     libraryBuilder.addFields(
-        documentationComment,
         metadata,
         modifiers,
         /* isTopLevel = */ true,
@@ -1826,9 +1800,7 @@
     }
     List<MetadataBuilder> metadata = pop();
     if (fieldInfos == null) return;
-    String documentationComment = getDocumentationComment(beginToken);
     libraryBuilder.addFields(
-        documentationComment,
         metadata,
         modifiers,
         /* isTopLevel = */ false,
@@ -2034,9 +2006,7 @@
           TypeParameterScopeKind.factoryMethod, "<syntax-error>");
       return;
     }
-    String documentationComment = getDocumentationComment(beginToken);
     libraryBuilder.addFactoryMethod(
-      documentationComment,
       metadata,
       modifiers,
       name,
@@ -2170,34 +2140,6 @@
         super.isIgnoredError(code, token);
   }
 
-  /// Return the documentation comment for the entity that starts at the
-  /// given [token], or `null` if there is no preceding documentation comment.
-  static String getDocumentationComment(Token token) {
-    Token docToken = token.precedingComments;
-    if (docToken == null) return null;
-    bool inSlash = false;
-    StringBuffer buffer = new StringBuffer();
-    while (docToken != null) {
-      String lexeme = docToken.lexeme;
-      if (lexeme.startsWith('/**')) {
-        inSlash = false;
-        buffer.clear();
-        buffer.write(lexeme);
-      } else if (lexeme.startsWith('///')) {
-        if (!inSlash) {
-          inSlash = true;
-          buffer.clear();
-        }
-        if (buffer.isNotEmpty) {
-          buffer.writeln();
-        }
-        buffer.write(lexeme);
-      }
-      docToken = docToken.next;
-    }
-    return buffer.toString();
-  }
-
   @override
   void debugEvent(String name) {
     // printEvent('OutlineBuilder: $name');
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 99d21e9..59b38d9 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -94,8 +94,6 @@
         compareProcedures,
         toKernelCombinators;
 
-import '../kernel/metadata_collector.dart';
-
 import '../kernel/type_algorithms.dart'
     show
         calculateBounds,
@@ -168,8 +166,6 @@
 
   final List<Object> accessors = <Object>[];
 
-  String documentationComment;
-
   String name;
 
   String partOfName;
@@ -733,13 +729,8 @@
     }
   }
 
-  void addFields(
-      String documentationComment,
-      List<MetadataBuilder> metadata,
-      int modifiers,
-      bool isTopLevel,
-      TypeBuilder type,
-      List<FieldInfo> fieldInfos) {
+  void addFields(List<MetadataBuilder> metadata, int modifiers, bool isTopLevel,
+      TypeBuilder type, List<FieldInfo> fieldInfos) {
     for (FieldInfo info in fieldInfos) {
       bool isConst = modifiers & constMask != 0;
       bool isFinal = modifiers & finalMask != 0;
@@ -756,17 +747,8 @@
         new Token.eof(startToken.previous.offset).setNext(startToken);
       }
       bool hasInitializer = info.initializerToken != null;
-      addField(
-          documentationComment,
-          metadata,
-          modifiers,
-          isTopLevel,
-          type,
-          info.name,
-          info.charOffset,
-          info.charEndOffset,
-          startToken,
-          hasInitializer,
+      addField(metadata, modifiers, isTopLevel, type, info.name,
+          info.charOffset, info.charEndOffset, startToken, hasInitializer,
           constInitializerToken:
               potentiallyNeedInitializerInOutline ? startToken : null);
     }
@@ -936,9 +918,6 @@
     library.isSynthetic = isSynthetic;
     addDependencies(library, new Set<SourceLibraryBuilder>());
 
-    loader.target.metadataCollector
-        ?.setDocumentationComment(library, documentationComment);
-
     library.name = name;
     library.procedures.sort(compareProcedures);
 
@@ -1362,7 +1341,7 @@
     addBuilder(
         "Never",
         new NeverTypeDeclarationBuilder(
-            const NeverType(Nullability.nonNullable), this, -1),
+            const NeverType.nonNullable(), this, -1),
         -1);
   }
 
@@ -1443,7 +1422,6 @@
   }
 
   void addClass(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       String className,
@@ -1456,7 +1434,6 @@
       int supertypeOffset) {
     _addClass(
         TypeParameterScopeKind.classDeclaration,
-        documentationComment,
         metadata,
         modifiers,
         className,
@@ -1470,7 +1447,6 @@
   }
 
   void addMixinDeclaration(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       String className,
@@ -1483,7 +1459,6 @@
       int supertypeOffset) {
     _addClass(
         TypeParameterScopeKind.mixinDeclaration,
-        documentationComment,
         metadata,
         modifiers,
         className,
@@ -1498,7 +1473,6 @@
 
   void _addClass(
       TypeParameterScopeKind kind,
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       String className,
@@ -1565,8 +1539,6 @@
         referencesFromClass,
         _currentClassReferencesFromIndexed,
         isMixinDeclaration: isMixinDeclaration);
-    loader.target.metadataCollector
-        ?.setDocumentationComment(classBuilder.cls, documentationComment);
 
     constructorReferences.clear();
     Map<String, TypeVariableBuilder> typeVariablesByName =
@@ -1735,7 +1707,6 @@
   }
 
   void addExtensionDeclaration(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       String extensionName,
@@ -1775,8 +1746,6 @@
         nameOffset,
         endOffset,
         referenceFrom);
-    loader.target.metadataCollector?.setDocumentationComment(
-        extensionBuilder.extension, documentationComment);
 
     constructorReferences.clear();
     Map<String, TypeVariableBuilder> typeVariablesByName =
@@ -1814,8 +1783,7 @@
 
   TypeBuilder applyMixins(TypeBuilder type, int startCharOffset, int charOffset,
       int charEndOffset, String subclassName, bool isMixinDeclaration,
-      {String documentationComment,
-      List<MetadataBuilder> metadata,
+      {List<MetadataBuilder> metadata,
       String name,
       List<TypeVariableBuilder> typeVariables,
       int modifiers,
@@ -1823,10 +1791,7 @@
     if (name == null) {
       // The following parameters should only be used when building a named
       // mixin application.
-      if (documentationComment != null) {
-        unhandled("documentationComment", "unnamed mixin application",
-            charOffset, fileUri);
-      } else if (metadata != null) {
+      if (metadata != null) {
         unhandled("metadata", "unnamed mixin application", charOffset, fileUri);
       } else if (interfaces != null) {
         unhandled(
@@ -2041,10 +2006,6 @@
           referencesFromIndexedClass,
           mixedInTypeBuilder: isMixinDeclaration ? null : mixin,
         );
-        if (isNamedMixinApplication) {
-          loader.target.metadataCollector
-              ?.setDocumentationComment(application.cls, documentationComment);
-        }
         // TODO(ahe, kmillikin): Should always be true?
         // pkg/analyzer/test/src/summary/resynthesize_kernel_test.dart can't
         // handle that :(
@@ -2061,7 +2022,6 @@
   }
 
   void addNamedMixinApplication(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       String name,
       List<TypeVariableBuilder> typeVariables,
@@ -2076,7 +2036,6 @@
         .resolveTypes(typeVariables, this);
     NamedTypeBuilder supertype = applyMixins(mixinApplication, startCharOffset,
         charOffset, charEndOffset, name, false,
-        documentationComment: documentationComment,
         metadata: metadata,
         name: name,
         typeVariables: typeVariables,
@@ -2086,7 +2045,6 @@
   }
 
   void addField(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       bool isTopLevel,
@@ -2200,12 +2158,9 @@
         registerImplicitlyTypedField(fieldBuilder);
       }
     }
-    loader.target.metadataCollector
-        ?.setDocumentationComment(fieldBuilder.field, documentationComment);
   }
 
   void addConstructor(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       TypeBuilder returnType,
@@ -2219,7 +2174,6 @@
       int charEndOffset,
       String nativeMethodName,
       {Token beginInitializers}) {
-    MetadataCollector metadataCollector = loader.target.metadataCollector;
     Member referenceFrom;
     if (_currentClassReferencesFromIndexed != null) {
       referenceFrom = _currentClassReferencesFromIndexed.lookupConstructor(
@@ -2240,10 +2194,6 @@
         charEndOffset,
         referenceFrom,
         nativeMethodName);
-    metadataCollector?.setDocumentationComment(
-        constructorBuilder.constructor, documentationComment);
-    metadataCollector?.setConstructorNameOffset(
-        constructorBuilder.constructor, name);
     checkTypeVariables(typeVariables, constructorBuilder);
     addBuilder(constructorName, constructorBuilder, charOffset,
         getterReference: referenceFrom?.reference);
@@ -2260,7 +2210,6 @@
   }
 
   void addProcedure(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       TypeBuilder returnType,
@@ -2279,7 +2228,6 @@
     assert(isTopLevel != null);
     assert(isExtensionInstanceMember != null);
 
-    MetadataCollector metadataCollector = loader.target.metadataCollector;
     if (returnType == null) {
       if (kind == ProcedureKind.Operator &&
           identical(name, indexSetName.text)) {
@@ -2358,8 +2306,6 @@
         asyncModifier,
         isExtensionInstanceMember,
         nativeMethodName);
-    metadataCollector?.setDocumentationComment(
-        procedureBuilder.procedure, documentationComment);
     checkTypeVariables(typeVariables, procedureBuilder);
     addBuilder(name, procedureBuilder, charOffset,
         getterReference: procedureReference);
@@ -2369,7 +2315,6 @@
   }
 
   void addFactoryMethod(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       int modifiers,
       Object name,
@@ -2449,12 +2394,6 @@
           nativeMethodName);
     }
 
-    MetadataCollector metadataCollector = loader.target.metadataCollector;
-    metadataCollector?.setDocumentationComment(
-        procedureBuilder.procedure, documentationComment);
-    metadataCollector?.setConstructorNameOffset(
-        procedureBuilder.procedure, name);
-
     TypeParameterScopeBuilder savedDeclaration =
         currentTypeParameterScopeBuilder;
     currentTypeParameterScopeBuilder = factoryDeclaration;
@@ -2474,14 +2413,12 @@
   }
 
   void addEnum(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       String name,
       List<EnumConstantInfo> enumConstantInfos,
       int startCharOffset,
       int charOffset,
       int charEndOffset) {
-    MetadataCollector metadataCollector = loader.target.metadataCollector;
     Class referencesFromClass;
     IndexedClass referencesFromIndexedClass;
     if (referencesFrom != null) {
@@ -2490,7 +2427,6 @@
           referencesFromIndexed.lookupIndexedClass(name);
     }
     EnumBuilder builder = new EnumBuilder(
-        metadataCollector,
         metadata,
         name,
         enumConstantInfos,
@@ -2502,12 +2438,9 @@
         referencesFromIndexedClass);
     addBuilder(name, builder, charOffset,
         getterReference: referencesFromClass?.reference);
-    metadataCollector?.setDocumentationComment(
-        builder.cls, documentationComment);
   }
 
   void addFunctionTypeAlias(
-      String documentationComment,
       List<MetadataBuilder> metadata,
       String name,
       List<TypeVariableBuilder> typeVariables,
@@ -2522,8 +2455,6 @@
     TypeAliasBuilder typedefBuilder = new SourceTypeAliasBuilder(
         metadata, name, typeVariables, type, this, charOffset,
         referenceFrom: referenceFrom);
-    loader.target.metadataCollector
-        ?.setDocumentationComment(typedefBuilder.typedef, documentationComment);
     checkTypeVariables(typeVariables, typedefBuilder);
     // Nested declaration began in `OutlineBuilder.beginFunctionTypeAlias`.
     endNestedDeclaration(TypeParameterScopeKind.typedef, "#typedef")
@@ -3585,7 +3516,7 @@
     if (parameters.length != arguments.length) return;
 
     final DartType bottomType = isNonNullableByDefault
-        ? const NeverType(Nullability.nonNullable)
+        ? const NeverType.nonNullable()
         : const NullType();
     List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
         library,
@@ -3662,7 +3593,7 @@
     }
 
     final DartType bottomType = isNonNullableByDefault
-        ? const NeverType(Nullability.nonNullable)
+        ? const NeverType.nonNullable()
         : const NullType();
     List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
         library,
diff --git a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
index e162fa2..161109e 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
@@ -322,7 +322,8 @@
       } else {
         // No explicit return and the function doesn't complete normally; that
         // is, it throws.
-        actualReturnedType = new NeverType(inferrer.library.nonNullable);
+        actualReturnedType =
+            NeverType.fromNullability(inferrer.library.nonNullable);
       }
       // Use the types seen from the explicit return statements.
       for (int i = 0; i < _returnStatements.length; i++) {
@@ -721,7 +722,7 @@
       } else {
         // No explicit return and the function doesn't complete normally; that
         // is, it throws.
-        inferredType = new NeverType(inferrer.library.nonNullable);
+        inferredType = NeverType.fromNullability(inferrer.library.nonNullable);
       }
       // Use the types seen from the explicit return statements.
       for (int i = 0; i < _returnStatements.length; i++) {
@@ -954,7 +955,8 @@
       // No explicit return and the function doesn't complete normally; that is,
       // it throws.
       if (inferrer.isNonNullableByDefault) {
-        inferredElementType = new NeverType(inferrer.library.nonNullable);
+        inferredElementType =
+            NeverType.fromNullability(inferrer.library.nonNullable);
       } else {
         inferredElementType = const NullType();
       }
@@ -1082,7 +1084,8 @@
       // No explicit return and the function doesn't complete normally; that is,
       // it throws.
       if (inferrer.isNonNullableByDefault) {
-        inferredElementType = new NeverType(inferrer.library.nonNullable);
+        inferredElementType =
+            NeverType.fromNullability(inferrer.library.nonNullable);
       } else {
         inferredElementType = const NullType();
       }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/factor_type.dart b/pkg/front_end/lib/src/fasta/type_inference/factor_type.dart
index 1083882..a72c547 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/factor_type.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/factor_type.dart
@@ -13,7 +13,7 @@
 DartType factorType(TypeEnvironment typeEnvironment, DartType T, DartType S) {
   // * If T <: S then Never
   if (typeEnvironment.isSubtypeOf(T, S, SubtypeCheckMode.withNullabilities)) {
-    return const NeverType(Nullability.nonNullable);
+    return const NeverType.nonNullable();
   }
 
   // * Else if T is R? and Null <: S then factor(R, S)
diff --git a/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart b/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart
index 43b4e0e..5f149f6 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart
@@ -4,7 +4,7 @@
 
 // @dart = 2.9
 
-import 'package:kernel/ast.dart' show DartType, Library, NeverType, Nullability;
+import 'package:kernel/ast.dart' show DartType, Library, NeverType;
 
 import 'package:kernel/src/standard_bounds.dart';
 
@@ -21,10 +21,10 @@
     //  S2` where `Si` is the greatest closure of `Ti` with respect to `_`.
     if (type1 is UnknownType) return type2;
     if (type2 is UnknownType) return type1;
-    type1 = greatestClosure(type1, coreTypes.objectNullableRawType,
-        const NeverType(Nullability.nonNullable));
-    type2 = greatestClosure(type2, coreTypes.objectNullableRawType,
-        const NeverType(Nullability.nonNullable));
+    type1 = greatestClosure(
+        type1, coreTypes.objectNullableRawType, const NeverType.nonNullable());
+    type2 = greatestClosure(
+        type2, coreTypes.objectNullableRawType, const NeverType.nonNullable());
 
     return super.getNullabilityAwareStandardLowerBoundInternal(
         type1, type2, clientLibrary);
@@ -52,10 +52,10 @@
     //  where `Si` is the least closure of `Ti` with respect to `_`.
     if (type1 is UnknownType) return type2;
     if (type2 is UnknownType) return type1;
-    type1 = leastClosure(type1, coreTypes.objectNullableRawType,
-        const NeverType(Nullability.nonNullable));
-    type2 = leastClosure(type2, coreTypes.objectNullableRawType,
-        const NeverType(Nullability.nonNullable));
+    type1 = leastClosure(
+        type1, coreTypes.objectNullableRawType, const NeverType.nonNullable());
+    type2 = leastClosure(
+        type2, coreTypes.objectNullableRawType, const NeverType.nonNullable());
     return super.getNullabilityAwareStandardUpperBoundInternal(
         type1, type2, clientLibrary);
   }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
index fd6c2f6e..705469d 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
@@ -799,7 +799,7 @@
               new NullabilityAwareTypeVariableEliminator(
                   eliminationTargets:
                       freshTypeParameters.freshTypeParameters.toSet(),
-                  bottomType: const NeverType(Nullability.nonNullable),
+                  bottomType: const NeverType.nonNullable(),
                   topType: coreTypes.objectNullableRawType,
                   topFunctionType: coreTypes.functionNonNullableRawType,
                   unhandledTypeHandler: (DartType type, ignored) =>
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
index f2cb2c4..dc3235f 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
@@ -323,7 +323,7 @@
       }
       return type;
     } else if (type is NullType) {
-      return const NeverType(Nullability.nonNullable);
+      return const NeverType.nonNullable();
     }
     return type.withDeclaredNullability(Nullability.nonNullable);
   }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index c3c6475..b98da53 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -245,9 +245,8 @@
   bool get useNewMethodInvocationEncoding =>
       library.loader.target.backendTarget.supportsNewMethodInvocationEncoding;
 
-  DartType get bottomType => isNonNullableByDefault
-      ? const NeverType(Nullability.nonNullable)
-      : const NullType();
+  DartType get bottomType =>
+      isNonNullableByDefault ? const NeverType.nonNullable() : const NullType();
 
   DartType computeGreatestClosure(DartType type) {
     return greatestClosure(type, const DynamicType(), bottomType);
@@ -271,9 +270,7 @@
 
   DartType computeNonNullable(DartType type) {
     if (type is NullType) {
-      return isNonNullableByDefault
-          ? const NeverType(Nullability.nonNullable)
-          : type;
+      return isNonNullableByDefault ? const NeverType.nonNullable() : type;
     }
     if (type is TypeParameterType && type.promotedBound != null) {
       return new TypeParameterType(type.parameter, Nullability.nonNullable,
@@ -1190,7 +1187,7 @@
       case ObjectAccessTargetKind.ambiguous:
         return const DynamicType();
       case ObjectAccessTargetKind.never:
-        return const NeverType(Nullability.nonNullable);
+        return const NeverType.nonNullable();
       case ObjectAccessTargetKind.instanceMember:
       case ObjectAccessTargetKind.objectMember:
       case ObjectAccessTargetKind.nullableInstanceMember:
@@ -1385,7 +1382,7 @@
         }
         break;
       case ObjectAccessTargetKind.never:
-        return const NeverType(Nullability.nonNullable);
+        return const NeverType.nonNullable();
       case ObjectAccessTargetKind.invalid:
         return const InvalidType();
       case ObjectAccessTargetKind.callFunction:
@@ -2649,7 +2646,7 @@
         ..fileOffset = fileOffset;
     }
     return createNullAwareExpressionInferenceResult(
-        const NeverType(Nullability.nonNullable),
+        const NeverType.nonNullable(),
         result.applyResult(expression),
         nullAwareGuards);
   }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_elimination.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_elimination.dart
index d65acf4..f6598b6 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_elimination.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_elimination.dart
@@ -75,8 +75,8 @@
             topType.classNode.enclosingLibrary.importUri.scheme == "dart" &&
             topType.classNode.enclosingLibrary.importUri.path == "core" &&
             topType.classNode.name == "Object");
-    assert(bottomType == const NeverType(Nullability.nonNullable) ||
-        bottomType is NullType);
+    assert(
+        bottomType == const NeverType.nonNullable() || bottomType is NullType);
     _TypeSchemaEliminationVisitor visitor =
         new _TypeSchemaEliminationVisitor(topType, bottomType);
     DartType result = schema.accept1(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index 0e12020..5e32e19 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -266,7 +266,7 @@
       if (isConst) {
         returnContextType = new TypeVariableEliminator(
                 clientLibrary.isNonNullableByDefault
-                    ? const NeverType(Nullability.nonNullable)
+                    ? const NeverType.nonNullable()
                     : const NullType(),
                 clientLibrary.isNonNullableByDefault
                     ? objectNullableRawType
@@ -430,7 +430,7 @@
   DartType solveTypeConstraint(
       TypeConstraint constraint, DartType topType, DartType bottomType,
       {bool grounded: false, bool isContravariant: false}) {
-    assert(bottomType == const NeverType(Nullability.nonNullable) ||
+    assert(bottomType == const NeverType.nonNullable() ||
         bottomType == const NullType());
     if (!isContravariant) {
       // Prefer the known bound, if any.
@@ -503,7 +503,7 @@
             ? coreTypes.objectNullableRawType
             : const DynamicType(),
         clientLibrary.isNonNullableByDefault
-            ? const NeverType(Nullability.nonNullable)
+            ? const NeverType.nonNullable()
             : const NullType(),
         grounded: true,
         isContravariant: isContravariant);
@@ -517,7 +517,7 @@
             ? coreTypes.objectNullableRawType
             : const DynamicType(),
         clientLibrary.isNonNullableByDefault
-            ? const NeverType(Nullability.nonNullable)
+            ? const NeverType.nonNullable()
             : const NullType());
     if (!isKnown(t)) {
       return t;
@@ -539,7 +539,7 @@
               ? coreTypes.objectNullableRawType
               : const DynamicType(),
           clientLibrary.isNonNullableByDefault
-              ? const NeverType(Nullability.nonNullable)
+              ? const NeverType.nonNullable()
               : const NullType());
     }
     return t;
diff --git a/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart b/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart
index 9a2fcea..424a9aa 100644
--- a/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart
+++ b/pkg/front_end/lib/src/fasta/util/direct_parser_ast.dart
@@ -35,8 +35,15 @@
       enableNonNullable: enableNonNullable,
       enableTripleShift: enableTripleShift);
 
-  Utf8BytesScanner scanner = new Utf8BytesScanner(bytes,
-      includeComments: includeComments, configuration: scannerConfiguration);
+  Utf8BytesScanner scanner = new Utf8BytesScanner(
+    bytes,
+    includeComments: includeComments,
+    configuration: scannerConfiguration,
+    languageVersionChanged: (scanner, languageVersion) {
+      // For now don't do anything, but having it (making it non-null) means the
+      // configuration won't be reset.
+    },
+  );
   Token firstToken = scanner.tokenize();
   if (firstToken == null) {
     throw "firstToken is null";
@@ -53,6 +60,301 @@
   return listener.data.single;
 }
 
+/// Best-effort visitor for DirectParserASTContent that visits top-level entries
+/// and class members only (i.e. no bodies, no field initializer content, no
+/// names etc).
+class DirectParserASTContentVisitor {
+  void accept(DirectParserASTContent node) {
+    if (node is DirectParserASTContentCompilationUnitEnd ||
+        node is DirectParserASTContentTopLevelDeclarationEnd ||
+        node is DirectParserASTContentClassOrMixinBodyEnd ||
+        node is DirectParserASTContentMemberEnd) {
+      visitChildren(node);
+      return;
+    }
+
+    if (node.type == DirectParserASTType.BEGIN) {
+      // Ignored. These are basically just dummy nodes anyway.
+      assert(node.children == null);
+      return;
+    }
+    if (node.type == DirectParserASTType.HANDLE) {
+      // Ignored at least for know.
+      assert(node.children == null);
+      return;
+    }
+    if (node is DirectParserASTContentTypeVariablesEnd ||
+        node is DirectParserASTContentTypeArgumentsEnd ||
+        node is DirectParserASTContentTypeListEnd ||
+        node is DirectParserASTContentFunctionTypeEnd ||
+        node is DirectParserASTContentBlockEnd) {
+      // Ignored at least for know.
+      return;
+    }
+    if (node is DirectParserASTContentMetadataStarEnd) {
+      DirectParserASTContentMetadataStarEnd metadata = node;
+      visitMetadataStar(metadata);
+      return;
+    }
+    if (node is DirectParserASTContentFunctionTypeAliasEnd) {
+      DirectParserASTContentFunctionTypeAliasEnd typedefDecl = node;
+      visitTypedef(
+          typedefDecl, typedefDecl.typedefKeyword, typedefDecl.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentClassDeclarationEnd) {
+      DirectParserASTContentClassDeclarationEnd cls = node;
+      visitClass(cls, cls.beginToken, cls.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentTopLevelMethodEnd) {
+      DirectParserASTContentTopLevelMethodEnd method = node;
+      visitTopLevelMethod(method, method.beginToken, method.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentClassMethodEnd) {
+      DirectParserASTContentClassMethodEnd method = node;
+      visitClassMethod(method, method.beginToken, method.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentExtensionMethodEnd) {
+      DirectParserASTContentExtensionMethodEnd method = node;
+      visitExtensionMethod(method, method.beginToken, method.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentMixinMethodEnd) {
+      DirectParserASTContentMixinMethodEnd method = node;
+      visitMixinMethod(method, method.beginToken, method.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentImportEnd) {
+      DirectParserASTContentImportEnd import = node;
+      visitImport(import, import.importKeyword, import.semicolon);
+      return;
+    }
+    if (node is DirectParserASTContentExportEnd) {
+      DirectParserASTContentExportEnd export = node;
+      visitExport(export, export.exportKeyword, export.semicolon);
+      return;
+    }
+    if (node is DirectParserASTContentTopLevelFieldsEnd) {
+      // TODO(jensj): Possibly this could go into more details too
+      // (e.g. to split up a field declaration).
+      DirectParserASTContentTopLevelFieldsEnd fields = node;
+      visitTopLevelFields(fields, fields.beginToken, fields.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentClassFieldsEnd) {
+      // TODO(jensj): Possibly this could go into more details too
+      // (e.g. to split up a field declaration).
+      DirectParserASTContentClassFieldsEnd fields = node;
+      visitClassFields(fields, fields.beginToken, fields.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentExtensionFieldsEnd) {
+      // TODO(jensj): Possibly this could go into more details too
+      // (e.g. to split up a field declaration).
+      DirectParserASTContentExtensionFieldsEnd fields = node;
+      visitExtensionFields(fields, fields.beginToken, fields.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentMixinFieldsEnd) {
+      // TODO(jensj): Possibly this could go into more details too
+      // (e.g. to split up a field declaration).
+      DirectParserASTContentMixinFieldsEnd fields = node;
+      visitMixinFields(fields, fields.beginToken, fields.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentNamedMixinApplicationEnd) {
+      DirectParserASTContentNamedMixinApplicationEnd namedMixin = node;
+      visitNamedMixin(namedMixin, namedMixin.begin, namedMixin.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentMixinDeclarationEnd) {
+      DirectParserASTContentMixinDeclarationEnd declaration = node;
+      visitMixin(declaration, declaration.mixinKeyword, declaration.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentEnumEnd) {
+      DirectParserASTContentEnumEnd declaration = node;
+      visitEnum(
+          declaration, declaration.enumKeyword, declaration.leftBrace.endGroup);
+      return;
+    }
+    if (node is DirectParserASTContentLibraryNameEnd) {
+      DirectParserASTContentLibraryNameEnd name = node;
+      visitLibraryName(name, name.libraryKeyword, name.semicolon);
+      return;
+    }
+    if (node is DirectParserASTContentPartEnd) {
+      DirectParserASTContentPartEnd part = node;
+      visitPart(part, part.partKeyword, part.semicolon);
+      return;
+    }
+    if (node is DirectParserASTContentPartOfEnd) {
+      DirectParserASTContentPartOfEnd partOf = node;
+      visitPartOf(partOf, partOf.partKeyword, partOf.semicolon);
+      return;
+    }
+    if (node is DirectParserASTContentExtensionDeclarationEnd) {
+      DirectParserASTContentExtensionDeclarationEnd ext = node;
+      visitExtension(ext, ext.extensionKeyword, ext.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentClassConstructorEnd) {
+      DirectParserASTContentClassConstructorEnd decl = node;
+      visitClassConstructor(decl, decl.beginToken, decl.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentExtensionConstructorEnd) {
+      DirectParserASTContentExtensionConstructorEnd decl = node;
+      visitExtensionConstructor(decl, decl.beginToken, decl.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentClassFactoryMethodEnd) {
+      DirectParserASTContentClassFactoryMethodEnd decl = node;
+      visitClassFactoryMethod(decl, decl.beginToken, decl.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentExtensionFactoryMethodEnd) {
+      DirectParserASTContentExtensionFactoryMethodEnd decl = node;
+      visitExtensionFactoryMethod(decl, decl.beginToken, decl.endToken);
+      return;
+    }
+    if (node is DirectParserASTContentMetadataEnd) {
+      DirectParserASTContentMetadataEnd decl = node;
+      // TODO(jensj): endToken is not part of the metadata! It's the first token
+      // of the next thing.
+      visitMetadata(decl, decl.beginToken, decl.endToken.previous);
+      return;
+    }
+
+    throw "Unknown: $node (${node.runtimeType} @ ${node.what})";
+  }
+
+  void visitChildren(DirectParserASTContent node) {
+    if (node.children == null) return;
+    final int numChildren = node.children.length;
+    for (int i = 0; i < numChildren; i++) {
+      DirectParserASTContent child = node.children[i];
+      accept(child);
+    }
+  }
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitImport(DirectParserASTContentImportEnd node, Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitExport(DirectParserASTContentExportEnd node, Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitTypedef(DirectParserASTContentFunctionTypeAliasEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers can call visitChildren on this node.
+  void visitMetadataStar(DirectParserASTContentMetadataStarEnd node) {
+    visitChildren(node);
+  }
+
+  /// Note: Implementers can call visitChildren on this node.
+  void visitClass(DirectParserASTContentClassDeclarationEnd node,
+      Token startInclusive, Token endInclusive) {
+    visitChildren(node);
+  }
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitTopLevelMethod(DirectParserASTContentTopLevelMethodEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitClassMethod(DirectParserASTContentClassMethodEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitExtensionMethod(DirectParserASTContentExtensionMethodEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitMixinMethod(DirectParserASTContentMixinMethodEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitTopLevelFields(DirectParserASTContentTopLevelFieldsEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitClassFields(DirectParserASTContentClassFieldsEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitExtensionFields(DirectParserASTContentExtensionFieldsEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitMixinFields(DirectParserASTContentMixinFieldsEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers can call visitChildren on this node.
+  void visitNamedMixin(DirectParserASTContentNamedMixinApplicationEnd node,
+      Token startInclusive, Token endInclusive) {
+    visitChildren(node);
+  }
+
+  /// Note: Implementers can call visitChildren on this node.
+  void visitMixin(DirectParserASTContentMixinDeclarationEnd node,
+      Token startInclusive, Token endInclusive) {
+    visitChildren(node);
+  }
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitEnum(DirectParserASTContentEnumEnd node, Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitLibraryName(DirectParserASTContentLibraryNameEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitPart(DirectParserASTContentPartEnd node, Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitPartOf(DirectParserASTContentPartOfEnd node, Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers can call visitChildren on this node.
+  void visitExtension(DirectParserASTContentExtensionDeclarationEnd node,
+      Token startInclusive, Token endInclusive) {
+    visitChildren(node);
+  }
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitClassConstructor(DirectParserASTContentClassConstructorEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitExtensionConstructor(
+      DirectParserASTContentExtensionConstructorEnd node,
+      Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitClassFactoryMethod(DirectParserASTContentClassFactoryMethodEnd node,
+      Token startInclusive, Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitExtensionFactoryMethod(
+      DirectParserASTContentExtensionFactoryMethodEnd node,
+      Token startInclusive,
+      Token endInclusive) {}
+
+  /// Note: Implementers are NOT expected to call visitChildren on this node.
+  void visitMetadata(DirectParserASTContentMetadataEnd node,
+      Token startInclusive, Token endInclusive) {}
+}
+
 extension GeneralASTContentExtension on DirectParserASTContent {
   bool isClass() {
     if (this is! DirectParserASTContentTopLevelDeclarationEnd) {
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index e5a4357..2b4078a 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -6,11 +6,18 @@
 
 library fasta.testing.suite;
 
-import 'dart:convert' show jsonDecode;
+import 'dart:convert' show jsonDecode, utf8;
 
 import 'dart:io' show Directory, File, Platform;
 
+import 'dart:typed_data' show Uint8List;
+
+import 'package:_fe_analyzer_shared/src/scanner/token.dart'
+    show LanguageVersionToken, Token;
+
 import 'package:_fe_analyzer_shared/src/util/colors.dart' as colors;
+import 'package:compiler/src/kernel/dart2js_target.dart';
+import 'package:dev_compiler/dev_compiler.dart';
 
 import 'package:front_end/src/api_prototype/compiler_options.dart'
     show
@@ -32,6 +39,9 @@
         defaultAllowedExperimentalFlags,
         isExperimentEnabled;
 
+import 'package:front_end/src/api_prototype/file_system.dart'
+    show FileSystem, FileSystemEntity, FileSystemException;
+
 import 'package:front_end/src/api_prototype/standard_file_system.dart'
     show StandardFileSystem;
 
@@ -42,11 +52,14 @@
     show ProcessedOptions;
 
 import 'package:front_end/src/compute_platform_binaries_location.dart'
-    show computePlatformBinariesLocation;
+    show computePlatformBinariesLocation, computePlatformDillName;
 
-import 'package:front_end/src/base/command_line_options.dart';
+import 'package:front_end/src/base/command_line_options.dart' show Flags;
 
-import 'package:front_end/src/base/nnbd_mode.dart';
+import 'package:front_end/src/base/nnbd_mode.dart' show NnbdMode;
+
+import 'package:front_end/src/fasta/builder/library_builder.dart'
+    show LibraryBuilder;
 
 import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
 
@@ -72,6 +85,11 @@
 
 import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
 
+import 'package:front_end/src/fasta/util/direct_parser_ast.dart'
+    show DirectParserASTContentVisitor, getAST;
+
+import 'package:front_end/src/fasta/util/direct_parser_ast_helper.dart';
+
 import 'package:kernel/ast.dart'
     show
         AwaitExpression,
@@ -84,6 +102,7 @@
         FileUriNode,
         InvalidExpression,
         Library,
+        LibraryPart,
         Member,
         Node,
         NonNullableByDefaultCompiledMode,
@@ -197,6 +216,10 @@
   {
     "name": "SemiFuzzFailure",
     "group": "Fail"
+  },
+  {
+    "name": "SemiFuzzCrash",
+    "group": "Fail"
   }
 ]
 ''';
@@ -311,9 +334,7 @@
   final ExpectationSet expectationSet =
       new ExpectationSet.fromJsonList(jsonDecode(EXPECTATIONS));
 
-  Uri platformUri;
-
-  Component platform;
+  Map<Uri, Component> _platforms = {};
 
   FastaContext(
       this.baseUri,
@@ -656,19 +677,16 @@
 
   Expectation get verificationError => expectationSet["VerificationError"];
 
-  Future ensurePlatformUris() async {
-    if (platformUri == null) {
-      platformUri = platformBinaries.resolve("vm_platform_strong.dill");
-    }
-  }
-
-  Future<Component> loadPlatform() async {
-    if (platform == null) {
-      await ensurePlatformUris();
-      platform = loadComponentFromBytes(
-          new File.fromUri(platformUri).readAsBytesSync());
-    }
-    return platform;
+  Future<Component> loadPlatform(Target target, NnbdMode nnbdMode) async {
+    String fileName = computePlatformDillName(
+        target,
+        nnbdMode,
+        () => throw new UnsupportedError(
+            "No platform dill for target '${target.name}' with $nnbdMode."));
+    Uri uri = platformBinaries.resolve(fileName);
+    return _platforms.putIfAbsent(uri, () {
+      return loadComponentFromBytes(new File.fromUri(uri).readAsBytesSync());
+    });
   }
 
   @override
@@ -761,7 +779,7 @@
         .computeExplicitExperimentalFlags(context.explicitExperimentalFlags);
     switch (folderOptions.target) {
       case "vm":
-        if (context.platformUri == null) {
+        if (context._platforms.isEmpty) {
           throw "Executed `Run` step before initializing the context.";
         }
         File generated = new File.fromUri(result.outputUri);
@@ -796,6 +814,9 @@
             result, runResult.outcome, runResult.error);
       case "none":
       case "noneWithJs":
+      case "dart2js":
+      case "dartdevc":
+        // TODO(johnniwinther): Support running dart2js and/or dartdevc.
         return pass(result);
       default:
         throw new ArgumentError(
@@ -981,17 +1002,27 @@
 class CompilationSetup {
   final TestOptions testOptions;
   final FolderOptions folderOptions;
+  final CompilerOptions compilerOptions;
   final ProcessedOptions options;
   final List<Iterable<String>> errors;
-  final ProcessedOptions Function(
+  final CompilerOptions Function(
           NnbdMode nnbdMode,
           AllowedExperimentalFlags allowedExperimentalFlags,
           Map<ExperimentalFlag, Version> experimentEnabledVersion,
           Map<ExperimentalFlag, Version> experimentReleasedVersion)
+      createCompilerOptions;
+
+  final ProcessedOptions Function(CompilerOptions compilerOptions)
       createProcessedOptions;
 
-  CompilationSetup(this.testOptions, this.folderOptions, this.options,
-      this.errors, this.createProcessedOptions);
+  CompilationSetup(
+      this.testOptions,
+      this.folderOptions,
+      this.compilerOptions,
+      this.options,
+      this.errors,
+      this.createCompilerOptions,
+      this.createProcessedOptions);
 }
 
 CompilationSetup createCompilationSetup(
@@ -1011,7 +1042,7 @@
       : (folderOptions.nnbdAgnosticMode ? NnbdMode.Agnostic : NnbdMode.Strong);
   List<Uri> inputs = <Uri>[description.uri];
 
-  ProcessedOptions createProcessedOptions(
+  CompilerOptions createCompilerOptions(
       NnbdMode nnbdMode,
       AllowedExperimentalFlags allowedExperimentalFlags,
       Map<ExperimentalFlag, Version> experimentEnabledVersion,
@@ -1033,6 +1064,10 @@
       compilerOptions.currentSdkVersion =
           folderOptions.overwriteCurrentSdkVersion;
     }
+    return compilerOptions;
+  }
+
+  ProcessedOptions createProcessedOptions(CompilerOptions compilerOptions) {
     return new ProcessedOptions(options: compilerOptions, inputs: inputs);
   }
 
@@ -1040,14 +1075,14 @@
   // platforms and independent of stdin/stderr.
   colors.enableColors = false;
 
-  ProcessedOptions options = createProcessedOptions(
+  CompilerOptions compilerOptions = createCompilerOptions(
       nnbdMode,
       testOptions.allowedExperimentalFlags,
       testOptions.experimentEnabledVersion,
       testOptions.experimentReleasedVersion);
-
-  return new CompilationSetup(
-      testOptions, folderOptions, options, errors, createProcessedOptions);
+  ProcessedOptions options = createProcessedOptions(compilerOptions);
+  return new CompilationSetup(testOptions, folderOptions, compilerOptions,
+      options, errors, createCompilerOptions, createProcessedOptions);
 }
 
 class FuzzCompiles
@@ -1069,16 +1104,45 @@
         createCompilationSetup(result.description, context);
 
     Target backendTarget = compilationSetup.options.target;
-    if (backendTarget is TestVmTarget) {
+    if (backendTarget is TestTarget) {
       // For the fuzzing we want to run the VM transformations, i.e. have the
       // incremental compiler behave as normal.
-      backendTarget.enabled = true;
+      backendTarget.performModularTransformations = true;
     }
 
     UriTranslator uriTranslator =
         await context.computeUriTranslator(result.description);
 
-    Component platform = await context.loadPlatform();
+    Component platform = await context.loadPlatform(
+        backendTarget, compilationSetup.options.nnbdMode);
+    Result<ComponentResult> passResult = await performFileInvalidation(
+        compilationSetup,
+        platform,
+        uriTranslator,
+        result,
+        context,
+        originalFlag);
+    if (passResult != null) return passResult;
+
+    passResult = await performChunkReordering(compilationSetup, platform,
+        uriTranslator, result, context, originalFlag);
+    if (passResult != null) return passResult;
+
+    return pass(result);
+  }
+
+  /// Perform a number of compilations where each user-file is invalidated
+  /// one at a time, and the code recompiled after each invalidation.
+  /// Verifies that either it's an error in all cases or in no cases.
+  /// Verifies that the same libraries comes out as a result.
+  Future<Result<ComponentResult>> performFileInvalidation(
+      CompilationSetup compilationSetup,
+      Component platform,
+      UriTranslator uriTranslator,
+      ComponentResult result,
+      FastaContext context,
+      bool originalFlag) async {
+    compilationSetup.errors.clear();
     IncrementalCompiler incrementalCompiler =
         new IncrementalCompiler.fromComponent(
             new CompilerContext(compilationSetup.options), platform);
@@ -1156,7 +1220,410 @@
     context.explicitExperimentalFlags[
         ExperimentalFlag.alternativeInvalidationStrategy] = originalFlag;
 
-    return pass(result);
+    return null;
+  }
+
+  /// Perform a number of compilations where each user-file is in turn sorted
+  /// in both ascending and descending order (i.e. the procedures and classes
+  /// etc are sorted).
+  /// Verifies that either it's an error in all cases or in no cases.
+  /// Verifies that the same libraries comes out as a result.
+  Future<Result<ComponentResult>> performChunkReordering(
+      CompilationSetup compilationSetup,
+      Component platform,
+      UriTranslator uriTranslator,
+      ComponentResult result,
+      FastaContext context,
+      bool originalFlag) async {
+    compilationSetup.errors.clear();
+
+    FileSystem orgFileSystem = compilationSetup.options.fileSystem;
+    compilationSetup.options.clearFileSystemCache();
+    _FakeFileSystem fs = new _FakeFileSystem(orgFileSystem);
+    compilationSetup.compilerOptions.fileSystem = fs;
+    IncrementalCompiler incrementalCompiler =
+        new IncrementalCompiler.fromComponent(
+            new CompilerContext(compilationSetup.options), platform);
+    await incrementalCompiler.computeDelta();
+
+    final bool expectErrors = compilationSetup.errors.isNotEmpty;
+    List<Iterable<String>> originalErrors =
+        new List<Iterable<String>>.from(compilationSetup.errors);
+    compilationSetup.errors.clear();
+
+    // Create lookup-table from file uri to whatever.
+    Map<Uri, LibraryBuilder> builders = {};
+    for (LibraryBuilder builder
+        in incrementalCompiler.userCode.loader.builders.values) {
+      if (builder.importUri.scheme == "dart" && !builder.isSynthetic) continue;
+      builders[builder.fileUri] = builder;
+      for (LibraryPart part in builder.library.parts) {
+        Uri thisPartUri = builder.importUri.resolve(part.partUri);
+        if (thisPartUri.scheme == "package") {
+          thisPartUri =
+              incrementalCompiler.userCode.uriTranslator.translate(thisPartUri);
+        }
+        builders[thisPartUri] = builder;
+      }
+    }
+
+    for (Uri uri in fs.data.keys) {
+      print("Work on $uri");
+      LibraryBuilder builder = builders[uri];
+      if (builder == null) {
+        print("Skipping $uri -- couldn't find builder for it.");
+        continue;
+      }
+      Uint8List orgData = fs.data[uri];
+      FuzzAstVisitorSorter fuzzAstVisitorSorter =
+          new FuzzAstVisitorSorter(orgData, builder.isNonNullableByDefault);
+
+      // Sort ascending and then compile. Then sort descending and try again.
+      for (void Function() sorter in [
+        () => fuzzAstVisitorSorter.sortAscending(),
+        () => fuzzAstVisitorSorter.sortDescending(),
+      ]) {
+        sorter();
+        StringBuffer sb = new StringBuffer();
+        for (FuzzAstVisitorSorterChunk chunk in fuzzAstVisitorSorter.chunks) {
+          sb.writeln(chunk.getSource());
+        }
+        Uint8List sortedData = utf8.encode(sb.toString());
+        fs.data[uri] = sortedData;
+        incrementalCompiler = new IncrementalCompiler.fromComponent(
+            new CompilerContext(compilationSetup.options), platform);
+        try {
+          await incrementalCompiler.computeDelta();
+        } catch (e, st) {
+          return new Result<ComponentResult>(
+              result,
+              context.expectationSet["SemiFuzzCrash"],
+              "Crashed with '$e' after reordering '$uri' to\n\n"
+              "$sb\n\n"
+              "$st");
+        }
+        final bool gotErrors = compilationSetup.errors.isNotEmpty;
+        String errorsString = compilationSetup.errors
+            .map((error) => error.join('\n'))
+            .join('\n\n');
+        compilationSetup.errors.clear();
+
+        // TODO(jensj): When we get errors we should try to verify it's
+        // "the same" errors (note, though, that they will naturally be at a
+        // changed location --- some will likely have different wording).
+        if (expectErrors != gotErrors) {
+          if (expectErrors) {
+            String errorsString =
+                originalErrors.map((error) => error.join('\n')).join('\n\n');
+            return new Result<ComponentResult>(
+                result,
+                context.expectationSet["SemiFuzzFailure"],
+                "Expected these errors:\n${errorsString}\n\n"
+                "but didn't get any after reordering $uri "
+                "to have this content:\n\n"
+                "$sb");
+          } else {
+            return new Result<ComponentResult>(
+                result,
+                context.expectationSet["SemiFuzzFailure"],
+                "Unexpected errors:\n${errorsString}\n\n"
+                "after reordering $uri to have this content:\n\n"
+                "$sb");
+          }
+        }
+      }
+    }
+
+    compilationSetup.options.clearFileSystemCache();
+    compilationSetup.compilerOptions.fileSystem = orgFileSystem;
+    return null;
+  }
+}
+
+class FuzzAstVisitorSorterChunk {
+  final String data;
+  final String metadataAndComments;
+  final int layer;
+
+  FuzzAstVisitorSorterChunk(this.data, this.metadataAndComments, this.layer);
+
+  String toString() {
+    return "FuzzAstVisitorSorterChunk[${getSource()}]";
+  }
+
+  String getSource() {
+    if (metadataAndComments != null) {
+      return "$metadataAndComments\n$data";
+    }
+    return "$data";
+  }
+}
+
+enum FuzzSorterState { nonSortable, importExportSortable, sortableRest }
+
+class FuzzAstVisitorSorter extends DirectParserASTContentVisitor {
+  final Uint8List bytes;
+  final String asString;
+  final bool nnbd;
+
+  FuzzAstVisitorSorter(this.bytes, this.nnbd) : asString = utf8.decode(bytes) {
+    DirectParserASTContentCompilationUnitEnd ast = getAST(bytes,
+        includeBody: false,
+        includeComments: true,
+        enableExtensionMethods: true,
+        enableNonNullable: nnbd);
+    accept(ast);
+    if (metadataStart != null) {
+      String metadata = asString.substring(
+          metadataStart.charOffset, metadataEndInclusive.charEnd);
+      layer++;
+      chunks.add(new FuzzAstVisitorSorterChunk(
+        "",
+        metadata,
+        layer,
+      ));
+    }
+  }
+
+  void sortAscending() {
+    chunks.sort(_ascendingSorter);
+  }
+
+  void sortDescending() {
+    chunks.sort(_descendingSorter);
+  }
+
+  int _ascendingSorter(
+      FuzzAstVisitorSorterChunk a, FuzzAstVisitorSorterChunk b) {
+    if (a.layer < b.layer) return -1;
+    if (a.layer > b.layer) return 1;
+    return a.data.compareTo(b.data);
+  }
+
+  int _descendingSorter(
+      FuzzAstVisitorSorterChunk a, FuzzAstVisitorSorterChunk b) {
+    // Only sort layers differently internally.
+    if (a.layer < b.layer) return -1;
+    if (a.layer > b.layer) return 1;
+    return b.data.compareTo(a.data);
+  }
+
+  List<FuzzAstVisitorSorterChunk> chunks = [];
+  Token metadataStart;
+  Token metadataEndInclusive;
+  int layer = 0;
+  FuzzSorterState state = null;
+
+  /// If there's any LanguageVersionToken in the comment preceding the given
+  /// token add it as a separate chunk to keep it in place.
+  void _chunkOutLanguageVersionComment(Token fromToken) {
+    Token comment = fromToken.precedingComments;
+    bool hasLanguageVersion = comment is LanguageVersionToken;
+    while (comment.next != null) {
+      comment = comment.next;
+      hasLanguageVersion |= comment is LanguageVersionToken;
+    }
+    if (hasLanguageVersion) {
+      layer++;
+      chunks.add(new FuzzAstVisitorSorterChunk(
+        asString.substring(
+            fromToken.precedingComments.charOffset, comment.charEnd),
+        null,
+        layer,
+      ));
+      layer++;
+    }
+  }
+
+  void handleData(
+      FuzzSorterState thisState, Token startInclusive, Token endInclusive) {
+    // Non-sortable things always gets a new layer.
+    if (state != thisState || thisState == FuzzSorterState.nonSortable) {
+      state = thisState;
+      layer++;
+    }
+
+    // "Chunk out" any language version at the top, i.e. if there are no other
+    // chunks and there is a metadata, any language version chunk on the
+    // metadata will be "chunked out". If there is no metadata, any language
+    // version on the non-metadata will be "chunked out".
+    // Note that if there is metadata and there is a language version on the
+    // non-metadata it will not be chunked out as it's in an illegal place
+    // anyway, so possibly allowing it to be sorted (and put in another place)
+    // won't make it more or less illegal.
+    if (metadataStart != null &&
+        metadataStart.precedingComments != null &&
+        chunks.isEmpty) {
+      _chunkOutLanguageVersionComment(metadataStart);
+    } else if (metadataStart == null &&
+        startInclusive.precedingComments != null &&
+        chunks.isEmpty) {
+      _chunkOutLanguageVersionComment(startInclusive);
+    }
+
+    String metadata;
+    if (metadataStart != null || metadataEndInclusive != null) {
+      metadata = asString.substring(
+          metadataStart.charOffset, metadataEndInclusive.charEnd);
+    }
+    chunks.add(new FuzzAstVisitorSorterChunk(
+      asString.substring(startInclusive.charOffset, endInclusive.charEnd),
+      metadata,
+      layer,
+    ));
+    metadataStart = null;
+    metadataEndInclusive = null;
+  }
+
+  @override
+  void visitExport(DirectParserASTContentExportEnd node, Token startInclusive,
+      Token endInclusive) {
+    handleData(
+        FuzzSorterState.importExportSortable, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitImport(DirectParserASTContentImportEnd node, Token startInclusive,
+      Token endInclusive) {
+    handleData(
+        FuzzSorterState.importExportSortable, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitClass(DirectParserASTContentClassDeclarationEnd node,
+      Token startInclusive, Token endInclusive) {
+    // TODO(jensj): Possibly sort stuff inside of this too.
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitEnum(DirectParserASTContentEnumEnd node, Token startInclusive,
+      Token endInclusive) {
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitExtension(DirectParserASTContentExtensionDeclarationEnd node,
+      Token startInclusive, Token endInclusive) {
+    // TODO(jensj): Possibly sort stuff inside of this too.
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitLibraryName(DirectParserASTContentLibraryNameEnd node,
+      Token startInclusive, Token endInclusive) {
+    handleData(FuzzSorterState.nonSortable, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitMetadata(DirectParserASTContentMetadataEnd node,
+      Token startInclusive, Token endInclusive) {
+    if (metadataStart == null) {
+      metadataStart = startInclusive;
+      metadataEndInclusive = endInclusive;
+    } else {
+      metadataEndInclusive = endInclusive;
+    }
+  }
+
+  @override
+  void visitMixin(DirectParserASTContentMixinDeclarationEnd node,
+      Token startInclusive, Token endInclusive) {
+    // TODO(jensj): Possibly sort stuff inside of this too.
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitNamedMixin(DirectParserASTContentNamedMixinApplicationEnd node,
+      Token startInclusive, Token endInclusive) {
+    // TODO(jensj): Possibly sort stuff inside of this too.
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitPart(DirectParserASTContentPartEnd node, Token startInclusive,
+      Token endInclusive) {
+    handleData(FuzzSorterState.nonSortable, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitPartOf(DirectParserASTContentPartOfEnd node, Token startInclusive,
+      Token endInclusive) {
+    handleData(FuzzSorterState.nonSortable, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitTopLevelFields(DirectParserASTContentTopLevelFieldsEnd node,
+      Token startInclusive, Token endInclusive) {
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitTopLevelMethod(DirectParserASTContentTopLevelMethodEnd node,
+      Token startInclusive, Token endInclusive) {
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+
+  @override
+  void visitTypedef(DirectParserASTContentFunctionTypeAliasEnd node,
+      Token startInclusive, Token endInclusive) {
+    handleData(FuzzSorterState.sortableRest, startInclusive, endInclusive);
+  }
+}
+
+class _FakeFileSystem extends FileSystem {
+  bool redirectAndRecord = true;
+  final Map<Uri, Uint8List> data = {};
+  final FileSystem fs;
+  _FakeFileSystem(this.fs);
+
+  @override
+  FileSystemEntity entityForUri(Uri uri) {
+    return new _FakeFileSystemEntity(this, uri);
+  }
+}
+
+class _FakeFileSystemEntity extends FileSystemEntity {
+  final _FakeFileSystem fs;
+  final Uri uri;
+  _FakeFileSystemEntity(this.fs, this.uri);
+
+  Future<void> _ensureCachedIfOk() async {
+    if (fs.data.containsKey(uri)) return;
+    if (!fs.redirectAndRecord) {
+      throw "Asked for file in non-recording mode that wasn't known";
+    }
+
+    FileSystemEntity f = fs.fs.entityForUri(uri);
+    if (!await f.exists()) {
+      fs.data[uri] = null;
+      return;
+    }
+    fs.data[uri] = await f.readAsBytes();
+  }
+
+  @override
+  Future<bool> exists() async {
+    await _ensureCachedIfOk();
+    Uint8List data = fs.data[uri];
+    if (data == null) return false;
+    return true;
+  }
+
+  @override
+  Future<List<int>> readAsBytes() async {
+    await _ensureCachedIfOk();
+    Uint8List data = fs.data[uri];
+    if (data == null) throw new FileSystemException(uri, "File doesn't exist.");
+    return data;
+  }
+
+  @override
+  Future<String> readAsString() async {
+    await _ensureCachedIfOk();
+    Uint8List data = fs.data[uri];
+    if (data == null) throw new FileSystemException(uri, "File doesn't exist.");
+    return utf8.decode(data);
   }
 }
 
@@ -1181,6 +1648,12 @@
     case "noneWithJs":
       target = new NoneWithJsTarget(targetFlags);
       break;
+    case "dart2js":
+      target = new TestDart2jsTarget('dart2js', targetFlags);
+      break;
+    case "dartdevc":
+      target = new TestDevCompilerTarget(targetFlags);
+      break;
     default:
       throw new ArgumentError(
           "Unsupported test target '${folderOptions.target}'.");
@@ -1233,10 +1706,11 @@
       ProcessedOptions linkOptions = compilationSetup.options;
       if (compilationSetup.testOptions.nnbdMode != null) {
         linkOptions = compilationSetup.createProcessedOptions(
-            compilationSetup.testOptions.nnbdMode,
-            compilationSetup.testOptions.allowedExperimentalFlags,
-            compilationSetup.testOptions.experimentEnabledVersion,
-            compilationSetup.testOptions.experimentReleasedVersion);
+            compilationSetup.createCompilerOptions(
+                compilationSetup.testOptions.nnbdMode,
+                compilationSetup.testOptions.allowedExperimentalFlags,
+                compilationSetup.testOptions.experimentEnabledVersion,
+                compilationSetup.testOptions.experimentReleasedVersion));
       }
       await CompilerContext.runWithOptions(linkOptions, (_) async {
         KernelTarget sourceTarget = await outlineInitialization(
@@ -1259,16 +1733,16 @@
         // of the user of this linked dependency we have to transform this too.
         // We do that now.
         Target backendTarget = sourceTarget.backendTarget;
-        if (backendTarget is TestVmTarget) {
-          backendTarget.enabled = true;
+        if (backendTarget is TestTarget) {
+          backendTarget.performModularTransformations = true;
         }
         try {
           if (sourceTarget.loader.coreTypes != null) {
             sourceTarget.runBuildTransformations();
           }
         } finally {
-          if (backendTarget is TestVmTarget) {
-            backendTarget.enabled = false;
+          if (backendTarget is TestTarget) {
+            backendTarget.performModularTransformations = false;
           }
         }
 
@@ -1338,7 +1812,8 @@
       ProcessedOptions options,
       List<Uri> entryPoints,
       {Component alsoAppend}) async {
-    Component platform = await context.loadPlatform();
+    Component platform =
+        await context.loadPlatform(options.target, options.nnbdMode);
     Ticker ticker = new Ticker();
     UriTranslator uriTranslator =
         await context.computeUriTranslator(description);
@@ -1372,19 +1847,19 @@
       KernelTarget sourceTarget = context.componentToTarget[component];
       context.componentToTarget.remove(component);
       Target backendTarget = sourceTarget.backendTarget;
-      if (backendTarget is TestVmTarget) {
-        backendTarget.enabled = true;
+      if (backendTarget is TestTarget) {
+        backendTarget.performModularTransformations = true;
       }
       try {
         if (sourceTarget.loader.coreTypes != null) {
           sourceTarget.runBuildTransformations();
         }
       } finally {
-        if (backendTarget is TestVmTarget) {
-          backendTarget.enabled = false;
+        if (backendTarget is TestTarget) {
+          backendTarget.performModularTransformations = false;
         }
       }
-      List<String> errors = VerifyTransformed.verify(component);
+      List<String> errors = VerifyTransformed.verify(component, backendTarget);
       if (errors.isNotEmpty) {
         return new Result<ComponentResult>(
             result,
@@ -1442,8 +1917,11 @@
 // TODO(johnniwinther): Add checks for all nodes that are unsupported after
 // transformation.
 class VerifyTransformed extends Visitor<void> {
+  final Target target;
   List<String> errors = [];
 
+  VerifyTransformed(this.target);
+
   @override
   void defaultNode(Node node) {
     node.visitChildren(this);
@@ -1451,22 +1929,20 @@
 
   @override
   void visitAwaitExpression(AwaitExpression node) {
-    errors.add("ERROR: Untransformed await expression: $node");
+    if (target is VmTarget) {
+      errors.add("ERROR: Untransformed await expression: $node");
+    }
   }
 
-  static List<String> verify(Component component) {
-    VerifyTransformed visitor = new VerifyTransformed();
+  static List<String> verify(Component component, Target target) {
+    VerifyTransformed visitor = new VerifyTransformed(target);
     component.accept(visitor);
     return visitor.errors;
   }
 }
 
-class TestVmTarget extends VmTarget {
-  bool enabled = false;
-
-  TestVmTarget(TargetFlags flags) : super(flags);
-
-  String get name => "vm";
+mixin TestTarget on Target {
+  bool performModularTransformations = false;
 
   @override
   void performModularTransformationsOnLibraries(
@@ -1479,7 +1955,7 @@
       ReferenceFromIndex referenceFromIndex,
       {void logger(String msg),
       ChangedStructureNotifier changedStructureNotifier}) {
-    if (enabled) {
+    if (performModularTransformations) {
       super.performModularTransformationsOnLibraries(
           component,
           coreTypes,
@@ -1493,6 +1969,10 @@
   }
 }
 
+class TestVmTarget extends VmTarget with TestTarget {
+  TestVmTarget(TargetFlags flags) : super(flags);
+}
+
 class EnsureNoErrors
     extends Step<ComponentResult, ComponentResult, FastaContext> {
   const EnsureNoErrors();
@@ -1568,3 +2048,11 @@
   @override
   NumberSemantics get numberSemantics => NumberSemantics.js;
 }
+
+class TestDart2jsTarget extends Dart2jsTarget with TestTarget {
+  TestDart2jsTarget(String name, TargetFlags flags) : super(name, flags);
+}
+
+class TestDevCompilerTarget extends DevCompilerTarget with TestTarget {
+  TestDevCompilerTarget(TargetFlags flags) : super(flags);
+}
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart
index 1643d2f..d50b5c6 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart
@@ -27,12 +27,12 @@
 
   DartType greatestClosure(DartType schema) {
     return typeSchemaElimination.greatestClosure(
-        schema, const DynamicType(), const NeverType(Nullability.nonNullable));
+        schema, const DynamicType(), const NeverType.nonNullable());
   }
 
   DartType leastClosure(DartType schema) {
     return typeSchemaElimination.leastClosure(
-        schema, const DynamicType(), const NeverType(Nullability.nonNullable));
+        schema, const DynamicType(), const NeverType.nonNullable());
   }
 
   void testGreatest(String type, String expectedClosure) {
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
index 68f81d6..d17ed22 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
@@ -1461,7 +1461,7 @@
         typeSchemaEnvironment.solveTypeConstraint(
             parseConstraint(constraint),
             coreTypes.objectNullableRawType,
-            new NeverType(Nullability.nonNullable),
+            new NeverType.internal(Nullability.nonNullable),
             grounded: grounded),
         parseType(expected));
   }
diff --git a/pkg/front_end/test/fasta/util/direct_parser_ast_test.dart b/pkg/front_end/test/fasta/util/direct_parser_ast_test.dart
index aab38dd..c50e360 100644
--- a/pkg/front_end/test/fasta/util/direct_parser_ast_test.dart
+++ b/pkg/front_end/test/fasta/util/direct_parser_ast_test.dart
@@ -343,10 +343,13 @@
           fields.endToken.offset + fields.endToken.length)
     ];
   } else if (item.isEnum()) {
-    DirectParserASTContentEnumEnd enm = item.asEnum();
+    DirectParserASTContentEnumEnd declaration = item.asEnum();
     return [
-      getCutContent(data, enm.enumKeyword.offset,
-          enm.leftBrace.endGroup.offset + enm.leftBrace.endGroup.length)
+      getCutContent(
+          data,
+          declaration.enumKeyword.offset,
+          declaration.leftBrace.endGroup.offset +
+              declaration.leftBrace.endGroup.length)
     ];
   } else if (item.isMixinDeclaration()) {
     DirectParserASTContentMixinDeclarationEnd mixinDecl =
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 55be1a1..2ec515d 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -102,6 +102,7 @@
 charset
 checkme
 checkout
+chunked
 cipd
 circular
 class1a
@@ -183,6 +184,7 @@
 depended
 depfile
 desc
+descending
 descriptors
 detector
 deviation
@@ -229,7 +231,6 @@
 edits
 elapse
 ell
-enm
 entrypoint
 entrypoints
 eoo
@@ -422,6 +423,7 @@
 la
 launch
 launching
+layers
 le
 legs
 lengths
@@ -604,6 +606,8 @@
 remap
 remaps
 rendition
+reorder
+reordering
 repaint
 repro
 reproduce
@@ -654,6 +658,7 @@
 smoke
 snull
 somehow
+sorter
 spans
 spawn
 spell
@@ -773,6 +778,7 @@
 wherever
 whiskers
 wins
+wording
 workflow
 worlds
 wrongly
diff --git a/pkg/front_end/testcases/dart2js/folder.options b/pkg/front_end/testcases/dart2js/folder.options
new file mode 100644
index 0000000..53ae728
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/folder.options
@@ -0,0 +1 @@
+--target=dart2js
\ No newline at end of file
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart b/pkg/front_end/testcases/dart2js/list_generate.dart
new file mode 100644
index 0000000..5d45d67
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+main() {
+  new List<int>.generate(10, (i) => i * 2);
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.outline.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.outline.expect
new file mode 100644
index 0000000..e2cba6b
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.outline.expect
@@ -0,0 +1,5 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
new file mode 100644
index 0000000..c9e4703
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
@@ -0,0 +1,7 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method main() → dynamic {
+  core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2));
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
new file mode 100644
index 0000000..a118ecb
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
@@ -0,0 +1,14 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:_interceptors" as _in;
+import "dart:core" as core;
+
+static method main() → dynamic {
+  block {
+    final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+    for (core::int* i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+      core::int i = i;
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2));
+    }
+  } =>_list;
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.textual_outline.expect
new file mode 100644
index 0000000..bae895a
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.textual_outline.expect
@@ -0,0 +1 @@
+main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..bae895a
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.textual_outline_modelled.expect
@@ -0,0 +1 @@
+main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
new file mode 100644
index 0000000..c9e4703
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
@@ -0,0 +1,7 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method main() → dynamic {
+  core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2));
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
new file mode 100644
index 0000000..a118ecb
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
@@ -0,0 +1,14 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:_interceptors" as _in;
+import "dart:core" as core;
+
+static method main() → dynamic {
+  block {
+    final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+    for (core::int* i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+      core::int i = i;
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2));
+    }
+  } =>_list;
+}
diff --git a/pkg/front_end/testcases/dartdevc/folder.options b/pkg/front_end/testcases/dartdevc/folder.options
new file mode 100644
index 0000000..72277e9
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/folder.options
@@ -0,0 +1 @@
+--target=dartdevc
\ No newline at end of file
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart b/pkg/front_end/testcases/dartdevc/private_covariant.dart
new file mode 100644
index 0000000..d914c79
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class Class {
+  _privateMethod(covariant int i) {}
+}
+
+main() {
+  new Class()._privateMethod(0);
+}
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.outline.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.outline.expect
new file mode 100644
index 0000000..3a09c94
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.outline.expect
@@ -0,0 +1,12 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    ;
+  method _privateMethod(covariant core::int i) → dynamic
+    ;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.strong.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.strong.expect
new file mode 100644
index 0000000..3fed750
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.strong.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  method _privateMethod(covariant core::int i) → dynamic {}
+}
+static method main() → dynamic {
+  new self::Class::•().{self::Class::_privateMethod}(0){(core::int) → dynamic};
+}
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.strong.transformed.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.strong.transformed.expect
new file mode 100644
index 0000000..965d2e1
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.strong.transformed.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  method _privateMethod(core::int i) → dynamic {}
+}
+static method main() → dynamic {
+  new self::Class::•().{self::Class::_privateMethod}(0){(core::int) → dynamic};
+}
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.textual_outline.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.textual_outline.expect
new file mode 100644
index 0000000..31f9462
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+class Class {
+  _privateMethod(covariant int i) {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..31f9462
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+class Class {
+  _privateMethod(covariant int i) {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.weak.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.weak.expect
new file mode 100644
index 0000000..3fed750
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.weak.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  method _privateMethod(covariant core::int i) → dynamic {}
+}
+static method main() → dynamic {
+  new self::Class::•().{self::Class::_privateMethod}(0){(core::int) → dynamic};
+}
diff --git a/pkg/front_end/testcases/dartdevc/private_covariant.dart.weak.transformed.expect b/pkg/front_end/testcases/dartdevc/private_covariant.dart.weak.transformed.expect
new file mode 100644
index 0000000..965d2e1
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/private_covariant.dart.weak.transformed.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  method _privateMethod(core::int i) → dynamic {}
+}
+static method main() → dynamic {
+  new self::Class::•().{self::Class::_privateMethod}(0){(core::int) → dynamic};
+}
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index def8457..c7f9575 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -7,6 +7,10 @@
 # strong-mode enabled.
 
 # general/platform_invalid_uris/main: SemiFuzzFailure # semi fuzz fails but isn't currently enabled by default.
+# general/error_recovery/issue_39058.crash: SemiFuzzFailure # semi fuzz fails but isn't currently enabled by default.
+# general/error_recovery/issue_39058_prime.crash: SemiFuzzFailure # semi fuzz fails but isn't currently enabled by default.
+# general/error_recovery/issue_39202.crash: SemiFuzzCrash # semi fuzz fails but isn't currently enabled by default.
+# regress/utf_16_le_content.crash: Crash # semi fuzz fails but isn't currently enabled by default.
 
 extensions/call_methods: TypeCheckError
 extensions/extension_setter_error: TypeCheckError
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index b5e4768..df116f8 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -6,6 +6,7 @@
 # the round trip for Kernel textual serialization where the initial binary
 # Kernel files are produced by compiling Dart code via Fasta.
 
+dartdevc/private_covariant: TextSerializationFailure
 extensions/call_methods: TypeCheckError
 extensions/extension_setter_error: TypeCheckError
 extensions/instance_access_of_static: RuntimeError
diff --git a/pkg/front_end/tool/_fasta/command_line.dart b/pkg/front_end/tool/_fasta/command_line.dart
index 433c88a..754f7f2 100644
--- a/pkg/front_end/tool/_fasta/command_line.dart
+++ b/pkg/front_end/tool/_fasta/command_line.dart
@@ -30,7 +30,7 @@
     show ProcessedOptions;
 
 import 'package:front_end/src/compute_platform_binaries_location.dart'
-    show computePlatformBinariesLocation;
+    show computePlatformBinariesLocation, computePlatformDillName;
 
 import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
 
@@ -399,32 +399,15 @@
 
   final Uri librariesJson = options[Flags.librariesJson];
 
-  String computePlatformDillName() {
-    switch (target.name) {
-      case 'dartdevc':
-        return 'dartdevc.dill';
-      case 'dart2js':
-        return 'dart2js_platform.dill';
-      case 'dart2js_server':
-        return 'dart2js_platform.dill';
-      case 'vm':
-        // TODO(johnniwinther): Stop generating 'vm_platform.dill' and rename
-        // 'vm_platform_strong.dill' to 'vm_platform.dill'.
-        return "vm_platform_strong.dill";
-      case 'none':
-        return "vm_platform_strong.dill";
-      default:
-        throwCommandLineProblem("Target '${target.name}' requires an explicit "
-            "'${Flags.platform}' option.");
-    }
-    return null;
-  }
-
   final Uri platform = compileSdk
       ? null
       : (options[Flags.platform] ??
           computePlatformBinariesLocation(forceBuildDir: true)
-              .resolve(computePlatformDillName()));
+              .resolve(computePlatformDillName(target, nnbdMode, () {
+            throwCommandLineProblem(
+                "Target '${target.name}' requires an explicit "
+                "'${Flags.platform}' option.");
+          })));
   compilerOptions
     ..sdkRoot = sdk
     ..sdkSummary = platform
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 4bb433a33..e42fe77 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3460,7 +3460,7 @@
       case DynamicAccessKind.Dynamic:
         return const DynamicType();
       case DynamicAccessKind.Never:
-        return const NeverType(Nullability.nonNullable);
+        return const NeverType.nonNullable();
       case DynamicAccessKind.Invalid:
       case DynamicAccessKind.Unresolved:
         return const InvalidType();
@@ -4359,7 +4359,7 @@
       case DynamicAccessKind.Dynamic:
         return const DynamicType();
       case DynamicAccessKind.Never:
-        return const NeverType(Nullability.nonNullable);
+        return const NeverType.nonNullable();
       case DynamicAccessKind.Invalid:
       case DynamicAccessKind.Unresolved:
         return const InvalidType();
@@ -6137,7 +6137,7 @@
   DartType getStaticTypeInternal(StaticTypeContext context) {
     DartType operandType = operand.getStaticType(context);
     return operandType is NullType
-        ? const NeverType(Nullability.nonNullable)
+        ? const NeverType.nonNullable()
         : operandType.withDeclaredNullability(Nullability.nonNullable);
   }
 
@@ -6413,7 +6413,7 @@
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.isNonNullableByDefault
-          ? const NeverType(Nullability.nonNullable)
+          ? const NeverType.nonNullable()
           : const BottomType();
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitRethrow(this);
@@ -6447,7 +6447,7 @@
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.isNonNullableByDefault
-          ? const NeverType(Nullability.nonNullable)
+          ? const NeverType.nonNullable()
           : const BottomType();
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitThrow(this);
@@ -8715,7 +8715,29 @@
   @override
   final Nullability declaredNullability;
 
-  const NeverType(this.declaredNullability);
+  const NeverType.nullable() : this.internal(Nullability.nullable);
+
+  const NeverType.nonNullable() : this.internal(Nullability.nonNullable);
+
+  const NeverType.legacy() : this.internal(Nullability.legacy);
+
+  const NeverType.undetermined() : this.internal(Nullability.undetermined);
+
+  const NeverType.internal(this.declaredNullability);
+
+  static NeverType fromNullability(Nullability nullability) {
+    switch (nullability) {
+      case Nullability.nullable:
+        return const NeverType.nullable();
+      case Nullability.nonNullable:
+        return const NeverType.nonNullable();
+      case Nullability.legacy:
+        return const NeverType.legacy();
+      case Nullability.undetermined:
+        return const NeverType.undetermined();
+    }
+    throw new StateError("Unhandled nullability value '${nullability}'.");
+  }
 
   @override
   Nullability get nullability => declaredNullability;
@@ -8746,7 +8768,7 @@
   NeverType withDeclaredNullability(Nullability declaredNullability) {
     return this.declaredNullability == declaredNullability
         ? this
-        : new NeverType(declaredNullability);
+        : NeverType.fromNullability(declaredNullability);
   }
 
   @override
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 732e8e6..125f605 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -2821,7 +2821,7 @@
 
   DartType _readNeverType() {
     int nullabilityIndex = readByte();
-    return new NeverType(Nullability.values[nullabilityIndex]);
+    return NeverType.fromNullability(Nullability.values[nullabilityIndex]);
   }
 
   DartType _readInterfaceType() {
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index 4aa0115..272679f 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -194,7 +194,7 @@
   List<List<int>> stronglyConnected = computeStrongComponents(graph);
   final DartType topType = const DynamicType();
   final DartType bottomType = isNonNullableByDefault
-      ? const NeverType(Nullability.nonNullable)
+      ? const NeverType.nonNullable()
       : const BottomType();
   for (List<int> component in stronglyConnected) {
     Map<TypeParameter, DartType> upperBounds = <TypeParameter, DartType>{};
@@ -461,8 +461,7 @@
     DartType bottomType,
     {Map<FunctionType, List<DartType>> typedefInstantiations}) {
   assert(arguments.length == parameters.length);
-  assert(bottomType == const NeverType(Nullability.nonNullable) ||
-      bottomType is NullType);
+  assert(bottomType == const NeverType.nonNullable() || bottomType is NullType);
   List<TypeArgumentIssue> result = <TypeArgumentIssue>[];
   Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
   for (int i = 0; i < arguments.length; ++i) {
@@ -541,7 +540,7 @@
 
   DartType get bottomType {
     return isNonNullableByDefault
-        ? const NeverType(Nullability.nonNullable)
+        ? const NeverType.nonNullable()
         : const NullType();
   }
 
diff --git a/pkg/kernel/lib/src/const_canonical_type.dart b/pkg/kernel/lib/src/const_canonical_type.dart
index 202e731..9a1dd42 100644
--- a/pkg/kernel/lib/src/const_canonical_type.dart
+++ b/pkg/kernel/lib/src/const_canonical_type.dart
@@ -30,7 +30,7 @@
   // CONST_CANONICAL_TYPE(T) = T* if T is Never or Object
   if (type is NeverType &&
       type.declaredNullability == Nullability.nonNullable) {
-    return const NeverType(Nullability.legacy);
+    return const NeverType.legacy();
   }
   if (type == coreTypes.objectNonNullableRawType) {
     return coreTypes.objectLegacyRawType;
diff --git a/pkg/kernel/lib/src/merge_visitor.dart b/pkg/kernel/lib/src/merge_visitor.dart
index dc1b77b..75505b6 100644
--- a/pkg/kernel/lib/src/merge_visitor.dart
+++ b/pkg/kernel/lib/src/merge_visitor.dart
@@ -203,7 +203,7 @@
     if (b is NeverType) {
       Nullability nullability = mergeNullability(a.nullability, b.nullability);
       if (nullability != null) {
-        return new NeverType(nullability);
+        return NeverType.fromNullability(nullability);
       }
     }
     return null;
diff --git a/pkg/kernel/lib/src/non_null.dart b/pkg/kernel/lib/src/non_null.dart
index 2a90dad..8254073 100644
--- a/pkg/kernel/lib/src/non_null.dart
+++ b/pkg/kernel/lib/src/non_null.dart
@@ -65,12 +65,12 @@
     if (node.declaredNullability == Nullability.nonNullable) {
       return null;
     }
-    return const NeverType(Nullability.nonNullable);
+    return const NeverType.nonNullable();
   }
 
   @override
   DartType visitNullType(NullType node) {
-    return const NeverType(Nullability.nonNullable);
+    return const NeverType.nonNullable();
   }
 
   @override
diff --git a/pkg/kernel/lib/src/norm.dart b/pkg/kernel/lib/src/norm.dart
index aa49ef7..9a39987 100644
--- a/pkg/kernel/lib/src/norm.dart
+++ b/pkg/kernel/lib/src/norm.dart
@@ -95,8 +95,7 @@
     if (node.promotedBound == null) {
       DartType bound = node.parameter.bound;
       if (normalizesToNever(bound)) {
-        DartType result = new NeverType(Nullability.nonNullable)
-            .withDeclaredNullability(node.nullability);
+        DartType result = NeverType.fromNullability(node.nullability);
         return result.accept1(this, variance) ?? result;
       }
       assert(!coreTypes.isBottom(bound));
diff --git a/pkg/kernel/lib/src/replacement_visitor.dart b/pkg/kernel/lib/src/replacement_visitor.dart
index b7cbd3b..6e9a020 100644
--- a/pkg/kernel/lib/src/replacement_visitor.dart
+++ b/pkg/kernel/lib/src/replacement_visitor.dart
@@ -191,7 +191,7 @@
       // No nullability needed to be substituted.
       return null;
     } else {
-      return new NeverType(newNullability);
+      return NeverType.fromNullability(newNullability);
     }
   }
 
diff --git a/pkg/kernel/lib/src/standard_bounds.dart b/pkg/kernel/lib/src/standard_bounds.dart
index de68c1d..c8a1db4 100644
--- a/pkg/kernel/lib/src/standard_bounds.dart
+++ b/pkg/kernel/lib/src/standard_bounds.dart
@@ -331,14 +331,14 @@
           type2Nullability == Nullability.nullable) {
         return type1;
       }
-      return const NeverType(Nullability.nonNullable);
+      return const NeverType.nonNullable();
     } else if (coreTypes.isNull(type2)) {
       Nullability type1Nullability = type1.declaredNullability;
       if (type1Nullability == Nullability.legacy ||
           type1Nullability == Nullability.nullable) {
         return type2;
       }
-      return const NeverType(Nullability.nonNullable);
+      return const NeverType.nonNullable();
     }
 
     // DOWN(T1, T2) where OBJECT(T1) and OBJECT(T2) =
@@ -363,7 +363,7 @@
       if (type2.nullability == Nullability.nonNullable) {
         return type2;
       }
-      return const NeverType(Nullability.nonNullable);
+      return const NeverType.nonNullable();
     } else if (coreTypes.isObject(type2)) {
       if (type1.nullability == Nullability.nonNullable) {
         return type1;
@@ -372,7 +372,7 @@
       if (type1.nullability == Nullability.nonNullable) {
         return type1;
       }
-      return const NeverType(Nullability.nonNullable);
+      return const NeverType.nonNullable();
     }
 
     // DOWN(T1*, T2*) = S* where S is DOWN(T1, T2)
@@ -500,7 +500,7 @@
     }
 
     // DOWN(T1, T2) = Never otherwise.
-    return new NeverType(intersectNullabilities(
+    return NeverType.fromNullability(intersectNullabilities(
         type1.declaredNullability, type2.declaredNullability));
   }
 
@@ -939,7 +939,7 @@
 
     // The fallback result for whenever the following rule applies:
     //     DOWN(T Function<...>(...), S Function<...>(...)) = Never otherwise.
-    final DartType fallbackResult = new NeverType(
+    final DartType fallbackResult = NeverType.fromNullability(
         intersectNullabilities(f.declaredNullability, g.declaredNullability));
 
     if (haveNamed && haveOptionalPositional) return fallbackResult;
@@ -1244,7 +1244,7 @@
       NullabilityAwareTypeVariableEliminator eliminator =
           new NullabilityAwareTypeVariableEliminator(
               eliminationTargets: <TypeParameter>{type1.parameter},
-              bottomType: const NeverType(Nullability.nonNullable),
+              bottomType: const NeverType.nonNullable(),
               topType: coreTypes.objectNullableRawType,
               topFunctionType: coreTypes.functionNonNullableRawType,
               unhandledTypeHandler: (type, recursor) => false);
@@ -1276,7 +1276,7 @@
       NullabilityAwareTypeVariableEliminator eliminator =
           new NullabilityAwareTypeVariableEliminator(
               eliminationTargets: <TypeParameter>{type1.parameter},
-              bottomType: const NeverType(Nullability.nonNullable),
+              bottomType: const NeverType.nonNullable(),
               topType: coreTypes.objectNullableRawType,
               topFunctionType: coreTypes.functionNonNullableRawType,
               unhandledTypeHandler: (type, recursor) => false);
diff --git a/pkg/kernel/lib/testing/type_parser_environment.dart b/pkg/kernel/lib/testing/type_parser_environment.dart
index 48a843a..7cf174b 100644
--- a/pkg/kernel/lib/testing/type_parser_environment.dart
+++ b/pkg/kernel/lib/testing/type_parser_environment.dart
@@ -276,7 +276,8 @@
     } else if (name == "Never") {
       // Don't return a const object to ensure we test implementations that use
       // identical.
-      return new NeverType(interpretParsedNullability(node.parsedNullability));
+      return NeverType.fromNullability(
+          interpretParsedNullability(node.parsedNullability));
     } else if (name == "Null") {
       // Don't return a const object to ensure we test implementations that use
       // identical.
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index f81886b..f3b00a1 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -881,7 +881,7 @@
 
 void unwrapNeverType(NeverType type) {}
 
-NeverType wrapNeverType(void ignored) => const NeverType(Nullability.legacy);
+NeverType wrapNeverType(void ignored) => const NeverType.legacy();
 
 // TODO(dmitryas):  Also handle nameParameters, and typedefType.
 TextSerializer<FunctionType> functionTypeSerializer = new Wrapped(
diff --git a/pkg/vm/test/transformations/type_flow/types_test.dart b/pkg/vm/test/transformations/type_flow/types_test.dart
index dbdf093..5ebdfb04 100644
--- a/pkg/vm/test/transformations/type_flow/types_test.dart
+++ b/pkg/vm/test/transformations/type_flow/types_test.dart
@@ -70,7 +70,7 @@
     final FunctionType f1 =
         new FunctionType([t1], const VoidType(), Nullability.legacy);
 
-    expect(tb.fromStaticType(const NeverType(Nullability.nonNullable), false),
+    expect(tb.fromStaticType(const NeverType.nonNullable(), false),
         equals(const EmptyType()));
     expect(tb.fromStaticType(const BottomType(), true),
         equals(new NullableType(const EmptyType())));
diff --git a/tools/VERSION b/tools/VERSION
index 34da45f..d37ec91 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 12
+PRERELEASE 13
 PRERELEASE_PATCH 0
\ No newline at end of file