Version 2.14.0-190.0.dev

Merge commit '80749188ca1670ba5759730fd1f533413318b891' into 'dev'
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index e06b2fa..eff5ead 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1653,7 +1653,8 @@
   for (intptr_t i = 0; i < types.Length(); i++) {
     type ^= types.At(i);
     bool present = types_table.Insert(type);
-    ASSERT(!present);
+    // Two recursive types with different topology (and hashes) may be equal.
+    ASSERT(!present || type.IsRecursive());
   }
   object_store->set_canonical_types(types_table.Release());
 
@@ -1673,7 +1674,8 @@
   for (intptr_t i = 0; i < function_types.Length(); i++) {
     function_type ^= function_types.At(i);
     bool present = function_types_table.Insert(function_type);
-    ASSERT(!present);
+    // Two recursive types with different topology (and hashes) may be equal.
+    ASSERT(!present || function_type.IsRecursive());
   }
   object_store->set_canonical_function_types(function_types_table.Release());
 
@@ -1693,7 +1695,8 @@
   for (intptr_t i = 0; i < typeparams.Length(); i++) {
     typeparam ^= typeparams.At(i);
     bool present = typeparams_table.Insert(typeparam);
-    ASSERT(!present);
+    // Two recursive types with different topology (and hashes) may be equal.
+    ASSERT(!present || typeparam.IsRecursive());
   }
   object_store->set_canonical_type_parameters(typeparams_table.Release());
 
@@ -1717,7 +1720,8 @@
   for (intptr_t i = 0; i < typeargs.Length(); i++) {
     typearg ^= typeargs.At(i);
     bool present = typeargs_table.Insert(typearg);
-    ASSERT(!present);
+    // Two recursive types with different topology (and hashes) may be equal.
+    ASSERT(!present || typearg.IsRecursive());
   }
   object_store->set_canonical_type_arguments(typeargs_table.Release());
 }
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 9f9eb8b..243f69a 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -533,8 +533,14 @@
         element ^= ptr;
         intptr_t entry = -1;
         const bool present = table.FindKeyOrDeletedOrUnused(element, &entry);
-        ASSERT(!present);
-        table.InsertKey(entry, element);
+        if (!present) {
+          table.InsertKey(entry, element);
+        } else {
+          // Two recursive types with different topology (and hashes)
+          // may be equal.
+          ASSERT(element.IsRecursive());
+          objects_[num_occupied++] = ptr;
+        }
       } else {
         objects_[num_occupied++] = ptr;
       }
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 6c7b768..2078dfa 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -2796,8 +2796,8 @@
       _HttpProfileData? profileData) {
     Iterator<_Proxy> proxies = proxyConf.proxies.iterator;
 
-    Future<_ConnectionInfo> connect(error) {
-      if (!proxies.moveNext()) return new Future.error(error);
+    Future<_ConnectionInfo> connect(error, stackTrace) {
+      if (!proxies.moveNext()) return new Future.error(error, stackTrace);
       _Proxy proxy = proxies.current;
       String host = proxy.isDirect ? uriHost : proxy.host!;
       int port = proxy.isDirect ? uriPort : proxy.port!;
@@ -2807,7 +2807,7 @@
           .catchError(connect);
     }
 
-    return connect(new HttpException("No proxies given"));
+    return connect(new HttpException("No proxies given"), StackTrace.current);
   }
 
   _SiteCredentials? _findCredentials(Uri url, [_AuthenticationScheme? scheme]) {
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 6c3a77a..f04ffbb 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -673,10 +673,13 @@
             "Must be a string or native InternetAddress");
       }
     }
+
+    final stackTrace = StackTrace.current;
+
     return new Future.value(host).then<ConnectionTask<_NativeSocket>>((host) {
       if (host is _InternetAddress) {
-        return tryConnectToResolvedAddresses(
-            host, port, source, Stream.value(<_InternetAddress>[host]));
+        return tryConnectToResolvedAddresses(host, port, source,
+            Stream.value(<_InternetAddress>[host]), stackTrace);
       }
       final hostname = host as String;
       final staggeredLookupOverride = bool.fromEnvironment(
@@ -692,7 +695,8 @@
               ? staggeredLookup(hostname)
               : lookupAsStream(hostname);
 
-      return tryConnectToResolvedAddresses(host, port, source, stream);
+      return tryConnectToResolvedAddresses(
+          host, port, source, stream, stackTrace);
     });
   }
 
