Version 2.12.0-134.0.dev

Merge commit '1b160f79fed8fa1439ffff0de6a7d4b4aa843e18' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 0c72699..aff0dd8 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -408,7 +408,7 @@
       "name": "mime",
       "rootUri": "../third_party/pkg/mime",
       "packageUri": "lib/",
-      "languageVersion": "2.0"
+      "languageVersion": "2.12"
     },
     {
       "name": "mockito",
diff --git a/DEPS b/DEPS
index cd9bba0..690a3f3 100644
--- a/DEPS
+++ b/DEPS
@@ -101,7 +101,7 @@
 "dart_style_tag": "1.3.10",  # Please see the note above before updating.
 
   "chromedriver_tag": "83.0.4103.39",
-  "dartdoc_rev" : "d79877d0764ce23ffea7055049f8da5dffce0308",
+  "dartdoc_rev" : "a1d86f2c992f4660ddcc09b27733396e92765d2a",
   "ffi_rev": "a5d4232cd38562c75a3ed847baa340e399538028",
   "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
   "file_rev": "0e09370f581ab6388d46fda4cdab66638c0171a1",
@@ -123,7 +123,7 @@
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
   "matcher_rev": "9cae8faa7868bf3a88a7ba45eb0bd128e66ac515",
-  "mime_rev": "07635f7774447503248fbc6afb3911e9000a477e",
+  "mime_rev": "c931f4bed87221beaece356494b43731445ce7b8",
   "mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
   "mustache_rev": "664737ecad027e6b96d0d1e627257efa0e46fcb1",
   "oauth2_tag": "1.6.0",
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
index e829ce1..41f9906 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
@@ -92,16 +92,16 @@
   }
 
   for (var param in namedParams) {
-    if (param.hasRequired) {
+    if (param.hasRequired || param.isRequiredNamed) {
       if (sb.isNotEmpty) {
         sb.write(', ');
       }
       var name = param.name;
       sb.write('$name: ');
       offset = sb.length;
-      var defaultValue = _getDefaultValue(param);
-      sb.write(defaultValue);
-      ranges.addAll([offset, defaultValue.length]);
+      // TODO(pq): fix to use getDefaultStringParameterValue()
+      sb.write(name);
+      ranges.addAll([offset, name.length]);
     }
   }
 
@@ -275,9 +275,6 @@
   return type.getDisplayString(withNullability: false);
 }
 
-/// TODO(pq): fix to use getDefaultStringParameterValue()
-String _getDefaultValue(ParameterElement param) => 'null';
-
 /// A tuple of text to insert and an (optional) location for the cursor.
 class DefaultArgument {
   /// The text to insert.
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index f71db6c..fa74016 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -1398,10 +1398,7 @@
   @override
   String get testPackageLanguageVersion => latestLanguageVersion;
 
-  @failingTest
   Future<void> test_completeFunctionCalls_requiredNamed() async {
-    // TODO(dantup): Find out how we can tell this parameter is required
-    // (in the completion mapping).
     final content = '''
     void myFunction(String a, int b, {required String c, String d = ''}) {}
 
@@ -1425,11 +1422,55 @@
     // Ensure the snippet comes through in the expected format with the expected
     // placeholders.
     expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
-    expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, ${2:c})'));
+    expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
     expect(item.textEdit.newText, equals(item.insertText));
     expect(
       item.textEdit.range,
       equals(rangeFromMarkers(content)),
     );
   }
+
+  Future<void> test_completeFunctionCalls_requiredNamed_suggestionSet() async {
+    final otherFile = join(projectFolderPath, 'lib', 'other.dart');
+    newFile(
+      otherFile,
+      content:
+          "void myFunction(String a, int b, {required String c, String d = ''}) {}",
+    );
+    final content = '''
+    main() {
+      [[myFu^]]
+    }
+    ''';
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await provideConfig(
+      () => initialize(
+        textDocumentCapabilities: withCompletionItemSnippetSupport(
+            emptyTextDocumentClientCapabilities),
+        workspaceCapabilities: withApplyEditSupport(
+            withConfigurationSupport(emptyWorkspaceClientCapabilities)),
+      ),
+      {'completeFunctionCalls': true},
+    );
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+    final item = res.singleWhere((c) => c.label == 'myFunction(…)');
+    // Ensure the snippet comes through in the expected format with the expected
+    // placeholders.
+    expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
+    expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
+    expect(item.textEdit, isNull);
+
+    // Ensure the item can be resolved and gets a proper TextEdit.
+    final resolved = await resolveCompletion(item);
+    expect(resolved.textEdit, isNotNull);
+    expect(resolved.textEdit.newText, equals(item.insertText));
+    expect(
+      resolved.textEdit.range,
+      equals(rangeFromMarkers(content)),
+    );
+  }
 }
diff --git a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
index ff603a4..a83e323 100644
--- a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
@@ -49,8 +49,7 @@
 void main() {f^}''');
     await computeSuggestions();
 
-    assertSuggestFunction('foo', 'bool',
-        defaultArgListString: 'bar, baz: null');
+    assertSuggestFunction('foo', 'bool', defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_ArgumentList() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
index 0c7657a..571d7b2 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
@@ -70,8 +70,8 @@
     await computeSuggestions();
 
     assertSuggestFunction('foo', 'bool',
-        defaultArgListString: 'bar, baz: null',
-        defaultArgumentListTextRanges: [0, 3, 10, 4]);
+        defaultArgListString: 'bar, baz: baz',
+        defaultArgumentListTextRanges: [0, 3, 10, 3]);
   }
 
   Future<void> test_ArgDefaults_inherited_method_with_required_named() async {
@@ -92,7 +92,7 @@
     await computeSuggestions();
 
     assertSuggestMethod('foo', 'A', 'bool',
-        defaultArgListString: 'bar, baz: null');
+        defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_ArgDefaults_method_with_required_named() async {
@@ -109,8 +109,8 @@
     await computeSuggestions();
 
     assertSuggestMethod('foo', 'A', 'bool',
-        defaultArgListString: 'bar, baz: null',
-        defaultArgumentListTextRanges: [0, 3, 10, 4]);
+        defaultArgListString: 'bar, baz: baz',
+        defaultArgumentListTextRanges: [0, 3, 10, 3]);
   }
 
   Future<void> test_ArgumentList() async {
@@ -4139,7 +4139,7 @@
   }
 }''');
     await computeSuggestions();
-    assertSuggestConstructor('A', defaultArgListString: 'bar, baz: null');
+    assertSuggestConstructor('A', defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_localConstructor2() async {
@@ -4194,7 +4194,7 @@
   }
 }''');
     await computeSuggestions();
-    assertSuggestConstructor('A', defaultArgListString: 'bar, baz: null');
+    assertSuggestConstructor('A', defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_localConstructor_shadowed() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
index aea6d40..894b3d0 100644
--- a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
@@ -104,7 +104,7 @@
     await computeSuggestions();
 
     assertSuggestMethod('foo', 'A', 'bool',
-        defaultArgListString: 'bar, baz: null');
+        defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_ArgumentList() async {
diff --git a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
index 4a2d307..623b668 100644
--- a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
+++ b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
@@ -102,8 +102,8 @@
     expect(fff.defaultArgumentListTextRanges, [0, 3, 5, 3]);
 
     var ggg = aSet.items.singleWhere((e) => e.label == 'ggg');
-    expect(ggg.defaultArgumentListString, 'bbb: null, ccc: null');
-    expect(ggg.defaultArgumentListTextRanges, [5, 4, 16, 4]);
+    expect(ggg.defaultArgumentListString, 'bbb: bbb, ccc: ccc');
+    expect(ggg.defaultArgumentListTextRanges, [5, 3, 15, 3]);
   }
 
   Future<void> test_displayUri_file() async {
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index 6c1f831..1936e65 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -1689,7 +1689,7 @@
         _typeProvider.typeType,
         TypeState(_typeProvider.dynamicType),
       );
-    } else if (variableElement is FunctionTypeAliasElement) {
+    } else if (variableElement is TypeAliasElement) {
       var type = variableElement.instantiate(
         typeArguments: variableElement.typeParameters
             .map((t) => _typeProvider.dynamicType)
diff --git a/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart b/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
index 9daaea2..377e713 100644
--- a/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
@@ -6,9 +6,11 @@
 import 'package:analyzer/dart/ast/standard_ast_factory.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/scope.dart';
+import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
 import 'package:analyzer/src/error/codes.dart';
+import 'package:meta/meta.dart';
 
 /// Helper for [MethodInvocation]s into [InstanceCreationExpression] to support
 /// the optional `new` and `const` feature, or [ExtensionOverride].
@@ -34,14 +36,10 @@
       }
       Element element = nameScope.lookup(methodName.name).getter;
       if (element is ClassElement) {
-        TypeName typeName = astFactory.typeName(methodName, node.typeArguments);
-        ConstructorName constructorName =
-            astFactory.constructorName(typeName, null, null);
-        InstanceCreationExpression instanceCreationExpression =
-            astFactory.instanceCreationExpression(
-                null, constructorName, node.argumentList);
-        NodeReplacer.replace(node, instanceCreationExpression);
-        return instanceCreationExpression;
+        return _toInstanceCreation_type(
+          node: node,
+          typeIdentifier: methodName,
+        );
       } else if (element is ExtensionElement) {
         ExtensionOverride extensionOverride = astFactory.extensionOverride(
             extensionName: methodName,
@@ -49,6 +47,12 @@
             argumentList: node.argumentList);
         NodeReplacer.replace(node, extensionOverride);
         return extensionOverride;
+      } else if (element is TypeAliasElement &&
+          element.aliasedType is InterfaceType) {
+        return _toInstanceCreation_type(
+          node: node,
+          typeIdentifier: methodName,
+        );
       }
     } else if (target is SimpleIdentifier) {
       // Possible cases: C.n(), p.C() or p.C<>()
@@ -58,41 +62,23 @@
       }
       Element element = nameScope.lookup(target.name).getter;
       if (element is ClassElement) {
-        // Possible case: C.n()
-        var constructorElement = element.getNamedConstructor(methodName.name);
-        if (constructorElement != null) {
-          var typeArguments = node.typeArguments;
-          if (typeArguments != null) {
-            _errorReporter.reportErrorForNode(
-                CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
-                typeArguments,
-                [element.name, constructorElement.name]);
-          }
-          TypeName typeName = astFactory.typeName(target, null);
-          ConstructorName constructorName =
-              astFactory.constructorName(typeName, node.operator, methodName);
-          // TODO(scheglov) I think we should drop "typeArguments" below.
-          InstanceCreationExpression instanceCreationExpression =
-              astFactory.instanceCreationExpression(
-                  null, constructorName, node.argumentList,
-                  typeArguments: typeArguments);
-          NodeReplacer.replace(node, instanceCreationExpression);
-          return instanceCreationExpression;
-        }
+        // class C { C.named(); }
+        // C.named()
+        return _toInstanceCreation_type_constructor(
+          node: node,
+          typeIdentifier: target,
+          constructorIdentifier: methodName,
+          classElement: element,
+        );
       } else if (element is PrefixElement) {
         // Possible cases: p.C() or p.C<>()
         Element prefixedElement = element.scope.lookup(methodName.name).getter;
         if (prefixedElement is ClassElement) {
-          TypeName typeName = astFactory.typeName(
-              astFactory.prefixedIdentifier(target, node.operator, methodName),
-              node.typeArguments);
-          ConstructorName constructorName =
-              astFactory.constructorName(typeName, null, null);
-          InstanceCreationExpression instanceCreationExpression =
-              astFactory.instanceCreationExpression(
-                  null, constructorName, node.argumentList);
-          NodeReplacer.replace(node, instanceCreationExpression);
-          return instanceCreationExpression;
+          return _toInstanceCreation_prefix_type(
+            node: node,
+            prefixIdentifier: target,
+            typeIdentifier: methodName,
+          );
         } else if (prefixedElement is ExtensionElement) {
           PrefixedIdentifier extensionName =
               astFactory.prefixedIdentifier(target, node.operator, methodName);
@@ -102,38 +88,143 @@
               argumentList: node.argumentList);
           NodeReplacer.replace(node, extensionOverride);
           return extensionOverride;
+        } else if (prefixedElement is TypeAliasElement &&
+            prefixedElement.aliasedType is InterfaceType) {
+          return _toInstanceCreation_prefix_type(
+            node: node,
+            prefixIdentifier: target,
+            typeIdentifier: methodName,
+          );
+        }
+      } else if (element is TypeAliasElement) {
+        var aliasedType = element.aliasedType;
+        if (aliasedType is InterfaceType) {
+          // class C { C.named(); }
+          // typedef X = C;
+          // X.named()
+          return _toInstanceCreation_type_constructor(
+            node: node,
+            typeIdentifier: target,
+            constructorIdentifier: methodName,
+            classElement: aliasedType.element,
+          );
         }
       }
     } else if (target is PrefixedIdentifier) {
       // Possible case: p.C.n()
-      Element prefixElement = nameScope.lookup(target.prefix.name).getter;
+      var prefixElement = nameScope.lookup(target.prefix.name).getter;
       target.prefix.staticElement = prefixElement;
       if (prefixElement is PrefixElement) {
-        Element element =
-            prefixElement.scope.lookup(target.identifier.name).getter;
+        var prefixedName = target.identifier.name;
+        var element = prefixElement.scope.lookup(prefixedName).getter;
         if (element is ClassElement) {
-          var constructorElement = element.getNamedConstructor(methodName.name);
-          if (constructorElement != null) {
-            var typeArguments = node.typeArguments;
-            if (typeArguments != null) {
-              _errorReporter.reportErrorForNode(
-                  CompileTimeErrorCode
-                      .WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
-                  typeArguments,
-                  [element.name, constructorElement.name]);
-            }
-            TypeName typeName = astFactory.typeName(target, typeArguments);
-            ConstructorName constructorName =
-                astFactory.constructorName(typeName, node.operator, methodName);
-            InstanceCreationExpression instanceCreationExpression =
-                astFactory.instanceCreationExpression(
-                    null, constructorName, node.argumentList);
-            NodeReplacer.replace(node, instanceCreationExpression);
-            return instanceCreationExpression;
+          return _instanceCreation_prefix_type_name(
+            node: node,
+            typeNameIdentifier: target,
+            constructorIdentifier: methodName,
+            classElement: element,
+          );
+        } else if (element is TypeAliasElement) {
+          var aliasedType = element.aliasedType;
+          if (aliasedType is InterfaceType) {
+            return _instanceCreation_prefix_type_name(
+              node: node,
+              typeNameIdentifier: target,
+              constructorIdentifier: methodName,
+              classElement: aliasedType.element,
+            );
           }
         }
       }
     }
     return node;
   }
