@@ -22,6 +22,8 @@ const intptr_t k_StreamController__STATE_SUBSCRIPTION_MASK = 3;
22
22
const intptr_t k_StreamController__STATE_ADDSTREAM = 8 ;
23
23
// - _BufferingStreamSubscription._STATE_HAS_ERROR_HANDLER.
24
24
const intptr_t k_BufferingStreamSubscription__STATE_HAS_ERROR_HANDLER = 1 << 5 ;
25
+ // - _Future._stateIgnoreError
26
+ const intptr_t k_Future__stateIgnoreError = 1 ;
25
27
// - _FutureListener.stateThen.
26
28
const intptr_t k_FutureListener_stateThen = 1 ;
27
29
// - _FutureListener.stateCatchError.
@@ -79,31 +81,34 @@ bool TryGetAwaiterLink(const Closure& closure, Object* link) {
79
81
// of awaiters.
80
82
class AsyncAwareStackUnwinder : public ValueObject {
81
83
public:
82
- explicit AsyncAwareStackUnwinder (Thread* thread)
84
+ explicit AsyncAwareStackUnwinder (Thread* thread,
85
+ bool * encountered_async_catch_error)
83
86
: zone_(thread->zone ()),
84
87
sync_frames_(thread, StackFrameIterator::kNoCrossThreadIteration ),
85
88
sync_frame_(nullptr ),
86
89
awaiter_frame_{Closure::Handle (zone_), Object::Handle (zone_)},
90
+ encountered_async_catch_error_ (encountered_async_catch_error),
87
91
closure_(Closure::Handle (zone_)),
88
92
code_(Code::Handle (zone_)),
89
93
context_(Context::Handle (zone_)),
90
94
function_(Function::Handle (zone_)),
91
95
parent_function_(Function::Handle (zone_)),
92
96
object_(Object::Handle (zone_)),
97
+ result_future_(Object::Handle (zone_)),
93
98
suspend_state_(SuspendState::Handle (zone_)),
94
99
controller_(Object::Handle (zone_)),
95
100
subscription_(Object::Handle (zone_)),
96
101
stream_iterator_(Object::Handle (zone_)),
97
102
async_lib_(Library::Handle (zone_, Library::AsyncLibrary())),
98
- null_closure_(Closure::Handle (zone_)) {}
103
+ null_closure_(Closure::Handle (zone_)) {
104
+ if (encountered_async_catch_error_ != nullptr ) {
105
+ *encountered_async_catch_error_ = false ;
106
+ }
107
+ }
99
108
100
109
void Unwind (intptr_t skip_frames,
101
110
std::function<void (const StackTraceUtils::Frame&)> handle_frame);
102
111
103
- bool encountered_async_catch_error () const {
104
- return encountered_async_catch_error_;
105
- }
106
-
107
112
private:
108
113
bool HandleSynchronousFrame ();
109
114
@@ -137,6 +142,28 @@ class AsyncAwareStackUnwinder : public ValueObject {
137
142
138
143
ObjectPtr GetReceiver () const ;
139
144
145
+ // Returns |true| if propagating an error to the listeners of this `_Future`
146
+ // will always encounter an error handler. Future handles error iff:
147
+ //
148
+ // * It has no listeners and is marked as ignoring errors
149
+ // * All of its listeners either have an error handler or corresponding
150
+ // result future handles error.
151
+ //
152
+ // Note: this ignores simple error forwarding/rethrow which occurs in `await`
153
+ // or patterns like `fut.then(onError: c.completeError)`.
154
+ bool WillFutureHandleError (const Object& future, intptr_t depth = 0 );
155
+
156
+ void MarkAsHandlingAsyncError () const {
157
+ if (ShouldComputeIfAsyncErrorIsHandled ()) {
158
+ *encountered_async_catch_error_ = true ;
159
+ }
160
+ }
161
+
162
+ bool ShouldComputeIfAsyncErrorIsHandled () const {
163
+ return encountered_async_catch_error_ != nullptr &&
164
+ !*encountered_async_catch_error_;
165
+ }
166
+
140
167
#define USED_CLASS_LIST (V ) \
141
168
V (_AsyncStarStreamController) \
142
169
V(_BufferingStreamSubscription) \
@@ -167,9 +194,11 @@ class AsyncAwareStackUnwinder : public ValueObject {
167
194
V(_BufferingStreamSubscription, _state) \
168
195
V(_Completer, future) \
169
196
V(_Future, _resultOrListeners) \
197
+ V(_Future, _state) \
170
198
V(_FutureListener, callback) \
171
199
V(_FutureListener, result) \
172
200
V(_FutureListener, state) \
201
+ V(_FutureListener, _nextListener) \
173
202
V(_StreamController, _state) \
174
203
V(_StreamController, _varData) \
175
204
V(_StreamControllerAddStreamState, _varData) \
@@ -226,14 +255,15 @@ class AsyncAwareStackUnwinder : public ValueObject {
226
255
StackFrame* sync_frame_;
227
256
AwaiterFrame awaiter_frame_;
228
257
229
- bool encountered_async_catch_error_ = false ;
258
+ bool * encountered_async_catch_error_;
230
259
231
260
Closure& closure_;
232
261
Code& code_;
233
262
Context& context_;
234
263
Function& function_;
235
264
Function& parent_function_;
236
265
Object& object_;
266
+ Object& result_future_;
237
267
SuspendState& suspend_state_;
238
268
239
269
Object& controller_;
@@ -423,11 +453,47 @@ void AsyncAwareStackUnwinder::UnwindFrameToFutureListener() {
423
453
if (object_.GetClassId () == _FutureListener ().id ()) {
424
454
InitializeAwaiterFrameFromFutureListener (object_);
425
455
} else {
456
+ if (ShouldComputeIfAsyncErrorIsHandled ()) {
457
+ // Check if error on this future was ignored through |Future.ignore|.
458
+ const auto state =
459
+ Smi::Value (Smi::RawCast (Get_Future__state (awaiter_frame_.next )));
460
+ if ((state & k_Future__stateIgnoreError) != 0 ) {
461
+ MarkAsHandlingAsyncError ();
462
+ }
463
+ }
464
+
426
465
awaiter_frame_.closure = Closure::null ();
427
466
awaiter_frame_.next = Object::null ();
428
467
}
429
468
}
430
469
470
+ bool AsyncAwareStackUnwinder::WillFutureHandleError (const Object& future,
471
+ intptr_t depth /* = 0 */ ) {
472
+ if (depth > 100 || future.GetClassId () != _Future ().id ()) {
473
+ return true ; // Conservative.
474
+ }
475
+
476
+ if (Get_Future__resultOrListeners (future) == Object::null ()) {
477
+ // No listeners: check if future is simply ignoring errors.
478
+ const auto state = Smi::Value (Smi::RawCast (Get_Future__state (future)));
479
+ return (state & k_Future__stateIgnoreError) != 0 ;
480
+ }
481
+
482
+ for (auto & listener = Object::Handle (Get_Future__resultOrListeners (future));
483
+ listener.GetClassId () == _FutureListener ().id ();
484
+ listener = Get_FutureListener__nextListener (listener)) {
485
+ const auto state =
486
+ Smi::Value (Smi::RawCast (Get_FutureListener_state (listener)));
487
+ if ((state & k_FutureListener_stateCatchError) == 0 &&
488
+ !WillFutureHandleError (
489
+ Object::Handle (Get_FutureListener_result (listener)), depth + 1 )) {
490
+ return false ;
491
+ }
492
+ }
493
+
494
+ return true ;
495
+ }
496
+
431
497
void AsyncAwareStackUnwinder::InitializeAwaiterFrameFromFutureListener (
432
498
const Object& listener) {
433
499
if (listener.GetClassId () != _FutureListener ().id ()) {
@@ -445,15 +511,30 @@ void AsyncAwareStackUnwinder::InitializeAwaiterFrameFromFutureListener(
445
511
(k_FutureListener_stateThen | k_FutureListener_stateCatchError) ||
446
512
state == (k_FutureListener_stateThen | k_FutureListener_stateCatchError |
447
513
k_FutureListener_maskAwait)) {
448
- awaiter_frame_. next = Get_FutureListener_result (listener);
514
+ result_future_ = Get_FutureListener_result (listener);
449
515
} else {
450
- awaiter_frame_. next = Object::null ();
516
+ result_future_ = Object::null ();
451
517
}
452
518
awaiter_frame_.closure =
453
519
Closure::RawCast (Get_FutureListener_callback (listener));
520
+ awaiter_frame_.next = result_future_.ptr ();
454
521
455
522
ComputeNextFrameFromAwaiterLink ();
456
523
524
+ if (ShouldComputeIfAsyncErrorIsHandled () && !result_future_.IsNull () &&
525
+ result_future_.ptr () != awaiter_frame_.next .ptr ()) {
526
+ // We have unwound through closure rather than followed result future, this
527
+ // can be caused by unwinding through `await` or code like
528
+ // `fut.whenComplete(c.complete)`. If the current future does not
529
+ // catch the error then the error will be forwarded into result_future_.
530
+ // Check if result_future_ handles it and set
531
+ // encountered_async_catch_error_ respectively.
532
+ if ((state & k_FutureListener_stateCatchError) == 0 &&
533
+ WillFutureHandleError (result_future_)) {
534
+ MarkAsHandlingAsyncError ();
535
+ }
536
+ }
537
+
457
538
// If the Future has catchError callback attached through either
458
539
// `Future.catchError` or `Future.then(..., onError: ...)` then we should
459
540
// treat this listener as a catch all exception handler. However we should
@@ -468,7 +549,7 @@ void AsyncAwareStackUnwinder::InitializeAwaiterFrameFromFutureListener(
468
549
if ((state & k_FutureListener_stateCatchError) != 0 &&
469
550
((state & k_FutureListener_maskAwait) == 0 ||
470
551
!awaiter_frame_.next .IsSuspendState ())) {
471
- encountered_async_catch_error_ = true ;
552
+ MarkAsHandlingAsyncError () ;
472
553
}
473
554
}
474
555
@@ -531,7 +612,7 @@ void AsyncAwareStackUnwinder::UnwindFrameToStreamController() {
531
612
if (Get_StreamIterator__hasValue (stream_iterator_) ==
532
613
Object::bool_true ().ptr ()) {
533
614
if (has_error_handler) {
534
- encountered_async_catch_error_ = true ;
615
+ MarkAsHandlingAsyncError () ;
535
616
}
536
617
return ;
537
618
}
@@ -546,7 +627,7 @@ void AsyncAwareStackUnwinder::UnwindFrameToStreamController() {
546
627
awaiter_frame_.next = object_.ptr ();
547
628
} else {
548
629
if (has_error_handler) {
549
- encountered_async_catch_error_ = true ;
630
+ MarkAsHandlingAsyncError () ;
550
631
}
551
632
}
552
633
return ;
@@ -611,7 +692,7 @@ void AsyncAwareStackUnwinder::UnwindFrameToStreamController() {
611
692
612
693
if (has_error_handler && object_.GetClassId () != _Future ().id () &&
613
694
object_.GetClassId () != _SyncStreamController ().id ()) {
614
- encountered_async_catch_error_ = true ;
695
+ MarkAsHandlingAsyncError () ;
615
696
}
616
697
617
698
if (found_awaiter_link_in_sibling_handler) {
@@ -666,11 +747,8 @@ void StackTraceUtils::CollectFrames(
666
747
int skip_frames,
667
748
const std::function<void (const StackTraceUtils::Frame&)>& handle_frame,
668
749
bool* has_async_catch_error /* = null */ ) {
669
- AsyncAwareStackUnwinder it (thread);
750
+ AsyncAwareStackUnwinder it (thread, has_async_catch_error );
670
751
it.Unwind (skip_frames, handle_frame);
671
- if (has_async_catch_error != nullptr ) {
672
- *has_async_catch_error = it.encountered_async_catch_error ();
673
- }
674
752
}
675
753
676
754
bool StackTraceUtils::GetSuspendState (const Closure& closure,
0 commit comments