Version 2.12.0-31.0.dev

Merge commit 'cfccd80ef718f58860b9160965b09295bfe5e6fa' into 'dev'
diff --git a/DEPS b/DEPS
index bdb73d4..1784f7a 100644
--- a/DEPS
+++ b/DEPS
@@ -110,7 +110,7 @@
   "http_multi_server_rev" : "ea269f79321d659208402088f3297e8920a88ee6",
   "http_parser_rev": "5dd4d16693242049dfb43b5efa429fedbf932e98",
   "http_retry_tag": "0.1.1",
-  "http_rev": "a8efbef05a58919dc7aa2dab42198334f2459ffb",
+  "http_rev": "1617b728fc48f64fb0ed7dc16078c03adcc64179",
   "http_throttle_tag" : "1.0.2",
   "icu_rev" : "79326efe26e5440f530963704c3c0ff965b3a4ac",
   "idl_parser_rev": "5fb1ebf49d235b5a70c9f49047e83b0654031eb7",
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index c8acbe8..60ac532 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -31,6 +31,7 @@
 import 'package:analysis_server/src/protocol_server.dart' as protocol;
 import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
 import 'package:analysis_server/src/server/diagnostic_server.dart';
+import 'package:analysis_server/src/server/error_notifier.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart'
     show CompletionPerformance;
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
@@ -43,6 +44,7 @@
 import 'package:analyzer/src/context/context_root.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
 import 'package:analyzer/src/dart/analysis/status.dart' as nd;
+import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
@@ -355,14 +357,16 @@
   /// Logs an exception by sending it to the client (window/logMessage) and
   /// recording it in a buffer on the server for diagnostics.
   void logException(String message, exception, stackTrace) {
+    var fullMessage = message;
     if (exception is CaughtException) {
       stackTrace ??= exception.stackTrace;
-      message = '$message: ${exception.exception}';
+      fullMessage = '$fullMessage: ${exception.exception}';
     } else if (exception != null) {
-      message = '$message: $exception';
+      fullMessage = '$fullMessage: $exception';
     }
 
-    final fullError = stackTrace == null ? message : '$message\n$stackTrace';
+    final fullError =
+        stackTrace == null ? fullMessage : '$fullMessage\n$stackTrace';
 
     // Log the full message since showMessage above may be truncated or
     // formatted badly (eg. VS Code takes the newlines out).
@@ -375,6 +379,16 @@
       stackTrace is StackTrace ? stackTrace : null,
       false,
     ));
+
+    AnalysisEngine.instance.instrumentationService.logException(
+      FatalException(
+        message,
+        exception,
+        stackTrace,
+      ),
+      null,
+      crashReportingAttachmentsBuilder.forException(exception),
+    );
   }
 
   void onOverlayCreated(String path, String content) {
diff --git a/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart b/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart
index 477e325..0cf5684 100644
--- a/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart
@@ -28,7 +28,7 @@
     Directory(path.join(sdkPath, 'lib', 'async')).createSync(recursive: true);
     Directory(path.join(sdkPath, 'lib', 'fake')).createSync(recursive: true);
 
-    File(path.join(sdkPath, 'version')).writeAsStringSync('2.10.0');
+    File(path.join(sdkPath, 'version')).writeAsStringSync('2.12.0');
 
     File(path.join(sdkPath, 'lib', 'core', 'core.dart')).writeAsStringSync(r'''
 library dart.core;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
index b29d1e8..16ec311 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -517,7 +518,9 @@
   A a = new A(callback: (int a) {  });
   print(a);
 }
-''');
+''',
+        errorFilter: (error) =>
+            error.errorCode == HintCode.MISSING_REQUIRED_PARAM);
   }
 
   Future<void> test_constructor_single_closure_nnbd_into_legacy() async {
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 990feb4..38b3ce4 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -489,6 +489,7 @@
   HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE,
   HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
   HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION,
+  HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE,
   HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL,
   HintCode.INFERENCE_FAILURE_ON_FUNCTION_RETURN_TYPE,
   HintCode.INFERENCE_FAILURE_ON_INSTANCE_CREATION,
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 60447c0..2a7a07a 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -572,6 +572,16 @@
           "rename the function in the imported library.");
 
   /**
+   * https://github.com/dart-lang/sdk/issues/44063
+   */
+  static const HintCode IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE = HintCode(
+    'IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE',
+    "The library '{0}' is legacy, and should not be imported into "
+        "a null safe library.",
+    correction: "Try migrating the imported library.",
+  );
+
+  /**
    * When "strict-inference" is enabled, collection literal types must be
    * inferred via the context type, or have type arguments.
    */
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 5ee814d..acc8ac7 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -504,6 +504,7 @@
       _checkForLoadLibraryFunction(node, importElement);
     }
     _invalidAccessVerifier.verifyImport(node);
+    _checkForImportOfLegacyLibraryIntoNullSafe(node);
     super.visitImportDirective(node);
   }
 
@@ -922,6 +923,24 @@
     }
   }
 
+  void _checkForImportOfLegacyLibraryIntoNullSafe(ImportDirective node) {
+    if (!_isNonNullableByDefault) {
+      return;
+    }
+
+    var importElement = node.element as ImportElement;
+    var importedLibrary = importElement.importedLibrary;
+    if (importedLibrary == null || importedLibrary.isNonNullableByDefault) {
+      return;
+    }
+
+    _errorReporter.reportErrorForNode(
+      HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE,
+      node.uri,
+      [importedLibrary.source.uri],
+    );
+  }
+
   /// Check that the namespace exported by [node] does not include any elements
   /// annotated with `@internal`.
   void _checkForInternalExport(ExportDirective node) {
diff --git a/pkg/analyzer/test/src/dart/analysis/base.dart b/pkg/analyzer/test/src/dart/analysis/base.dart
index dc79e24..f9e47a8 100644
--- a/pkg/analyzer/test/src/dart/analysis/base.dart
+++ b/pkg/analyzer/test/src/dart/analysis/base.dart
@@ -63,6 +63,7 @@
   final List<ExceptionResult> allExceptions = <ExceptionResult>[];
 
   String testProject;
+  String testProject2;
   String testFile;
   String testCode;
 
@@ -81,7 +82,7 @@
       {Map<String, List<Folder>> packageMap,
       SummaryDataStore externalSummaries}) {
     packageMap ??= <String, List<Folder>>{
-      'test': [getFolder(testProject)],
+      'test': [getFolder('$testProject/lib')],
       'aaa': [getFolder('/aaa/lib')],
       'bbb': [getFolder('/bbb/lib')],
     };
@@ -161,7 +162,8 @@
 
   void setUp() {
     sdk = MockSdk(resourceProvider: resourceProvider);
-    testProject = convertPath('/test/lib');
+    testProject = convertPath('/test');
+    testProject2 = convertPath('/test/lib');
     testFile = convertPath('/test/lib/test.dart');
     logger = PerformanceLog(logBuffer);
     scheduler = AnalysisDriverScheduler(logger);
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
index 70061ed..b73f5bf 100644
--- a/pkg/analyzer/test/src/dart/analysis/index_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -136,7 +136,7 @@
   }
 
   test_isExtendedBy_ClassDeclaration_isQualified() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 ''');
     await _indexTestUnit('''
@@ -169,7 +169,7 @@
   }
 
   test_isExtendedBy_ClassTypeAlias_isQualified() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 ''');
     await _indexTestUnit('''
@@ -195,7 +195,7 @@
   }
 
   test_isImplementedBy_ClassDeclaration_isQualified() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 ''');
     await _indexTestUnit('''
@@ -243,7 +243,7 @@
   }
 
   test_isInvokedBy_FunctionElement() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 library lib;
 foo() {}
 ''');
@@ -419,7 +419,7 @@
   }
 
   test_isMixedInBy_ClassDeclaration_isQualified() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 ''');
     await _indexTestUnit('''
@@ -547,7 +547,7 @@
   }
 
   test_isReferencedBy_ClassElement_invocation_isQualified() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 ''');
     await _indexTestUnit('''
@@ -586,7 +586,7 @@
   }
 
   test_isReferencedBy_CompilationUnitElement_export() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 library lib;
 ''');
     await _indexTestUnit('''