+
+  AstNode _instanceCreation_prefix_type_name({
+    @required MethodInvocation node,
+    @required PrefixedIdentifier typeNameIdentifier,
+    @required SimpleIdentifier constructorIdentifier,
+    @required ClassElement classElement,
+  }) {
+    var constructorElement = classElement.getNamedConstructor(
+      constructorIdentifier.name,
+    );
+    if (constructorElement == null) {
+      return node;
+    }
+
+    var typeArguments = node.typeArguments;
+    if (typeArguments != null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
+          typeArguments,
+          [classElement.name, constructorElement.name]);
+    }
+
+    var typeName = astFactory.typeName(typeNameIdentifier, typeArguments);
+    var constructorName = astFactory.constructorName(
+        typeName, node.operator, constructorIdentifier);
+    var instanceCreationExpression = astFactory.instanceCreationExpression(
+        null, constructorName, node.argumentList);
+    NodeReplacer.replace(node, instanceCreationExpression);
+    return instanceCreationExpression;
+  }
+
+  InstanceCreationExpression _toInstanceCreation_prefix_type({
+    @required MethodInvocation node,
+    @required SimpleIdentifier prefixIdentifier,
+    @required SimpleIdentifier typeIdentifier,
+  }) {
+    var typeName = astFactory.typeName(
+        astFactory.prefixedIdentifier(
+            prefixIdentifier, node.operator, typeIdentifier),
+        node.typeArguments);
+    var constructorName = astFactory.constructorName(typeName, null, null);
+    var instanceCreationExpression = astFactory.instanceCreationExpression(
+        null, constructorName, node.argumentList);
+    NodeReplacer.replace(node, instanceCreationExpression);
+    return instanceCreationExpression;
+  }
+
+  InstanceCreationExpression _toInstanceCreation_type({
+    @required MethodInvocation node,
+    @required SimpleIdentifier typeIdentifier,
+  }) {
+    var typeName = astFactory.typeName(typeIdentifier, node.typeArguments);
+    var constructorName = astFactory.constructorName(typeName, null, null);
+    var instanceCreationExpression = astFactory.instanceCreationExpression(
+        null, constructorName, node.argumentList);
+    NodeReplacer.replace(node, instanceCreationExpression);
+    return instanceCreationExpression;
+  }
+
+  AstNode _toInstanceCreation_type_constructor({
+    @required MethodInvocation node,
+    @required SimpleIdentifier typeIdentifier,
+    @required SimpleIdentifier constructorIdentifier,
+    @required ClassElement classElement,
+  }) {
+    var name = constructorIdentifier.name;
+    var constructorElement = classElement.getNamedConstructor(name);
+    if (constructorElement == null) {
+      return node;
+    }
+
+    var typeArguments = node.typeArguments;
+    if (typeArguments != null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
+          typeArguments,
+          [classElement.name, constructorElement.name]);
+    }
+    var typeName = astFactory.typeName(typeIdentifier, null);
+    var constructorName = astFactory.constructorName(
+        typeName, node.operator, constructorIdentifier);
+    // TODO(scheglov) I think we should drop "typeArguments" below.
+    var instanceCreationExpression = astFactory.instanceCreationExpression(
+        null, constructorName, node.argumentList,
+        typeArguments: typeArguments);
+    NodeReplacer.replace(node, instanceCreationExpression);
+    return instanceCreationExpression;
+  }
 }
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
index c077118..eef2897 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
@@ -196,12 +196,6 @@
           typeArguments: typeArguments,
           nullabilitySuffix: nullability,
         );
-      } else if (_isInstanceCreation(node)) {
-        _ErrorHelper(errorReporter).reportNewWithNonType(node);
-        return dynamicType;
-      } else if (element is DynamicElementImpl) {
-        _buildTypeArguments(node, 0);
-        return DynamicTypeImpl.instance;
       } else if (element is TypeAliasElement) {
         var typeArguments = _buildTypeArguments(
           node,
@@ -212,7 +206,13 @@
           nullabilitySuffix: nullability,
         );
         type = typeSystem.toLegacyType(type);
-        return type;
+        return _verifyTypeAliasForContext(node, type);
+      } else if (_isInstanceCreation(node)) {
+        _ErrorHelper(errorReporter).reportNewWithNonType(node);
+        return dynamicType;
+      } else if (element is DynamicElementImpl) {
+        _buildTypeArguments(node, 0);
+        return DynamicTypeImpl.instance;
       } else if (element is NeverElementImpl) {
         _buildTypeArguments(node, 0);
         return _instantiateElementNever(nullability);
@@ -244,16 +244,17 @@
         classElement: element,
         nullabilitySuffix: nullability,
       );
+    } else if (element is TypeAliasElement) {
+      var type = typeSystem.instantiateToBounds2(
+        typeAliasElement: element,
+        nullabilitySuffix: nullability,
+      );
+      return _verifyTypeAliasForContext(node, type);
     } else if (_isInstanceCreation(node)) {
       _ErrorHelper(errorReporter).reportNewWithNonType(node);
       return dynamicType;
     } else if (element is DynamicElementImpl) {
       return DynamicTypeImpl.instance;
-    } else if (element is TypeAliasElement) {
-      return typeSystem.instantiateToBounds2(
-        typeAliasElement: element,
-        nullabilitySuffix: nullability,
-      );
     } else if (element is NeverElementImpl) {
       return _instantiateElementNever(nullability);
     } else if (element is TypeParameterElement) {
@@ -366,6 +367,14 @@
     }
   }
 
+  DartType _verifyTypeAliasForContext(TypeName node, DartType type) {
+    if (type is! InterfaceType && _isInstanceCreation(node)) {
+      _ErrorHelper(errorReporter).reportNewWithNonType(node);
+      return dynamicType;
+    }
+    return type;
+  }
+
   static bool _isInstanceCreation(TypeName node) {
     return node.parent is ConstructorName &&
         node.parent.parent is InstanceCreationExpression;
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 45b860a..23bc84d 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -1295,6 +1295,9 @@
     if (expressionMap.isNotEmpty) {
       Declaration parent = expression.thisOrAncestorMatching(
           (e) => e is FunctionDeclaration || e is MethodDeclaration);
+      if (parent == null) {
+        return;
+      }
       for (var entry in expressionMap.entries) {
         _errorReporter.reportErrorForNode(
           HintCode.RETURN_OF_DO_NOT_STORE,
@@ -1480,6 +1483,11 @@
           addTo: expressions);
       _getSubExpressionsMarkedDoNotStore(expression.rightOperand,
           addTo: expressions);
+    } else if (expression is FunctionExpression) {
+      var body = expression.body;
+      if (body is ExpressionFunctionBody) {
+        _getSubExpressionsMarkedDoNotStore(body.expression, addTo: expressions);
+      }
     }
     if (element is PropertyAccessorElement && element.isSynthetic) {
       element = (element as PropertyAccessorElement).variable;
diff --git a/pkg/analyzer/lib/src/services/available_declarations.dart b/pkg/analyzer/lib/src/services/available_declarations.dart
index 5227a27..4edfaae 100644
--- a/pkg/analyzer/lib/src/services/available_declarations.dart
+++ b/pkg/analyzer/lib/src/services/available_declarations.dart
@@ -1828,32 +1828,22 @@
     var buffer = StringBuffer();
     var ranges = <int>[];
     for (var parameter in parameters.parameters) {
-      if (parameter.isRequired) {
+      if (parameter.isRequired ||
+          (parameter.isNamed && _hasRequiredAnnotation(parameter))) {
         if (buffer.isNotEmpty) {
           buffer.write(', ');
         }
+
         if (parameter.isNamed) {
           buffer.write(parameter.identifier.name);
           buffer.write(': ');
         }
+
         var valueOffset = buffer.length;
         buffer.write(parameter.identifier.name);
         var valueLength = buffer.length - valueOffset;
         ranges.add(valueOffset);
         ranges.add(valueLength);
-      } else if (parameter.isNamed && _hasRequiredAnnotation(parameter)) {
-        if (buffer.isNotEmpty) {
-          buffer.write(', ');
-        }
-        buffer.write(parameter.identifier.name);
-        buffer.write(': ');
-
-        var valueOffset = buffer.length;
-        buffer.write('null');
-        var valueLength = buffer.length - valueOffset;
-
-        ranges.add(valueOffset);
-        ranges.add(valueLength);
       }
     }
     if (buffer.isEmpty) return null;
diff --git a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
index a51644b..4225019 100644
--- a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
@@ -15,11 +15,17 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(AstRewriteMethodInvocationTest);
+    defineReflectiveTests(
+      AstRewriteMethodInvocationWithNonFunctionTypeAliasesTest,
+    );
   });
 }
 
 @reflectiveTest
-class AstRewriteMethodInvocationTest extends PubPackageResolutionTest {
+class AstRewriteMethodInvocationTest extends PubPackageResolutionTest
+    with AstRewriteMethodInvocationTestCases {}
+
+mixin AstRewriteMethodInvocationTestCases on PubPackageResolutionTest {
   test_targetNull_cascade() async {
     await assertNoErrorsInCode(r'''
 class A {
@@ -379,3 +385,107 @@
     expect(argumentStrings, expectedArguments);
   }
 }
+
+@reflectiveTest
+class AstRewriteMethodInvocationWithNonFunctionTypeAliasesTest
+    extends PubPackageResolutionTest
+    with WithNonFunctionTypeAliasesMixin, AstRewriteMethodInvocationTestCases {
+  test_targetNull_typeAlias_interfaceType() async {
+    await assertNoErrorsInCode(r'''
+class A<T, U> {
+  A(int _);
+}
+
+typedef X<T, U> = A<T, U>;
+
+void f() {
+  X<int, String>(0);
+}
+''');
+
+    var creation = findNode.instanceCreation('X<int, String>(0);');
+    assertInstanceCreation(
+      creation,
+      findElement.class_('A'),
+      'A<int, String>',
+      expectedConstructorMember: true,
+      expectedSubstitution: {'T': 'int', 'U': 'String'},
+      expectedTypeNameElement: findElement.typeAlias('X'),
+    );
+    _assertArgumentList(creation.argumentList, ['0']);
+  }
+
+  test_targetNull_typeAlias_Never() async {
+    await assertErrorsInCode(r'''
+typedef X = Never;
+
+void f() {
+  X(0);
+}
+''', [
+      error(CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION, 33, 1),
+    ]);
+
+    // Not rewritten.
+    findNode.methodInvocation('X(0)');
+  }
+
+  test_targetPrefixedIdentifier_typeAlias_interfaceType_constructor() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+class A<T> {
+  A.named(T a);
+}
+
+typedef X<T> = A<T>;
+''');
+
+    await assertNoErrorsInCode(r'''
+import 'a.dart' as prefix;
+
+void f() {
+  prefix.X.named(0);
+}
+''');
+
+    var importFind = findElement.importFind('package:test/a.dart');
+
+    var creation = findNode.instanceCreation('X.named(0);');
+    assertInstanceCreation(
+      creation,
+      importFind.class_('A'),
+      'A<int>',
+      constructorName: 'named',
+      expectedConstructorMember: true,
+      expectedSubstitution: {'T': 'int'},
+      expectedPrefix: findElement.prefix('prefix'),
+      expectedTypeNameElement: importFind.typeAlias('X'),
+    );
+    _assertArgumentList(creation.argumentList, ['0']);
+  }
+
+  test_targetSimpleIdentifier_typeAlias_interfaceType_constructor() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {
+  A.named(T a);
+}
+
+typedef X<T> = A<T>;
+
+void f() {
+  X.named(0);
+}
+''');
+
+    var creation = findNode.instanceCreation('X.named(0);');
+    assertInstanceCreation(
+      creation,
+      findElement.class_('A'),
+      'A<int>',
+      constructorName: 'named',
+      expectedConstructorMember: true,
+      expectedSubstitution: {'T': 'int'},
+      expectedTypeNameElement: findElement.typeAlias('X'),
+    );
+    _assertArgumentList(creation.argumentList, ['0']);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 5e7e81c..31bed01 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -400,12 +400,16 @@
     }
   }
 
-  void assertInstanceCreation(InstanceCreationExpression creation,
-      ClassElement expectedClassElement, String expectedType,
-      {String constructorName,
-      bool expectedConstructorMember = false,
-      Map<String, String> expectedSubstitution,
-      PrefixElement expectedPrefix}) {
+  void assertInstanceCreation(
+    InstanceCreationExpression creation,
+    ClassElement expectedClassElement,
+    String expectedType, {
+    String constructorName,
+    bool expectedConstructorMember = false,
+    Map<String, String> expectedSubstitution,
+    PrefixElement expectedPrefix,
+    Element expectedTypeNameElement,
+  }) {
     String expectedClassName = expectedClassElement.name;
 
     ConstructorElement expectedConstructorElement;
@@ -444,7 +448,8 @@
     assertType(creation, expectedType);
 
     var typeName = creation.constructorName.type;
-    assertTypeName(typeName, expectedClassElement, expectedType,
+    expectedTypeNameElement ??= expectedClassElement;
+    assertTypeName(typeName, expectedTypeNameElement, expectedType,
         expectedPrefix: expectedPrefix);
   }
 
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index a58eac5..146b2a1 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -291,6 +291,61 @@
 @reflectiveTest
 class TypeNameResolutionWithNonFunctionTypeAliasesTest
     extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+  test_typeAlias_asInstanceCreation_explicitNew_typeArguments_interfaceType_none() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+
+typedef X<T> = A<T>;
+
+void f() {
+  new X<int>();
+}
+''');
+
+    assertTypeName(
+      findNode.typeName('X<int>()'),
+      findElement.typeAlias('X'),
+      'A<int>',
+    );
+  }
+
+  @FailingTest(reason: 'We attempt to do type inference on A')
+  test_typeAlias_asInstanceCreation_implicitNew_toBounds_noTypeParameters_interfaceType_none() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+
+typedef X = A<int>;
+
+void f() {
+  X();
+}
+''');
+
+    assertTypeName(
+      findNode.typeName('X()'),
+      findElement.typeAlias('X'),
+      'A<int>',
+    );
+  }
+
+  test_typeAlias_asInstanceCreation_implicitNew_typeArguments_interfaceType_none() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+
+typedef X<T> = A<T>;
+
+void f() {
+  X<int>();
+}
+''');
+
+    assertTypeName(
+      findNode.typeName('X<int>()'),
+      findElement.typeAlias('X'),
+      'A<int>',
+    );
+  }
+
   test_typeAlias_asParameter_Never_none() async {
     await assertNoErrorsInCode(r'''
 typedef X = Never;
diff --git a/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart b/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart
index e72359b..3266124 100644
--- a/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assignment_of_do_not_store_test.dart
@@ -193,6 +193,21 @@
     ]);
   }
 
+  test_topLevelVariable_assignment_functionExpression() async {
+    await assertErrorsInCode('''
+import 'package:meta/meta.dart';
+
+@doNotStore
+String _v = '';
+
+var c = ()=> _v;
+
+String v = c();
+''', [
+      error(HintCode.ASSIGNMENT_OF_DO_NOT_STORE, 76, 2),
+    ]);
+  }
+
   test_topLevelVariable_assignment_getter() async {
     await assertErrorsInCode('''
 import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
index 8e28cd4..79c1c58 100644
--- a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
@@ -10,6 +10,9 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConstInitializedWithNonConstantValueTest);
+    defineReflectiveTests(
+      ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest,
+    );
   });
 }
 
