Version 2.15.0-12.0.dev

Merge commit 'eae58445e904440ff3fcb2a12d2905990d66bbf4' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 20cdef0..b41bf1d 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-08-10T10:51:47.272341",
+  "generated": "2021-08-12T14:38:50.585343",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -617,7 +617,7 @@
       "name": "smith",
       "rootUri": "../pkg/smith",
       "packageUri": "lib/",
-      "languageVersion": "2.3"
+      "languageVersion": "2.12"
     },
     {
       "name": "source_map_stack_trace",
diff --git a/pkg/analysis_server/test/lsp/completion_yaml_test.dart b/pkg/analysis_server/test/lsp/completion_yaml_test.dart
index 52bf479..246d2f7 100644
--- a/pkg/analysis_server/test/lsp/completion_yaml_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_yaml_test.dart
@@ -524,7 +524,7 @@
       openCloseFile: false,
     );
 
-    // Also veryify the detail fields were populated as expected.
+    // Also verify the detail fields were populated as expected.
     expect(
       completionResults.singleWhere((c) => c.label == '^2.3.4').detail,
       equals('latest compatible'),
diff --git a/pkg/dart2js_runtime_metrics/analysis_options.yaml b/pkg/dart2js_runtime_metrics/analysis_options.yaml
index edcd64e..bfd745e 100644
--- a/pkg/dart2js_runtime_metrics/analysis_options.yaml
+++ b/pkg/dart2js_runtime_metrics/analysis_options.yaml
@@ -1,5 +1,6 @@
 analyzer:
   errors:
     import_internal_library: ignore
+    export_internal_library: ignore
   strong-mode:
     implicit-casts: false
diff --git a/pkg/smith/lib/builder.dart b/pkg/smith/lib/builder.dart
index 2772be2..ebe550c 100644
--- a/pkg/smith/lib/builder.dart
+++ b/pkg/smith/lib/builder.dart
@@ -21,13 +21,13 @@
   final String script;
   final List<String> arguments;
   final Map<String, String> environment;
-  final String fileSet;
-  final int shards;
+  final String? fileSet;
+  final int? shards;
   final bool isTestRunner;
-  final Configuration testedConfiguration;
+  final Configuration? testedConfiguration;
 
-  Step(this.name, String script, this.arguments, this.environment, this.fileSet,
-      this.shards, this.isTestRunner, this.testedConfiguration)
+  Step(this.name, String? script, this.arguments, this.environment,
+      this.fileSet, this.shards, this.isTestRunner, this.testedConfiguration)
       : script = script ?? testScriptName;
 
   static const testScriptName = "tools/test.py";
@@ -36,14 +36,14 @@
 
   /// Create a [Step] from the 'step template' [map], values for supported
   /// variables [configuration], and the list of supported named configurations.
-  static Step parse(Map map, Map<String, String> configuration,
+  static Step parse(Map map, Map<String, String?> configuration,
       List<Configuration> configurations) {
-    var arguments = (map["arguments"] as List ?? [])
+    var arguments = (map["arguments"] as List? ?? [])
         .map((argument) => _expandVariables(argument as String, configuration))
         .toList();
     var testedConfigurations = <Configuration>[];
-    var script = map["script"] as String ?? testScriptName;
-    var isTestRunner = map["testRunner"] as bool ?? false;
+    var script = map["script"] as String? ?? testScriptName;
+    var isTestRunner = map["testRunner"] as bool? ?? false;
     if (script == testScriptName || isTestRunner) {
       // TODO(karlklose): replace with argument parser that can handle all
       // arguments to test.py.
@@ -78,8 +78,8 @@
         script,
         arguments,
         <String, String>{...?map["environment"]},
-        map["fileset"] as String,
-        map["shards"] as int,
+        map["fileset"] as String?,
+        map["shards"] as int?,
         isTestRunner,
         testedConfigurations.isEmpty ? null : testedConfigurations.single);
   }
@@ -92,13 +92,13 @@
 /// the test matrix.
 class Builder {
   final String name;
-  final String description;
+  final String? description;
   final List<Step> steps;
-  final System system;
-  final Mode mode;
-  final Architecture arch;
-  final Sanitizer sanitizer;
-  final Runtime runtime;
+  final System? system;
+  final Mode? mode;
+  final Architecture? arch;
+  final Sanitizer? sanitizer;
+  final Runtime? runtime;
   final Set<Configuration> testedConfigurations;
 
   Builder(this.name, this.description, this.steps, this.system, this.mode,
@@ -111,7 +111,7 @@
   /// `${arch}`, and `${runtime}. The values for these variables are inferred
   /// from the builder's name.
   static Builder parse(String builderName, List<Map> steps,
-      List<Configuration> configurations, String description) {
+      List<Configuration> configurations, String? description) {
     var builderParts = builderName.split("-");
     var systemName = _findPart(builderParts, System.names, 'linux');
     var modeName = _findPart(builderParts, Mode.names, 'release');
@@ -146,7 +146,7 @@
 
 /// Tries to replace a variable named [variableName] with [value] and throws
 /// and exception if the variable is used but `value == null`.
-String _tryReplace(String string, String variableName, String value) {
+String _tryReplace(String string, String variableName, String? value) {
   var variable = "\${$variableName}";
   if (string.contains(variable)) {
     if (value == null) {
@@ -160,7 +160,7 @@
 
 /// Replace the use of supported variable names with the their value given
 /// in [values] and throws an exception if an unsupported variable name is used.
-String _expandVariables(String string, Map<String, String> values) {
+String _expandVariables(String string, Map<String, String?> values) {
   for (var variable in ["system", "mode", "arch", "sanitizer", "runtime"]) {
     string = _tryReplace(string, variable, values[variable]);
   }
@@ -171,17 +171,19 @@
   return steps
       .where((step) => step.isTestStep)
       .map((step) => step.testedConfiguration)
+      .whereType<Configuration>()
       .toSet();
 }
 
-T _findIfNotNull<T>(T Function(String) find, String name) {
+T? _findIfNotNull<T>(T Function(String) find, String? name) {
   return name != null ? find(name) : null;
 }
 
-String _findPart(List<String> builderParts, List<String> parts,
-    [String fallback]) {
-  return builderParts.firstWhere((part) => parts.contains(part),
-      orElse: () => fallback);
+String? _findPart(List<String> builderParts, List<String> parts,
+    [String? fallback]) {
+  return builderParts
+      .cast<String?>()
+      .firstWhere((part) => parts.contains(part), orElse: () => fallback);
 }
 
 List<Builder> parseBuilders(
@@ -189,7 +191,7 @@
   var builders = <Builder>[];
   var names = <String>{};
   for (var builderConfiguration in builderConfigurations) {
-    var meta = builderConfiguration["meta"] as Map ?? <String, String>{};
+    var meta = builderConfiguration["meta"] as Map? ?? <String, String>{};
     var builderNames = <String>[...?builderConfiguration["builders"]];
     var steps = <Map>[...?builderConfiguration["steps"]];
     for (var builderName in builderNames) {
@@ -197,7 +199,7 @@
         throw FormatException('Duplicate builder name: "$builderName"');
       }
       builders.add(Builder.parse(
-          builderName, steps, configurations, meta["description"] as String));
+          builderName, steps, configurations, meta["description"] as String?));
     }
   }
   return builders;
diff --git a/pkg/smith/lib/configuration.dart b/pkg/smith/lib/configuration.dart
index 0c4138a..5861fbf 100644
--- a/pkg/smith/lib/configuration.dart
+++ b/pkg/smith/lib/configuration.dart
@@ -117,10 +117,10 @@
     var words = name.split("-").toSet();
     var optionsCopy = Map.of(optionsJson);
 
-    T enumOption<T extends NamedEnum>(
+    T? enumOption<T extends NamedEnum>(
         String option, List<String> allowed, T Function(String) parse) {
       // Look up the value from the words in the name.
-      T fromName;
+      T? fromName;
       for (var value in allowed) {
         // Don't treat "none" as matchable since it's ambiguous as to whether
         // it refers to compiler or runtime.
@@ -137,7 +137,7 @@
       }
 
       // Look up the value from the options.
-      T fromOption;
+      T? fromOption;
       if (optionsCopy.containsKey(option)) {
         fromOption = parse(optionsCopy[option] as String);
         optionsCopy.remove(option);
@@ -157,7 +157,7 @@
       return fromName ?? fromOption;
     }
 
-    bool boolOption(String option) {
+    bool? boolOption(String option) {
       if (!optionsCopy.containsKey(option)) return null;
 
       var value = optionsCopy.remove(option);
@@ -166,10 +166,10 @@
         throw FormatException('Option "$option" had value "$value", which is '
             'not a bool.');
       }
-      return value as bool;
+      return value;
     }
 
-    int intOption(String option) {
+    int? intOption(String option) {
       if (!optionsCopy.containsKey(option)) return null;
 
       var value = optionsCopy.remove(option);
@@ -178,10 +178,10 @@
         throw FormatException('Option "$option" had value "$value", which is '
             'not an int.');
       }
-      return value as int;
+      return value;
     }
 
-    String stringOption(String option) {
+    String? stringOption(String option) {
       if (!optionsCopy.containsKey(option)) return null;
 
       var value = optionsCopy.remove(option);
@@ -190,10 +190,10 @@
         throw FormatException('Option "$option" had value "$value", which is '
             'not a string.');
       }
-      return value as String;
+      return value;
     }
 
-    List<String> stringListOption(String option) {
+    List<String>? stringListOption(String option) {
       if (!optionsCopy.containsKey(option)) return null;
 
       var value = optionsCopy.remove(option);
@@ -202,7 +202,7 @@
         throw FormatException('Option "$option" had value "$value", which is '
             'not a List.');
       }
-      return List<String>.from(value as List);
+      return List<String>.from(value);
     }
 
     // Extract options from the name and map.
@@ -340,27 +340,27 @@
 
   Configuration(this.name, this.architecture, this.compiler, this.mode,
       this.runtime, this.system,
-      {NnbdMode nnbdMode,
-      Sanitizer sanitizer,
-      String babel,
-      String builderTag,
-      List<String> genKernelOptions,
-      List<String> vmOptions,
-      List<String> dart2jsOptions,
-      List<String> experiments,
-      int timeout,
-      bool enableAsserts,
-      bool isChecked,
-      bool isCsp,
-      bool isHostChecked,
-      bool isMinified,
-      bool useAnalyzerCfe,
-      bool useAnalyzerFastaParser,
-      bool useElf,
-      bool useHotReload,
-      bool useHotReloadRollback,
-      bool useSdk,
-      bool useQemu})
+      {NnbdMode? nnbdMode,
+      Sanitizer? sanitizer,
+      String? babel,
+      String? builderTag,
+      List<String>? genKernelOptions,
+      List<String>? vmOptions,
+      List<String>? dart2jsOptions,
+      List<String>? experiments,
+      int? timeout,
+      bool? enableAsserts,
+      bool? isChecked,
+      bool? isCsp,
+      bool? isHostChecked,
+      bool? isMinified,
+      bool? useAnalyzerCfe,
+      bool? useAnalyzerFastaParser,
+      bool? useElf,
+      bool? useHotReload,
+      bool? useHotReloadRollback,
+      bool? useSdk,
+      bool? useQemu})
       : nnbdMode = nnbdMode ?? NnbdMode.legacy,
         sanitizer = sanitizer ?? Sanitizer.none,
         babel = babel ?? "",
diff --git a/pkg/smith/lib/test_matrix.dart b/pkg/smith/lib/test_matrix.dart
index 3d10b21..0af1ca8 100644
--- a/pkg/smith/lib/test_matrix.dart
+++ b/pkg/smith/lib/test_matrix.dart
@@ -22,7 +22,7 @@
 
   static TestMatrix fromJson(Map<String, dynamic> json) {
     var configurationsJson =
-        json["configurations"] as Map<String, dynamic> ?? <String, dynamic>{};
+        json["configurations"] as Map<String, dynamic>? ?? <String, dynamic>{};
 
     // Keep track of the configurations and which templates they were expanded
     // from.
@@ -60,8 +60,8 @@
     var testedOn = <Configuration, Builder>{};
     for (var builder in builders) {
       for (var configuration in builder.testedConfigurations) {
-        if (testedOn.containsKey(configuration)) {
-          var other = testedOn[configuration];
+        var other = testedOn[configuration];
+        if (other != null) {
           throw FormatException('Configuration "${configuration.name}" is '
               'tested on both "${builder.name}" and "${other.name}"');
         } else {
diff --git a/pkg/smith/pubspec.yaml b/pkg/smith/pubspec.yaml
index 551d7e5..de47131 100644
--- a/pkg/smith/pubspec.yaml
+++ b/pkg/smith/pubspec.yaml
@@ -3,7 +3,7 @@
 # This package is not intended for consumption on pub.dev. DO NOT publish.
 publish_to: none
 environment:
-  sdk: "^2.3.0"
+  sdk: "^2.12.0"
 dev_dependencies:
   expect:
     path: ../expect
diff --git a/pkg/smith/test/builder_test.dart b/pkg/smith/test/builder_test.dart
index 2f1c2be..d1c022e 100644
--- a/pkg/smith/test/builder_test.dart
+++ b/pkg/smith/test/builder_test.dart
@@ -29,7 +29,7 @@
         "arguments": ["-nfoo-x64-none-debug-d8-linux"]
       }, {}, configurations);
       expect(step.isTestStep, isTrue);
-      expect(step.testedConfiguration.name, "foo-x64-none-debug-d8-linux");
+      expect(step.testedConfiguration?.name, "foo-x64-none-debug-d8-linux");
     });
     test("custom test runner step", () {
       var step = Step.parse({
@@ -39,7 +39,7 @@
         "arguments": ["-nfoo-x64-none-debug-d8-linux"]
       }, {}, configurations);
       expect(step.isTestStep, isTrue);
-      expect(step.testedConfiguration.name, "foo-x64-none-debug-d8-linux");
+      expect(step.testedConfiguration?.name, "foo-x64-none-debug-d8-linux");
     });
     test("implicit test step", () {
       var step = Step.parse({
@@ -47,7 +47,7 @@
         "arguments": ["-nfoo-x64-none-debug-d8-linux"]
       }, {}, configurations);
       expect(step.isTestStep, isTrue);
-      expect(step.testedConfiguration.name, "foo-x64-none-debug-d8-linux");
+      expect(step.testedConfiguration?.name, "foo-x64-none-debug-d8-linux");
     });
     test("a step can only test one configuration", () {
       expectFormatError(
@@ -69,7 +69,7 @@
         "arguments": ["--named_configuration foo-x64-none-debug-d8-linux"]
       }, {}, configurations);
       expect(step.isTestStep, isTrue);
-      expect(step.testedConfiguration.name, "foo-x64-none-debug-d8-linux");
+      expect(step.testedConfiguration?.name, "foo-x64-none-debug-d8-linux");
     });
     test("a test step using multiple values for the argument", () {
       expectFormatError(
@@ -89,7 +89,7 @@
         "arguments": ["-nfoo-x64-none-debug-d8-linux", "--bar", "-b=az"]
       }, {}, configurations);
       expect(step.isTestStep, isTrue);
-      expect(step.testedConfiguration.name, "foo-x64-none-debug-d8-linux");
+      expect(step.testedConfiguration?.name, "foo-x64-none-debug-d8-linux");
     });
     test("in non-test steps, argument -n can have arbitrary values", () {
       var step = Step.parse({
@@ -229,6 +229,6 @@
     var step = builder.steps[0];
     expect(step.isTestStep, isTrue);
   }
-  expect(builders.map((b) => b.steps[0].testedConfiguration.name).toList(),
+  expect(builders.map((b) => b.steps[0].testedConfiguration!.name).toList(),
       equals(expectedConfigurations));
 }
diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h
index c6c8686..e2635e0 100644
--- a/runtime/bin/eventhandler.h
+++ b/runtime/bin/eventhandler.h
@@ -32,6 +32,7 @@
   kSetEventMaskCommand = 12,
   kListeningSocket = 16,
   kPipe = 17,
+  kSignalSocket = 18,
 };
 
 // clang-format off
@@ -54,6 +55,8 @@
      (data & ~(1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) == 0)
 #define IS_LISTENING_SOCKET(data)                                              \
     ((data & (1 << kListeningSocket)) != 0)  // NOLINT
+#define IS_SIGNAL_SOCKET(data)                                                 \
+    ((data & (1 << kSignalSocket)) != 0)  // NOLINT
 #define TOKEN_COUNT(data) (data & ((1 << kCloseCommand) - 1))
 // clang-format on
 
diff --git a/runtime/bin/eventhandler_android.cc b/runtime/bin/eventhandler_android.cc
index b6dfc17..65ec9eb 100644
--- a/runtime/bin/eventhandler_android.cc
+++ b/runtime/bin/eventhandler_android.cc
@@ -20,6 +20,7 @@
 #include "bin/dartutils.h"
 #include "bin/fdutils.h"
 #include "bin/lockers.h"
+#include "bin/process.h"
 #include "bin/socket.h"
 #include "bin/thread.h"
 #include "bin/utils.h"
@@ -212,6 +213,9 @@
       } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
         // Close the socket and free system resources and move on to next
         // message.
+        if (IS_SIGNAL_SOCKET(msg[i].data)) {
+          Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port());
+        }
         intptr_t old_mask = di->Mask();
         Dart_Port port = msg[i].dart_port;
         if (port != ILLEGAL_PORT) {
diff --git a/runtime/bin/eventhandler_linux.cc b/runtime/bin/eventhandler_linux.cc
index ac9deb5..fd8297c 100644
--- a/runtime/bin/eventhandler_linux.cc
+++ b/runtime/bin/eventhandler_linux.cc
@@ -21,6 +21,7 @@
 #include "bin/dartutils.h"
 #include "bin/fdutils.h"
 #include "bin/lockers.h"
+#include "bin/process.h"
 #include "bin/socket.h"
 #include "bin/thread.h"
 #include "platform/syslog.h"
@@ -221,6 +222,9 @@
       } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
         // Close the socket and free system resources and move on to next
         // message.
+        if (IS_SIGNAL_SOCKET(msg[i].data)) {
+          Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port());
+        }
         intptr_t old_mask = di->Mask();
         Dart_Port port = msg[i].dart_port;
         if (port != ILLEGAL_PORT) {
diff --git a/runtime/bin/eventhandler_macos.cc b/runtime/bin/eventhandler_macos.cc
index ee8f0fb..e425b87 100644
--- a/runtime/bin/eventhandler_macos.cc
+++ b/runtime/bin/eventhandler_macos.cc
@@ -19,6 +19,7 @@
 #include "bin/dartutils.h"
 #include "bin/fdutils.h"
 #include "bin/lockers.h"
+#include "bin/process.h"
 #include "bin/socket.h"
 #include "bin/thread.h"
 #include "bin/utils.h"
@@ -228,6 +229,9 @@
       } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
         // Close the socket and free system resources and move on to next
         // message.
+        if (IS_SIGNAL_SOCKET(msg[i].data)) {
+          Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port());
+        }
         intptr_t old_mask = di->Mask();
         Dart_Port port = msg[i].dart_port;
         if (port != ILLEGAL_PORT) {
diff --git a/runtime/bin/eventhandler_win.cc b/runtime/bin/eventhandler_win.cc
index abdba9a..2c254d8 100644
--- a/runtime/bin/eventhandler_win.cc
+++ b/runtime/bin/eventhandler_win.cc
@@ -17,6 +17,7 @@
 #include "bin/builtin.h"
 #include "bin/dartutils.h"
 #include "bin/lockers.h"
+#include "bin/process.h"
 #include "bin/socket.h"
 #include "bin/thread.h"
 #include "bin/utils.h"
@@ -1152,6 +1153,9 @@
         ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(handle);
         client_socket->Shutdown(SD_SEND);
       } else if (IS_COMMAND(msg->data, kCloseCommand)) {
+        if (IS_SIGNAL_SOCKET(msg->data)) {
+          Process::ClearSignalHandlerByFd(socket->fd(), socket->isolate_port());
+        }
         handle->SetPortAndMask(msg->dart_port, 0);
         handle->Close();
         socket->CloseFd();
diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc
index 1f39427c..c0cf579 100644
--- a/runtime/bin/socket.cc
+++ b/runtime/bin/socket.cc
@@ -1258,24 +1258,20 @@
 
 static void NormalSocketFinalizer(void* isolate_data, void* data) {
   Socket* socket = reinterpret_cast<Socket*>(data);
-  if (socket->fd() >= 0) {
-    const int64_t flags = 1 << kCloseCommand;
-    socket->Retain();
-    EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
-                                 socket->port(), flags);
-  }
-  socket->Release();
+  const int64_t flags = 1 << kCloseCommand;
+  socket->Retain();  // Bump reference till we send the message.
+  EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
+                               socket->port(), flags);
+  socket->Release();  // Release the reference we just added above.
 }
 
 static void ListeningSocketFinalizer(void* isolate_data, void* data) {
   Socket* socket = reinterpret_cast<Socket*>(data);
-  if (socket->fd() >= 0) {
-    const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand);
-    socket->Retain();
-    EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
-                                 socket->port(), flags);
-  }
-  socket->Release();
+  const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand);
+  socket->Retain();  // Bump reference till we send the message.
+  EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
+                               socket->port(), flags);
+  socket->Release();  // Release the reference we just added above.
 }
 
 static void StdioSocketFinalizer(void* isolate_data, void* data) {
@@ -1288,15 +1284,11 @@
 
 static void SignalSocketFinalizer(void* isolate_data, void* data) {
   Socket* socket = reinterpret_cast<Socket*>(data);
-  if (socket->fd() >= 0) {
-    Process::ClearSignalHandlerByFd(socket->fd(), socket->isolate_port());
-    const int64_t flags = 1 << kCloseCommand;
-    socket->Retain();
-    EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
-                                 socket->port(), flags);
-  }
-
-  socket->Release();
+  const int64_t flags = (1 << kSignalSocket) | (1 << kCloseCommand);
+  socket->Retain();  // Bump reference till we send the message.
+  EventHandler::SendFromNative(reinterpret_cast<intptr_t>(socket),
+                               socket->port(), flags);
+  socket->Release();  // Release the reference we just added above.
 }
 
 void Socket::ReuseSocketIdNativeField(Dart_Handle handle,
diff --git a/tools/VERSION b/tools/VERSION
index 3cfedd8..bd31ebf 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 11
+PRERELEASE 12
 PRERELEASE_PATCH 0
\ No newline at end of file