diff --git a/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect index ec6a7f6922c..e2189027e1f 100644 --- a/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect +++ b/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect @@ -28,19 +28,19 @@ static method main() → dynamic Extra constant evaluation status: -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> SymbolConstant(#catchError) -Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> ListConstant(const []) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> SymbolConstant(#test) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> SymbolConstant(#whenComplete) -Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> ListConstant(const []) -Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> MapConstant(const {}) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> SymbolConstant(#timeout) -Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> ListConstant(const []) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> SymbolConstant(#onTimeout) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:763:13 -> SymbolConstant(#then) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:763:13 -> SymbolConstant(#onError) -Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> SymbolConstant(#asStream) -Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> ListConstant(const []) -Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> ListConstant(const []) -Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> MapConstant(const {}) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> SymbolConstant(#catchError) +Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> ListConstant(const []) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> SymbolConstant(#test) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> SymbolConstant(#whenComplete) +Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> ListConstant(const []) +Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> MapConstant(const {}) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> SymbolConstant(#timeout) +Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> ListConstant(const []) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> SymbolConstant(#onTimeout) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:770:13 -> SymbolConstant(#then) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:770:13 -> SymbolConstant(#onError) +Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> SymbolConstant(#asStream) +Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> ListConstant(const []) +Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> ListConstant(const []) +Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> MapConstant(const {}) Extra constant evaluation: evaluated: 61, effectively constant: 15 diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart index f4da9cab159..ca05fc66a65 100644 --- a/sdk/lib/async/async.dart +++ b/sdk/lib/async/async.dart @@ -109,7 +109,6 @@ import "dart:_internal" CastStreamTransformer, checkNotNullable, EmptyIterator, - isNullFuture, IterableElementError, nullFuture, printToZone, diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart index 908e5e198c0..f83386d30a3 100644 --- a/sdk/lib/async/future.dart +++ b/sdk/lib/async/future.dart @@ -223,6 +223,13 @@ abstract class FutureOr { /// it's very clearly documented. @pragma("wasm:entry-point") abstract class Future { + /// A `Future` completed with `null`. + /// + /// Currently shared with `dart:internal`. + /// If that future can be removed, then change this back to + /// `_Future.zoneValue(null, _rootZone);` + static final _Future _nullFuture = nullFuture as _Future; + /// A `Future` completed with `false`. static final _Future _falseFuture = new _Future.zoneValue(false, _rootZone); diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart index bb942d9da02..038c3bb5513 100644 --- a/sdk/lib/async/stream.dart +++ b/sdk/lib/async/stream.dart @@ -515,7 +515,7 @@ abstract class Stream { controller ..onCancel = () { timer.cancel(); - return nullFuture; + return Future._nullFuture; } ..onPause = () { watch.stop(); diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart index 2d381c5f21e..2baf00836a8 100644 --- a/sdk/lib/async/stream_controller.dart +++ b/sdk/lib/async/stream_controller.dart @@ -588,10 +588,7 @@ abstract class _StreamController implements _StreamControllerBase { Future get done => _ensureDoneFuture(); Future _ensureDoneFuture() => - _doneFuture ?? - (_isCanceled - ? nullFuture as Future - : _doneFuture = _Future()); + _doneFuture ??= _isCanceled ? Future._nullFuture : _Future(); /// Send or enqueue a data event. void add(T value) { @@ -922,7 +919,7 @@ class _AddStreamState { var cancel = addSubscription.cancel(); if (cancel == null) { addStreamFuture._asyncComplete(null); - return nullFuture; + return Future._nullFuture; } return cancel.whenComplete(() { addStreamFuture._asyncComplete(null); diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart index 9065c46f298..b4fb5d8df18 100644 --- a/sdk/lib/async/stream_impl.dart +++ b/sdk/lib/async/stream_impl.dart @@ -197,7 +197,7 @@ class _BufferingStreamSubscription if (!_isCanceled) { _cancel(); } - return _cancelFuture ?? nullFuture; + return _cancelFuture ?? Future._nullFuture; } Future asFuture([E? futureValue]) { @@ -217,7 +217,7 @@ class _BufferingStreamSubscription }; _onError = (Object error, StackTrace stackTrace) { Future cancelFuture = cancel(); - if (!isNullFuture(Zone._current, cancelFuture)) { + if (!identical(cancelFuture, Future._nullFuture)) { cancelFuture.whenComplete(() { result._completeError(error, stackTrace); }); @@ -297,7 +297,7 @@ class _BufferingStreamSubscription // Hooks called when the input is paused, unpaused or canceled. // These must not throw. If overwritten to call user code, include suitable // try/catch wrapping and send any errors to - // [Zone._current.handleUncaughtError]. + // [_Zone.current.handleUncaughtError]. void _onPause() { assert(_isInputPaused); } @@ -352,6 +352,7 @@ class _BufferingStreamSubscription // future to finish we must not report the error. if (_isCanceled && !_waitsForCancel) return; _state |= _STATE_IN_CALLBACK; + // TODO(floitsch): this dynamic should be 'void'. var onError = _onError; if (onError is void Function(Object, StackTrace)) { _zone.runBinaryGuarded(onError, error, stackTrace); @@ -365,7 +366,8 @@ class _BufferingStreamSubscription _state |= _STATE_WAIT_FOR_CANCEL; _cancel(); var cancelFuture = _cancelFuture; - if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) { + if (cancelFuture != null && + !identical(cancelFuture, Future._nullFuture)) { cancelFuture.whenComplete(sendError); } else { sendError(); @@ -394,7 +396,7 @@ class _BufferingStreamSubscription _cancel(); _state |= _STATE_WAIT_FOR_CANCEL; var cancelFuture = _cancelFuture; - if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) { + if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) { cancelFuture.whenComplete(sendDone); } else { sendDone(); @@ -670,7 +672,7 @@ class _DoneStreamSubscription implements StreamSubscription { } } - Future cancel() => nullFuture; + Future cancel() => Future._nullFuture; Future asFuture([E? futureValue]) { E resultValue; @@ -817,7 +819,7 @@ class _BroadcastSubscriptionWrapper implements StreamSubscription { Future cancel() { _stream._cancelSubscription(); - return nullFuture; + return Future._nullFuture; } bool get isPaused { @@ -961,7 +963,7 @@ class _StreamIterator implements StreamIterator { } return subscription.cancel(); } - return nullFuture; + return Future._nullFuture; } void _onData(T data) { diff --git a/sdk/lib/async/stream_pipe.dart b/sdk/lib/async/stream_pipe.dart index 78ad6753fcf..cd707be13e4 100644 --- a/sdk/lib/async/stream_pipe.dart +++ b/sdk/lib/async/stream_pipe.dart @@ -26,7 +26,7 @@ _runUserCode(T userCode(), onSuccess(T value), void _cancelAndError(StreamSubscription subscription, _Future future, Object error, StackTrace stackTrace) { var cancelFuture = subscription.cancel(); - if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) { + if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) { cancelFuture.whenComplete(() => future._completeError(error, stackTrace)); } else { future._completeError(error, stackTrace); @@ -55,7 +55,7 @@ void Function(Object error, StackTrace stackTrace) _cancelAndErrorClosure( before completing with a value. */ void _cancelAndValue(StreamSubscription subscription, _Future future, value) { var cancelFuture = subscription.cancel(); - if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) { + if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) { cancelFuture.whenComplete(() => future._complete(value)); } else { future._complete(value); diff --git a/sdk/lib/internal/internal.dart b/sdk/lib/internal/internal.dart index 92f7a343484..ff3d890b188 100644 --- a/sdk/lib/internal/internal.dart +++ b/sdk/lib/internal/internal.dart @@ -136,7 +136,7 @@ int parseHexByte(String source, int index) { return digit1 * 16 + digit2 - (digit2 & 256); } -/// A reusable `null`-valued future per zone used by `dart:async`. +/// A reusable `null`-valued future used by `dart:async`. /// /// **DO NOT USE.** /// @@ -156,14 +156,7 @@ int parseHexByte(String source, int index) { /// This future will be removed again if we can ever do so. /// Do not use it for anything other than preserving timing /// during the null safety migration. -Future get nullFuture => - _nullFutures[Zone.current] ??= Future.value(null); - -/// Whether [future] is the null future of the current zone. -bool isNullFuture(Zone zone, Future future) => - identical(_nullFutures[zone], future); - -final Expando> _nullFutures = Expando>(); +final Future nullFuture = Zone.root.run(() => Future.value(null)); /// A default hash function used by the platform in various places. /// diff --git a/tests/lib/async/null_future_zone_test.dart b/tests/lib/async/null_future_zone_test.dart index c74554b0ca4..9a266bee8a1 100644 --- a/tests/lib/async/null_future_zone_test.dart +++ b/tests/lib/async/null_future_zone_test.dart @@ -13,33 +13,21 @@ main() { Expect.isFalse(await it.moveNext()); late Future nullFuture; - - bool nullFutureZoneUsed = false; - runZoned(() { - // Known code that exposes the special "nullFuture". - nullFuture = (new StreamController()..stream.listen(null).cancel()).done; - }, zoneSpecification: new ZoneSpecification(scheduleMicrotask: - (Zone self, ZoneDelegate parent, Zone zone, void f()) { - Expect.identical(zone, self); - nullFutureZoneUsed = true; - parent.scheduleMicrotask(zone, f); - })); - - nullFuture.then((value) { - Expect.isNull(value); - Expect.isTrue(nullFutureZoneUsed); - asyncEnd(); - }); - late Future falseFuture; runZoned(() { + nullFuture = (new StreamController()..stream.listen(null).cancel()).done; falseFuture = it.moveNext(); }, zoneSpecification: new ZoneSpecification(scheduleMicrotask: (Zone self, ZoneDelegate parent, Zone zone, void f()) { Expect.fail("Should not be called"); })); + nullFuture.then((value) { + Expect.isNull(value); + asyncEnd(); + }); + falseFuture.then((value) { Expect.isFalse(value); asyncEnd(); diff --git a/tests/lib_2/async/null_future_zone_test.dart b/tests/lib_2/async/null_future_zone_test.dart index 77cb1b7fcbf..33229160396 100644 --- a/tests/lib_2/async/null_future_zone_test.dart +++ b/tests/lib_2/async/null_future_zone_test.dart @@ -15,32 +15,21 @@ main() { Expect.isFalse(await it.moveNext()); Future nullFuture; - - bool nullFutureZoneUsed = false; - runZoned(() { - nullFuture = (new StreamController()..stream.listen(null).cancel()).done; - }, zoneSpecification: new ZoneSpecification(scheduleMicrotask: - (Zone self, ZoneDelegate parent, Zone zone, void f()) { - Expect.identical(zone, self); - nullFutureZoneUsed = true; - parent.scheduleMicrotask(zone, f); - })); - - nullFuture.then((value) { - Expect.isNull(value); - Expect.isTrue(nullFutureZoneUsed); - asyncEnd(); - }); - Future falseFuture; runZoned(() { + nullFuture = (new StreamController()..stream.listen(null).cancel()).done; falseFuture = it.moveNext(); }, zoneSpecification: new ZoneSpecification(scheduleMicrotask: (Zone self, ZoneDelegate parent, Zone zone, void f()) { Expect.fail("Should not be called"); })); + nullFuture.then((value) { + Expect.isNull(value); + asyncEnd(); + }); + falseFuture.then((value) { Expect.isFalse(value); asyncEnd();