@@ -103,3 +106,20 @@
     ]);
   }
 }
+
+@reflectiveTest
+class ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest
+    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+  test_typeLiteral_interfaceType() async {
+    await assertNoErrorsInCode(r'''
+const a = int;
+''');
+  }
+
+  test_typeLiteral_typeAlias_interfaceType() async {
+    await assertNoErrorsInCode(r'''
+typedef A = int;
+const a = A;
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart b/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart
index c747e6a..c8f61f5 100644
--- a/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/return_of_do_not_store_test.dart
@@ -21,6 +21,22 @@
     writeTestPackageConfigWithMeta();
   }
 
+  test_returnFromClosureInFunction() async {
+    await assertErrorsInCode('''
+import 'package:meta/meta.dart';
+
+@doNotStore
+String _v = '';
+
+String f() {
+  var v = () => _v;
+  return v();
+}
+''', [
+      error(HintCode.RETURN_OF_DO_NOT_STORE, 92, 2),
+    ]);
+  }
+
   test_returnFromFunction() async {
     await assertErrorsInCode('''
 import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/src/services/available_declarations_test.dart b/pkg/analyzer/test/src/services/available_declarations_test.dart
index 3e2aa12..b5304ba 100644
--- a/pkg/analyzer/test/src/services/available_declarations_test.dart
+++ b/pkg/analyzer/test/src/services/available_declarations_test.dart
@@ -1934,8 +1934,8 @@
       _getDeclaration(library.declarations, 'd'),
       'd',
       DeclarationKind.FUNCTION,
-      defaultArgumentListString: 'a, c: null, d: null',
-      defaultArgumentListTextRanges: [0, 1, 6, 4, 15, 4],
+      defaultArgumentListString: 'a, c: c, d: d',
+      defaultArgumentListTextRanges: [0, 1, 6, 1, 12, 1],
       parameters: '(int a, {int b, @required int c, @required int d, int e})',
       parameterNames: ['a', 'b', 'c', 'd', 'e'],
       parameterTypes: ['int', 'int', 'int', 'int', 'int'],
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
index 71b5b58..cea8688 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
@@ -46,16 +46,15 @@
     }
 
     for (var param in namedParams) {
-      if (param.hasRequired) {
+      if (param.hasRequired || param.isRequiredNamed) {
         if (buffer.isNotEmpty) {
           buffer.write(', ');
         }
         var name = param.name;
         buffer.write('$name: ');
         offset = buffer.length;
-        var defaultValue = 'null'; // originally _getDefaultValue(param)
-        buffer.write(defaultValue);
-        ranges.addAll([offset, defaultValue.length]);
+        buffer.write(name);
+        ranges.addAll([offset, name.length]);
       }
     }
 
diff --git a/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart b/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart
index 49d103d..2b63605 100644
--- a/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart
+++ b/pkg/analyzer_plugin/test/utilities/completion/inherited_reference_contributor_test.dart
@@ -53,7 +53,7 @@
     await computeSuggestions();
 
     assertSuggestMethod('foo', 'A', 'bool',
-        defaultArgListString: 'bar, baz: null');
+        defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_AwaitExpression_inherited() async {
diff --git a/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart b/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart
index c268c4e..59a19c1 100644
--- a/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart
+++ b/pkg/analyzer_plugin/test/utilities/completion/type_member_contributor_test.dart
@@ -114,7 +114,7 @@
     await computeSuggestions();
 
     assertSuggestMethod('foo', 'A', 'bool',
-        defaultArgListString: 'bar, baz: null');
+        defaultArgListString: 'bar, baz: baz');
   }
 
   Future<void> test_ArgumentList() async {
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 1c7d0ed..c93d471 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -117,6 +117,7 @@
 
   static const String soundNullSafety = '--sound-null-safety';
   static const String noSoundNullSafety = '--no-sound-null-safety';
+  static const String mergeFragmentsThreshold = '--merge-fragments-threshold';
 
   /// Flag for a combination of flags for 'production' mode.
   static const String benchmarkingProduction = '--benchmarking-production';
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 11b8c5d..5a69e20 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -559,6 +559,7 @@
     new OptionHandler(Flags.experimentUnreachableMethodsThrow, passThrough),
     new OptionHandler(Flags.experimentCallInstrumentation, passThrough),
     new OptionHandler(Flags.experimentNewRti, ignoreOption),
+    new OptionHandler('${Flags.mergeFragmentsThreshold}=.+', passThrough),
 
     // The following three options must come last.
     new OptionHandler('-D.+=.*', addInEnvironment),
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index 364fe9a..829e2c5 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -84,6 +84,13 @@
 
   Set<ImportEntity> get importsForTesting => _imports;
 
+  void merge(OutputUnit that) {
+    assert(this != that);
+    // We don't currently support merging code into the main output unit.
+    assert(!isMainOutput);
+    this._imports.addAll(that._imports);
+  }
+
   @override
   String toString() => "OutputUnit($name, $_imports)";
 }
diff --git a/pkg/compiler/lib/src/js/size_estimator.dart b/pkg/compiler/lib/src/js/size_estimator.dart
index 60d0354..347c146 100644
--- a/pkg/compiler/lib/src/js/size_estimator.dart
+++ b/pkg/compiler/lib/src/js/size_estimator.dart
@@ -12,6 +12,13 @@
 import '../js_backend/type_reference.dart';
 import '../js_emitter/metadata_collector.dart';
 
+/// Estimates the size of the Javascript AST represented by the provided [Node].
+int estimateSize(Node node) {
+  var estimator = SizeEstimator();
+  estimator.visit(node);
+  return estimator.charCount;
+}
+
 /// [SizeEstimator] is a [NodeVisitor] designed to produce a consistent size
 /// estimate for a given JavaScript AST. [SizeEstimator] trades accuracy for
 /// stability and performance. In addition, [SizeEstimator] assumes we will emit
@@ -55,12 +62,20 @@
     } else if (node is StringReference) {
       // Worst case we have to inline the string so size of string + 2 bytes for
       // quotes.
-      return "'${node.constant}'";
+      return "'${node.constant.toDartString()}'";
     } else {
       throw UnsupportedError('$node type is not supported');
     }
   }
 
