Version 2.12.0-240.0.dev

Merge commit '4563164eec32b64ff99c18e4611195a38f3c72da' into 'dev'
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index 0921ddd..3ec08f6 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -356,4 +356,145 @@
     expect(File(outFile).existsSync(), true,
         reason: 'File not found: $outFile');
   });
+
+  test('Compile AOT snapshot with sound null safety', () {
+    final p = project(mainSrc: '''void main() {}''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myaot'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'aot-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(soundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile AOT snapshot with unsound null safety', () {
+    final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myaot'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'aot-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(unsoundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile kernel with sound null safety', () {
+    final p = project(mainSrc: '''void main() {}''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'mydill'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'kernel',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(soundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile kernel with unsound null safety', () {
+    final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'mydill'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'kernel',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(unsoundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile JIT snapshot with sound null safety', () {
+    final p = project(mainSrc: '''void main() {}''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myjit'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'jit-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(soundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile JIT snapshot with unsound null safety', () {
+    final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myjit'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'jit-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(unsoundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
 }
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index a0fc1de..5f226e7 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -23,7 +23,9 @@
 import 'dart:async' show Future, ZoneSpecification, runZoned;
 import 'dart:collection' show UnmodifiableMapBase;
 import 'dart:convert' show utf8;
-import 'dart:io' show Directory, File, Platform, stderr hide FileSystemEntity;
+import 'dart:io'
+    show Directory, File, Platform, stderr, stdout
+    hide FileSystemEntity;
 import 'dart:isolate';
 import 'dart:typed_data' show Uint8List;
 
@@ -92,7 +94,8 @@
     int nullSafety,
     List<String> experimentalFlags,
     Uri packagesUri,
-    List<String> errors) {
+    List<String> errors,
+    String invocationModes) {
   final expFlags = <String>[];
   if (experimentalFlags != null) {
     for (String flag in experimentalFlags) {
@@ -116,27 +119,33 @@
         ? NnbdMode.Strong
         : NnbdMode.Weak
     ..onDiagnostic = (DiagnosticMessage message) {
-      bool printMessage;
+      bool printToStdErr = false;
+      bool printToStdOut = false;
       switch (message.severity) {
         case Severity.error:
         case Severity.internalProblem:
           // TODO(sigmund): support emitting code with errors as long as they
           // are handled in the generated code.
-          printMessage = false; // errors are printed by VM
+          printToStdErr = false; // errors are printed by VM
           errors.addAll(message.plainTextFormatted);
           break;
         case Severity.warning:
+          printToStdErr = !suppressWarnings;
+          break;
         case Severity.info:
-          printMessage = !suppressWarnings;
+          printToStdOut = !suppressWarnings;
           break;
         case Severity.context:
         case Severity.ignored:
           throw "Unexpected severity: ${message.severity}";
       }
-      if (printMessage) {
+      if (printToStdErr) {
         printDiagnosticMessage(message, stderr.writeln);
+      } else if (printToStdOut) {
+        printDiagnosticMessage(message, stdout.writeln);
       }
-    };
+    }
+    ..invocationModes = InvocationMode.parseArguments(invocationModes);
 }
 
 abstract class Compiler {
@@ -148,6 +157,7 @@
   final int nullSafety;
   final List<String> experimentalFlags;
   final String packageConfig;
+  final String invocationModes;
 
   // Code coverage and hot reload are only supported by incremental compiler,
   // which is used if vm-service is enabled.
@@ -165,7 +175,8 @@
       this.experimentalFlags: null,
       this.supportCodeCoverage: false,
       this.supportHotReload: false,
-      this.packageConfig: null}) {
+      this.packageConfig: null,
+      this.invocationModes: ''}) {
     Uri packagesUri = null;
     if (packageConfig != null) {
       packagesUri = Uri.parse(packageConfig);
@@ -188,7 +199,8 @@
         nullSafety,
         experimentalFlags,
         packagesUri,
-        errors);
+        errors,
+        invocationModes);
   }
 
   Future<CompilerResult> compile(Uri script) {
@@ -278,7 +290,8 @@
       bool enableAsserts: false,
       int nullSafety: kNullSafetyOptionUnspecified,
       List<String> experimentalFlags: null,
-      String packageConfig: null})
+      String packageConfig: null,
+      String invocationModes: ''})
       : super(isolateId, fileSystem, platformKernelPath,
             suppressWarnings: suppressWarnings,
             enableAsserts: enableAsserts,
@@ -286,7 +299,8 @@
             experimentalFlags: experimentalFlags,
             supportHotReload: true,
             supportCodeCoverage: true,
-            packageConfig: packageConfig);
+            packageConfig: packageConfig,
+            invocationModes: invocationModes);
 
   factory IncrementalCompilerWrapper.forExpressionCompilationOnly(
       Component component,
@@ -296,13 +310,15 @@
       {bool suppressWarnings: false,
       bool enableAsserts: false,
       List<String> experimentalFlags: null,
-      String packageConfig: null}) {
+      String packageConfig: null,
+      String invocationModes: ''}) {
     IncrementalCompilerWrapper result = IncrementalCompilerWrapper(
         isolateId, fileSystem, platformKernelPath,
         suppressWarnings: suppressWarnings,
         enableAsserts: enableAsserts,
         experimentalFlags: experimentalFlags,
-        packageConfig: packageConfig);
+        packageConfig: packageConfig,
+        invocationModes: invocationModes);
     result.generator = new IncrementalCompiler.forExpressionCompilationOnly(
         component,
         result.options,
@@ -331,7 +347,8 @@
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
         experimentalFlags: experimentalFlags,
-        packageConfig: packageConfig);
+        packageConfig: packageConfig,
+        invocationModes: invocationModes);
 
     generator.resetDeltaState();
     Component fullComponent = await generator.compile();
@@ -361,13 +378,15 @@
       bool enableAsserts: false,
       int nullSafety: kNullSafetyOptionUnspecified,
       List<String> experimentalFlags: null,
-      String packageConfig: null})
+      String packageConfig: null,
+      String invocationModes: ''})
       : super(isolateId, fileSystem, platformKernelPath,
             suppressWarnings: suppressWarnings,
             enableAsserts: enableAsserts,
             nullSafety: nullSafety,
             experimentalFlags: experimentalFlags,
-            packageConfig: packageConfig);
+            packageConfig: packageConfig,
+            invocationModes: invocationModes);
 
   @override
   Future<CompilerResult> compileInternal(Uri script) async {
@@ -403,7 +422,8 @@
     List<String> experimentalFlags: null,
     String packageConfig: null,
     String multirootFilepaths,
-    String multirootScheme}) async {
+    String multirootScheme,
+    String invocationModes: ''}) async {
   IncrementalCompilerWrapper compiler = lookupIncrementalCompiler(isolateId);
   if (compiler != null) {
     updateSources(compiler, sourceFiles);
@@ -432,7 +452,8 @@
           enableAsserts: enableAsserts,
           nullSafety: nullSafety,
           experimentalFlags: experimentalFlags,
-          packageConfig: packageConfig);
+          packageConfig: packageConfig,
+          invocationModes: invocationModes);
     }
     isolateCompilers[isolateId] = compiler;
   }
@@ -652,10 +673,7 @@
   return utf8.encode(uris.map(_escapeDependency).join(" "));
 }
 
-Future _processListDependenciesRequest(request) async {
-  final SendPort port = request[1];
-  final int isolateId = request[6];
-
+Future _processListDependenciesRequest(SendPort port, int isolateId) async {
   final List<Uri> dependencies = isolateDependencies[isolateId] ?? <Uri>[];
 
   CompilationResult result;
@@ -709,32 +727,34 @@
     return;
   }
 
-  if (tag == kListDependenciesTag) {
-    await _processListDependenciesRequest(request);
-    return;
-  }
-
   if (tag == kNotifyIsolateShutdownTag) {
     await _processIsolateShutdownNotification(request);
     return;
   }
 
   final SendPort port = request[1];
+  final int isolateId = request[7];
+
+  if (tag == kListDependenciesTag) {
+    await _processListDependenciesRequest(port, isolateId);
+    return;
+  }
+
   final String inputFileUri = request[2];
   final Uri script =
       inputFileUri != null ? Uri.base.resolve(inputFileUri) : null;
   final bool incremental = request[4];
-  final int nullSafety = request[5];
-  final int isolateId = request[6];
-  final List sourceFiles = request[7];
-  final bool suppressWarnings = request[8];
-  final bool enableAsserts = request[9];
+  final bool snapshot = request[5];
+  final int nullSafety = request[6];
+  final List sourceFiles = request[8];
+  final bool suppressWarnings = request[9];
+  final bool enableAsserts = request[10];
   final List<String> experimentalFlags =
-      request[10] != null ? request[10].cast<String>() : null;
-  final String packageConfig = request[11];
-  final String multirootFilepaths = request[12];
-  final String multirootScheme = request[13];
-  final String workingDirectory = request[14];
+      request[11] != null ? request[11].cast<String>() : null;
+  final String packageConfig = request[12];
+  final String multirootFilepaths = request[13];
+  final String multirootScheme = request[14];
+  final String workingDirectory = request[15];
 
   Uri platformKernelPath = null;
   List<int> platformKernel = null;
@@ -748,6 +768,8 @@
         computePlatformBinariesLocation().resolve('vm_platform_strong.dill');
   }
 
+  final String invocationModes = snapshot ? 'compile' : '';
+
   Compiler compiler;
 
   // Update the in-memory file system with the provided sources. Currently, only
@@ -792,8 +814,16 @@
       packagesUri = Uri.directory(workingDirectory).resolveUri(packagesUri);
     }
     final List<String> errors = <String>[];
-    var options = setupCompilerOptions(fileSystem, platformKernelPath, false,
-        false, nullSafety, experimentalFlags, packagesUri, errors);
+    var options = setupCompilerOptions(
+        fileSystem,
+        platformKernelPath,
+        false,
+        false,
+        nullSafety,
+        experimentalFlags,
+        packagesUri,
+        errors,
+        invocationModes);
 
     // script should only be null for kUpdateSourcesTag.
     assert(script != null);
@@ -819,7 +849,8 @@
         experimentalFlags: experimentalFlags,
         packageConfig: packageConfig,
         multirootFilepaths: multirootFilepaths,
-        multirootScheme: multirootScheme);
+        multirootScheme: multirootScheme,
+        invocationModes: invocationModes);
   } else {
     FileSystem fileSystem = _buildFileSystem(
         sourceFiles, platformKernel, multirootFilepaths, multirootScheme);
@@ -830,7 +861,8 @@
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
         experimentalFlags: experimentalFlags,
-        packageConfig: packageConfig);
+        packageConfig: packageConfig,
+        invocationModes: invocationModes);
   }
 
   CompilationResult result;
@@ -974,6 +1006,7 @@
     scriptUri,
     platformKernelPath,
     false /* incremental */,
+    false /* snapshot */,
     kNullSafetyOptionUnspecified /* null safety */,
     1 /* isolateId chosen randomly */,
     [] /* source files */,
diff --git a/runtime/bin/dfe.cc b/runtime/bin/dfe.cc
index f0aa87a..0a6bc48 100644
--- a/runtime/bin/dfe.cc
+++ b/runtime/bin/dfe.cc
@@ -182,14 +182,15 @@
 
 Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri,
                                                 bool incremental,
-                                                const char* package_config) {
+                                                const char* package_config,
+                                                bool snapshot) {
   // TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill
   // instead of vm_platform.dill to Frontend for compilation.
   PathSanitizer path_sanitizer(script_uri);
   const char* sanitized_uri = path_sanitizer.sanitized_uri();
 
   return Dart_CompileToKernel(sanitized_uri, platform_strong_dill,
-                              platform_strong_dill_size, incremental,
+                              platform_strong_dill_size, incremental, snapshot,
                               package_config);
 }
 
@@ -198,9 +199,10 @@
                                intptr_t* kernel_buffer_size,
                                char** error,
                                int* exit_code,
-                               const char* package_config) {
-  Dart_KernelCompilationResult result =
-      CompileScript(script_uri, use_incremental_compiler(), package_config);
+                               const char* package_config,
+                               bool snapshot) {
+  Dart_KernelCompilationResult result = CompileScript(
+      script_uri, use_incremental_compiler(), package_config, snapshot);
   switch (result.status) {
     case Dart_KernelCompilationStatus_Ok:
       *kernel_buffer = result.kernel;
diff --git a/runtime/bin/dfe.h b/runtime/bin/dfe.h
index eb475bf..0c38270 100644
--- a/runtime/bin/dfe.h
+++ b/runtime/bin/dfe.h
@@ -59,20 +59,28 @@
 
   // Compiles specified script.
   // Returns result from compiling the script.
+  //
+  // `snapshot` is used by the frontend to determine if compilation
+  // related information should be printed to console (e.g., null safety mode).
   Dart_KernelCompilationResult CompileScript(const char* script_uri,
                                              bool incremental,
-                                             const char* package_config);
+                                             const char* package_config,
+                                             bool snapshot);
 
   // Compiles specified script and reads the resulting kernel file.
   // If the compilation is successful, returns a valid in memory kernel
   // representation of the script, NULL otherwise
   // 'error' and 'exit_code' have the error values in case of errors.
+  //
+  // `snapshot` is used by the frontend to determine if compilation
+  // related information should be printed to console (e.g., null safety mode).
   void CompileAndReadScript(const char* script_uri,
                             uint8_t** kernel_buffer,
                             intptr_t* kernel_buffer_size,
                             char** error,
                             int* exit_code,
-                            const char* package_config);
+                            const char* package_config,
+                            bool snapshot);
 
   // Reads the script kernel file if specified 'script_uri' is a kernel file.
   // Returns an in memory kernel representation of the specified script is a
diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc
index 9f62982..e855baf 100644
--- a/runtime/bin/loader.cc
+++ b/runtime/bin/loader.cc
@@ -198,7 +198,7 @@
     uint8_t* kernel_buffer = NULL;
     intptr_t kernel_buffer_size = -1;
     dfe.CompileAndReadScript(url_string, &kernel_buffer, &kernel_buffer_size,
-                             &error, &exit_code, NULL);
+                             &error, &exit_code, NULL, false);
     if (exit_code == 0) {
       return Dart_LoadLibraryFromKernel(kernel_buffer, kernel_buffer_size);
     } else if (exit_code == kCompilationErrorExitCode) {
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index fec166f..12e64808 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -298,9 +298,14 @@
     }
     uint8_t* application_kernel_buffer = NULL;
     intptr_t application_kernel_buffer_size = 0;
+    // Only pass snapshot = true when generating an AppJIT snapshot to avoid
+    // duplicate null-safety info messages from the frontend when generating
+    // a kernel snapshot (this flag is instead set in
+    // Snapshot::GenerateKernel()).
+    const bool snapshot = Options::gen_snapshot_kind() == kAppJIT;
     dfe.CompileAndReadScript(script_uri, &application_kernel_buffer,
                              &application_kernel_buffer_size, error, exit_code,
-                             resolved_packages_config);
+                             resolved_packages_config, snapshot);
     if (application_kernel_buffer == NULL) {
       Dart_ExitScope();
       Dart_ShutdownIsolate();
diff --git a/runtime/bin/snapshot_utils.cc b/runtime/bin/snapshot_utils.cc
index 4a19ba2..5168ac5 100644
--- a/runtime/bin/snapshot_utils.cc
+++ b/runtime/bin/snapshot_utils.cc
@@ -471,7 +471,7 @@
     free(kernel_buffer);
   } else {
     Dart_KernelCompilationResult result =
-        dfe.CompileScript(script_name, false, package_config);
+        dfe.CompileScript(script_name, false, package_config, true);
     if (result.status != Dart_KernelCompilationStatus_Ok) {
       ErrorExit(kErrorExitCode, "%s\n", result.error);
     }
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 5cff8c3..aae9199 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -3537,6 +3537,10 @@
  *
  * \param platform_kernel_size The length of the platform_kernel buffer.
  *
+ * \param snapshot_compile Set to `true` when the compilation is for a snapshot.
+ * This is used by the frontend to determine if compilation related information
+ * should be printed to console (e.g., null safety mode).
+ *
  * \return Returns the result of the compilation.
  *
  * On a successful compilation the returned [Dart_KernelCompilationResult] has
@@ -3554,6 +3558,7 @@
                      const uint8_t* platform_kernel,
                      const intptr_t platform_kernel_size,
                      bool incremental_compile,
+                     bool snapshot_compile,
                      const char* package_config);
 
 typedef struct {
diff --git a/runtime/tests/vm/dart/sdk_hash_test.dart b/runtime/tests/vm/dart/sdk_hash_test.dart
index 8e31050..0ad4dba 100644
--- a/runtime/tests/vm/dart/sdk_hash_test.dart
+++ b/runtime/tests/vm/dart/sdk_hash_test.dart
@@ -35,7 +35,7 @@
       ]);
       Expect.equals('', result.stderr);
       Expect.equals(0, result.exitCode);
-      Expect.equals('', result.stdout);
+      Expect.equals('$unsoundNullSafetyMessage\n', result.stdout);
     }
 
     {
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 1b578b9..0de3272 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -39,7 +39,10 @@
 
 void expectOutput(String what, Result result) {
   if (result.output != what) {
-    reportError(result, 'Expected test to print \'${what}\' to stdout');
+    reportError(
+        result,
+        'Expected test to print \'${what}\' to stdout. '
+        'Actual: ${result.output}');
   }
 }
 
@@ -127,6 +130,12 @@
     final snapshot1Path = p.join(temp, 'snapshot1');
     final snapshot2Path = p.join(temp, 'snapshot2');
 
+    if (expectedStdout.isEmpty) {
+      expectedStdout = nullSafetyMessage;
+    } else {
+      expectedStdout = '$nullSafetyMessage\n$expectedStdout';
+    }
+
     print("Version ${Platform.version}");
 
     final generate1Result = await runDart('GENERATE SNAPSHOT 1', [
@@ -183,8 +192,15 @@
       testPath,
       '--train'
     ]);
-    expectOutput("OK(Trained)", trainingResult);
+    expectOutput("$nullSafetyMessage\nOK(Trained)", trainingResult);
     final runResult = await runSnapshot!(snapshotPath);
     expectOutput("OK(Run)", runResult);
   });
 }
+
+final String nullSafetyMessage =
+    hasSoundNullSafety ? soundNullSafetyMessage : unsoundNullSafetyMessage;
+
+const String soundNullSafetyMessage = 'Info: Compiling with sound null safety';
+const String unsoundNullSafetyMessage =
+    'Info: Compiling with unsound null safety';
diff --git a/runtime/tests/vm/dart_2/sdk_hash_test.dart b/runtime/tests/vm/dart_2/sdk_hash_test.dart
index c73dc03..40f245d 100644
--- a/runtime/tests/vm/dart_2/sdk_hash_test.dart
+++ b/runtime/tests/vm/dart_2/sdk_hash_test.dart
@@ -35,7 +35,7 @@
       ]);
       Expect.equals('', result.stderr);
       Expect.equals(0, result.exitCode);
-      Expect.equals('', result.stdout);
+      Expect.equals('$unsoundNullSafetyMessage\n', result.stdout);
     }
 
     {
diff --git a/runtime/tests/vm/dart_2/snapshot_test_helper.dart b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
index eef877f..428cac2 100644
--- a/runtime/tests/vm/dart_2/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
@@ -39,7 +39,10 @@
 
 void expectOutput(String what, Result result) {
   if (result.output != what) {
-    reportError(result, 'Expected test to print \'${what}\' to stdout');
+    reportError(
+        result,
+        'Expected test to print \'${what}\' to stdout. '
+        'Actual: ${result.output}');
   }
 }
 
@@ -127,6 +130,12 @@
     final snapshot1Path = p.join(temp, 'snapshot1');
     final snapshot2Path = p.join(temp, 'snapshot2');
 
+    if (expectedStdout.isEmpty) {
+      expectedStdout = unsoundNullSafetyMessage;
+    } else {
+      expectedStdout = '$unsoundNullSafetyMessage\n$expectedStdout';
+    }
+
     print("Version ${Platform.version}");
 
     final generate1Result = await runDart('GENERATE SNAPSHOT 1', [
@@ -183,8 +192,11 @@
       testPath,
       '--train'
     ]);
-    expectOutput("OK(Trained)", trainingResult);
+    expectOutput("$unsoundNullSafetyMessage\nOK(Trained)", trainingResult);
     final runResult = await runSnapshot(snapshotPath);
     expectOutput("OK(Run)", runResult);
   });
 }
+
+const String unsoundNullSafetyMessage =
+    'Info: Compiling with unsound null safety';
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 35356fa..1bfc864 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6094,6 +6094,7 @@
                      const uint8_t* platform_kernel,
                      intptr_t platform_kernel_size,
                      bool incremental_compile,
+                     bool snapshot_compile,
                      const char* package_config) {
   API_TIMELINE_DURATION(Thread::Current());
 
@@ -6102,9 +6103,9 @@
   result.status = Dart_KernelCompilationStatus_Unknown;
   result.error = Utils::StrDup("Dart_CompileToKernel is unsupported.");
 #else
-  result = KernelIsolate::CompileToKernel(script_uri, platform_kernel,
-                                          platform_kernel_size, 0, NULL,
-                                          incremental_compile, package_config);
+  result = KernelIsolate::CompileToKernel(
+      script_uri, platform_kernel, platform_kernel_size, 0, NULL,
+      incremental_compile, snapshot_compile, package_config);
   if (result.status == Dart_KernelCompilationStatus_Ok) {
     Dart_KernelCompilationResult accept_result =
         KernelIsolate::AcceptCompilation();
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 21ffcf2..3445ef8 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -1048,9 +1048,9 @@
   {
     const char* root_lib_url = root_lib_url_.ToCString();
     TransitionVMToNative transition(Thread::Current());
-    retval = KernelIsolate::CompileToKernel(root_lib_url, nullptr, 0,
-                                            modified_scripts_count,
-                                            modified_scripts, true, nullptr);
+    retval = KernelIsolate::CompileToKernel(
+        root_lib_url, nullptr, 0, modified_scripts_count, modified_scripts,
+        true, false, nullptr);
   }
   if (retval.status != Dart_KernelCompilationStatus_Ok) {
     if (retval.kernel != nullptr) {
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index 9ab6116..36bfb1b 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -708,6 +708,7 @@
       int source_files_count,
       Dart_SourceFile source_files[],
       bool incremental_compile,
+      bool snapshot_compile,
       const char* package_config,
       const char* multiroot_filepaths,
       const char* multiroot_scheme,
@@ -755,6 +756,10 @@
     dart_incremental.type = Dart_CObject_kBool;
     dart_incremental.value.as_bool = incremental_compile;
 
+    Dart_CObject dart_snapshot;
+    dart_snapshot.type = Dart_CObject_kBool;
+    dart_snapshot.value.as_bool = snapshot_compile;
+
     // TODO(aam): Assert that isolate exists once we move CompileAndReadScript
     // compilation logic out of CreateIsolateAndSetupHelper and into
     // IsolateSetupHelper in main.cc.
@@ -857,6 +862,7 @@
                                    &uri,
                                    &dart_platform_kernel,
                                    &dart_incremental,
+                                   &dart_snapshot,
                                    &null_safety,
                                    &isolate_id,
                                    &files,
@@ -1008,6 +1014,7 @@
     int source_file_count,
     Dart_SourceFile source_files[],
     bool incremental_compile,
+    bool snapshot_compile,
     const char* package_config,
     const char* multiroot_filepaths,
     const char* multiroot_scheme) {
@@ -1033,8 +1040,8 @@
   return request.SendAndWaitForResponse(
       kCompileTag, kernel_port, script_uri, platform_kernel,
       platform_kernel_size, source_file_count, source_files,
-      incremental_compile, package_config, multiroot_filepaths,
-      multiroot_scheme, experimental_flags_, NULL);
+      incremental_compile, snapshot_compile, package_config,
+      multiroot_filepaths, multiroot_scheme, experimental_flags_, NULL);
 }
 
 bool KernelIsolate::DetectNullSafety(const char* script_uri,
@@ -1052,7 +1059,7 @@
   KernelCompilationRequest request;
   Dart_KernelCompilationResult result = request.SendAndWaitForResponse(
       kDetectNullabilityTag, kernel_port, script_uri, nullptr, -1, 0, nullptr,
-      false, package_config, nullptr, nullptr, experimental_flags_,
+      false, false, package_config, nullptr, nullptr, experimental_flags_,
       original_working_directory);
   return result.null_safety;
 }
@@ -1068,8 +1075,8 @@
 
   KernelCompilationRequest request;
   return request.SendAndWaitForResponse(kListDependenciesTag, kernel_port, NULL,
-                                        NULL, 0, 0, NULL, false, NULL, NULL,
-                                        NULL, experimental_flags_, NULL);
+                                        NULL, 0, 0, NULL, false, false, NULL,
+                                        NULL, NULL, experimental_flags_, NULL);
 }
 
 Dart_KernelCompilationResult KernelIsolate::AcceptCompilation() {
@@ -1085,7 +1092,7 @@
 
   KernelCompilationRequest request;
   return request.SendAndWaitForResponse(kAcceptTag, kernel_port, NULL, NULL, 0,
-                                        0, NULL, true, NULL, NULL, NULL,
+                                        0, NULL, true, false, NULL, NULL, NULL,
                                         experimental_flags_, NULL);
 }
 
@@ -1131,7 +1138,7 @@
   KernelCompilationRequest request;
   return request.SendAndWaitForResponse(
       kUpdateSourcesTag, kernel_port, NULL, NULL, 0, source_files_count,
-      source_files, true, NULL, NULL, NULL, experimental_flags_, NULL);
+      source_files, true, false, NULL, NULL, NULL, experimental_flags_, NULL);
 }
 
 void KernelIsolate::NotifyAboutIsolateShutdown(const Isolate* isolate) {
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index 0aa5221..f312aee 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -51,6 +51,7 @@
       int source_files_count = 0,
       Dart_SourceFile source_files[] = NULL,
       bool incremental_compile = true,
+      bool snapshot_compile = false,
       const char* package_config = NULL,
       const char* multiroot_filepaths = NULL,
       const char* multiroot_scheme = NULL);
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 2a2a861..0d77711 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -322,7 +322,8 @@
   Zone* zone = Thread::Current()->zone();
   Dart_KernelCompilationResult result = KernelIsolate::CompileToKernel(
       url, platform_strong_dill, platform_strong_dill_size, sourcefiles_count,
-      sourcefiles, incrementally, NULL, multiroot_filepaths, multiroot_scheme);
+      sourcefiles, incrementally, false, NULL, multiroot_filepaths,
+      multiroot_scheme);
   if (result.status == Dart_KernelCompilationStatus_Ok) {
     if (KernelIsolate::AcceptCompilation().status !=
         Dart_KernelCompilationStatus_Ok) {
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index 981cfb2..cb305fe 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -198,8 +198,6 @@
 
   /// Sends or enqueues an error event.
   ///
-  /// If [error] is `null`, it is replaced by a [NullThrownError].
-  ///
   /// Listeners receive this event at a later microtask. This behavior can be
   /// overridden by using `sync` controllers. Note, however, that sync
   /// controllers have to satisfy the preconditions mentioned in the
diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart
index 7ef44c0..f0d6178 100644
--- a/sdk/lib/core/errors.dart
+++ b/sdk/lib/core/errors.dart
@@ -118,6 +118,9 @@
 class CastError extends Error {}
 
 /// Error thrown when attempting to throw `null`.
+///
+/// In null safe code, you are statically disallowed from throwing `null`,
+/// so this error will go away when non-null safe code stops being supported.
 class NullThrownError extends Error {
   @pragma("vm:entry-point")
   NullThrownError();
diff --git a/tools/VERSION b/tools/VERSION
index af6f28b..9f61f00 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 239
+PRERELEASE 240
 PRERELEASE_PATCH 0
\ No newline at end of file