Skip to content

Commit 9200023

Browse files
lrhnCommit Queue
authored andcommitted
Use AsyncError more in future error handling.
Avoids some cases of calling `Zone.errorCallback` and getting an `AsyncError`, then unpacking it again to separate errors and stack trace objects. CoreLibraryReviewExempt: No API changes, no platform-specific code change. Change-Id: I903dbfa6da74cfdf8f00e765e48311cc0cb40cac Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405022 Commit-Queue: Lasse Nielsen <[email protected]> Reviewed-by: Nate Bosch <[email protected]>
1 parent 7566327 commit 9200023

File tree

5 files changed

+123
-133
lines changed

5 files changed

+123
-133
lines changed

sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ _async<T>(Function() initGenerator) {
127127
if (isRunningAsEvent) {
128128
_completeWithErrorCallback(asyncFuture, e, s);
129129
} else {
130-
_asyncCompleteWithErrorCallback(asyncFuture, e, s);
130+
asyncFuture._asyncCompleteErrorObject(_interceptCaughtError(e, s));
131131
}
132132
}
133133
}

sdk/lib/async/future.dart

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,8 @@ abstract interface class Future<T> {
314314
try {
315315
result = computation();
316316
} catch (error, stackTrace) {
317-
var future = new _Future<T>();
318-
_asyncCompleteWithErrorCallback(future, error, stackTrace);
319-
return future;
317+
return _Future<T>()
318+
.._asyncCompleteErrorObject(_interceptCaughtError(error, stackTrace));
320319
}
321320
return result is Future<T> ? result : _Future<T>.value(result);
322321
}
@@ -369,10 +368,8 @@ abstract interface class Future<T> {
369368
///
370369
/// final error = await getFuture(); // Throws.
371370
/// ```
372-
factory Future.error(Object error, [StackTrace? stackTrace]) {
373-
AsyncError(:error, :stackTrace) = _interceptUserError(error, stackTrace);
374-
return new _Future<T>.immediateError(error, stackTrace);
375-
}
371+
factory Future.error(Object error, [StackTrace? stackTrace]) =>
372+
_Future<T>.immediateError(_interceptUserError(error, stackTrace));
376373

377374
/// Creates a future that runs its computation after a delay.
378375
///
@@ -561,25 +558,21 @@ abstract interface class Future<T> {
561558
return _future.._completeWithValue(<T>[]);
562559
}
563560
values = List<T?>.filled(remaining, null);
564-
} catch (e, st) {
561+
} catch (e, s) {
565562
// The error must have been thrown while iterating over the futures
566563
// list, or while installing a callback handler on the future.
567564
// This is a breach of the `Future` protocol, but we try to handle it
568565
// gracefully.
569566
if (remaining == 0 || eagerError) {
570-
// Throw a new Future.error.
571-
// Don't just call `_future._completeError` since that would propagate
572-
// the error too eagerly, not giving the callers time to install
573-
// error handlers.
574-
// Also, don't use `_asyncCompleteError` since that one doesn't give
575-
// zones the chance to intercept the error.
576-
return new Future.error(e, st);
567+
// Complete asynchronously to give receiver time to handle
568+
// the result.
569+
return _future.._asyncCompleteErrorObject(_interceptCaughtError(e, s));
577570
} else {
578571
// Don't allocate a list for values, thus indicating that there was an
579572
// error.
580573
// Set error to the caught exception.
581574
error = e;
582-
stackTrace = st;
575+
stackTrace = s;
583576
}
584577
}
585578
return _future;
@@ -716,9 +709,11 @@ abstract interface class Future<T> {
716709
try {
717710
result = action();
718711
} catch (error, stackTrace) {
719-
// Cannot use _completeWithErrorCallback because it completes
712+
// Cannot use `_completeWithErrorCallback` because it completes
720713
// the future synchronously.
721-
_asyncCompleteWithErrorCallback(doneSignal, error, stackTrace);
714+
doneSignal._asyncCompleteErrorObject(
715+
_interceptCaughtError(error, stackTrace),
716+
);
722717
return;
723718
}
724719
if (result is Future<bool>) {
@@ -1331,7 +1326,7 @@ abstract interface class Completer<T> {
13311326
bool get isCompleted;
13321327
}
13331328

1334-
// Helper function completing a _Future with error, but checking the zone
1329+
// Helper function completing a _Future with an error, but checking the zone
13351330
// for error replacement and missing stack trace first.
13361331
// Only used for errors that are *caught*.
13371332
// A user provided error object should use `_interceptUserError` which
@@ -1341,24 +1336,5 @@ void _completeWithErrorCallback(
13411336
Object error,
13421337
StackTrace stackTrace,
13431338
) {
1344-
var replacement = _interceptError(error, stackTrace);
1345-
if (replacement != null) {
1346-
error = replacement.error;
1347-
stackTrace = replacement.stackTrace;
1348-
}
1349-
result._completeError(error, stackTrace);
1350-
}
1351-
1352-
// Like [_completeWithErrorCallback] but completes asynchronously.
1353-
void _asyncCompleteWithErrorCallback(
1354-
_Future result,
1355-
Object error,
1356-
StackTrace stackTrace,
1357-
) {
1358-
var replacement = _interceptError(error, stackTrace);
1359-
if (replacement != null) {
1360-
error = replacement.error;
1361-
stackTrace = replacement.stackTrace;
1362-
}
1363-
result._asyncCompleteError(error, stackTrace);
1339+
result._completeErrorObject(_interceptCaughtError(error, stackTrace));
13641340
}

sdk/lib/async/future_impl.dart

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,28 @@ AsyncError? _interceptError(Object error, StackTrace? stackTrace) {
2424
return replacement;
2525
}
2626

27+
/// As [_interceptError], but an `AsyncError` of the error if not replaced.
28+
///
29+
/// Also like [_interceptUserError], but for errors that are already
30+
/// asynchronous.
31+
AsyncError _interceptCaughtError(Object error, StackTrace? stackTrace) =>
32+
_interceptError(error, stackTrace) ?? AsyncError(error, stackTrace);
33+
2734
/// Used for user-provided error and stack trace that are to become async
2835
/// errors.
2936
///
3037
/// Allows `Zone.current.errorCallback` to intercept and modify the error,
3138
/// and sets the stack trace on the error as if it was thrown.
39+
///
40+
/// Used for errors given to async functions like [Completer.completeError]
41+
/// or [StreamController.addError].
42+
///
43+
/// For errors that are caught (and have therefore been thrown already)
44+
/// use [_interceptCaughtError], which doesn't try to set a stack trace on the
45+
/// error object.
46+
///
47+
/// Errors that are already asynchronous (coming out of streams or futures)
48+
/// should not call [Zone.errorCallback].
3249
AsyncError _interceptUserError(Object error, StackTrace? stackTrace) {
3350
var zone = Zone.current;
3451
if (!identical(zone, _rootZone)) {
@@ -64,12 +81,11 @@ abstract class _Completer<T> implements Completer<T> {
6481
@pragma("wasm:entry-point")
6582
void completeError(Object error, [StackTrace? stackTrace]) {
6683
if (!future._mayComplete) throw new StateError("Future already completed");
67-
AsyncError(:error, :stackTrace) = _interceptUserError(error, stackTrace);
68-
_completeError(error, stackTrace);
84+
_completeErrorObject(_interceptUserError(error, stackTrace));
6985
}
7086

7187
// Overridden by either a synchronous or asynchronous implementation.
72-
void _completeError(Object error, StackTrace stackTrace);
88+
void _completeErrorObject(AsyncError error);
7389

7490
// The future's _isComplete doesn't take into account pending completions.
7591
// We therefore use _mayComplete.
@@ -85,8 +101,8 @@ class _AsyncCompleter<T> extends _Completer<T> {
85101
future._asyncComplete(value == null ? value as dynamic : value);
86102
}
87103

88-
void _completeError(Object error, StackTrace stackTrace) {
89-
future._asyncCompleteError(error, stackTrace);
104+
void _completeErrorObject(AsyncError error) {
105+
future._asyncCompleteErrorObject(error);
90106
}
91107
}
92108

@@ -100,8 +116,8 @@ class _SyncCompleter<T> extends _Completer<T> {
100116
future._complete(value == null ? value as dynamic : value);
101117
}
102118

103-
void _completeError(Object error, StackTrace stackTrace) {
104-
future._completeError(error, stackTrace);
119+
void _completeErrorObject(AsyncError error) {
120+
future._completeErrorObject(error);
105121
}
106122
}
107123

@@ -351,9 +367,8 @@ class _Future<T> implements Future<T> {
351367
_setValue(value);
352368
}
353369

354-
_Future.immediateError(var error, StackTrace stackTrace)
355-
: _zone = Zone._current {
356-
_asyncCompleteError(error, stackTrace);
370+
_Future.immediateError(AsyncError error) : _zone = Zone._current {
371+
_asyncCompleteErrorObject(error);
357372
}
358373

359374
/// Creates a future that is already completed with the value.
@@ -497,10 +512,6 @@ class _Future<T> implements Future<T> {
497512
_resultOrListeners = error;
498513
}
499514

500-
void _setError(Object error, StackTrace stackTrace) {
501-
_setErrorObject(new AsyncError(error, stackTrace));
502-
}
503-
504515
/// Copy the completion result of [source] into this future.
505516
///
506517
/// Used when a chained future notices that its source is completed.
@@ -722,14 +733,18 @@ class _Future<T> implements Future<T> {
722733
_propagateToListeners(this, listeners);
723734
}
724735

725-
void _completeError(Object error, StackTrace stackTrace) {
736+
void _completeErrorObject(AsyncError error) {
726737
assert(!_isComplete);
727738

728739
_FutureListener? listeners = _removeListeners();
729-
_setError(error, stackTrace);
740+
_setErrorObject(error);
730741
_propagateToListeners(this, listeners);
731742
}
732743

744+
void _completeError(Object error, StackTrace stackTrace) {
745+
_completeErrorObject(AsyncError(error, stackTrace));
746+
}
747+
733748
// Completes future in a later microtask.
734749
void _asyncComplete(FutureOr<T> value) {
735750
assert(!_isComplete); // Allows both pending complete and incomplete.
@@ -809,11 +824,15 @@ class _Future<T> implements Future<T> {
809824
}
810825

811826
void _asyncCompleteError(Object error, StackTrace stackTrace) {
827+
_asyncCompleteErrorObject(AsyncError(error, stackTrace));
828+
}
829+
830+
void _asyncCompleteErrorObject(AsyncError error) {
812831
assert(!_isComplete);
813832

814833
_setPendingComplete();
815834
_zone.scheduleMicrotask(() {
816-
_completeError(error, stackTrace);
835+
_completeErrorObject(error);
817836
});
818837
}
819838

@@ -919,7 +938,7 @@ class _Future<T> implements Future<T> {
919938
joinedResult._completeWithResultOf(originalSource);
920939
},
921940
onError: (Object e, StackTrace s) {
922-
joinedResult._completeError(e, s);
941+
joinedResult._completeErrorObject(AsyncError(e, s));
923942
},
924943
);
925944
listenerValueOrError = joinedResult;
@@ -1020,9 +1039,11 @@ class _Future<T> implements Future<T> {
10201039
Timer timer;
10211040
if (onTimeout == null) {
10221041
timer = new Timer(timeLimit, () {
1023-
_future._completeError(
1024-
new TimeoutException("Future not completed", timeLimit),
1025-
StackTrace.current,
1042+
_future._completeErrorObject(
1043+
AsyncError(
1044+
new TimeoutException("Future not completed", timeLimit),
1045+
StackTrace.current,
1046+
),
10261047
);
10271048
});
10281049
} else {
@@ -1035,7 +1056,7 @@ class _Future<T> implements Future<T> {
10351056
try {
10361057
_future._complete(zone.run(onTimeoutHandler));
10371058
} catch (e, s) {
1038-
_future._completeError(e, s);
1059+
_future._completeErrorObject(AsyncError(e, s));
10391060
}
10401061
});
10411062
}
@@ -1049,7 +1070,7 @@ class _Future<T> implements Future<T> {
10491070
onError: (Object e, StackTrace s) {
10501071
if (timer.isActive) {
10511072
timer.cancel();
1052-
_future._completeError(e, s);
1073+
_future._completeErrorObject(AsyncError(e, s));
10531074
}
10541075
},
10551076
);

0 commit comments

Comments
 (0)