+  String literalStringToString(LiteralString node) {
+    if (node.isFinalized) {
+      return node.value;
+    } else {
+      return sizeEstimate(node);
+    }
+  }
+
   /// Always emit a newline, even under `enableMinification`.
   void forceLine() {
     out('\n'); // '\n'
@@ -757,7 +772,7 @@
         newInForInit: inForInit, newAtStatementBegin: atStatementBegin);
     Node selector = access.selector;
     if (selector is LiteralString) {
-      String fieldWithQuotes = selector.value;
+      String fieldWithQuotes = literalStringToString(selector);
       if (isValidJavaScriptId(fieldWithQuotes)) {
         if (access.receiver is LiteralNumber) {
           // We can eliminate the space in some cases, but for simplicity we
@@ -846,11 +861,7 @@
 
   @override
   void visitLiteralString(LiteralString node) {
-    if (node.isFinalized) {
-      out(node.value); // '${node.value}'
-    } else {
-      out(sizeEstimate(node));
-    }
+    out(literalStringToString(node));
   }
 
   @override
@@ -943,7 +954,7 @@
   void visitProperty(Property node) {
     Node name = node.name;
     if (name is LiteralString) {
-      String text = name.value;
+      String text = literalStringToString(name);
       if (isValidJavaScriptId(text)) {
         // '${text.substring(1, text.length - 1)}
         out('${text.substring(1, text.length - 1)}');
@@ -1034,9 +1045,3 @@
     visit(node.expression);
   }
 }
-
-int EstimateSize(Node node) {
-  var estimator = SizeEstimator();
-  estimator.visit(node);
-  return estimator.charCount;
-}
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index b412bd7..fb72825 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -20,6 +20,7 @@
 import '../world.dart' show JClosedWorld;
 import 'program_builder/program_builder.dart';
 import 'startup_emitter/emitter.dart' as startup_js_emitter;
+import 'startup_emitter/fragment_merger.dart' as fragment_merger;
 
 import 'metadata_collector.dart' show MetadataCollector;
 import 'model.dart';
@@ -206,6 +207,8 @@
 abstract class Emitter implements ModularEmitter {
   Program get programForTesting;
 
+  List<fragment_merger.PreFragment> get preDeferredFragmentsForTesting;
+
   /// Uses the [programBuilder] to generate a model of the program, emits
   /// the program, and returns the size of the generated output.
   int emitProgram(ProgramBuilder programBuilder, CodegenWorld codegenWorld);
diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
index 6fac2f9..db10350 100644
--- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
+++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
@@ -115,8 +115,7 @@
   }
 
   /// A map used to canonicalize the entries of metadata.
-  Map<OutputUnit, Map<String, BoundMetadataEntry>> _metadataMap =
-      <OutputUnit, Map<String, BoundMetadataEntry>>{};
+  Map<OutputUnit, Map<String, List<BoundMetadataEntry>>> _metadataMap = {};
 
   /// A map with a token for a lists of JS expressions, one token for each
   /// output unit. Once finalized, the entries represent types including
@@ -129,8 +128,35 @@
   }
 
   /// A map used to canonicalize the entries of types.
-  Map<OutputUnit, Map<DartType, BoundMetadataEntry>> _typesMap =
-      <OutputUnit, Map<DartType, BoundMetadataEntry>>{};
+  Map<OutputUnit, Map<DartType, List<BoundMetadataEntry>>> _typesMap = {};
+
+  void mergeOutputUnitMetadata(OutputUnit target, OutputUnit source) {
+    assert(target != source);
+
+    // Merge _metadataMap
+    var sourceMetadataMap = _metadataMap[source];
+    if (sourceMetadataMap != null) {
+      var targetMetadataMap =
+          _metadataMap[target] ??= Map<String, List<BoundMetadataEntry>>();
+      _metadataMap.remove(source);
+      sourceMetadataMap.forEach((str, entries) {
+        var targetMetadataMapList = targetMetadataMap[str] ??= [];
+        targetMetadataMapList.addAll(entries);
+      });
+    }
+
+    // Merge _typesMap
+    var sourceTypesMap = _typesMap[source];
+    if (sourceTypesMap != null) {
+      var targetTypesMap =
+          _typesMap[target] ??= Map<DartType, List<BoundMetadataEntry>>();
+      _typesMap.remove(source);
+      sourceTypesMap.forEach((type, entries) {
+        var targetTypesMapList = targetTypesMap[type] ??= [];
+        targetTypesMapList.addAll(entries);
+      });
+    }
+  }
 
   MetadataCollector(this._options, this.reporter, this._emitter,
       this._rtiRecipeEncoder, this._elementEnvironment);
@@ -166,10 +192,9 @@
     String printed = jsAst.prettyPrint(node,
         enableMinification: _options.enableMinification,
         renamerForNames: nameToKey);
-    _metadataMap[outputUnit] ??= new Map<String, BoundMetadataEntry>();
-    return _metadataMap[outputUnit].putIfAbsent(printed, () {
-      return new BoundMetadataEntry(node);
-    });
+    final submap = _metadataMap[outputUnit] ??= {};
+    final entries = submap[printed] ??= [BoundMetadataEntry(node)];
+    return entries.single;
   }
 
   jsAst.Expression _computeTypeRepresentationNewRti(DartType type) {
@@ -178,10 +203,20 @@
   }
 
   jsAst.Expression addTypeInOutputUnit(DartType type, OutputUnit outputUnit) {
-    _typesMap[outputUnit] ??= new Map<DartType, BoundMetadataEntry>();
-    return _typesMap[outputUnit].putIfAbsent(type, () {
-      return new BoundMetadataEntry(_computeTypeRepresentationNewRti(type));
-    });
+    _typesMap[outputUnit] ??= Map<DartType, List<BoundMetadataEntry>>();
+    BoundMetadataEntry metadataEntry;
+
+    // See comment for _addGlobalMetadata.
+    if (_typesMap[outputUnit].containsKey(type)) {
+      metadataEntry = _typesMap[outputUnit][type].single;
+    } else {
+      _typesMap[outputUnit].putIfAbsent(type, () {
+        metadataEntry =
+            BoundMetadataEntry(_computeTypeRepresentationNewRti(type));
+        return [metadataEntry];
+      });
+    }
+    return metadataEntry;
   }
 
   @override
@@ -194,9 +229,13 @@
           .forEach(counter.countTokens);
     }
 
-    jsAst.ArrayInitializer finalizeMap(Map<dynamic, BoundMetadataEntry> map) {
-      bool isUsed(BoundMetadataEntry entry) => entry.isUsed;
-      List<BoundMetadataEntry> entries = map.values.where(isUsed).toList();
+    jsAst.ArrayInitializer finalizeMap(
+        Map<dynamic, List<BoundMetadataEntry>> map) {
+      List<BoundMetadataEntry> entries = [
+        for (var entriesList in map.values)
+          for (var entry in entriesList)
+            if (entry.isUsed) entry
+      ];
       entries.sort();
 
       // TODO(herhut): Bucket entries by index length and use a stable
@@ -222,9 +261,9 @@
     });
 
     _typesTokens.forEach((OutputUnit outputUnit, _MetadataList token) {
-      Map typesMap = _typesMap[outputUnit];
+      Map<DartType, List<BoundMetadataEntry>> typesMap = _typesMap[outputUnit];
       if (typesMap != null) {
-        countTokensInTypes(typesMap.values);
+        typesMap.values.forEach(countTokensInTypes);
         token.setExpression(finalizeMap(typesMap));
       } else {
         token.setExpression(new jsAst.ArrayInitializer([]));
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index 7479339..fb2ffc6 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -42,6 +42,10 @@
     assert(outputContainsConstantList != null);
   }
 
+  void mergeOutputUnitMetadata(OutputUnit target, OutputUnit source) {
+    _metadataCollector.mergeOutputUnitMetadata(target, source);
+  }
+
   /// Accessor for the list of metadata entries for a given [OutputUnit].
   ///
   /// There is one list for each output unit. The list belonging to the main
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
index b2b924b..acf7e3c 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -24,6 +24,7 @@
 import '../model.dart';
 import '../native_emitter.dart';
 import '../program_builder/program_builder.dart' show ProgramBuilder;
+import 'fragment_merger.dart';
 import 'model_emitter.dart';
 
 abstract class ModularEmitterBase implements ModularEmitter {
@@ -156,6 +157,9 @@
   @override
   Program programForTesting;
 
+  @override
+  List<PreFragment> preDeferredFragmentsForTesting;
+
   EmitterImpl(
       CompilerOptions options,
       this._reporter,
@@ -196,7 +200,12 @@
       programForTesting = program;
     }
     return _task.measureSubtask('emit program', () {
-      return _emitter.emitProgram(program, codegenWorld);
+      var size = _emitter.emitProgram(program, codegenWorld);
+      if (retainDataForTesting) {
+        preDeferredFragmentsForTesting =
+            _emitter.preDeferredFragmentsForTesting;
+      }
+      return size;
     });
   }
 
@@ -238,11 +247,9 @@
 
   @override
   int generatedSize(OutputUnit unit) {
-    if (_emitter.omittedFragments.any((f) => f.outputUnit == unit)) {
+    if (_emitter.omittedOutputUnits.contains(unit)) {
       return 0;
     }
-    Fragment key = _emitter.outputBuffers.keys
-        .firstWhere((Fragment fragment) => fragment.outputUnit == unit);
-    return _emitter.outputBuffers[key].length;
+    return _emitter.emittedOutputBuffers[unit].length;
   }
 }
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 597f756..dd7bd3a 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -690,6 +690,35 @@
     if (library != null) _dumpInfoTask.registerEntityAst(library, code);
   }
 
+  PreFragment emitPreFragment(DeferredFragment fragment, bool estimateSize) {
+    var classPrototypes = emitPrototypes(fragment, includeClosures: false);
+    var closurePrototypes = emitPrototypes(fragment, includeClosures: true);
+    var inheritance = emitInheritance(fragment);
+    var methodAliases = emitInstanceMethodAliases(fragment);
+    var tearOffs = emitInstallTearOffs(fragment);
+    var constants = emitConstants(fragment);
+    var typeRules = emitTypeRules(fragment);
+    var variances = emitVariances(fragment);
+    var staticNonFinalFields = emitStaticNonFinalFields(fragment);
+    var lazyInitializers = emitLazilyInitializedStatics(fragment);
+    // TODO(floitsch): only call emitNativeSupport if we need native.
+    var nativeSupport = emitNativeSupport(fragment);
+    return PreFragment(
+        fragment,
+        classPrototypes,
+        closurePrototypes,
+        inheritance,
+        methodAliases,
+        tearOffs,
+        constants,
+        typeRules,
+        variances,
+        staticNonFinalFields,
+        lazyInitializers,
+        nativeSupport,
+        estimateSize);
+  }
+
   js.Statement emitMainFragment(
       Program program, DeferredLoadingState deferredLoadingState) {
     MainFragment fragment = program.fragments.first;
@@ -699,8 +728,8 @@
 
     String softDeferredId = "softDeferred${new Random().nextInt(0x7FFFFFFF)}";
 
-    HolderCode holderCode =
-        emitHolders(program.holders, fragment, initializeEmptyHolders: true);
+    HolderCode holderCode = emitHolders(program.holders, fragment.libraries,
+        initializeEmptyHolders: true);
 
     js.Statement mainCode = js.js.statement(_mainBoilerplate, {
       // TODO(29455): 'hunkHelpers' displaces other names, so don't minify it.
@@ -791,10 +820,10 @@
     return new js.Block(holderInits);
   }
 
-  js.Expression emitDeferredFragment(DeferredFragment fragment,
-      js.Expression deferredTypes, List<Holder> holders) {
+  js.Expression emitDeferredFragment(
+      FinalizedFragment fragment, List<Holder> holders) {
     HolderCode holderCode =
-        emitHolders(holders, fragment, initializeEmptyHolders: false);
+        emitHolders(holders, fragment.libraries, initializeEmptyHolders: false);
 
     List<Holder> nonStaticStateHolders = holders
         .where((Holder holder) => !holder.isStaticStateHolder)
@@ -821,19 +850,6 @@
       }
     }
 
-    var classPrototypes = emitPrototypes(fragment, includeClosures: false);
-    var closurePrototypes = emitPrototypes(fragment, includeClosures: true);
-    var inheritance = emitInheritance(fragment);
-    var methodAliases = emitInstanceMethodAliases(fragment);
-    var tearOffs = emitInstallTearOffs(fragment);
-    var constants = emitConstants(fragment);
-    var typeRules = emitTypeRules(fragment);
-    var variances = emitVariances(fragment);
-    var staticNonFinalFields = emitStaticNonFinalFields(fragment);
-    var lazyInitializers = emitLazilyInitializedStatics(fragment);
-    // TODO(floitsch): only call emitNativeSupport if we need native.
-    var nativeSupport = emitNativeSupport(fragment);
-
     // TODO(sra): How do we tell if [deferredTypes] is empty? It is filled-in
     // later via the program finalizers. So we should defer the decision on the
     // emptiness of the fragment until the finalizers have run.  For now we seem
@@ -843,16 +859,7 @@
     // not emit any functions, then we probably did not use the signature types
     // in the OutputUnit's types, leaving them unused and tree-shaken.
 
-    if (holderCode.activeHolders.isEmpty &&
-        isEmptyStatement(classPrototypes) &&
-        isEmptyStatement(closurePrototypes) &&
-        isEmptyStatement(inheritance) &&
-        isEmptyStatement(methodAliases) &&
-        isEmptyStatement(tearOffs) &&
-        isEmptyStatement(constants) &&
-        isEmptyStatement(staticNonFinalFields) &&
-        isEmptyStatement(lazyInitializers) &&
-        isEmptyStatement(nativeSupport)) {
+    if (holderCode.activeHolders.isEmpty && fragment.isEmpty) {
       return null;
     }
 
@@ -865,18 +872,18 @@
           .map((holder) => js.js("#", holder.name))
           .toList(growable: false)),
       'updateHolders': new js.Block(updateHolderAssignments),
-      'prototypes': classPrototypes,
-      'closures': closurePrototypes,
-      'inheritance': inheritance,
-      'aliases': methodAliases,
-      'tearOffs': tearOffs,
-      'typeRules': typeRules,
-      'variances': variances,
-      'constants': constants,
-      'staticNonFinalFields': staticNonFinalFields,
-      'lazyStatics': lazyInitializers,
-      'types': deferredTypes,
-      'nativeSupport': nativeSupport,
+      'prototypes': fragment.classPrototypes,
+      'closures': fragment.closurePrototypes,
+      'inheritance': fragment.inheritance,
+      'aliases': fragment.methodAliases,
+      'tearOffs': fragment.tearOffs,
+      'typeRules': fragment.typeRules,
+      'variances': fragment.variances,
+      'constants': fragment.constants,
+      'staticNonFinalFields': fragment.staticNonFinalFields,
+      'lazyStatics': fragment.lazyInitializers,
+      'types': fragment.deferredTypes,
+      'nativeSupport': fragment.nativeSupport,
       'typesOffset': _namer.typesOffsetName,
       'sharedStrings': StringReferenceResource(),
       'sharedTypeRtis': TypeReferenceResource(),
@@ -904,7 +911,7 @@
   ///
   /// The emitted holders contain classes (only the constructors) and all
   /// static functions.
-  HolderCode emitHolders(List<Holder> holders, Fragment fragment,
+  HolderCode emitHolders(List<Holder> holders, List<Library> libraries,
       {bool initializeEmptyHolders}) {
     assert(initializeEmptyHolders != null);
     // Skip the static-state holder in this function.
@@ -918,7 +925,7 @@
       holderCode[holder] = <js.Property>[];
     }
 
-    for (Library library in fragment.libraries) {
+    for (Library library in libraries) {
       for (StaticMethod method in library.statics) {
         assert(!method.holder.isStaticStateHolder);
         Map<js.Name, js.Expression> propertyMap = emitStaticMethod(method);
@@ -1488,13 +1495,6 @@
     return js.js.statement('(function #(){#})();', [name, block]);
   }
 
-  bool isEmptyStatement(js.Statement statement) {
-    if (statement is js.Block) {
-      return statement.statements.isEmpty;
-    }
-    return statement is js.EmptyStatement;
-  }
-
   /// Emits the section that installs tear-off getters.
   js.Statement emitInstallTearOffs(Fragment fragment,
       {bool softDeferred = false}) {
@@ -1864,20 +1864,20 @@
   // array of hashes indexed by part.
   // [deferredLoadHashes] may have missing entries to indicate empty parts.
   void finalizeDeferredLoadingData(
-      Map<String, List<Fragment>> loadMap,
-      Map<DeferredFragment, String> deferredLoadHashes,
+      Map<String, List<FinalizedFragment>> loadMap,
+      Map<FinalizedFragment, String> deferredLoadHashes,
       DeferredLoadingState deferredLoadingState) {
     if (loadMap.isEmpty) return;
 
-    Map<Fragment, int> fragmentIndexes = {};
+    Map<FinalizedFragment, int> fragmentIndexes = {};
     List<String> fragmentUris = [];
     List<String> fragmentHashes = [];
 
     List<js.Property> libraryPartsMapEntries = [];
 
-    loadMap.forEach((String loadId, List<Fragment> fragmentList) {
+    loadMap.forEach((String loadId, List<FinalizedFragment> fragmentList) {
       List<js.Expression> indexes = [];
-      for (Fragment fragment in fragmentList) {
+      for (FinalizedFragment fragment in fragmentList) {
         String fragmentHash = deferredLoadHashes[fragment];
         if (fragmentHash == null) continue;
         int index = fragmentIndexes[fragment];
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
new file mode 100644
index 0000000..576f4ee
--- /dev/null
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
@@ -0,0 +1,372 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../../deferred_load.dart' show OutputUnit;
+import '../../js/js.dart' as js;
+import '../../js/size_estimator.dart';
+import '../../options.dart';
+import '../model.dart';
+
+class PreFragment {
+  final List<DeferredFragment> fragments = [];
+  final List<js.Statement> classPrototypes = [];
+  final List<js.Statement> closurePrototypes = [];
+  final List<js.Statement> inheritance = [];
+  final List<js.Statement> methodAliases = [];
+  final List<js.Statement> tearOffs = [];
+  final List<js.Statement> constants = [];
+  final List<js.Statement> typeRules = [];
+  final List<js.Statement> variances = [];
+  final List<js.Statement> staticNonFinalFields = [];
+  final List<js.Statement> lazyInitializers = [];
+  final List<js.Statement> nativeSupport = [];
+  final Set<PreFragment> successors = {};
+  final Set<PreFragment> predecessors = {};
+  int size = 0;
+
+  PreFragment(
+      Fragment fragment,
+      js.Statement classPrototypes,
+      js.Statement closurePrototypes,
+      js.Statement inheritance,
+      js.Statement methodAliases,
+      js.Statement tearOffs,
+      js.Statement constants,
+      js.Statement typeRules,
+      js.Statement variances,
+      js.Statement staticNonFinalFields,
+      js.Statement lazyInitializers,
+      js.Statement nativeSupport,
+      bool estimateSize) {
+    this.fragments.add(fragment);
+    this.classPrototypes.add(classPrototypes);
+    this.closurePrototypes.add(closurePrototypes);
+    this.inheritance.add(inheritance);
+    this.methodAliases.add(methodAliases);
+    this.tearOffs.add(tearOffs);
+    this.constants.add(constants);
+    this.typeRules.add(typeRules);
+    this.variances.add(variances);
+    this.staticNonFinalFields.add(staticNonFinalFields);
+    this.lazyInitializers.add(lazyInitializers);
+    this.nativeSupport.add(nativeSupport);
+    if (estimateSize) {
+      var estimator = SizeEstimator();
+      estimator.visit(classPrototypes);
+      estimator.visit(closurePrototypes);
+      estimator.visit(inheritance);
+      estimator.visit(methodAliases);
+      estimator.visit(tearOffs);
+      estimator.visit(constants);
+      estimator.visit(typeRules);
+      estimator.visit(variances);
+      estimator.visit(staticNonFinalFields);
+      estimator.visit(lazyInitializers);
+      estimator.visit(nativeSupport);
+      size = estimator.charCount;
+    }
+  }
+
+  void mergeAfter(PreFragment that) {
+    assert(this != that);
+    assert(
+        (that.predecessors.length == 1 && that.predecessors.single == this) ||
+            (this.successors.length == 1 && this.successors.single == that));
+    this.fragments.addAll(that.fragments);
+    this.classPrototypes.addAll(that.classPrototypes);
+    this.closurePrototypes.addAll(that.closurePrototypes);
+    this.inheritance.addAll(that.inheritance);
+    this.methodAliases.addAll(that.methodAliases);
+    this.tearOffs.addAll(that.tearOffs);
+    this.constants.addAll(that.constants);
+    this.typeRules.addAll(that.typeRules);
+    this.variances.addAll(that.variances);
+    this.staticNonFinalFields.addAll(that.staticNonFinalFields);
+    this.lazyInitializers.addAll(that.lazyInitializers);
+    this.nativeSupport.addAll(that.nativeSupport);
+    this.successors.remove(that);
+    that.successors.forEach((fragment) {
+      fragment.predecessors.remove(that);
+      fragment.predecessors.add(this);
+    });
+    this.successors.addAll(that.successors);
+    that.predecessors.remove(this);
+    that.predecessors.forEach((fragment) {
+      fragment.successors.remove(that);
+      fragment.successors.add(this);
+    });
+    this.predecessors.addAll(that.predecessors);
+    this.size += that.size;
+  }
+
+  FinalizedFragment finalize(
+      Program program, Map<Fragment, FinalizedFragment> fragmentMap) {
+    FinalizedFragment finalizedFragment;
+    var seedFragment = fragments.first;
+
+    // If we only have a single fragment, then wen just finalize it by itself.
+    // Otherwise, we finalize an entire group of fragments into a single
+    // merged and finalized fragment.
+    if (fragments.length == 1) {
+      finalizedFragment = FinalizedFragment(
+          seedFragment.outputFileName,
+          seedFragment.outputUnit,
+          seedFragment.libraries,
+          classPrototypes.first,
+          closurePrototypes.first,
+          inheritance.first,
+          methodAliases.first,
+          tearOffs.first,
+          constants.first,
+          typeRules.first,
+          variances.first,
+          staticNonFinalFields.first,
+          lazyInitializers.first,
+          nativeSupport.first,
+          program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+      fragmentMap[seedFragment] = finalizedFragment;
+    } else {
+      List<Library> libraries = [];
+      for (var fragment in fragments) {
+        if (seedFragment.outputUnit != fragment.outputUnit) {
+          program.mergeOutputUnitMetadata(
+              seedFragment.outputUnit, fragment.outputUnit);
+          seedFragment.outputUnit.merge(fragment.outputUnit);
+        }
+        libraries.addAll(fragment.libraries);
+      }
+      finalizedFragment = FinalizedFragment(
+          seedFragment.outputFileName,
+          seedFragment.outputUnit,
+          libraries,
+          js.Block(classPrototypes),
+          js.Block(closurePrototypes),
+          js.Block(inheritance),
+          js.Block(methodAliases),
+          js.Block(tearOffs),
+          js.Block(constants),
+          js.Block(typeRules),
+          js.Block(variances),
+          js.Block(staticNonFinalFields),
+          js.Block(lazyInitializers),
+          js.Block(nativeSupport),
+          program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+      for (var fragment in fragments) {
+        fragmentMap[fragment] = finalizedFragment;
+      }
+    }
+    return finalizedFragment;
+  }
+
+  @override
+  String toString() {
+    // This is not an efficient operation and should only be used for debugging.
+    var successors =
+        this.successors.map((fragment) => fragment.debugName()).join(',');
+    var predecessors =
+        this.predecessors.map((fragment) => fragment.debugName()).join(',');
+    var name = debugName();
+    return 'PreFragment(fragments=[$name], successors=[$successors], '
+        'predecessors=[$predecessors])';
+  }
+
+  String debugName() {
+    List<String> names = [];
+    this.fragments.forEach((fragment) => names.add(fragment.name));
+    return names.join(',');
+  }
+
+  static int compare(PreFragment l, PreFragment r) {
+    return l.size.compareTo(r.size);
+  }
+}
+
+class FinalizedFragment {
+  final String outputFileName;
+  final OutputUnit outputUnit;
+  final List<Library> libraries;
+  final js.Statement classPrototypes;
+  final js.Statement closurePrototypes;
+  final js.Statement inheritance;
+  final js.Statement methodAliases;
+  final js.Statement tearOffs;
+  final js.Statement constants;
+  final js.Statement typeRules;
+  final js.Statement variances;
+  final js.Statement staticNonFinalFields;
+  final js.Statement lazyInitializers;
+  final js.Statement nativeSupport;
+  final js.Expression deferredTypes;
+
+  FinalizedFragment(
+      this.outputFileName,
+      this.outputUnit,
+      this.libraries,
+      this.classPrototypes,
+      this.closurePrototypes,
+      this.inheritance,
+      this.methodAliases,
+      this.tearOffs,
+      this.constants,
+      this.typeRules,
+      this.variances,
+      this.staticNonFinalFields,
+      this.lazyInitializers,
+      this.nativeSupport,
+      this.deferredTypes);
+
+  bool isEmptyStatement(js.Statement statement) {
+    if (statement is js.Block) {
+      return statement.statements.isEmpty;
+    }
+    return statement is js.EmptyStatement;
+  }
+
+  bool get isEmpty {
+    // TODO(sra): How do we tell if [deferredTypes] is empty? It is filled-in
+    // later via the program finalizers. So we should defer the decision on the
+    // emptiness of the fragment until the finalizers have run.  For now we seem
+    // to get away with the fact that type indexes are either (1) main unit or
+    // (2) local to the emitted unit, so there is no such thing as a type in a
+    // deferred unit that is referenced from another deferred unit.  If we did
+    // not emit any functions, then we probably did not use the signature types
+    // in the OutputUnit's types, leaving them unused and tree-shaken.
+    // TODO(joshualitt): Currently, we ignore [typeRules] when determining
+    // emptiness because the type rules never seem to be empty.
+    return isEmptyStatement(classPrototypes) &&
+        isEmptyStatement(closurePrototypes) &&
+        isEmptyStatement(inheritance) &&
+        isEmptyStatement(methodAliases) &&
+        isEmptyStatement(tearOffs) &&
+        isEmptyStatement(constants) &&
+        isEmptyStatement(staticNonFinalFields) &&
+        isEmptyStatement(lazyInitializers) &&
+        isEmptyStatement(nativeSupport);
+  }
+}
+
+class FragmentMerger {
+  final CompilerOptions _options;
+
+  FragmentMerger(this._options);
+
+  // Converts a map of (loadId, List<fragments>) to a map of
+  // (loadId, List<FinalizedFragment>).
+  static Map<String, List<FinalizedFragment>> processLoadMap(
+      Map<String, List<Fragment>> programLoadMap,
+      Map<Fragment, FinalizedFragment> fragmentMap) {
+    Map<String, List<FinalizedFragment>> loadMap = {};
+    programLoadMap.forEach((loadId, fragments) {
+      Set<FinalizedFragment> unique = {};
+      List<FinalizedFragment> finalizedFragments = [];
+      loadMap[loadId] = finalizedFragments;
+      for (var fragment in fragments) {
+        var finalizedFragment = fragmentMap[fragment];
+        if (unique.add(finalizedFragment)) {
+          finalizedFragments.add(finalizedFragment);
+        }
+      }
+    });
+    return loadMap;
+  }
+
+  // Attaches predecessors to each PreFragment. We only care about
+  // direct predecessors.
+  static void attachDependencies(Map<String, List<Fragment>> programLoadMap,
+      Map<Fragment, PreFragment> fragmentMap) {
+    programLoadMap.forEach((loadId, fragments) {
+      for (int i = 0; i < fragments.length - 1; i++) {
+        var fragment = fragmentMap[fragments[i]];
+        var nextFragment = fragmentMap[fragments[i + 1]];
+        fragment.successors.add(nextFragment);
+        nextFragment.predecessors.add(fragment);
+      }
+    });
+  }
+
+  // Iterates through preDeferredFragments making as many merges as possible
+  // until either there are no more valid merges to make, or until there are
+  // only mergeFragmentsThreshold remaining.
+  List<PreFragment> mergeFragments(List<PreFragment> preDeferredFragments) {
+    Set<PreFragment> fragmentsBySize = {};
+
+    // We greedily look for a valid merge which results in the smallest
+    // possible increase in size. Currently, we only merge fragments in two
+    // cases:
+    // 1) We will merge two fragments A and B if B is A's single dependent.
+    // 2) We will merge two fragments C and D if C is D's single dependency.
+    bool mergeTwo() {
+      PreFragment aFragment = null;
+      PreFragment bFragment = null;
+      PreFragment cFragment = null;
+      PreFragment dFragment = null;
+      for (var fragment in fragmentsBySize) {
+        if (fragment.successors.length == 1 &&
+            (aFragment == null && bFragment == null ||
+                (fragment.size + fragment.successors.single.size <
+                    aFragment.size + bFragment.size))) {
+          aFragment = fragment;
+          bFragment = fragment.successors.single;
+        }
+        if (fragment.predecessors.length == 1 &&
+            (cFragment == null && dFragment == null ||
+                (fragment.size + fragment.predecessors.single.size <
+                    cFragment.size + dFragment.size))) {
+          cFragment = fragment.predecessors.single;
+          dFragment = fragment;
+        }
+      }
+      assert((aFragment != null &&
+              bFragment != null &&
+              aFragment != bFragment &&
+              aFragment.successors.single == bFragment) ||
+          (cFragment != null &&
+              dFragment != null &&
+              cFragment != dFragment &&
+              dFragment.predecessors.single == cFragment) ||
+          (aFragment == null &&
+              bFragment == null &&
+              cFragment == null &&
+              dFragment == null));
+      int mergeSentinel = 0x10000000000;
+      bool abCanMerge = aFragment != null && bFragment != null;
+      bool cdCanMerge = cFragment != null && dFragment != null;
+      int abMergeSize =
+          abCanMerge ? aFragment.size + bFragment.size : mergeSentinel;
+      int cdMergeSize =
+          cdCanMerge ? cFragment.size + dFragment.size : mergeSentinel;
+      bool abShouldMerge() => abCanMerge && abMergeSize <= cdMergeSize;
+      bool cdShouldMerge() => cdCanMerge && cdMergeSize <= abMergeSize;
+      void innerMerge(PreFragment a, PreFragment b) {
+        fragmentsBySize.remove(a);
+        fragmentsBySize.remove(b);
+        a.mergeAfter(b);
+        fragmentsBySize.add(a);
+      }
+
+      bool merged = abShouldMerge() || cdShouldMerge();
+      if (abShouldMerge()) {
+        innerMerge(aFragment, bFragment);
+      } else if (cdShouldMerge()) {
+        innerMerge(cFragment, dFragment);
+      } else {
+        assert(aFragment == null &&
+            bFragment == null &&
+            cFragment == null &&
+            dFragment == null);
+      }
+      return merged;
+    }
+
+    fragmentsBySize.addAll(preDeferredFragments);
+    var numFragments = preDeferredFragments.length;
+    while (numFragments-- > _options.mergeFragmentsThreshold) {
+      if (!mergeTwo()) {
+        // No further valid merges can be made.
+        break;
+      }
+    }
+    return fragmentsBySize.toList();
+  }
+}
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index f549f60..60d8baa 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -38,6 +38,7 @@
 import '../../constants/values.dart'
     show ConstantValue, FunctionConstantValue, NullConstantValue;
 import '../../common_elements.dart' show CommonElements, JElementEnvironment;
+import '../../deferred_load.dart' show OutputUnit;
 import '../../dump_info.dart';
 import '../../elements/entities.dart';
 import '../../elements/types.dart';
@@ -75,6 +76,7 @@
 import '../js_emitter.dart' show buildTearOffCode, NativeGenerator;
 import '../model.dart';
 import '../native_emitter.dart';
+import 'fragment_merger.dart';
 
 part 'fragment_emitter.dart';
 
@@ -94,9 +96,11 @@
   final SourceInformationStrategy _sourceInformationStrategy;
 
   // The full code that is written to each hunk part-file.
-  final Map<Fragment, CodeOutput> outputBuffers = {};
+  final Map<OutputUnit, CodeOutput> emittedOutputBuffers = {};
 
-  Set<Fragment> omittedFragments = Set();
+  final Set<OutputUnit> omittedOutputUnits = {};
+
+  List<PreFragment> preDeferredFragmentsForTesting;
 
   /// For deferred loading we communicate the initializers via this global var.
   static const String deferredInitializersGlobal =
@@ -183,6 +187,8 @@
         [_namer.globalObjectForConstant(value), _namer.constantName(value)]);
   }
 
+  bool get shouldMergeFragments => _options.mergeFragmentsThreshold != null;
+
   int emitProgram(Program program, CodegenWorld codegenWorld) {
     MainFragment mainFragment = program.fragments.first;
     List<DeferredFragment> deferredFragments =
@@ -203,17 +209,44 @@
     js.Statement mainCode =
         fragmentEmitter.emitMainFragment(program, deferredLoadingState);
 
-    Map<DeferredFragment, js.Expression> deferredFragmentsCode = {};
+    // In order to get size estimates, we partially emit deferred fragments.
+    List<PreFragment> preDeferredFragments = [];
+    Map<DeferredFragment, PreFragment> preFragmentMap = {};
+    _task.measureSubtask('emit prefragments', () {
+      for (var fragment in deferredFragments) {
+        var preFragment =
+            fragmentEmitter.emitPreFragment(fragment, shouldMergeFragments);
+        preFragmentMap[fragment] = preFragment;
+        preDeferredFragments.add(preFragment);
+      }
+    });
 
-    for (DeferredFragment fragment in deferredFragments) {
-      js.Expression types =
-          program.metadataTypesForOutputUnit(fragment.outputUnit);
+    // Attach dependencies to each PreFragment.
+    FragmentMerger.attachDependencies(program.loadMap, preFragmentMap);
+
+    if (shouldMergeFragments) {
+      preDeferredFragments = _task.measureSubtask('merge fragments', () {
+        FragmentMerger fragmentMerger = FragmentMerger(_options);
+        return fragmentMerger.mergeFragments(preDeferredFragments);
+      });
+    }
+
+    // If necessary, we retain the merged PreFragments for testing.
+    if (retainDataForTesting) {
+      preDeferredFragmentsForTesting = preDeferredFragments;
+    }
+
+    Map<DeferredFragment, FinalizedFragment> fragmentMap = {};
+    Map<FinalizedFragment, js.Expression> deferredFragmentsCode = {};
+    for (var preDeferredFragment in preDeferredFragments) {
+      var finalizedFragment =
+          preDeferredFragment.finalize(program, fragmentMap);
       js.Expression fragmentCode = fragmentEmitter.emitDeferredFragment(
-          fragment, types, program.holders);
+          finalizedFragment, program.holders);
       if (fragmentCode != null) {
-        deferredFragmentsCode[fragment] = fragmentCode;
+        deferredFragmentsCode[finalizedFragment] = fragmentCode;
       } else {
-        omittedFragments.add(fragment);
+        omittedOutputUnits.add(finalizedFragment.outputUnit);
       }
     }
 
@@ -227,15 +260,17 @@
     // deferred ASTs inside the parts) have any contents. We should wait until
     // this point to decide if a part is empty.
 
-    Map<DeferredFragment, String> hunkHashes =
+    Map<FinalizedFragment, String> hunkHashes =
         _task.measureSubtask('write fragments', () {
       return writeDeferredFragments(deferredFragmentsCode);
     });
 
     // Now that we have written the deferred hunks, we can create the deferred
     // loading data.
+    Map<String, List<FinalizedFragment>> loadMap =
+        FragmentMerger.processLoadMap(program.loadMap, fragmentMap);
     fragmentEmitter.finalizeDeferredLoadingData(
-        program.loadMap, hunkHashes, deferredLoadingState);
+        loadMap, hunkHashes, deferredLoadingState);
 
     _task.measureSubtask('write fragments', () {
       writeMainFragment(mainFragment, mainCode,
@@ -254,7 +289,7 @@
     }
 
     // Return the total program size.
-    return outputBuffers.values.fold(0, (a, b) => a + b.length);
+    return emittedOutputBuffers.values.fold(0, (a, b) => a + b.length);
   }
 
   /// Generates a simple header that provides the compiler's build id.
@@ -278,11 +313,11 @@
   /// library code).
   ///
   /// Updates the shared [outputBuffers] field with the output.
-  Map<DeferredFragment, String> writeDeferredFragments(
-      Map<DeferredFragment, js.Expression> fragmentsCode) {
-    Map<DeferredFragment, String> hunkHashes = {};
+  Map<FinalizedFragment, String> writeDeferredFragments(
+      Map<FinalizedFragment, js.Expression> fragmentsCode) {
+    Map<FinalizedFragment, String> hunkHashes = {};
 
-    fragmentsCode.forEach((DeferredFragment fragment, js.Expression code) {
+    fragmentsCode.forEach((FinalizedFragment fragment, js.Expression code) {
       hunkHashes[fragment] = writeDeferredFragment(fragment, code);
     });
 
@@ -313,7 +348,7 @@
     CodeOutput mainOutput = StreamCodeOutput(
         _outputProvider.createOutputSink('', 'js', OutputType.js),
         codeOutputListeners);
-    outputBuffers[fragment] = mainOutput;
+    emittedOutputBuffers[fragment.outputUnit] = mainOutput;
 
     js.Program program = js.Program([
       buildGeneratedBy(),
@@ -358,7 +393,7 @@
   // Returns the deferred fragment's hash.
   //
   // Updates the shared [outputBuffers] field with the output.
-  String writeDeferredFragment(DeferredFragment fragment, js.Expression code) {
+  String writeDeferredFragment(FinalizedFragment fragment, js.Expression code) {
     List<CodeOutputListener> outputListeners = [];
     Hasher hasher = new Hasher();
     outputListeners.add(hasher);
@@ -378,7 +413,7 @@
             hunkPrefix, deferredExtension, OutputType.jsPart),
         outputListeners);
 
-    outputBuffers[fragment] = output;
+    emittedOutputBuffers[fragment.outputUnit] = output;
 
     // The [code] contains the function that must be invoked when the deferred
     // hunk is loaded.
@@ -456,8 +491,7 @@
         "needed for a given deferred library import.";
     mapping.addAll(_closedWorld.outputUnitData.computeDeferredMap(
         _options, _closedWorld.elementEnvironment,
-        omittedUnits:
-            omittedFragments.map((fragment) => fragment.outputUnit).toSet()));
+        omittedUnits: omittedOutputUnits));
     _outputProvider.createOutputSink(
         _options.deferredMapUri.path, '', OutputType.deferredMap)
       ..add(const JsonEncoder.withIndent("  ").convert(mapping))
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 47b0985..6e86321 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -161,6 +161,15 @@
   /// libraries are subdivided.
   Uri deferredMapUri;
 
+  /// The maximum number of deferred fragments to generate. If the number of
+  /// fragments exceeds this amount, then they may be merged.
+  /// Note: Currently, we only merge fragments in a single dependency chain. We
+  /// will not merge fragments with unrelated dependencies and thus we may
+  /// generate more fragments than the 'mergeFragmentsThreshold' under some
+  /// situations.
+  int mergeFragmentsThreshold = null; // default value, no max.
+  int _mergeFragmentsThreshold;
+
   /// Whether to disable inlining during the backend optimizations.
   // TODO(sigmund): negate, so all flags are positive
   bool disableInlining = false;
@@ -524,7 +533,9 @@
       ..cfeOnly = _hasOption(options, Flags.cfeOnly)
       ..debugGlobalInference = _hasOption(options, Flags.debugGlobalInference)
       .._soundNullSafety = _hasOption(options, Flags.soundNullSafety)
-      .._noSoundNullSafety = _hasOption(options, Flags.noSoundNullSafety);
+      .._noSoundNullSafety = _hasOption(options, Flags.noSoundNullSafety)
+      .._mergeFragmentsThreshold =
+          _extractIntOption(options, '${Flags.mergeFragmentsThreshold}=');
   }
 
   void validate() {
@@ -628,6 +639,10 @@
     if (_noNativeNullAssertions || nullSafetyMode != NullSafetyMode.sound) {
       nativeNullAssertions = false;
     }
+
+    if (_mergeFragmentsThreshold != null) {
+      mergeFragmentsThreshold = _mergeFragmentsThreshold;
+    }
   }
 
   /// Returns `true` if warnings and hints are shown for all packages.
diff --git a/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart b/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart
index ca84a9f..ab1d73d 100644
--- a/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/basic_deferred/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart
index bb45d8c..af43fe3 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_class/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart
index 5a1c77d..fa722a4 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_constant1/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib2}], usedBy: [], needs: []}],
+ steps=[lib2=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib1.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart
index 71a715b..b4cc534 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_constant2/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'package:expect/expect.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart
index 5bc723e..0810049 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_constant3/main.dart
@@ -2,6 +2,15 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[
+  f1: {units: [1{l1}], usedBy: [], needs: []},
+  f2: {units: [2{l2}], usedBy: [], needs: []}],
+ steps=[
+  l1=(f1),
+  l2=(f2)]
+*/
+
 // @dart = 2.7
 
 import 'shared.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart
index 167b145..ba1eed1 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_fail_and_retry/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 // Test that when a deferred import fails to load, it is possible to retry.
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart
index 3c99316..48dd14a 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_function/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 // Test that loading of a library (with top-level functions only) can
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
index 782e177..1da36d1 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [1{lib1, lib2}], usedBy: [2, 3], needs: []},
+  f2: {units: [2{lib1}], usedBy: [], needs: [1]},
+  f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [1{lib1, lib2}, 2{lib1}], usedBy: [2], needs: []},
+  f2: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1),
+  lib2=(f1, f2)]
+*/
+
 // @dart = 2.7
 
 import 'lib1.dart' deferred as lib1;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart
index 20bb44c..45bddff 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_typed_map/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib1.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart
index 9c56b84..c838c8f 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_typedef/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib1}], usedBy: [], needs: []}],
+ steps=[lib1=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib1.dart' deferred as lib1;
diff --git a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
index e55be81..d65299a 100644
--- a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: [1]},
+  f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [2{lib1, lib2}, 3{lib2}], usedBy: [2], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1)]
+*/
+
 // @dart = 2.7
 
 // TODO(sigmund): remove this indirection and move the main code here. This is
diff --git a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart
index 3a45b3e..59d567e 100644
--- a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_global/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart b/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart
index 74cd88d..0465c9f 100644
--- a/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/follow_implicit_super_regression_test/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import "lib.dart" deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/future_or/main.dart b/pkg/compiler/test/deferred_loading/data/future_or/main.dart
index de44d54..77ca4a0 100644
--- a/pkg/compiler/test/deferred_loading/data/future_or/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/future_or/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib1}], usedBy: [], needs: []}],
+ steps=[lib1=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'dart:async';
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart
index 5da17d9..9b83f62 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation0/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{b}], usedBy: [], needs: []}],
+ steps=[b=(f1)]
+*/
+
 // @dart = 2.7
 
 // Test instantiation used only in a deferred library.
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
index ae2c5a7..963d421 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
+  f2: {units: [1{b}], usedBy: [], needs: [1]},
+  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [2{b, c}, 1{b}], usedBy: [2], needs: []},
+  f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1),
+  c=(f1, f2)]
+*/
+
 // @dart = 2.7
 
 // Test instantiations with different type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