@@ -700,7 +704,8 @@
       dynamic host,
       int port,
       _InternetAddress? source,
-      Stream<List<InternetAddress>> addresses) {
+      Stream<List<InternetAddress>> addresses,
+      StackTrace callerStackTrace) {
     // Completer for result.
     final result = new Completer<_NativeSocket>();
     // Error, set if an error occurs.
@@ -780,7 +785,7 @@
         assert(error != null);
         if (!result.isCompleted) {
           // Might be already completed via onCancel
-          result.completeError(error);
+          result.completeError(error, callerStackTrace);
         }
         return;
       }
@@ -884,7 +889,7 @@
       if (!result.isCompleted) {
         error ??= createError(
             null, "Connection attempt cancelled, host: ${host}, port: ${port}");
-        result.completeError(error);
+        result.completeError(error, callerStackTrace);
       }
     }
 
diff --git a/tests/standalone/io/socket_connect_stacktrace_test.dart b/tests/standalone/io/socket_connect_stacktrace_test.dart
new file mode 100644
index 0000000..944bc4f
--- /dev/null
+++ b/tests/standalone/io/socket_connect_stacktrace_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Tests stack trace on socket exceptions.
+
+import "dart:async";
+import "dart:io";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+import 'dart:io';
+
+Future<void> main() async {
+  asyncStart();
+
+  // Test stacktrace when lookup fails
+  try {
+    await WebSocket.connect('ws://localhost.tld:0/ws');
+  } catch (err, stackTrace) {
+    Expect.contains("main ", stackTrace.toString());
+  }
+
+  // Test stacktrace when connection fails
+  try {
+    await WebSocket.connect('ws://localhost:0/ws');
+  } catch (err, stackTrace) {
+    Expect.contains("main ", stackTrace.toString());
+  }
+  asyncEnd();
+}
diff --git a/tests/standalone_2/io/socket_connect_stacktrace_test.dart b/tests/standalone_2/io/socket_connect_stacktrace_test.dart
new file mode 100644
index 0000000..944bc4f
--- /dev/null
+++ b/tests/standalone_2/io/socket_connect_stacktrace_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Tests stack trace on socket exceptions.
+
+import "dart:async";
+import "dart:io";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+import 'dart:io';
+
+Future<void> main() async {
+  asyncStart();
+
+  // Test stacktrace when lookup fails
+  try {
+    await WebSocket.connect('ws://localhost.tld:0/ws');
+  } catch (err, stackTrace) {
+    Expect.contains("main ", stackTrace.toString());
+  }
+
+  // Test stacktrace when connection fails
+  try {
+    await WebSocket.connect('ws://localhost:0/ws');
+  } catch (err, stackTrace) {
+    Expect.contains("main ", stackTrace.toString());
+  }
+  asyncEnd();
+}
diff --git a/tools/VERSION b/tools/VERSION
index 018052a..a229bf4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 189
+PRERELEASE 190
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/dart_sdk.py b/tools/bots/dart_sdk.py
index 4625472..233ff24 100755
--- a/tools/bots/dart_sdk.py
+++ b/tools/bots/dart_sdk.py
@@ -24,7 +24,8 @@
     if BUILD_OS == 'linux':
         return ['ia32', 'x64', 'arm', 'arm64']
     elif BUILD_OS == 'macos':
-        return [BUILD_ARCHITECTURE]
+        arch = 'arm64' if 'arm64' == os.uname().machine else 'x64'
+        return [arch]
     else:
         return ['ia32', 'x64']
 
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 6058679..d310455 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -314,14 +314,17 @@
       "xcodebuild/DebugSIMARM64C/",
       "xcodebuild/DebugX64/",
       "xcodebuild/DebugX64C/",
+      "xcodebuild/DebugXARM64/",
       "xcodebuild/ProductX64/",
       "xcodebuild/ProductX64C/",
+      "xcodebuild/ProductXARM64/",
       "xcodebuild/ReleaseIA32/",
       "xcodebuild/ReleaseSIMARM/",
       "xcodebuild/ReleaseSIMARM64/",
       "xcodebuild/ReleaseSIMARM64C/",
       "xcodebuild/ReleaseX64/",
       "xcodebuild/ReleaseX64C/",
+      "xcodebuild/ReleaseXARM64/",
       "pkg/",
       "samples/",
       "samples_2/",