@@ -597,7 +597,7 @@
   }
 
   test_isReferencedBy_CompilationUnitElement_import() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 library lib;
 ''');
     await _indexTestUnit('''
@@ -608,7 +608,7 @@
   }
 
   test_isReferencedBy_CompilationUnitElement_part() async {
-    newFile('$testProject/my_unit.dart', content: 'part of my_lib;');
+    newFile('$testProject2/my_unit.dart', content: 'part of my_lib;');
     await _indexTestUnit('''
 library my_lib;
 part 'my_unit.dart';
@@ -618,8 +618,8 @@
   }
 
   test_isReferencedBy_CompilationUnitElement_part_inPart() async {
-    newFile('$testProject/a.dart', content: 'part of lib;');
-    newFile('$testProject/b.dart', content: '''
+    newFile('$testProject2/a.dart', content: 'part of lib;');
+    newFile('$testProject2/b.dart', content: '''
 library lib;
 part 'a.dart';
 ''');
@@ -899,7 +899,7 @@
   }
 
   test_isReferencedBy_FunctionElement_with_LibraryElement() async {
-    newFile('$testProject/foo.dart', content: r'''
+    newFile('$testProject2/foo.dart', content: r'''
 bar() {}
 ''');
     await _indexTestUnit('''
@@ -957,8 +957,8 @@
   }
 
   test_isReferencedBy_MultiplyDefinedElement() async {
-    newFile('$testProject/a1.dart', content: 'class A {}');
-    newFile('$testProject/a2.dart', content: 'class A {}');
+    newFile('$testProject2/a1.dart', content: 'class A {}');
+    newFile('$testProject2/a2.dart', content: 'class A {}');
     await _indexTestUnit('''
 import 'a1.dart';
 import 'a2.dart';
@@ -1153,7 +1153,7 @@
   }
 
   test_isReferencedBy_TopLevelVariableElement() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 library lib;
 var V;
 ''');
@@ -1177,7 +1177,7 @@
   }
 
   test_isReferencedBy_TopLevelVariableElement_synthetic_hasGetterSetter() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 int get V => 0;
 void set V(_) {}
 ''');
@@ -1189,7 +1189,7 @@
   }
 
   test_isReferencedBy_TopLevelVariableElement_synthetic_hasSetter() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 void set V(_) {}
 ''');
     await _indexTestUnit('''
@@ -1224,7 +1224,7 @@
 
   test_subtypes_classDeclaration() async {
     String libP = 'package:test/lib.dart;package:test/lib.dart';
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 class B {}
 class C {}
@@ -1274,7 +1274,7 @@
 
   test_subtypes_classTypeAlias() async {
     String libP = 'package:test/lib.dart;package:test/lib.dart';
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 class B {}
 class C {}
@@ -1312,7 +1312,7 @@
 
   test_subtypes_mixinDeclaration() async {
     String libP = 'package:test/lib.dart;package:test/lib.dart';
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 class A {}
 class B {}
 class C {}
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index e5fefd3..3858d02 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -261,7 +261,7 @@
   }
 
   test_searchReferences_ClassElement_definedOutside() async {
-    newFile('$testProject/lib.dart', content: r'''
+    newFile('$testProject2/lib.dart', content: r'''
 class A {};
 ''');
     await _resolveTestUnit('''
@@ -317,7 +317,7 @@
   }
 
   test_searchReferences_CompilationUnitElement() async {
-    newFile('$testProject/foo.dart');
+    newFile('$testProject2/foo.dart');
     await _resolveTestUnit('''
 import 'foo.dart'; // import
 export 'foo.dart'; // export
@@ -354,7 +354,7 @@
   }
 
   test_searchReferences_ConstructorElement_default_otherFile() async {
-    String other = convertPath('$testProject/other.dart');
+    String other = convertPath('$testProject2/other.dart');
     String otherCode = '''
 import 'test.dart';
 main() {
@@ -686,8 +686,8 @@
   test_searchReferences_LibraryElement() async {
     var codeA = 'part of lib; // A';
     var codeB = 'part of lib; // B';
-    newFile('$testProject/unitA.dart', content: codeA);
-    newFile('$testProject/unitB.dart', content: codeB);
+    newFile('$testProject2/unitA.dart', content: codeA);
+    newFile('$testProject2/unitB.dart', content: codeB);
     await _resolveTestUnit('''
 library lib;
 part 'unitA.dart';
@@ -1103,7 +1103,7 @@
 part of my_lib;
 ppp.Future c;
 ''';
-    newFile('$testProject/my_part.dart', content: partCode);
+    newFile('$testProject2/my_part.dart', content: partCode);
     await _resolveTestUnit('''
 library my_lib;
 import 'dart:async' as ppp;
@@ -1156,9 +1156,9 @@
   }
 
   test_searchReferences_private_declaredInDefiningUnit() async {
-    String p1 = convertPath('$testProject/part1.dart');
-    String p2 = convertPath('$testProject/part2.dart');
-    String p3 = convertPath('$testProject/part3.dart');
+    String p1 = convertPath('$testProject2/part1.dart');
+    String p2 = convertPath('$testProject2/part2.dart');
+    String p3 = convertPath('$testProject2/part3.dart');
     String code1 = 'part of lib; _C v1;';
     String code2 = 'part of lib; _C v2;';
     newFile(p1, content: code1);
@@ -1192,9 +1192,9 @@
   }
 
   test_searchReferences_private_declaredInPart() async {
-    String p = convertPath('$testProject/lib.dart');
-    String p1 = convertPath('$testProject/part1.dart');
-    String p2 = convertPath('$testProject/part2.dart');
+    String p = convertPath('$testProject2/lib.dart');
+    String p1 = convertPath('$testProject2/part1.dart');
+    String p2 = convertPath('$testProject2/part2.dart');
 
     var code = '''
 library lib;
@@ -1369,7 +1369,7 @@
   }
 
   test_searchReferences_TopLevelVariableElement() async {
-    newFile('$testProject/lib.dart', content: '''
+    newFile('$testProject2/lib.dart', content: '''
 library lib;
 var V;
 ''');
@@ -1654,8 +1654,8 @@
   }
 
   test_subtypes_files() async {
-    String pathB = convertPath('$testProject/b.dart');
-    String pathC = convertPath('$testProject/c.dart');
+    String pathB = convertPath('$testProject2/b.dart');
+    String pathC = convertPath('$testProject2/c.dart');
     newFile(pathB, content: r'''
 import 'test.dart';
 class B extends A {}
diff --git a/pkg/analyzer/test/src/dart/resolution/constant_test.dart b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
index 34b271d..4090683 100644
--- a/pkg/analyzer/test/src/dart/resolution/constant_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
@@ -347,13 +347,15 @@
 const cString = const String.fromEnvironment('foo', defaultValue: 'bar');
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 const vBool = cBool;
 const vInt = cInt;
 const vString = cString;
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     DartObjectImpl evaluate(String name) {
       return findElement.topVar(name).computeConstantValue();
diff --git a/pkg/analyzer/test/src/dart/resolution/field_test.dart b/pkg/analyzer/test/src/dart/resolution/field_test.dart
index 90d814b..c8120a0 100644
--- a/pkg/analyzer/test/src/dart/resolution/field_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/field_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/src/dart/error/hint_codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'context_collection_resolution.dart';
@@ -67,13 +68,15 @@
 var a = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 class A {
   var f = a;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertType(findElement.field('f').type, 'int');
   }
diff --git a/pkg/analyzer/test/src/dart/resolution/language_version_test.dart b/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
index d200a01..c94064b 100644
--- a/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
@@ -81,14 +81,16 @@
 int a = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'dart:math';
 import 'package:aaa/a.dart';
 
 var x = 0;
 var y = a;
 var z = pi;
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 27, 20),
+    ]);
     assertType(findElement.topVar('x').type, 'int');
     assertType(findElement.topVar('y').type, 'int');
     assertType(findElement.topVar('z').type, 'double');
diff --git a/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart b/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart
index 7e27085..213fbbb 100644
--- a/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart
@@ -122,14 +122,16 @@
 var a = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 void f() {
   var x = a;
   x;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     var x = findElement.localVar('x');
     assertType(x.type, 'int');
diff --git a/pkg/analyzer/test/src/dart/resolution/top_level_variable_test.dart b/pkg/analyzer/test/src/dart/resolution/top_level_variable_test.dart
index 896aa45..fd045d0 100644
--- a/pkg/analyzer/test/src/dart/resolution/top_level_variable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/top_level_variable_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/src/dart/error/hint_codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'context_collection_resolution.dart';
@@ -59,11 +60,13 @@
 var a = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 var v = a;
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertType(findElement.topVar('v').type, 'int');
   }
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/function_expression_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/function_expression_test.dart
index 4ac90c4..d665880 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/function_expression_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/function_expression_test.dart
@@ -468,7 +468,7 @@
 int Function(int a) v;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 T foo<T>() => throw 0;
@@ -478,7 +478,9 @@
     return foo();
   };
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
     assertType(findElement.parameter('a').type, 'int');
     _assertReturnType('(a) {', 'int');
   }
@@ -489,13 +491,15 @@
 
 void foo(int Function() x) {}
 ''');
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 void test(int? a) {
   foo(() => a);
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
     _assertReturnType('() => a', 'int?');
   }
 
@@ -505,13 +509,15 @@
 
 void foo(int Function() x) {}
 ''');
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 void test(dynamic a) {
   foo(() => a);
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
     _assertReturnType('() => a', 'int');
   }
 
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 502c43d..562aebb 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -472,11 +472,13 @@
 class A {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(A a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('A a'),
@@ -491,11 +493,13 @@
 class A<T extends num> {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(A a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('A a'),
@@ -510,11 +514,13 @@
 class A<T> {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(A a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('A a'),
@@ -529,11 +535,13 @@
 class A<T> {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(A<int> a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('A<int> a'),
@@ -548,11 +556,13 @@
 typedef F = int Function();
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(F a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('F a'),
@@ -567,11 +577,13 @@
 typedef F<T extends num> = T Function();
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(F a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('F a'),
@@ -586,11 +598,13 @@
 typedef F<T> = T Function();
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(F a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('F a'),
@@ -605,11 +619,13 @@
 typedef F<T> = T Function();
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 f(F<int> a) {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
 
     assertTypeName(
       findNode.typeName('F<int> a'),
diff --git a/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart b/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart
index 9342301..aa368bb 100644
--- a/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart
@@ -138,11 +138,13 @@
 class C extends Bi implements Biq {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'b.dart';
 
 abstract class D implements C {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_class_topMerge() async {
@@ -169,11 +171,13 @@
 class B extends A<int> {}
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 import 'b.dart';
 
 class C extends B implements A<int> {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 24, 8),
+    ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/dead_null_aware_expression_test.dart b/pkg/analyzer/test/src/diagnostics/dead_null_aware_expression_test.dart
index c1e4746..9699bae 100644
--- a/pkg/analyzer/test/src/diagnostics/dead_null_aware_expression_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/dead_null_aware_expression_test.dart
@@ -22,13 +22,15 @@
 var x = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   x ??= 0;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_assignCompound_map() async {
@@ -68,13 +70,15 @@
 var x = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   x ?? 0;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_binary_nonNullable() async {
diff --git a/pkg/analyzer/test/src/diagnostics/import_of_legacy_library_into_null_safe_test.dart b/pkg/analyzer/test/src/diagnostics/import_of_legacy_library_into_null_safe_test.dart
new file mode 100644
index 0000000..2119b9f
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/import_of_legacy_library_into_null_safe_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ImportOfLegacyLibraryInoNullSafeTest);
+  });
+}
+
+@reflectiveTest
+class ImportOfLegacyLibraryInoNullSafeTest extends PubPackageResolutionTest
+    with WithNullSafetyMixin {
+  test_legacy_into_legacy() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+// @dart = 2.9
+class A {}
+''');
+    await assertNoErrorsInCode(r'''
+// @dart = 2.9
+import 'a.dart';
+
+void f(A a) {}
+''');
+  }
+
+  test_legacy_into_nullSafe() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+// @dart = 2.9
+class A {}
+''');
+    await assertErrorsInCode(r'''
+import 'a.dart';
+
+void f(A a) {}
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
+  }
+
+  test_nullSafe_into_legacy() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+class A {}
+''');
+    await assertNoErrorsInCode(r'''
+// @dart = 2.9
+import 'a.dart';
+
+void f(A a) {}
+''');
+  }
+
+  test_nullSafe_into_nullSafe() async {
+    newFile('$testPackageLibPath/a.dart', content: r'''
+class A {}
+''');
+    await assertNoErrorsInCode(r'''
+import 'a.dart';
+
+void f(A a) {}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
index bce4df1..8f903e5 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
@@ -201,14 +201,16 @@
 var x = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   x?.isEven;
   x?..isEven;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_getter_mixin() async {
@@ -268,14 +270,16 @@
 var x = [0];
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   x?[0];
   x?..[0];
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_index_nonNullable() async {
@@ -329,14 +333,16 @@
 var x = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   x?.round();
   x?..round();
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_method_mixin() async {
@@ -386,13 +392,15 @@
 var x = <int>[];
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   [...?x];
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_nullableSpread_nonNullableType() async {
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
index e3a0206..110480c 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
@@ -308,13 +308,15 @@
 }
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 class B extends A {
   void foo({int a = 0}) {}
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_concrete_equal_optOut_extends_optIn() async {
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
index 67d9fa8..50128c9 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
@@ -323,13 +323,15 @@
 }
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 class B extends A {
   void foo([int a = 0]) {}
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_concrete_equal_optOut_extends_optIn() async {
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_test.dart
index 17c1ae2..5d52603 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_test.dart
@@ -798,12 +798,14 @@
 class C with B {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 import 'b.dart';
 
 class D extends C implements Bq {}
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 24, 8),
+    ]);
   }
 
   test_mixedInheritance_2() async {
@@ -828,7 +830,7 @@
 class C extends B with Bq {}
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'b.dart';
 
 class D extends C {
@@ -836,7 +838,9 @@
   set a(List<int Function(int)> _) {}
   int Function(int) m(int Function(int) x) => x;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_setter_overrides_abstract_field_covariant_valid() async {
@@ -959,7 +963,7 @@
 class L extends A2 implements A1 {}
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 import 'b.dart';
 
@@ -971,7 +975,9 @@
   int? m() => 0;
   set s(int _) {}
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 24, 8),
+    ]);
   }
 
   test_viaLegacy_mixin() async {
@@ -996,7 +1002,7 @@
 class L extends Object with A2 implements A1 {}
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 import 'b.dart';
 
@@ -1008,6 +1014,8 @@
   int? m() => 0;
   set s(int _) {}
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 24, 8),
+    ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 2498c30..3d1585b 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -230,6 +230,8 @@
 import 'import_deferred_library_with_load_function_test.dart'
     as import_deferred_library_with_load_function;
 import 'import_internal_library_test.dart' as import_internal_library;
+import 'import_of_legacy_library_into_null_safe_test.dart'
+    as mport_of_legacy_library_into_null_safe;
 import 'import_of_non_library_test.dart' as import_of_non_library;
 import 'inconsistent_case_expression_types_test.dart'
     as inconsistent_case_expression_types;
@@ -808,6 +810,7 @@
     implicit_this_reference_in_initializer.main();
     import_deferred_library_with_load_function.main();
     import_internal_library.main();
+    mport_of_legacy_library_into_null_safe.main();
     import_of_non_library.main();
     inconsistent_case_expression_types.main();
     inconsistent_inheritance_getter_and_method.main();
diff --git a/pkg/analyzer/test/src/diagnostics/throw_of_invalid_type_test.dart b/pkg/analyzer/test/src/diagnostics/throw_of_invalid_type_test.dart
index d1ec82c..fa947bc 100644
--- a/pkg/analyzer/test/src/diagnostics/throw_of_invalid_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/throw_of_invalid_type_test.dart
@@ -29,13 +29,15 @@
 // @dart = 2.7
 int a = 0;
 ''');
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   throw a;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_nonNullable() async {
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart
index d927ad5..4b9d36e 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_cast_test.dart
@@ -231,6 +231,7 @@
   b;
 }
 ''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
       error(HintCode.UNNECESSARY_CAST, 39, 8),
     ]);
   }
@@ -241,13 +242,15 @@
 int a = 0;
 ''');
 
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode(r'''
 import 'a.dart';
 
 void f() {
   var b = a as int?;
   b;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart
index 3104f05..4c521d2 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart
@@ -22,13 +22,15 @@
 var x = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   x!;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_nonNull_function() async {
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_null_comparison_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_null_comparison_test.dart
index e48a345..ffd565a 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_null_comparison_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_null_comparison_test.dart
@@ -34,14 +34,16 @@
 var a = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   a == null;
   null == a;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_equal_legacyLibrary() async {
@@ -96,14 +98,16 @@
 var a = 0;
 ''');
 
-    await assertNoErrorsInCode('''
+    await assertErrorsInCode('''
 import 'a.dart';
 
 f() {
   a != null;
   null != a;
 }
-''');
+''', [
+      error(HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, 7, 8),
+    ]);
   }
 
   test_notEqual_legacyLibrary() async {
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index b042613..1c7d0ed 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -104,6 +104,8 @@
   static const String dillDependencies = '--dill-dependencies';
   static const String readData = '--read-data';
   static const String writeData = '--write-data';
+  static const String writeClosedWorld = '--write-closed-world';
+  static const String readClosedWorld = '--read-closed-world';
   static const String readCodegen = '--read-codegen';
   static const String writeCodegen = '--write-codegen';
   static const String codegenShard = '--codegen-shard';
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 62d62d5..a1dd1f5 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -8,6 +8,7 @@
 
 import 'package:front_end/src/api_unstable/dart2js.dart'
     show clearStringTokenCanonicalizer;
+import 'package:kernel/ast.dart' as ir;
 
 import '../compiler_new.dart' as api;
 import 'backend_strategy.dart';
@@ -37,6 +38,7 @@
 import 'js_backend/backend.dart' show CodegenInputs, JavaScriptImpactStrategy;
 import 'js_backend/inferred_data.dart';
 import 'js_model/js_strategy.dart';
+import 'js_model/js_world.dart';
 import 'kernel/kernel_strategy.dart';
 import 'kernel/loader.dart' show KernelLoaderTask, KernelResult;
 import 'null_compiler_output.dart' show NullCompilerOutput;
@@ -231,10 +233,23 @@
   Future runInternal(Uri uri) async {
     clearState();
     assert(uri != null);
-    // As far as I can tell, this branch is only used by test code.
     reporter.log('Compiling $uri (${options.buildId})');
 
-    if (options.readDataUri != null) {
+    if (options.readClosedWorldUri != null) {
+      ir.Component component =
+          await serializationTask.deserializeComponentAndUpdateOptions();
+      JsClosedWorld closedWorld =
+          await serializationTask.deserializeClosedWorld(
+              environment, abstractValueStrategy, component);
+      GlobalTypeInferenceResults globalTypeInferenceResults =
+          performGlobalTypeInference(closedWorld);
+      if (options.writeDataUri != null) {
+        serializationTask
+            .serializeGlobalTypeInference(globalTypeInferenceResults);
+        return;
+      }
+      await generateJavaScriptCode(globalTypeInferenceResults);
+    } else if (options.readDataUri != null) {
       GlobalTypeInferenceResults globalTypeInferenceResults =
           await serializationTask.deserializeGlobalTypeInference(
               environment, abstractValueStrategy);
@@ -362,9 +377,6 @@
 
     JClosedWorld closedWorld =
         closeResolution(mainFunction, resolutionEnqueuer.worldBuilder);
-    if (retainDataForTesting) {
-      backendClosedWorldForTesting = closedWorld;
-    }
     return closedWorld;
   }
 
@@ -409,37 +421,50 @@
     checkQueue(codegenEnqueuer);
   }
 
+  GlobalTypeInferenceResults globalTypeInferenceResultsTestMode(
+      GlobalTypeInferenceResults results) {
+    SerializationStrategy strategy = const BytesInMemorySerializationStrategy();
+    List<int> irData = strategy.unpackAndSerializeComponent(results);
+    List worldData = strategy.serializeGlobalTypeInferenceResults(results);
+    return strategy.deserializeGlobalTypeInferenceResults(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        strategy.deserializeComponent(irData),
+        worldData);
+  }
+
   void compileFromKernel(Uri rootLibraryUri, Iterable<Uri> libraries) {
     _userCodeLocations.add(new CodeLocation(rootLibraryUri));
     selfTask.measureSubtask("compileFromKernel", () {
-      JClosedWorld closedWorld = selfTask.measureSubtask("computeClosedWorld",
+      JsClosedWorld closedWorld = selfTask.measureSubtask("computeClosedWorld",
           () => computeClosedWorld(rootLibraryUri, libraries));
-      if (stopAfterClosedWorld) return;
-      if (closedWorld != null) {
-        GlobalTypeInferenceResults globalInferenceResults =
-            performGlobalTypeInference(closedWorld);
-        if (options.writeDataUri != null) {
-          serializationTask
-              .serializeGlobalTypeInference(globalInferenceResults);
-          return;
-        }
-        if (options.testMode) {
-          SerializationStrategy strategy =
-              const BytesInMemorySerializationStrategy();
-          List<int> irData =
-              strategy.serializeComponent(globalInferenceResults);
-          List worldData = strategy.serializeData(globalInferenceResults);
-          globalInferenceResults = strategy.deserializeData(
-              options,
-              reporter,
-              environment,
-              abstractValueStrategy,
-              strategy.deserializeComponent(irData),
-              worldData);
-        }
-        if (stopAfterTypeInference) return;
-        generateJavaScriptCode(globalInferenceResults);
+      if (closedWorld == null) return;
+
+      if (retainDataForTesting) {
+        backendClosedWorldForTesting = closedWorld;
       }
+
+      if (options.writeClosedWorldUri != null) {
+        serializationTask.serializeComponent(
+            closedWorld.elementMap.programEnv.mainComponent);
+        serializationTask.serializeClosedWorld(closedWorld);
+        return;
+      }
+      if (stopAfterClosedWorld) return;
+      GlobalTypeInferenceResults globalInferenceResults =
+          performGlobalTypeInference(closedWorld);
+      if (options.writeDataUri != null) {
+        serializationTask.serializeGlobalTypeInference(globalInferenceResults);
+        return;
+      }
+      if (options.testMode) {
+        globalInferenceResults =
+            globalTypeInferenceResultsTestMode(globalInferenceResults);
+      }
+      if (stopAfterTypeInference) return;
+      generateJavaScriptCode(globalInferenceResults);
     });
   }
 
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 7142b49..c1bb9b3 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -107,6 +107,8 @@
   Uri sourceMapOut;
   Uri readDataUri;
   Uri writeDataUri;
+  Uri readClosedWorldUri;
+  Uri writeClosedWorldUri;
   Uri readCodegenUri;
   Uri writeCodegenUri;
   int codegenShard;
@@ -267,6 +269,14 @@
     }
   }
 
+  void setReadClosedWorld(String argument) {
+    if (argument != Flags.readClosedWorld) {
+      readClosedWorldUri =
+          fe.nativeToUri(extractPath(argument, isDirectory: false));
+    }
+    readStrategy = ReadStrategy.fromClosedWorld;
+  }
+
   void setDillDependencies(String argument) {
     String dependencies = extractParameter(argument);
     String uriDependencies = dependencies.splitMapJoin(',',
@@ -299,6 +309,9 @@
       fail("Cannot use ${Flags.cfeOnly} "
           "and write serialized data simultaneously.");
     }
+    if (writeStrategy == WriteStrategy.toClosedWorld) {
+      fail("Cannot write closed world and data simultaneously.");
+    }
     if (writeStrategy == WriteStrategy.toCodegen) {
       fail("Cannot write serialized data and codegen simultaneously.");
     }
@@ -308,11 +321,32 @@
     writeStrategy = WriteStrategy.toData;
   }
 
+  void setWriteClosedWorld(String argument) {
+    if (writeStrategy == WriteStrategy.toKernel) {
+      fail("Cannot use ${Flags.cfeOnly} "
+          "and write serialized data simultaneously.");
+    }
+    if (writeStrategy == WriteStrategy.toData) {
+      fail("Cannot write both closed world and data");
+    }
+    if (writeStrategy == WriteStrategy.toCodegen) {
+      fail("Cannot write serialized data and codegen simultaneously.");
+    }
+    if (argument != Flags.writeClosedWorld) {
+      writeClosedWorldUri =
+          fe.nativeToUri(extractPath(argument, isDirectory: false));
+    }
+    writeStrategy = WriteStrategy.toClosedWorld;
+  }
+
   void setWriteCodegen(String argument) {
     if (writeStrategy == WriteStrategy.toKernel) {
       fail("Cannot use ${Flags.cfeOnly} "
           "and write serialized codegen simultaneously.");
     }
+    if (writeStrategy == WriteStrategy.toClosedWorld) {
+      fail("Cannot write closed world and codegen simultaneously.");
+    }
     if (writeStrategy == WriteStrategy.toData) {
       fail("Cannot write serialized data and codegen data simultaneously.");
     }
@@ -415,6 +449,10 @@
     new OptionHandler('${Flags.dillDependencies}=.+', setDillDependencies),
     new OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
     new OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
+    new OptionHandler('${Flags.readClosedWorld}|${Flags.readClosedWorld}=.+',
+        setReadClosedWorld),
+    new OptionHandler('${Flags.writeClosedWorld}|${Flags.writeClosedWorld}=.+',
+        setWriteClosedWorld),
     new OptionHandler(
         '${Flags.readCodegen}|${Flags.readCodegen}=.+', setReadCodegen),
     new OptionHandler(
@@ -614,6 +652,19 @@
             "and read serialized codegen simultaneously.");
       }
       break;
+    case WriteStrategy.toClosedWorld:
+      out ??= Uri.base.resolve('out.dill');
+      writeClosedWorldUri ??= Uri.base.resolve('$out.world');
+      options.add('${Flags.writeClosedWorld}=${writeClosedWorldUri}');
+      if (readStrategy == ReadStrategy.fromClosedWorld) {
+        fail("Cannot read and write serialized data simultaneously.");
+      } else if (readStrategy == ReadStrategy.fromData) {
+        fail("Cannot read from both closed world and data");
+      } else if (readStrategy == ReadStrategy.fromCodegen) {
+        fail("Cannot read serialized codegen and "
+            "write serialized data simultaneously.");
+      }
+      break;
     case WriteStrategy.toData:
       out ??= Uri.base.resolve('out.dill');
       writeDataUri ??= Uri.base.resolve('$out.data');
@@ -657,6 +708,10 @@
   switch (readStrategy) {
     case ReadStrategy.fromDart:
       break;
+    case ReadStrategy.fromClosedWorld:
+      readClosedWorldUri ??= Uri.base.resolve('$scriptName.world');
+      options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
+      break;
     case ReadStrategy.fromData:
       readDataUri ??= Uri.base.resolve('$scriptName.data');
       options.add('${Flags.readData}=${readDataUri}');
@@ -673,6 +728,7 @@
         fail("${Flags.codegenShards} must be a positive integer.");
       }
       options.add('${Flags.codegenShards}=$codegenShards');
+      break;
   }
   options.add('--out=$out');
   if (writeStrategy == WriteStrategy.toJs) {
@@ -707,6 +763,13 @@
         inputSize = inputProvider.dartCharactersRead;
         summary = 'Dart file $input ';
         break;
+      case ReadStrategy.fromClosedWorld:
+        inputName = 'bytes data';
+        inputSize = inputProvider.dartCharactersRead;
+        String dataInput =
+            fe.relativizeUri(Uri.base, readClosedWorldUri, Platform.isWindows);
+        summary = 'Data files $input and $dataInput ';
+        break;
       case ReadStrategy.fromData:
         inputName = 'bytes data';
         inputSize = inputProvider.dartCharactersRead;
@@ -742,6 +805,15 @@
         String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
         summary += 'compiled to dill: ${output}.';
         break;
+      case WriteStrategy.toClosedWorld:
+        processName = 'Serialized';
+        outputName = 'bytes data';
+        outputSize = outputProvider.totalDataWritten;
+        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
+        String dataOutput =
+            fe.relativizeUri(Uri.base, writeClosedWorldUri, Platform.isWindows);
+        summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
+        break;
       case WriteStrategy.toData:
         processName = 'Serialized';
         outputName = 'bytes data';
@@ -1235,5 +1307,5 @@
   });
 }
 