index 3ff8f46..53cbbdb 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
+  f2: {units: [2{b}], usedBy: [], needs: [1]},
+  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [1{b, c}, 2{b}], usedBy: [2], needs: []},
+  f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1),
+  c=(f1, f2)]
+*/
+
 // @dart = 2.7
 
 // Test instantiations with the same type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart
index b0d3a8f..10e0815 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation3/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{b}], usedBy: [], needs: []}],
+ steps=[b=(f1)]
+*/
+
 // @dart = 2.7
 
 // Test instantiation used only in a deferred library.
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
index 34bf22a..c6b601b 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
+  f2: {units: [1{b}], usedBy: [], needs: [1]},
+  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [2{b, c}, 1{b}], usedBy: [2], needs: []},
+  f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1),
+  c=(f1, f2)]
+*/
+
 // @dart = 2.7
 
 // Test instantiations with different type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
index 5612525..8dbfaf1 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
+  f2: {units: [2{b}], usedBy: [], needs: [1]},
+  f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1, f2),
+  c=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [1{b, c}, 2{b}], usedBy: [2], needs: []},
+  f2: {units: [3{c}], usedBy: [], needs: [1]}],
+ steps=[
+  b=(f1),
+  c=(f1, f2)]
+*/
+
 // @dart = 2.7
 
 // Test instantiations with the same type argument count used only in two
