Reland "Fix bug in Completer.complete."

This reverts commit 212d5e3a31e82f156ba1e9996ff2cf70d959582d.
This reapplies commit 3688a4723c3ea3c211003fbc899571d6ea4c8648.

CoreLibraryReviewExempt: Refactoring and relanding.
Change-Id: I9b52ef4073bfff50699919f8a5b85e5ff66d0c3b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/258929
Reviewed-by: Nate Bosch <nbosch@google.com>
Commit-Queue: Nate Bosch <nbosch@google.com>
Auto-Submit: Lasse Nielsen <lrn@google.com>
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart
index 553de50..997e724 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart
@@ -98,7 +98,11 @@
         // instead treat it as a completed value.
         if (value is Future) {
           if (value is _Future) {
-            _Future._chainCoreFuture(value, asyncFuture);
+            if (isRunningAsEvent) {
+              _Future._chainCoreFutureSync(value, asyncFuture);
+            } else {
+              _Future._chainCoreFutureAsync(value, asyncFuture);
+            }
           } else {
             asyncFuture._chainForeignFuture(value);
           }
@@ -108,7 +112,7 @@
           asyncFuture._asyncComplete(JS('', '#', value));
         }
       } else {
-        _Future._chainCoreFuture(onAwait(value), asyncFuture);
+        _Future._chainCoreFutureSync(onAwait(value), asyncFuture);
       }
     } catch (e, s) {
       if (isRunningAsEvent) {
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 812fff4..b92c5b0 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -7,6 +7,7 @@
 abstract class _Completer<T> implements Completer<T> {
   final _Future<T> future = new _Future<T>();
 
+  // Overridden by either a synchronous or asynchronous implementation.
   void complete([FutureOr<T>? value]);
 
   void completeError(Object error, [StackTrace? stackTrace]) {
@@ -23,6 +24,7 @@
     _completeError(error, stackTrace);
   }
 
+  // Overridden by either a synchronous or asynchronous implementation.
   void _completeError(Object error, StackTrace stackTrace);
 
   // The future's _isComplete doesn't take into account pending completions.
@@ -30,6 +32,7 @@
   bool get isCompleted => !future._mayComplete;
 }
 
+/// Completer which completes future asynchronously.
 class _AsyncCompleter<T> extends _Completer<T> {
   void complete([FutureOr<T>? value]) {
     if (!future._mayComplete) throw new StateError("Future already completed");
@@ -41,6 +44,9 @@
   }
 }
 
+/// Completer which completes future synchronously.
+///
+/// Created by [Completer.sync]. Use with caution.
 class _SyncCompleter<T> extends _Completer<T> {
   void complete([FutureOr<T>? value]) {
     if (!future._mayComplete) throw new StateError("Future already completed");
@@ -267,6 +273,7 @@
   // This constructor is used by async/await.
   _Future() : _zone = Zone._current;
 
+  // Constructor used by [Future.value].
   _Future.immediate(FutureOr<T> result) : _zone = Zone._current {
     _asyncComplete(result);
   }
@@ -505,10 +512,13 @@
     return prev;
   }
 
-  // Take the value (when completed) of source and complete this future with that
-  // value (or error). This function could chain all Futures, but is slower
-  // for _Future than _chainCoreFuture, so you must use _chainCoreFuture
-  // in that case.
+  /// Completes this future with the result of [source].
+  ///
+  /// The [source] future should not be a [_Future], use
+  /// [_chainCoreFutureSync] for those.
+  ///
+  /// Since [source] is an unknown [Future], it's interacted with
+  /// through [Future.then], which is required to be asynchronous.
   void _chainForeignFuture(Future source) {
     assert(!_isComplete);
     assert(source is! _Future);
@@ -539,9 +549,13 @@
     }
   }
 
-  // Take the value (when completed) of source and complete target with that
-  // value (or error). This function expects that source is a _Future.
-  static void _chainCoreFuture(_Future source, _Future target) {
+  /// Synchronously completes a target future with another, source, future.
+  ///
+  /// If the source future is already completed, its result is synchronously
+  /// propagated to the target future's listeners.
+  /// If the source future is not completed, the target future is made
+  /// to listen for its completion.
+  static void _chainCoreFutureSync(_Future source, _Future target) {
     assert(target._mayAddListener); // Not completed, not already chained.
     while (source._isChained) {
       source = source._chainSource;
@@ -557,11 +571,51 @@
     }
   }
 
+  /// Asynchronously completes a [target] future with a [source] future.
+  ///
+  /// If the [source] future is already completed, its result is
+  /// asynchronously propagated to the [target] future's listeners.
+  /// If the [source] future is not completed, the [target] future is made
+  /// to listen for its completion.
+  static void _chainCoreFutureAsync(_Future source, _Future target) {
+    assert(target._mayAddListener); // Not completed, not already chained.
+    while (source._isChained) {
+      source = source._chainSource;
+    }
+    if (!source._isComplete) {
+      // Chain immediately if the source is not complete.
+      // This won't call any listeners.
+      _FutureListener? listeners = target._resultOrListeners;
+      target._setChained(source);
+      source._prependListeners(listeners);
+      return;
+    }
+
+    // Complete a value synchronously, if no-one is listening.
+    // This won't call any listeners.
+    if (!source._hasError && target._resultOrListeners == null) {
+      target._cloneResult(source);
+      return;
+    }
+
+    // Otherwise delay the chaining to avoid any synchronous callbacks.
+    target._setPendingComplete();
+    target._zone.scheduleMicrotask(() {
+      _chainCoreFutureSync(source, target);
+    });
+  }
+
+  /// Synchronously completes this future with [value].
+  ///
+  /// If [value] is a value or an already completed [_Future],
+  /// the result is immediately used to complete this future.
+  /// If [value] is an incomplete future, this future will wait for
+  /// it to complete, then use the result.
   void _complete(FutureOr<T> value) {
     assert(!_isComplete);
     if (value is Future<T>) {
       if (value is _Future<T>) {
-        _chainCoreFuture(value, this);
+        _chainCoreFutureSync(value, this);
       } else {
         _chainForeignFuture(value);
       }
@@ -592,8 +646,9 @@
     _propagateToListeners(this, listeners);
   }
 
+  // Completes future in a later microtask.
   void _asyncComplete(FutureOr<T> value) {
-    assert(!_isComplete);
+    assert(!_isComplete); // Allows both pending complete and incomplete.
     // Two corner cases if the value is a future:
     //   1. the future is already completed and an error.
     //   2. the future is not yet completed but might become an error.
@@ -658,17 +713,15 @@
     });
   }
 
+  /// Asynchronously completes a future with another future.
+  ///
+  /// Even if [value] is already completed, it won't synchronously
+  /// complete this completer's future.
   void _chainFuture(Future<T> value) {
+    assert(_mayComplete);
     if (value is _Future<T>) {
-      if (value._hasError) {
-        // Delay completion to allow the user to register callbacks.
-        _setPendingComplete();
-        _zone.scheduleMicrotask(() {
-          _chainCoreFuture(value, this);
-        });
-      } else {
-        _chainCoreFuture(value, this);
-      }
+      // Chain ensuring that we don't complete synchronously.
+      _chainCoreFutureAsync(value, this);
       return;
     }
     // Just listen on the foreign future. This guarantees an async delay.
@@ -836,7 +889,7 @@
               source = chainSource;
               continue;
             } else {
-              _chainCoreFuture(chainSource, result);
+              _chainCoreFutureSync(chainSource, result);
             }
           } else {
             result._chainForeignFuture(chainSource);
diff --git a/tests/lib/async/future_regression_48493_test.dart b/tests/lib/async/future_regression_48493_test.dart
new file mode 100644
index 0000000..b452da5
--- /dev/null
+++ b/tests/lib/async/future_regression_48493_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, 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.
+
+// Regression test for http://dartbug.com/48493
+//
+// The `Completer.complete` method, given an already completed `_Future`
+// with a value result, will complete the `Completer.future` synchronously.
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async';
+
+void main() async {
+  asyncStart();
+
+  var completer = Completer<int>(); // Not synchronous.
+  var future = Future<int>.value(4);
+  await future; // Ensure future completed.
+  bool thenRun = false;
+  completer.future.then((_) {
+    thenRun = true;
+  });
+  Expect.isFalse(thenRun, "Sanity check");
+  completer.complete(future);
+  Expect.isFalse(thenRun, "Ran early"); // Bug would cause this to fail.
+  await Future(() {}); // Await timer, all microtasks completed.
+  Expect.isTrue(thenRun, "Did not run as microtask");
+
+  asyncEnd();
+}
diff --git a/tests/lib_2/async/future_regression_48493_test.dart b/tests/lib_2/async/future_regression_48493_test.dart
new file mode 100644
index 0000000..e167bf3
--- /dev/null
+++ b/tests/lib_2/async/future_regression_48493_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, 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.
+
+// @dart = 2.9
+
+// Regression test for http://dartbug.com/48493
+//
+// The `Completer.complete` method, given an already completed `_Future`
+// with a value result, will complete the `Completer.future` synchronously.
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async';
+
+void main() async {
+  asyncStart();
+
+  var completer = Completer<int>(); // Not synchronous.
+  var future = Future<int>.value(4);
+  await future; // Ensure future completed.
+  bool thenRun = false;
+  completer.future.then((_) {
+    thenRun = true;
+  });
+  Expect.isFalse(thenRun, "Sanity check");
+  completer.complete(future);
+  Expect.isFalse(thenRun, "Ran early"); // Bug would cause this to fail.
+  await Future(() {}); // Await timer, all microtasks completed.
+  Expect.isTrue(thenRun, "Did not run as microtask");
+
+  asyncEnd();
+}