Skip to content

Commit

Permalink
Don't allow completing a _Future with itself.
Browse files Browse the repository at this point in the history
This never worked. It was silently accepted, at least if the future had no listeners, then any later attempt to use the future would cause a stack overflow or other impossible results.

Moves some `_Future._complete` call out of try-catch.
The `_complete` shouldn't throw (but before this fix it could).
Moving them out of the `try`/`catch` makes such errors be reported
as unhandled, instead of catching them and trying to complete the same
future again with an error, when it's possibly in an inconsistent state.

Fixes #43662.
Based on #43662 (comment)

CoreLibraryReviewExempt: No response.
Bug: http://dartbug.com/43662
Change-Id: I96a4f01bcd5b6cee93bba267299852569a9b905c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/363060
Commit-Queue: Lasse Nielsen <lrn@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
  • Loading branch information
lrhn authored and Commit Queue committed May 14, 2024
1 parent e4c1985 commit b06a34f
Show file tree
Hide file tree
Showing 51 changed files with 237 additions and 103 deletions.
22 changes: 16 additions & 6 deletions sdk/lib/async/future.dart
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,14 @@ abstract interface class Future<T> {
factory Future(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
Timer.run(() {
FutureOr<T> computationResult;
try {
result._complete(computation());
computationResult = computation();
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
return;
}
result._complete(computationResult);
});
return result;
}
Expand All @@ -277,11 +280,14 @@ abstract interface class Future<T> {
factory Future.microtask(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
scheduleMicrotask(() {
FutureOr<T> computationResult;
try {
result._complete(computation());
computationResult = computation();
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
return;
}
result._complete(computationResult);
});
return result;
}
Expand All @@ -302,9 +308,9 @@ abstract interface class Future<T> {
/// final result = await Future<int>.sync(() => 12);
/// ```
factory Future.sync(FutureOr<T> computation()) {
FutureOr<T> result;
try {
var result = computation();
return result is Future<T> ? result : _Future<T>.value(result);
result = computation();
} catch (error, stackTrace) {
var future = new _Future<T>();
AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
Expand All @@ -315,6 +321,7 @@ abstract interface class Future<T> {
}
return future;
}
return result is Future<T> ? result : _Future<T>.value(result);
}

/// Creates a future completed with [value].
Expand Down Expand Up @@ -414,16 +421,19 @@ abstract interface class Future<T> {
throw ArgumentError.value(
null, "computation", "The type parameter is not nullable");
}
_Future<T> result = new _Future<T>();
_Future<T> result = _Future<T>();
new Timer(duration, () {
if (computation == null) {
result._complete(null as T);
} else {
FutureOr<T> computationResult;
try {
result._complete(computation());
computationResult = computation();
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
return;
}
result._complete(computationResult);
}
});
return result;
Expand Down
16 changes: 15 additions & 1 deletion sdk/lib/async/future_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ class _Future<T> implements Future<T> {
Stream<T> asStream() => new Stream<T>.fromFuture(this);

void _setPendingComplete() {
assert(_mayComplete); // Aka _statIncomplete
assert(_mayComplete); // Aka. _stateIncomplete
_state ^= _stateIncomplete ^ _statePendingComplete;
}

Expand Down Expand Up @@ -577,6 +577,13 @@ class _Future<T> implements Future<T> {
while (source._isChained) {
source = source._chainSource;
}
if (identical(source, target)) {
target._asyncCompleteError(
ArgumentError.value(
source, null, "Cannot complete a future with itself"),
StackTrace.current);
return;
}
source._state |= target._state & _stateIgnoreError;
if (source._isComplete) {
_FutureListener? listeners = target._removeListeners();
Expand All @@ -600,6 +607,13 @@ class _Future<T> implements Future<T> {
while (source._isChained) {
source = source._chainSource;
}
if (identical(source, target)) {
target._asyncCompleteError(
ArgumentError.value(
source, null, "Cannot complete a future with itself"),
StackTrace.current);
return;
}
if (!source._isComplete) {
// Chain immediately if the source is not complete.
// This won't call any listeners.
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors11_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors12_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors13_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors14_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors15_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors16_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors17_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors18_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors19_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors20_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors21_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors22_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors23_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors24_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors25_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors26_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors27_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors28_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors2_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/async/catch_errors3_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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.

import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import 'dart:async';

import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';

import 'catch_errors.dart';

main() {
Expand Down

0 comments on commit b06a34f

Please sign in to comment.