diff --git a/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart b/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart
index d4b10fb..491bcf6 100644
--- a/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/inteface_type_variable/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
index ca9bb68..441c28d 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
@@ -2,6 +2,41 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec.library: 
+ output_units=[
+  f1: {units: [3{libA, libB, libC}], usedBy: [2, 4], needs: []},
+  f2: {units: [4{libA, libC}], usedBy: [3, 6], needs: [1, 4]},
+  f3: {units: [6{libA}], usedBy: [], needs: [2]},
+  f4: {units: [5{libB, libC}], usedBy: [5, 2], needs: [1]},
+  f5: {units: [1{libB}], usedBy: [], needs: [4]},
+  f6: {units: [2{libC}], usedBy: [], needs: [2]}],
+ steps=[
+  libA=(f1, f2, f3),
+  libB=(f1, f4, f5),
+  libC=(f1, f4, f2, f6)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}, 6{libA}, 1{libB}], usedBy: [2], needs: []},
+  f2: {units: [2{libC}], usedBy: [], needs: [1]}],
+ steps=[
+  libA=(f1),
+  libB=(f1),
+  libC=(f1, f2)]
+*/
+
+/*three-frag.library: 
+ output_units=[
+  f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}, 6{libA}], usedBy: [2, 3], needs: []},
+  f2: {units: [1{libB}], usedBy: [], needs: [1]},
+  f3: {units: [2{libC}], usedBy: [], needs: [1]}],
+ steps=[
+  libA=(f1),
+  libB=(f1, f2),
+  libC=(f1, f3)]
+*/
+
 // @dart = 2.7
 
 import 'liba.dart' deferred as libA;
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
index 7f86fc1..85002e0 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
@@ -35,7 +35,8 @@
 f_010_11(Set<String> u, int b) => v(u, '01011', b);
 
 @pragma('dart2js:noInline')
-/*member: f_011_01:member_unit=8{b1, b3, b4}*/
+/*spec|two-frag.member: f_011_01:member_unit=8{b1, b3, b4}*/
+/*three-frag.member: f_011_01:member_unit=8{b1, b3, b4, b2, b5}*/
 f_011_01(Set<String> u, int b) => v(u, '01101', b);
 
 @pragma('dart2js:noInline')
@@ -51,7 +52,8 @@
 f_100_11(Set<String> u, int b) => v(u, '10011', b);
 
 @pragma('dart2js:noInline')