-enum ReadStrategy { fromDart, fromData, fromCodegen }
-enum WriteStrategy { toKernel, toData, toCodegen, toJs }
+enum ReadStrategy { fromDart, fromClosedWorld, fromData, fromCodegen }
+enum WriteStrategy { toKernel, toClosedWorld, toData, toCodegen, toJs }
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 9157cbf..378c5b4 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -77,6 +77,17 @@
   /// If this is set, the compilation stops after type inference.
   Uri writeDataUri;
 
+  /// Location from which the serialized closed world is read.
+  ///
+  /// If this is set, the [entryPoint] is expected to be a .dill file and the
+  /// frontend work is skipped.
+  Uri readClosedWorldUri;
+
+  /// Location to which inference data is serialized.
+  ///
+  /// If this is set, the compilation stops after computing the closed world.
+  Uri writeClosedWorldUri;
+
   /// Location from which codegen data is read.
   ///
   /// If this is set, the compilation starts at codegen enqueueing.
@@ -497,6 +508,10 @@
           _extractUriListOption(options, '${Flags.dillDependencies}')
       ..readDataUri = _extractUriOption(options, '${Flags.readData}=')
       ..writeDataUri = _extractUriOption(options, '${Flags.writeData}=')
+      ..readClosedWorldUri =
+          _extractUriOption(options, '${Flags.readClosedWorld}=')
+      ..writeClosedWorldUri =
+          _extractUriOption(options, '${Flags.writeClosedWorld}=')
       ..readCodegenUri = _extractUriOption(options, '${Flags.readCodegen}=')
       ..writeCodegenUri = _extractUriOption(options, '${Flags.writeCodegen}=')
       ..codegenShard = _extractIntOption(options, '${Flags.codegenShard}=')
