Version 2.19.0-277.0.dev

Merge ce7c036db217b43014a05dcbdc377e3bcb7743ae into dev
diff --git a/pkg/compiler/test/analyses/api_allowed.json b/pkg/compiler/test/analyses/api_allowed.json
index 406e762..a307b90 100644
--- a/pkg/compiler/test/analyses/api_allowed.json
+++ b/pkg/compiler/test/analyses/api_allowed.json
@@ -28,8 +28,6 @@
     "Dynamic update to 'dart.io::_owner'.": 1
   },
   "org-dartlang-sdk:///lib/io/secure_socket.dart": {
-    "Dynamic invocation of '[]'.": 4,
-    "Dynamic invocation of 'dart.io::_detachRaw'.": 2,
     "Dynamic access of 'closedReadEventSent'.": 1,
     "Dynamic update to 'dart.io::_owner'.": 1
   },
diff --git a/pkg/dart2wasm/lib/transformers.dart b/pkg/dart2wasm/lib/transformers.dart
index 41bc19b..4c6784d 100644
--- a/pkg/dart2wasm/lib/transformers.dart
+++ b/pkg/dart2wasm/lib/transformers.dart
@@ -134,10 +134,16 @@
     //
     //  {
     //    final StreamIterator<T> #forIterator = StreamIterator(<stream>);
-    //    for (; await #forIterator.moveNext() ;) {
-    //        {var/final} T variable = await #forIterator.current;
+    //    bool #jumpSentinel = false;
+    //    try {
+    //      for (; jumpSentinel = await #forIterator.moveNext() ;) {
+    //        {var/final} T variable = #forIterator.current;
     //        ...
     //      }
+    //    } finally {
+    //      if (#jumpSentinel) {
+    //        await #forIterator.cancel();
+    //      }
     //    }
     //  }
 
@@ -188,6 +194,11 @@
         type: iteratorType)
       ..fileOffset = iterable.fileOffset;
 
+    // Only used when `isAsync` is true.
+    final jumpSentinel = VariableDeclaration("#jumpSentinel",
+        initializer: ConstantExpression(BoolConstant(false)),
+        type: InterfaceType(coreTypes.boolClass, Nullability.nonNullable));
+
     final condition = InstanceInvocation(InstanceAccessKind.Instance,
         VariableGet(iterator), Name('moveNext'), Arguments(const []),
         interfaceTarget: iteratorMoveNext,
@@ -200,14 +211,39 @@
           interfaceTarget: iteratorCurrent, resultType: elementType)
         ..fileOffset = stmt.bodyOffset);
 
-    final Block body = Block([variable, stmt.body])
-      ..fileOffset = stmt.fileOffset;
+    Block body = Block([variable, stmt.body])..fileOffset = stmt.fileOffset;
 
-    return Block([
-      iterator,
-      ForStatement(const [], isAsync ? AwaitExpression(condition) : condition,
-          const [], body)
-    ]).accept<TreeNode>(this);
+    Statement forStatement = ForStatement(
+        const [],
+        isAsync
+            ? VariableSet(jumpSentinel, AwaitExpression(condition))
+            : condition,
+        const [],
+        body);
+
+    // Wrap the body with a try / finally to cancel the stream on breaking out
+    // of the loop.
+    if (isAsync) {
+      forStatement = TryFinally(
+        Block([forStatement]),
+        Block([
+          IfStatement(
+              VariableGet(jumpSentinel),
+              ExpressionStatement(AwaitExpression(InstanceInvocation(
+                  InstanceAccessKind.Instance,
+                  VariableGet(iterator),
+                  Name('cancel'),
+                  Arguments(const []),
+                  interfaceTarget: coreTypes.streamIteratorCancel,
+                  functionType: coreTypes.streamIteratorCancel.getterType
+                      as FunctionType))),
+              null)
+        ]),
+      );
+    }
+
+    return Block([iterator, if (isAsync) jumpSentinel, forStatement])
+        .accept<TreeNode>(this);
   }
 
   @override
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 3196397..24b17b5 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -2153,7 +2153,7 @@
   late _SocketStreamConsumer _consumer;
   late IOSink _sink;
   StreamSubscription? _subscription;
-  var _detachReady;
+  Completer<Object?>? _detachReady;
 
   _Socket(RawSocket raw) : _raw = raw {
     _controller
@@ -2273,10 +2273,11 @@
     return raw.remoteAddress;
   }
 
-  Future _detachRaw() {
-    _detachReady = new Completer();
+  Future<List<Object?>> _detachRaw() {
+    var completer = Completer<Object?>();
+    _detachReady = completer;
     _sink.close();
-    return _detachReady.future.then((_) {
+    return completer.future.then((_) {
       assert(_consumer.buffer == null);
       var raw = _raw;
       _raw = null;
@@ -2374,7 +2375,7 @@
 
   void _consumerDone() {
     if (_detachReady != null) {
-      _detachReady.complete(null);
+      _detachReady!.complete(null);
     } else {
       final raw = _raw;
       if (raw != null) {
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
index 30c4f5c..f0b7e3c 100644
--- a/sdk/lib/io/secure_socket.dart
+++ b/sdk/lib/io/secure_socket.dart
@@ -144,8 +144,7 @@
       bool onBadCertificate(X509Certificate certificate)?,
       void keyLog(String line)?,
       @Since("2.6") List<String>? supportedProtocols}) {
-    return ((socket as dynamic /*_Socket*/)._detachRaw() as Future)
-        .then<RawSecureSocket>((detachedRaw) {
+    return socket._detachRaw().then<RawSecureSocket>((detachedRaw) {
       return RawSecureSocket.secure(detachedRaw[0] as RawSocket,
           subscription: detachedRaw[1] as StreamSubscription<RawSocketEvent>?,
           host: host,
@@ -182,8 +181,7 @@
       bool requestClientCertificate = false,
       bool requireClientCertificate = false,
       List<String>? supportedProtocols}) {
-    return ((socket as dynamic /*_Socket*/)._detachRaw() as Future)
-        .then<RawSecureSocket>((detachedRaw) {
+    return socket._detachRaw().then<RawSecureSocket>((detachedRaw) {
       return RawSecureSocket.secureServer(detachedRaw[0] as RawSocket, context,
           subscription: detachedRaw[1] as StreamSubscription<RawSocketEvent>?,
           bufferedData: bufferedData,
diff --git a/sdk/lib/io/socket.dart b/sdk/lib/io/socket.dart
index 7087c65..b489198 100644
--- a/sdk/lib/io/socket.dart
+++ b/sdk/lib/io/socket.dart
@@ -773,6 +773,8 @@
   external static Future<ConnectionTask<Socket>> _startConnect(host, int port,
       {sourceAddress, int sourcePort = 0});
 
+  Future<List<Object?>> _detachRaw();
+
   /// Destroys the socket in both directions.
   ///
   /// Calling [destroy] will make the send a close event on the stream
diff --git a/tools/VERSION b/tools/VERSION
index 08d79d8..0600da2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 19
 PATCH 0
-PRERELEASE 276
+PRERELEASE 277
 PRERELEASE_PATCH 0