-/*member: f_101_01:member_unit=12{b1, b3, b5}*/
+/*spec|three-frag.member: f_101_01:member_unit=12{b1, b3, b5}*/
+/*two-frag.member: f_101_01:member_unit=12{b1, b3, b5, b4, b2}*/
 f_101_01(Set<String> u, int b) => v(u, '10101', b);
 
 @pragma('dart2js:noInline')
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
index 7c057b4..0fc9253 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
@@ -2,6 +2,72 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec.library: 
+ output_units=[
+  f10: {units: [7{b1, b2, b4}], usedBy: [11, 29], needs: [9, 8]},
+  f11: {units: [5{b1, b2, b3}], usedBy: [12, 21, 26], needs: [10, 8]},
+  f12: {units: [10{b1, b5}], usedBy: [13, 31], needs: [11, 21]},
+  f13: {units: [6{b1, b4}], usedBy: [14, 30], needs: [12, 22]},
+  f14: {units: [4{b1, b3}], usedBy: [15, 28], needs: [13, 23]},
+  f15: {units: [3{b1, b2}], usedBy: [16, 24], needs: [14, 23]},
+  f16: {units: [2{b1}], usedBy: [], needs: [15]},
+  f17: {units: [24{b2, b3, b4, b5}], usedBy: [3, 2], needs: [1]},
+  f18: {units: [23{b2, b4, b5}], usedBy: [19, 20], needs: [5, 25]},
+  f19: {units: [22{b2, b3, b5}], usedBy: [20, 6], needs: [18, 25]},
+  f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2, 17], needs: []},
+  f20: {units: [20{b2, b3, b4}], usedBy: [9, 7, 6], needs: [19, 18]},
+  f21: {units: [21{b2, b5}], usedBy: [22, 12], needs: [11, 26]},
+  f22: {units: [19{b2, b4}], usedBy: [23, 13], needs: [21, 27]},
+  f23: {units: [18{b2, b3}], usedBy: [15, 14], needs: [22, 27]},
+  f24: {units: [17{b2}], usedBy: [], needs: [15]},
+  f25: {units: [28{b3, b4, b5}], usedBy: [19, 18], needs: [5, 4]},
+  f26: {units: [27{b3, b5}], usedBy: [27, 21], needs: [11, 29]},
+  f27: {units: [26{b3, b4}], usedBy: [23, 22], needs: [26, 29]},
+  f28: {units: [25{b3}], usedBy: [], needs: [14]},
+  f29: {units: [30{b4, b5}], usedBy: [27, 26], needs: [10, 9]},
+  f2: {units: [16{b1, b3, b4, b5}], usedBy: [3, 4], needs: [1, 17]},
+  f30: {units: [29{b4}], usedBy: [], needs: [13]},
+  f31: {units: [31{b5}], usedBy: [], needs: [12]},
+  f3: {units: [15{b1, b2, b4, b5}], usedBy: [4, 5], needs: [2, 17]},
+  f4: {units: [13{b1, b2, b3, b5}], usedBy: [5, 25], needs: [3, 2]},
+  f5: {units: [9{b1, b2, b3, b4}], usedBy: [6, 18, 25], needs: [4, 3]},
+  f6: {units: [14{b1, b4, b5}], usedBy: [7, 8], needs: [5, 20, 19]},
+  f7: {units: [12{b1, b3, b5}], usedBy: [8, 9], needs: [6, 20]},
+  f8: {units: [8{b1, b3, b4}], usedBy: [9, 11, 10], needs: [7, 6]},
+  f9: {units: [11{b1, b2, b5}], usedBy: [10, 29], needs: [8, 20, 7]}],
+ steps=[
+  b1=(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16),
+  b2=(f1, f17, f3, f4, f5, f18, f19, f20, f9, f10, f11, f21, f22, f23, f15, f24),
+  b3=(f1, f17, f2, f4, f5, f25, f19, f20, f7, f8, f11, f26, f27, f23, f14, f28),
+  b4=(f1, f17, f2, f3, f5, f25, f18, f20, f6, f8, f10, f29, f27, f22, f13, f30),
+  b5=(f1, f17, f2, f3, f4, f25, f18, f19, f6, f7, f9, f29, f26, f21, f12, f31)]
+*/
+
+/*three-frag.library: 
+ output_units=[
+  f1: {units: [1{b1, b2, b3, b4, b5}, 24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}], usedBy: [2, 3], needs: []},
+  f2: {units: [12{b1, b3, b5}], usedBy: [3], needs: [1]},
+  f3: {units: [8{b1, b3, b4, b2, b5}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 31{b5}, 6{b1, b4}, 29{b4}, 4{b1, b3}, 25{b3}, 3{b1, b2}, 2{b1}, 17{b2}], usedBy: [], needs: [2, 1]}],
+ steps=[
+  b1=(f1, f2, f3),
+  b2=(f1, f3),
+  b3=(f1, f2, f3),
+  b4=(f1, f3),
+  b5=(f1, f2, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [1{b1, b2, b3, b4, b5}, 24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}], usedBy: [2], needs: []},
+  f2: {units: [12{b1, b3, b5, b4, b2}, 8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 31{b5}, 6{b1, b4}, 29{b4}, 4{b1, b3}, 25{b3}, 3{b1, b2}, 2{b1}, 17{b2}], usedBy: [], needs: [1]}],
+ steps=[
+  b1=(f1, f2),
+  b2=(f1, f2),
+  b3=(f1, f2),
+  b4=(f1, f2),
+  b5=(f1, f2)]
+*/
+
 import 'lib1.dart';
 import 'lib2.dart';
 import 'lib3.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/marker.options b/pkg/compiler/test/deferred_loading/data/marker.options
index 1415216..95010cc 100644
--- a/pkg/compiler/test/deferred_loading/data/marker.options
+++ b/pkg/compiler/test/deferred_loading/data/marker.options
@@ -1 +1,3 @@
 spec=pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+two-frag=pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+three-frag=pkg/compiler/test/deferred_loading/deferred_loading_test.dart
diff --git a/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart b/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart
index d10d45f..7c1acac 100644
--- a/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/regress_35311/main.dart
@@ -4,6 +4,10 @@
 
 // @dart = 2.7
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
 import 'lib.dart' deferred as lib;
 
 /*member: main:member_unit=main{}*/
diff --git a/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart b/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart
index dfbc3c4..486907a 100644
--- a/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/regress_43055/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{libb}], usedBy: [], needs: []}],
+ steps=[libb=(f1)]
+*/
+
 // @dart = 2.7
 import 'libb.dart' deferred as libb;
 import 'libc.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart b/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart
index 8337bc0..dea65bc 100644
--- a/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/shared_constant/main.dart
@@ -2,6 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{s1, s2}], usedBy: [], needs: []}],
+ steps=[
+  s1=(f1),
+  s2=(f1)]
+*/
+
 // @dart = 2.7
 
 /// Regression test for issue https://github.com/dart-lang/sdk/issues/31306.
diff --git a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
index 78918bf..78990e9 100644
--- a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: [1]},
+  f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1, f2),
+  lib2=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [2{lib1, lib2}, 1{lib1}], usedBy: [2], needs: []},
+  f2: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1),
+  lib2=(f1, f2)]
+*/
+
 // @dart = 2.7
 
 // The class lib1.C is referenced via lib1
diff --git a/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart b/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart
index c279b65..6049f85 100644
--- a/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/type_argument_dependency/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{c}], usedBy: [], needs: []}],
+ steps=[c=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib1.dart';
diff --git a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
index dd5873b..4cad65c 100644
--- a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
@@ -2,6 +2,25 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*spec|three-frag.library: 
+ output_units=[
+  f1: {units: [3{lib1, lib3}], usedBy: [2, 3], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: [1]},
+  f3: {units: [2{lib3}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1, f2),
+  lib3=(f1, f3)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [3{lib1, lib3}, 2{lib3}], usedBy: [2], needs: []},
+  f2: {units: [1{lib1}], usedBy: [], needs: [1]}],
+ steps=[
+  lib1=(f1, f2),
+  lib3=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib1.dart' deferred as lib1;
diff --git a/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart b/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart
index 8b8ad89..3ee33186 100644
--- a/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/uninstantiated_type_variable/main.dart
@@ -2,6 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+/*library: 
+ output_units=[f1: {units: [1{lib}], usedBy: [], needs: []}],
+ steps=[lib=(f1)]
+*/
+
 // @dart = 2.7
 
 import 'lib.dart' deferred as lib;
diff --git a/pkg/compiler/test/deferred_loading/deferred_loading_test.dart b/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
index 43f6ffa..e261408 100644
--- a/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+++ b/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
@@ -15,6 +15,9 @@
 import 'package:compiler/src/ir/util.dart';
 import 'package:compiler/src/js_model/element_map.dart';
 import 'package:compiler/src/js_model/js_world.dart';
+import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_emitter/startup_emitter/fragment_merger.dart';
+import 'package:compiler/src/kernel/kernel_strategy.dart';
 import 'package:expect/expect.dart';
 import '../equivalence/id_equivalence.dart';
 import '../equivalence/id_equivalence_helper.dart';
@@ -37,7 +40,9 @@
     await checkTests(dataDir, const OutputUnitDataComputer(),
         options: compilerOptions, args: args, setUpFunction: () {
       importPrefixes.clear();
-    }, testedConfigs: allSpecConfigs);
+    },
+        testedConfigs: allSpecConfigs +
+            [twoDeferredFragmentConfig, threeDeferredFragmentConfig]);
   });
 }
 
@@ -47,10 +52,7 @@
 // prefix name responds to two different libraries.
 Map<String, Uri> importPrefixes = {};
 
-/// Create a consistent string representation of [OutputUnit]s for both
-/// KImportEntities and ImportElements.
-String outputUnitString(OutputUnit unit) {
-  if (unit == null) return 'none';
+String importPrefixString(OutputUnit unit) {
   StringBuffer sb = StringBuffer();
   bool first = true;
   for (ImportEntity import in unit.importsForTesting) {
@@ -73,15 +75,48 @@
     }
     importPrefixes[import.name] = import.enclosingLibraryUri;
   }
+  return sb.toString();
+}
+
+/// Create a consistent string representation of [OutputUnit]s for both
+/// KImportEntities and ImportElements.
+String outputUnitString(OutputUnit unit) {
+  if (unit == null) return 'none';
+  String sb = importPrefixString(unit);
   return '${unit.name}{$sb}';
 }
 
+Map<String, List<PreFragment>> buildPreFragmentMap(
+    Map<String, List<Fragment>> loadMap,
+    List<PreFragment> preDeferredFragments) {
+  Map<DeferredFragment, PreFragment> fragmentMap = {};
+  for (var preFragment in preDeferredFragments) {
+    for (var fragment in preFragment.fragments) {
+      assert(!fragmentMap.containsKey(fragment));
+      fragmentMap[fragment] = preFragment;
+    }
+  }
+
+  Map<String, List<PreFragment>> preFragmentMap = {};
+  loadMap.forEach((loadId, fragments) {
+    Set<PreFragment> preFragments = {};
+    for (var fragment in fragments) {
+      preFragments.add(fragmentMap[fragment]);
+    }
+    assert(!preFragmentMap.containsKey(loadId));
+    preFragmentMap[loadId] = preFragments.toList();
+  });
+  return preFragmentMap;
+}
+
 class Tags {
   static const String cls = 'class_unit';
   static const String member = 'member_unit';
   static const String closure = 'closure_unit';
   static const String constants = 'constants';
   static const String type = 'type_unit';
+  static const String steps = 'steps';
+  static const String outputUnits = 'output_units';
 }
 
 class OutputUnitDataComputer extends DataComputer<Features> {
@@ -118,10 +153,82 @@
   }
 
   @override
+  void computeLibraryData(Compiler compiler, LibraryEntity library,
+      Map<Id, ActualData<Features>> actualMap,
+      {bool verbose}) {
+    KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
+    ir.Library node = frontendStrategy.elementMap.getLibraryNode(library);
+    List<PreFragment> preDeferredFragments = compiler
+        .backendStrategy.emitterTask.emitter.preDeferredFragmentsForTesting;
+    Program program =
+        compiler.backendStrategy.emitterTask.emitter.programForTesting;
+    Map<String, List<PreFragment>> preFragmentMap =
+        buildPreFragmentMap(program.loadMap, preDeferredFragments);
+    PreFragmentsIrComputer(compiler.reporter, actualMap, preFragmentMap)
+        .computeForLibrary(node);
+  }
+
+  @override
   DataInterpreter<Features> get dataValidator =>
       const FeaturesDataInterpreter();
 }
 
+class PreFragmentsIrComputer extends IrDataExtractor<Features> {
+  final Map<String, List<PreFragment>> _preFragmentMap;
+
+  PreFragmentsIrComputer(DiagnosticReporter reporter,
+      Map<Id, ActualData<Features>> actualMap, this._preFragmentMap)
+      : super(reporter, actualMap);
+
+  @override
+  Features computeLibraryValue(Id id, ir.Library library) {
+    var name = '${library.importUri.pathSegments.last}';
+    Features features = new Features();
+    if (!name.startsWith('main')) return features;
+
+    int index = 1;
+    Map<PreFragment, int> preFragmentIndices = {};
+    Map<int, PreFragment> reversePreFragmentIndices = {};
+    _preFragmentMap.forEach((loadId, preFragments) {
+      List<String> preFragmentNeeds = [];
+      for (var preFragment in preFragments) {
+        if (!preFragmentIndices.containsKey(preFragment)) {
+          preFragmentIndices[preFragment] = index;
+          reversePreFragmentIndices[index++] = preFragment;
+        }
+        preFragmentNeeds.add('f${preFragmentIndices[preFragment]}');
+      }
+      features.addElement(
+          Tags.steps, '$loadId=(${preFragmentNeeds.join(', ')})');
+    });
+
+    for (int i = 1; i < index; i++) {
+      var preFragment = reversePreFragmentIndices[i];
+      List<int> needs = [];
+      List<OutputUnit> supplied = [];
+      List<int> usedBy = [];
+      for (var dependent in preFragment.successors) {
+        assert(preFragmentIndices.containsKey(dependent));
+        usedBy.add(preFragmentIndices[dependent]);
+      }
+
+      for (var dependency in preFragment.predecessors) {
+        assert(preFragmentIndices.containsKey(dependency));
+        needs.add(preFragmentIndices[dependency]);
+      }
+
+      for (var fragment in preFragment.fragments) {
+        supplied.add(fragment.outputUnit);
+      }
+      var suppliedString = '[${supplied.map(outputUnitString).join(', ')}]';
+      features.addElement(Tags.outputUnits,
+          'f$i: {units: $suppliedString, usedBy: $usedBy, needs: $needs}');
+    }
+
+    return features;
+  }
+}
+
 class OutputUnitIrComputer extends IrDataExtractor<Features> {
   final JsToElementMap _elementMap;
   final OutputUnitData _data;
diff --git a/pkg/compiler/test/equivalence/id_equivalence_helper.dart b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
index 0a9c8d6..1e22b7c 100644
--- a/pkg/compiler/test/equivalence/id_equivalence_helper.dart
+++ b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
@@ -29,12 +29,24 @@
 
 const String specMarker = 'spec';
 const String prodMarker = 'prod';
+const String twoDeferredFragmentMarker = 'two-frag';
+const String threeDeferredFragmentMarker = 'three-frag';
 
 const TestConfig specConfig = TestConfig(specMarker, 'compliance mode', []);
 
 const TestConfig prodConfig = TestConfig(prodMarker, 'production mode',
     [Flags.omitImplicitChecks, Flags.laxRuntimeTypeToString]);
 
+const TestConfig twoDeferredFragmentConfig = TestConfig(
+    twoDeferredFragmentMarker,
+    'two deferred fragment mode',
+    ['${Flags.mergeFragmentsThreshold}=2']);
+
+const TestConfig threeDeferredFragmentConfig = TestConfig(
+    threeDeferredFragmentMarker,
+    'three deferred fragment mode',
+    ['${Flags.mergeFragmentsThreshold}=3']);
+
 /// Default internal configurations not including experimental features.
 const List<TestConfig> defaultInternalConfigs = [specConfig, prodConfig];
 
diff --git a/pkg/compiler/test/js/js_size_estimator_test.dart b/pkg/compiler/test/js/js_size_estimator_test.dart
index cb5ce76..e9fbfcf 100644
--- a/pkg/compiler/test/js/js_size_estimator_test.dart
+++ b/pkg/compiler/test/js/js_size_estimator_test.dart
@@ -24,7 +24,7 @@
 
   // Always verify the actual results from the [SizeEstimator].
   // This is the actual test, though DebugSizeEstimator is pretty trivial.
-  int actualEstimate = EstimateSize(node);
+  int actualEstimate = estimateSize(node);
   Expect.equals(actualEstimate, debugSizeEstimator.charCount);
   return debugSizeEstimator;
 }
diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart
index f6fa451..d3ef96e 100644
--- a/pkg/dartdev/lib/src/commands/fix.dart
+++ b/pkg/dartdev/lib/src/commands/fix.dart
@@ -19,22 +19,18 @@
 
   static final NumberFormat _numberFormat = NumberFormat.decimalPattern();
 
-  static const String cmdDescription = '''Fix Dart source code.
+  static const String cmdDescription =
+      '''Apply automated fixes to Dart source code.
 
-This tool looks for and fixes analysis issues that have associated automated fixes or issues that have associated package API migration information. See dart.dev/go/dart-fix for more information about how automated package API changes work.
+This tool looks for and fixes analysis issues that have associated automated fixes.
 
-To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed changes for a project, or ['dart fix --apply'] to apply the changes.''';
+To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed changes for a project, or ['dart fix --apply'] to apply the changes.
 
-  @override
-  String get description {
-    if (log != null && log.ansi.useAnsi) {
-      return cmdDescription
-          .replaceAll('[', log.ansi.bold)
-          .replaceAll(']', log.ansi.none);
-    } else {
-      return cmdDescription.replaceAll('[', '').replaceAll(']', '');
-    }
-  }
+[Note:] $disclaimer''';
+
+  static const disclaimer = 'The `fix` command is under development and '
+      'subject to change before the next stable release. Feedback is welcome - '
+      'please file at https://github.com/dart-lang/sdk/issues.';
 
   // This command is hidden as it's currently experimental.
   FixCommand({bool verbose = false})
@@ -61,24 +57,34 @@
   }
 
   @override
-  FutureOr<int> run() async {
-    log.stdout('\n${log.ansi.emphasized('Note:')} The `fix` command is '
-        'provisional and subject to change or removal in future releases.\n');
+  String get description {
+    if (log != null && log.ansi.useAnsi) {
+      return cmdDescription
+          .replaceAll('[', log.ansi.bold)
+          .replaceAll(']', log.ansi.none);
+    } else {
+      return cmdDescription.replaceAll('[', '').replaceAll(']', '');
+    }
+  }
 
+  @override
+  FutureOr<int> run() async {
     var dryRun = argResults['dry-run'];
     var testMode = argResults['compare-to-golden'];
     var apply = argResults['apply'];
+    if (!apply && !dryRun && !testMode) {
+      printUsage();
+      return 0;
+    }
+
+    log.stdout('\n${log.ansi.emphasized('Note:')} $disclaimer\n');
+
     var arguments = argResults.rest;
     var argumentCount = arguments.length;
     if (argumentCount > 1) {
       usageException('Only one file or directory is expected.');
     }
 
-    if (!apply && !dryRun && !testMode) {
-      printUsage();
-      return 0;
-    }
-
     var dir =
         argumentCount == 0 ? io.Directory.current : io.Directory(arguments[0]);
     if (!dir.existsSync()) {
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 8a92d7b..74ff1e4 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -5,6 +5,7 @@
 import 'dart:io';
 
 import 'package:cli_util/cli_logging.dart';
+import 'package:dartdev/src/commands/fix.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 
@@ -34,6 +35,18 @@
     return p.runSync(['fix', ...args], workingDir: workingDir);
   }
 
+  test('help', () {
+    p = project(mainSrc: 'int get foo => 1;\n');
+
+    var result = runFix([p.dirPath, '--help']);
+
+    expect(result.exitCode, 0);
+    expect(result.stderr, isEmpty);
+    expect(result.stdout, contains(FixCommand.disclaimer));
+    expect(
+        result.stdout, contains('Apply automated fixes to Dart source code.'));
+  });
+
   test('none', () {
     p = project(mainSrc: 'int get foo => 1;\n');
 
@@ -41,7 +54,9 @@
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
-    expect(result.stdout, contains('Fix Dart source code.'));
+    expect(result.stdout, contains(FixCommand.disclaimer));
+    expect(
+        result.stdout, contains('Apply automated fixes to Dart source code.'));
   });
 
   test('--apply (none)', () {
@@ -51,6 +66,7 @@
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
+    expect(result.stdout, contains(FixCommand.disclaimer));
     expect(result.stdout, contains('Nothing to fix!'));
   });
 
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 2071a31..92fa101 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6095,8 +6095,9 @@
 
   if (error) {
     CHECK_NULL(error_message);
-    unit.CompleteLoad(String::Handle(String::New(error_message)),
-                      transient_error);
+    return Api::NewHandle(
+        T, unit.CompleteLoad(String::Handle(String::New(error_message)),
+                             transient_error));
   } else {
 #if defined(SUPPORT_TIMELINE)
     TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
@@ -6120,10 +6121,8 @@
       return Api::NewHandle(T, error.raw());
     }
 
-    unit.CompleteLoad(String::Handle(), false);
+    return Api::NewHandle(T, unit.CompleteLoad(String::Handle(), false));
   }
-
-  return Api::Success();
 }
 
 DART_EXPORT Dart_Handle
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 57ff8c6..e673c0a 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -17481,8 +17481,8 @@
   return Isolate::Current()->CallDeferredLoadHandler(id());
 }
 
-void LoadingUnit::CompleteLoad(const String& error_message,
-                               bool transient_error) const {
+ObjectPtr LoadingUnit::CompleteLoad(const String& error_message,
+                                    bool transient_error) const {
   ASSERT(!loaded());
   ASSERT(load_outstanding());
   set_loaded(error_message.IsNull());
@@ -17496,12 +17496,7 @@
   args.SetAt(0, Smi::Handle(Smi::New(id())));
   args.SetAt(1, error_message);
   args.SetAt(2, Bool::Get(transient_error));
-  const Object& result = Object::Handle(DartEntry::InvokeFunction(func, args));
-  if (result.IsUnwindError()) {
-    Thread::Current()->set_sticky_error(Error::Cast(result));
-  } else if (result.IsError()) {
-    UNREACHABLE();
-  }
+  return DartEntry::InvokeFunction(func, args);
 }
 
 const char* Error::ToErrorCString() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 467e1b1..2a3b73d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -6996,7 +6996,8 @@
   }
 
   ObjectPtr IssueLoad() const;
-  void CompleteLoad(const String& error_message, bool transient_error) const;
+  ObjectPtr CompleteLoad(const String& error_message,
+                         bool transient_error) const;
 
  private:
   FINAL_HEAP_OBJECT_IMPLEMENTATION(LoadingUnit, Object);
diff --git a/sdk/lib/_internal/vm/lib/lib_prefix.dart b/sdk/lib/_internal/vm/lib/lib_prefix.dart
index 4259640..7604f62 100644
--- a/sdk/lib/_internal/vm/lib/lib_prefix.dart
+++ b/sdk/lib/_internal/vm/lib/lib_prefix.dart
@@ -31,7 +31,11 @@
 
 @pragma("vm:entry-point")
 void _completeLoads(Object unit, String? errorMessage, bool transientError) {
-  Completer<void> load = _LibraryPrefix._loads[unit]!;
+  Completer<void>? load = _LibraryPrefix._loads[unit];
+  if (load == null) {
+    // Embedder loaded even though prefix.loadLibrary() wasn't called.
+    _LibraryPrefix._loads[unit] = load = new Completer<void>();
+  }
   if (errorMessage == null) {
     load.complete(null);
   } else {
diff --git a/tests/dart2js/internal/deferred/load_in_correct_order_test.dart b/tests/dart2js/internal/deferred/load_in_correct_order_test.dart
index dc6727d..4f04a2c 100644
--- a/tests/dart2js/internal/deferred/load_in_correct_order_test.dart
+++ b/tests/dart2js/internal/deferred/load_in_correct_order_test.dart
@@ -74,7 +74,25 @@
 // This test has 3 loadLibrary calls, this array contains how many hunks will be
 // loaded by each call.
 self.currentLoadLibraryCall = 0;
-self.filesPerLoadLibraryCall = [4, 2, 1];
+self.filesPerLoadLibraryCall = null;
+
+self.initFilesPerLoadLibraryCall = function() {
+  // We assume we load d1, then d2, then d3.
+  var loadOrder = ['d1', 'd2', 'd3'];
+  var uniques = {};
+  self.filesPerLoadLibraryCall = [];
+  for (var i = 0; i < loadOrder.length; i++) {
+    var filesToLoad = 0;
+    var parts = init.deferredLibraryParts[loadOrder[i]];
+    for (var j = 0; j < parts.length; j++) {
+      if (!uniques.hasOwnProperty(parts[j])) {
+        uniques[parts[j]] = true;
+        filesToLoad++;
+      }
+    }
+    self.filesPerLoadLibraryCall.push(filesToLoad);
+  }
+};
 
 // Download uri via an XHR
 self.download = function(uri) {
@@ -99,6 +117,9 @@
 
 // Hook to control how we load hunks (we force them to be out of order).
 self.dartDeferredLibraryLoader = function(uri, success, error) {
+  if (self.filesPerLoadLibraryCall == null) {
+    self.initFilesPerLoadLibraryCall();
+  }
   self.uris.push(uri);
   self.successCallbacks.push(success);
   if (isD8) {
@@ -111,13 +132,13 @@
 // Do the actual load of the hunk and call the corresponding success callback.
 self.doLoad = function(i) {
   self.setTimeout(function () {
-  var uri = self.uris[i];
-  if (self.isD8) {
-    load(uri);
-  } else {
-    eval(self.content[uri]);
-  }
-  (self.successCallbacks[i])();
+    var uri = self.uris[i];
+    if (self.isD8) {
+      load(uri);
+    } else {
+      eval(self.content[uri]);
+    }
+    (self.successCallbacks[i])();
   }, 0);
 };
 
@@ -125,13 +146,10 @@
 // purposely load the hunks out of order.
 self.doActualLoads = function() {
   self.currentLoadLibraryCall++;
-  if (self.total == 4) {
-    self.doLoad(3); // load purposely out of order!
-    self.doLoad(0);
-    self.doLoad(1);
-    self.doLoad(2);
-  } else {
-    for (var i = 0; i < self.total; i++) {
+  if (self.total >= 1) {
+    // Load out of order, last first.
+    self.doLoad(self.total - 1);
+    for (var i = 0; i < self.total - 1; i++) {
       self.doLoad(i);
     }
   }
diff --git a/tools/VERSION b/tools/VERSION
index 8594b2d..d10270e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 133
+PRERELEASE 134
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 4b3cf6e..79fedf6 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -642,6 +642,18 @@
         "host-checked": true
       }
     },
+    "dart2js-hostasserts-weak-max-fragments-(linux|win)-x64-(d8|chrome)": {
+      "options": {
+        "builder-tag": "dart2js-weak",
+        "dart2js-options": [
+          "--libraries-spec=sdk/lib/libraries.json",
+          "--platform-binaries=out/ReleaseX64/",
+          "--merge-fragments-threshold=3"
+        ],
+        "timeout": 240,
+        "host-checked": true
+      }
+    },
     "dart2js-hostasserts-weak-mac-x64-(d8|chrome)": {
       "options": {
         "builder-tag": "dart2js-weak",
@@ -664,6 +676,19 @@
         "host-checked": true
       }
     },
+    "dart2js-hostasserts-strong-max-fragments-(linux|win)-x64-(d8|chrome)": {
+      "options": {
+        "builder-tag": "dart2js-strong",
+        "dart2js-options": [
+          "--libraries-spec=sdk/lib/libraries.json",
+          "--platform-binaries=out/ReleaseX64/",
+          "--merge-fragments-threshold=3"
+        ],
+        "timeout": 240,
+        "host-checked": true
+      }
+    },
+
     "dart2js-hostasserts-strong-mac-x64-(d8|chrome)": {
       "options": {
         "builder-tag": "dart2js-strong",
@@ -2469,6 +2494,16 @@
           "fileset": "web_platform_hostasserts_nnbd"
         },
         {
+          "name": "dart2js nnbd weak d8 fragment merging tests",
+          "arguments": [
+            "-ndart2js-hostasserts-weak-max-fragments-linux-x64-d8",
+            "--dart2js-batch",
+            "dart2js/deferred/"
+          ],
+          "shards": 1,
+          "fileset": "web_platform_hostasserts_nnbd"
+        },
+        {
           "name": "dart2js nnbd weak chrome tests",
           "arguments": [
             "-ndart2js-hostasserts-weak-linux-x64-chrome",
@@ -2510,6 +2545,16 @@
           "fileset": "web_platform_hostasserts_nnbd"
         },
         {
+          "name": "dart2js nnbd strong d8 fragment merging tests",
+          "arguments": [
+            "-ndart2js-hostasserts-strong-max-fragments-linux-x64-d8",
+            "--dart2js-batch",
+            "dart2js/deferred/"
+          ],
+          "shards": 1,
+          "fileset": "web_platform_hostasserts_nnbd"
+        },
+        {
           "name": "dart2js nnbd strong chrome tests",
           "arguments": [
             "-ndart2js-hostasserts-strong-linux-x64-chrome",