diff --git a/pkg/compiler/lib/src/serialization/strategies.dart b/pkg/compiler/lib/src/serialization/strategies.dart
index 0c159f9..37fbe89 100644
--- a/pkg/compiler/lib/src/serialization/strategies.dart
+++ b/pkg/compiler/lib/src/serialization/strategies.dart
@@ -24,13 +24,18 @@
 abstract class SerializationStrategy<T> {
   const SerializationStrategy();
 
-  List<int> serializeComponent(GlobalTypeInferenceResults results) {
+  List<int> unpackAndSerializeComponent(GlobalTypeInferenceResults results) {
     JsClosedWorld closedWorld = results.closedWorld;
     ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
-    return ir.serializeComponent(component);
+    return serializeComponent(component);
   }
 
-  List<T> serializeData(GlobalTypeInferenceResults results);
+  List<T> serializeGlobalTypeInferenceResults(
+      GlobalTypeInferenceResults results);
+
+  List<int> serializeComponent(ir.Component component) {
+    return ir.serializeComponent(component);
+  }
 
   ir.Component deserializeComponent(List<int> data) {
     ir.Component component = new ir.Component();
@@ -38,7 +43,17 @@
     return component;
   }
 
-  GlobalTypeInferenceResults deserializeData(
+  GlobalTypeInferenceResults deserializeGlobalTypeInferenceResults(
+      CompilerOptions options,
+      DiagnosticReporter reporter,
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy,
+      ir.Component component,
+      List<T> data);
+
+  List<T> serializeClosedWorld(JsClosedWorld closedWorld);
+
+  JsClosedWorld deserializeClosedWorld(
       CompilerOptions options,
       DiagnosticReporter reporter,
       Environment environment,
@@ -53,15 +68,16 @@
   const BytesInMemorySerializationStrategy({this.useDataKinds: false});
 
   @override
-  List<int> serializeData(GlobalTypeInferenceResults results) {
+  List<int> serializeGlobalTypeInferenceResults(
+      GlobalTypeInferenceResults results) {
     ByteSink byteSink = new ByteSink();
     DataSink sink = new BinarySink(byteSink, useDataKinds: useDataKinds);
-    serializeGlobalTypeInferenceResults(results, sink);
+    serializeGlobalTypeInferenceResultsToSink(results, sink);
     return byteSink.builder.takeBytes();
   }
 
   @override
-  GlobalTypeInferenceResults deserializeData(
+  GlobalTypeInferenceResults deserializeGlobalTypeInferenceResults(
       CompilerOptions options,
       DiagnosticReporter reporter,
       Environment environment,
@@ -69,7 +85,28 @@
       ir.Component component,
       List<int> data) {
     DataSource source = new BinarySourceImpl(data, useDataKinds: useDataKinds);
-    return deserializeGlobalTypeInferenceResults(options, reporter, environment,
+    return deserializeGlobalTypeInferenceResultsFromSource(options, reporter,
+        environment, abstractValueStrategy, component, source);
+  }
+
+  @override
+  List<int> serializeClosedWorld(JsClosedWorld closedWorld) {
+    ByteSink byteSink = new ByteSink();
+    DataSink sink = new BinarySink(byteSink, useDataKinds: useDataKinds);
+    serializeClosedWorldToSink(closedWorld, sink);
+    return byteSink.builder.takeBytes();
+  }
+
+  @override
+  JsClosedWorld deserializeClosedWorld(
+      CompilerOptions options,
+      DiagnosticReporter reporter,
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy,
+      ir.Component component,
+      List<int> data) {
+    DataSource source = new BinarySourceImpl(data, useDataKinds: useDataKinds);
+    return deserializeClosedWorldFromSource(options, reporter, environment,
         abstractValueStrategy, component, source);
   }
 }
@@ -80,17 +117,18 @@
   const BytesOnDiskSerializationStrategy({this.useDataKinds: false});
 
   @override
-  List<int> serializeData(GlobalTypeInferenceResults results) {
+  List<int> serializeGlobalTypeInferenceResults(
+      GlobalTypeInferenceResults results) {
     Uri uri = Uri.base.resolve('world.data');
     DataSink sink = new BinarySink(
         new BinaryOutputSinkAdapter(new RandomAccessBinaryOutputSink(uri)),
         useDataKinds: useDataKinds);
-    serializeGlobalTypeInferenceResults(results, sink);
+    serializeGlobalTypeInferenceResultsToSink(results, sink);
     return new File.fromUri(uri).readAsBytesSync();
   }
 
   @override
-  GlobalTypeInferenceResults deserializeData(
+  GlobalTypeInferenceResults deserializeGlobalTypeInferenceResults(
       CompilerOptions options,
       DiagnosticReporter reporter,
       Environment environment,
@@ -98,7 +136,30 @@
       ir.Component component,
       List<int> data) {
     DataSource source = new BinarySourceImpl(data, useDataKinds: useDataKinds);
-    return deserializeGlobalTypeInferenceResults(options, reporter, environment,
+    return deserializeGlobalTypeInferenceResultsFromSource(options, reporter,
+        environment, abstractValueStrategy, component, source);
+  }
+
+  @override
+  List<int> serializeClosedWorld(JsClosedWorld closedWorld) {
+    Uri uri = Uri.base.resolve('closed_world.data');
+    DataSink sink = new BinarySink(
+        new BinaryOutputSinkAdapter(new RandomAccessBinaryOutputSink(uri)),
+        useDataKinds: useDataKinds);
+    serializeClosedWorldToSink(closedWorld, sink);
+    return new File.fromUri(uri).readAsBytesSync();
+  }
+
+  @override
+  JsClosedWorld deserializeClosedWorld(
+      CompilerOptions options,
+      DiagnosticReporter reporter,
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy,
+      ir.Component component,
+      List<int> data) {
+    DataSource source = new BinarySourceImpl(data, useDataKinds: useDataKinds);
+    return deserializeClosedWorldFromSource(options, reporter, environment,
         abstractValueStrategy, component, source);
   }
 }
@@ -110,15 +171,16 @@
   const ObjectsInMemorySerializationStrategy({this.useDataKinds: true});
 
   @override
-  List<Object> serializeData(GlobalTypeInferenceResults results) {
+  List<Object> serializeGlobalTypeInferenceResults(
+      GlobalTypeInferenceResults results) {
     List<Object> data = [];
     DataSink sink = new ObjectSink(data, useDataKinds: useDataKinds);
-    serializeGlobalTypeInferenceResults(results, sink);
+    serializeGlobalTypeInferenceResultsToSink(results, sink);
     return data;
   }
 
   @override
-  GlobalTypeInferenceResults deserializeData(
+  GlobalTypeInferenceResults deserializeGlobalTypeInferenceResults(
       CompilerOptions options,
       DiagnosticReporter reporter,
       Environment environment,
@@ -126,7 +188,28 @@
       ir.Component component,
       List<Object> data) {
     DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
-    return deserializeGlobalTypeInferenceResults(options, reporter, environment,
+    return deserializeGlobalTypeInferenceResultsFromSource(options, reporter,
+        environment, abstractValueStrategy, component, source);
+  }
+
+  @override
+  List<Object> serializeClosedWorld(JsClosedWorld closedWorld) {
+    List<Object> data = [];
+    DataSink sink = new ObjectSink(data, useDataKinds: useDataKinds);
+    serializeClosedWorldToSink(closedWorld, sink);
+    return data;
+  }
+
+  @override
+  JsClosedWorld deserializeClosedWorld(
+      CompilerOptions options,
+      DiagnosticReporter reporter,
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy,
+      ir.Component component,
+      List<Object> data) {
+    DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
+    return deserializeClosedWorldFromSource(options, reporter, environment,
         abstractValueStrategy, component, source);
   }
 }
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 8a081d7..32d936c 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -24,7 +24,7 @@
 import '../world.dart';
 import 'serialization.dart';
 
-void serializeGlobalTypeInferenceResults(
+void serializeGlobalTypeInferenceResultsToSink(
     GlobalTypeInferenceResults results, DataSink sink) {
   JsClosedWorld closedWorld = results.closedWorld;
   InferredData inferredData = results.inferredData;
@@ -34,7 +34,7 @@
   sink.close();
 }
 
-GlobalTypeInferenceResults deserializeGlobalTypeInferenceResults(
+GlobalTypeInferenceResults deserializeGlobalTypeInferenceResultsFromSource(
     CompilerOptions options,
     DiagnosticReporter reporter,
     Environment environment,
@@ -49,6 +49,22 @@
       source, newClosedWorld.elementMap, newClosedWorld, newInferredData);
 }
 
+void serializeClosedWorldToSink(JsClosedWorld closedWorld, DataSink sink) {
+  closedWorld.writeToDataSink(sink);
+  sink.close();
+}
+
+JsClosedWorld deserializeClosedWorldFromSource(
+    CompilerOptions options,
+    DiagnosticReporter reporter,
+    Environment environment,
+    AbstractValueStrategy abstractValueStrategy,
+    ir.Component component,
+    DataSource source) {
+  return new JsClosedWorld.readFromDataSource(
+      options, reporter, environment, abstractValueStrategy, component, source);
+}
+
 class SerializationTask extends CompilerTask {
   final CompilerOptions _options;
   final DiagnosticReporter _reporter;
@@ -62,7 +78,7 @@
   @override
   String get name => 'Serialization';
 
-  void serializeGlobalTypeInference(GlobalTypeInferenceResults results) {
+  void serializeComponent(ir.Component component) {
     measureSubtask('serialize dill', () {
       // TODO(sigmund): remove entirely: we will do this immediately as soon as
       // we get the component in the kernel/loader.dart task once we refactor
@@ -70,28 +86,15 @@
       _reporter.log('Writing dill to ${_options.outputUri}');
       api.BinaryOutputSink dillOutput =
           _outputProvider.createBinarySink(_options.outputUri);
-      JsClosedWorld closedWorld = results.closedWorld;
-      ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
       BinaryOutputSinkAdapter irSink = new BinaryOutputSinkAdapter(dillOutput);
       ir.BinaryPrinter printer = new ir.BinaryPrinter(irSink);
       printer.writeComponentFile(component);
       irSink.close();
     });
-
-    measureSubtask('serialize data', () {
-      _reporter.log('Writing data to ${_options.writeDataUri}');
-      api.BinaryOutputSink dataOutput =
-          _outputProvider.createBinarySink(_options.writeDataUri);
-      DataSink sink = new BinarySink(new BinaryOutputSinkAdapter(dataOutput));
-      serializeGlobalTypeInferenceResults(results, sink);
-    });
   }
 
-  Future<GlobalTypeInferenceResults> deserializeGlobalTypeInference(
-      Environment environment,
-      AbstractValueStrategy abstractValueStrategy) async {
-    ir.Component component =
-        await measureIoSubtask('deserialize dill', () async {
+  Future<ir.Component> deserializeComponent() async {
+    return measureIoSubtask('deserialize dill', () async {
       _reporter.log('Reading dill from ${_options.entryPoint}');
       api.Input<List<int>> dillInput = await _provider
           .readFromUri(_options.entryPoint, inputKind: api.InputKind.binary);
@@ -99,7 +102,9 @@
       new ir.BinaryBuilder(dillInput.data).readComponent(component);
       return component;
     });
+  }
 
+  void updateOptionsFromComponent(ir.Component component) {
     var isStrongDill =
         component.mode == ir.NonNullableByDefaultCompiledMode.Strong;
     var incompatibleNullSafetyMode =
@@ -118,14 +123,65 @@
     } else {
       _options.nullSafetyMode = NullSafetyMode.unsound;
     }
+  }
+
+  Future<ir.Component> deserializeComponentAndUpdateOptions() async {
+    ir.Component component = await deserializeComponent();
+    updateOptionsFromComponent(component);
+    return component;
+  }
+
+  void serializeClosedWorld(JsClosedWorld closedWorld) {
+    measureSubtask('serialize closed world', () {
+      _reporter.log('Writing closed world to ${_options.writeClosedWorldUri}');
+      api.BinaryOutputSink dataOutput =
+          _outputProvider.createBinarySink(_options.writeClosedWorldUri);
+      DataSink sink = new BinarySink(new BinaryOutputSinkAdapter(dataOutput));
+      serializeClosedWorldToSink(closedWorld, sink);
+    });
+  }
+
+  Future<JsClosedWorld> deserializeClosedWorld(
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy,
+      ir.Component component) async {
+    return await measureIoSubtask('deserialize closed world', () async {
+      _reporter.log('Reading data from ${_options.readClosedWorldUri}');
+      api.Input<List<int>> dataInput = await _provider.readFromUri(
+          _options.readClosedWorldUri,
+          inputKind: api.InputKind.binary);
+      DataSource source = new BinarySourceImpl(dataInput.data);
+      return deserializeClosedWorldFromSource(_options, _reporter, environment,
+          abstractValueStrategy, component, source);
+    });
+  }
+
+  void serializeGlobalTypeInference(GlobalTypeInferenceResults results) {
+    JsClosedWorld closedWorld = results.closedWorld;
+    ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
+    serializeComponent(component);
+
+    measureSubtask('serialize data', () {
+      _reporter.log('Writing data to ${_options.writeDataUri}');
+      api.BinaryOutputSink dataOutput =
+          _outputProvider.createBinarySink(_options.writeDataUri);
+      DataSink sink = new BinarySink(new BinaryOutputSinkAdapter(dataOutput));
+      serializeGlobalTypeInferenceResultsToSink(results, sink);
+    });
+  }
+
+  Future<GlobalTypeInferenceResults> deserializeGlobalTypeInference(
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy) async {
+    ir.Component component = await deserializeComponentAndUpdateOptions();
 
     return await measureIoSubtask('deserialize data', () async {
       _reporter.log('Reading data from ${_options.readDataUri}');
       api.Input<List<int>> dataInput = await _provider
           .readFromUri(_options.readDataUri, inputKind: api.InputKind.binary);
       DataSource source = new BinarySourceImpl(dataInput.data);
-      return deserializeGlobalTypeInferenceResults(_options, _reporter,
-          environment, abstractValueStrategy, component, source);
+      return deserializeGlobalTypeInferenceResultsFromSource(_options,
+          _reporter, environment, abstractValueStrategy, component, source);
     });
   }
 
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index 389d370..a3d44ea 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -101,20 +101,21 @@
 
 GlobalTypeInferenceResults cloneInferenceResults(Compiler compiler,
     GlobalTypeInferenceResults results, SerializationStrategy strategy) {
-  List<int> irData = strategy.serializeComponent(results);
+  List<int> irData = strategy.unpackAndSerializeComponent(results);
 
-  List worldData = strategy.serializeData(results);
+  List worldData = strategy.serializeGlobalTypeInferenceResults(results);
   print('data size: ${worldData.length}');
 
   ir.Component newComponent = strategy.deserializeComponent(irData);
-  GlobalTypeInferenceResults newResults = strategy.deserializeData(
-      compiler.options,
-      compiler.reporter,
-      compiler.environment,
-      compiler.abstractValueStrategy,
-      newComponent,
-      worldData);
-  List newWorldData = strategy.serializeData(newResults);
+  GlobalTypeInferenceResults newResults =
+      strategy.deserializeGlobalTypeInferenceResults(
+          compiler.options,
+          compiler.reporter,
+          compiler.environment,
+          compiler.abstractValueStrategy,
+          newComponent,
+          worldData);
+  List newWorldData = strategy.serializeGlobalTypeInferenceResults(newResults);
   Expect.equals(worldData.length, newWorldData.length,
       "Reserialization data length mismatch.");
   for (int i = 0; i < worldData.length; i++) {
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index eb8953b..0fe73be 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -6,6 +6,7 @@
 ///
 /// This is a shell that runs multiple tests, one per folder under `data/`.
 import 'dart:io';
+import 'dart:async';
 
 import 'package:compiler/src/commandline_options.dart';
 import 'package:front_end/src/compute_platform_binaries_location.dart'
@@ -30,22 +31,40 @@
   _options = Options.parse(args);
   _packageConfig = await loadPackageConfigUri(packageConfigUri);
   await _resolveScripts();
-  await runSuite(
-      sdkRoot.resolve('tests/modular/'),
-      'tests/modular',
-      _options,
-      new IOPipeline([
-        SourceToDillStep(),
-        GlobalAnalysisStep(),
-        Dart2jsCodegenStep(codeId0),
-        Dart2jsCodegenStep(codeId1),
-        Dart2jsEmissionStep(),
-        RunD8(),
-      ], cacheSharedModules: true));
+  await Future.wait([
+    runSuite(
+        sdkRoot.resolve('tests/modular/'),
+        'tests/modular',
+        _options,
+        new IOPipeline([
+          SourceToDillStep(),
+          ComputeClosedWorldStep(),
+          GlobalAnalysisStep(),
+          Dart2jsCodegenStep(codeId0),
+          Dart2jsCodegenStep(codeId1),
+          Dart2jsEmissionStep(),
+          RunD8(),
+        ], cacheSharedModules: true)),
+    // TODO(joshualitt) Delete this when we stop supporting this way of running
+    // the compiler.
+    runSuite(
+        sdkRoot.resolve('tests/modular/'),
+        'tests/modular',
+        _options,
+        new IOPipeline([
+          SourceToDillStep(),
+          LegacyGlobalAnalysisStep(),
+          Dart2jsCodegenStep(codeId0),
+          Dart2jsCodegenStep(codeId1),
+          Dart2jsEmissionStep(),
+          RunD8(),
+        ], cacheSharedModules: true))
+  ]);
 }
 
 const dillId = const DataId("dill");
 const updatedDillId = const DataId("udill");
+const closedWorldId = const DataId("world");
 const globalDataId = const DataId("gdata");
 const codeId = const ShardsDataId("code", 2);
 const codeId0 = const ShardDataId(codeId, 0);
@@ -213,10 +232,106 @@
   }
 }
 
+// Step that invokes the dart2js closed world computation.
+class ComputeClosedWorldStep implements IOModularStep {
+  @override
+  List<DataId> get resultData => const [closedWorldId, updatedDillId];
+
+  @override
+  bool get needsSources => false;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [dillId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [dillId];
+
+  @override
+  bool get onlyOnMain => true;
+
+  @override
+  Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+      List<String> flags) async {
+    if (_options.verbose)
+      print("\nstep: dart2js compute closed world on $module");
+    Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
+    Iterable<String> dillDependencies =
+        transitiveDependencies.map((m) => '${toUri(m, dillId)}');
+    List<String> args = [
+      '--packages=${sdkRoot.toFilePath()}/.packages',
+      _dart2jsScript,
+      // TODO(sigmund): remove this dependency on libraries.json
+      if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+      '${toUri(module, dillId)}',
+      for (String flag in flags) '--enable-experiment=$flag',
+      '${Flags.dillDependencies}=${dillDependencies.join(',')}',
+      '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
+      '--out=${toUri(module, updatedDillId)}',
+    ];
+    var result =
+        await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+    _checkExitCode(result, this, module);
+  }
+
+  @override
+  void notifyCached(Module module) {
+    if (_options.verbose)
+      print("\ncached step: dart2js compute closed world on $module");
+  }
+}
+
 // Step that invokes the dart2js global analysis on the main module by providing
 // the .dill files of all transitive modules as inputs.
 class GlobalAnalysisStep implements IOModularStep {
   @override
+  List<DataId> get resultData => const [globalDataId];
+
+  @override
+  bool get needsSources => false;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [updatedDillId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [closedWorldId, updatedDillId];
+
+  @override
+  bool get onlyOnMain => true;
+
+  @override
+  Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+      List<String> flags) async {
+    if (_options.verbose) print("\nstep: dart2js global analysis on $module");
+    List<String> args = [
+      '--packages=${sdkRoot.toFilePath()}/.packages',
+      _dart2jsScript,
+      // TODO(sigmund): remove this dependency on libraries.json
+      if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+      '${toUri(module, updatedDillId)}',
+      for (String flag in flags) '--enable-experiment=$flag',
+      '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
+      '${Flags.writeData}=${toUri(module, globalDataId)}',
+    ];
+    var result =
+        await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+    _checkExitCode(result, this, module);
+  }
+
+  @override
+  void notifyCached(Module module) {
+    if (_options.verbose)
+      print("\ncached step: dart2js global analysis on $module");
+  }
+}
+
+// Step that invokes the dart2js global analysis on the main module by providing
+// the .dill files of all transitive modules as inputs.
+// NOTE: This is the legacy combined closed world computation alongside global
+// inference.
+class LegacyGlobalAnalysisStep implements IOModularStep {
+  @override
   List<DataId> get resultData => const [globalDataId, updatedDillId];
 
   @override
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index c14b8d16..950d052 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -332,7 +332,7 @@
 
   visitBlock(Block node) {
     return new Block(node.statements.map(clone).toList())
-      ..fileOffset = _cloneFileOffset(node.fileOffset);
+      ..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
   }
 
   visitAssertBlock(AssertBlock node) {
@@ -498,7 +498,6 @@
         returnType: visitType(node.returnType),
         asyncMarker: node.asyncMarker,
         dartAsyncMarker: node.dartAsyncMarker)
-      ..fileOffset = _cloneFileOffset(node.fileOffset)
       ..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
   }
 
diff --git a/runtime/tests/vm/dart/product_aot_kernel_test.dart b/runtime/tests/vm/dart/product_aot_kernel_test.dart
index d46dd14..5372d3d 100644
--- a/runtime/tests/vm/dart/product_aot_kernel_test.dart
+++ b/runtime/tests/vm/dart/product_aot_kernel_test.dart
@@ -9,8 +9,10 @@
 import "dart:io";
 
 import 'package:expect/expect.dart';
+// ignore: import_of_legacy_library_into_null_safe
 import 'package:kernel/binary/ast_from_binary.dart'
     show BinaryBuilderWithMetadata;
+// ignore: import_of_legacy_library_into_null_safe
 import 'package:kernel/kernel.dart';
 import 'package:path/path.dart' as path;
 
diff --git a/runtime/tests/vm/dart_2/product_aot_kernel_test.dart b/runtime/tests/vm/dart_2/product_aot_kernel_test.dart
index d46dd14..5372d3d 100644
--- a/runtime/tests/vm/dart_2/product_aot_kernel_test.dart
+++ b/runtime/tests/vm/dart_2/product_aot_kernel_test.dart
@@ -9,8 +9,10 @@
 import "dart:io";
 
 import 'package:expect/expect.dart';
+// ignore: import_of_legacy_library_into_null_safe
 import 'package:kernel/binary/ast_from_binary.dart'
     show BinaryBuilderWithMetadata;
+// ignore: import_of_legacy_library_into_null_safe
 import 'package:kernel/kernel.dart';
 import 'package:path/path.dart' as path;
 
diff --git a/tools/VERSION b/tools/VERSION
index a506401..d10bed3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 30
+PRERELEASE 31
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index f8cbe1d..dfe661f 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -3200,6 +3200,8 @@
             "--arch=ia32",
             "create_sdk",
             "runtime",
+            "dart2js_platform.dill",
+            "dart2js_nnbd_strong_platform.dill",
             "kernel-service.dart.snapshot"
           ]
         },
@@ -3234,6 +3236,8 @@
             "runtime",
             "gen_snapshot",
             "dart_precompiled_runtime",
+            "dart2js_platform.dill",
+            "dart2js_nnbd_strong_platform.dill",
             "kernel-service.dart.snapshot",
             "dartdevc_test"
           ]
diff --git a/tools/bots/try_benchmarks.sh b/tools/bots/try_benchmarks.sh
index 8407171..ea827b4 100755
--- a/tools/bots/try_benchmarks.sh
+++ b/tools/bots/try_benchmarks.sh
@@ -75,7 +75,7 @@
     rm -f linux-x64_profile.tar.gz
   elif [ "$command" = linux-ia32-build ]; then
     # NOTE: These are duplicated in tools/bots/test_matrix.json, keep in sync.
-    ./tools/build.py --mode=release --arch=ia32 create_sdk runtime kernel-service.dart.snapshot
+    ./tools/build.py --mode=release --arch=ia32 create_sdk runtime dart2js_platform.dill dart2js_nnbd_strong_platform.dill kernel-service.dart.snapshot
   elif [ "$command" = linux-ia32-archive ]; then
     strip -w \
       -K 'kDartVmSnapshotData' \
@@ -148,6 +148,7 @@
       out/ReleaseIA32/dart-sdk \
       out/ReleaseIA32/dart \
       out/ReleaseIA32/gen_snapshot \
+      out/ReleaseIA32/dart2js_nnbd_strong_platform.dill \
       out/ReleaseIA32/kernel-service.dart.snapshot \
       out/ReleaseIA32/run_vm_tests \
       sdk \
@@ -195,7 +196,7 @@
     rm -rf tmp
   elif [ "$command" = linux-x64-build ]; then
     # NOTE: These are duplicated in tools/bots/test_matrix.json, keep in sync.
-    ./tools/build.py --mode=release --arch=x64 create_sdk runtime gen_snapshot dart_precompiled_runtime kernel-service.dart.snapshot dartdevc_test
+    ./tools/build.py --mode=release --arch=x64 create_sdk runtime gen_snapshot dart_precompiled_runtime dart2js_platform.dill dart2js_nnbd_strong_platform.dill kernel-service.dart.snapshot dartdevc_test
   elif [ "$command" = linux-x64-archive ]; then
     strip -w \
       -K 'kDartVmSnapshotData' \
@@ -287,6 +288,7 @@
       out/ReleaseX64/dart-sdk \
       out/ReleaseX64/dart \
       out/ReleaseX64/gen_snapshot \
+      out/ReleaseX64/dart2js_nnbd_strong_platform.dill \
       out/ReleaseX64/kernel-service.dart.snapshot \
       out/ReleaseX64/run_vm_tests \
       third_party/d8/linux/x64 \
@@ -357,6 +359,7 @@
     out/ReleaseX64/run_vm_tests --dfe=out/ReleaseX64/kernel-service.dart.snapshot --sound-null-safety --enable-experiment=non-nullable UseDartApi
     out/ReleaseX64/dart --profile-period=10000 --packages=.packages benchmarks/Example/dart2/Example.dart
     out/ReleaseX64/dart --sound-null-safety --enable-experiment=non-nullable --profile-period=10000 --packages=.packages benchmarks/Example/dart/Example.dart
+    out/ReleaseX64/dart --profile-period=10000 --packages=.packages benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart
     cd ..
     rm -rf tmp
   else