| // Copyright (c) 2017, 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. | 
 |  | 
 | #include "vm/stack_trace.h" | 
 |  | 
 | #include "vm/dart_api_impl.h" | 
 | #include "vm/object_store.h" | 
 | #include "vm/stack_frame.h" | 
 | #include "vm/symbols.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | namespace { | 
 |  | 
 | // Keep in sync with: | 
 | // - sdk/lib/async/stream_controller.dart:_StreamController._STATE_SUBSCRIBED. | 
 | const intptr_t k_StreamController__STATE_SUBSCRIBED = 1; | 
 | // - sdk/lib/async/future_impl.dart:_FutureListener.stateThen. | 
 | const intptr_t k_FutureListener_stateThen = 1; | 
 | // - sdk/lib/async/future_impl.dart:_FutureListener.stateCatchError. | 
 | const intptr_t k_FutureListener_stateCatchError = 2; | 
 | // - sdk/lib/async/future_impl.dart:_FutureListener.stateWhenComplete. | 
 | const intptr_t k_FutureListener_stateWhenComplete = 8; | 
 | // - sdk/lib/async/future_impl.dart:_FutureListener.maskAwait. | 
 | const intptr_t k_FutureListener_maskAwait = 16; | 
 |  | 
 | bool WasPreviouslySuspended(const Function& function, | 
 |                             const Object& suspend_state_var) { | 
 |   if (!suspend_state_var.IsSuspendState()) { | 
 |     return false; | 
 |   } | 
 |   if (function.IsAsyncFunction()) { | 
 |     // Error callback is set after both 'then' and 'error' callbacks are | 
 |     // registered with the Zone. Callback registration may query | 
 |     // stack trace and should still collect the synchronous stack trace. | 
 |     return SuspendState::Cast(suspend_state_var).error_callback() != | 
 |            Object::null(); | 
 |   } else if (function.IsAsyncGenerator()) { | 
 |     return true; | 
 |   } else { | 
 |     UNREACHABLE(); | 
 |   } | 
 | } | 
 |  | 
 | // Unwinder which starts by unwinding the synchronous portion of the stack | 
 | // until it reaches a frame which has an asynchronous awaiter (e.g. an | 
 | // activation of the async function which has a listener attached to the | 
 | // corresponding Future object) and then unwinds through the chain | 
 | // of awaiters. | 
 | class AsyncAwareStackUnwinder : public ValueObject { | 
 |  public: | 
 |   explicit AsyncAwareStackUnwinder(Thread* thread) | 
 |       : zone_(thread->zone()), | 
 |         sync_frames_(thread, StackFrameIterator::kNoCrossThreadIteration), | 
 |         sync_frame_(nullptr), | 
 |         awaiter_frame_{Closure::Handle(zone_), Object::Handle(zone_)}, | 
 |         closure_(Closure::Handle(zone_)), | 
 |         code_(Code::Handle(zone_)), | 
 |         context_(Context::Handle(zone_)), | 
 |         function_(Function::Handle(zone_)), | 
 |         parent_function_(Function::Handle(zone_)), | 
 |         object_(Object::Handle(zone_)), | 
 |         suspend_state_(SuspendState::Handle(zone_)), | 
 |         controller_(Object::Handle(zone_)), | 
 |         subscription_(Object::Handle(zone_)), | 
 |         stream_iterator_(Object::Handle(zone_)), | 
 |         async_lib_(Library::Handle(zone_, Library::AsyncLibrary())), | 
 |         null_closure_(Closure::Handle(zone_)) {} | 
 |  | 
 |   bool Unwind(intptr_t skip_frames, | 
 |               std::function<void(const StackTraceUtils::Frame&)> handle_frame); | 
 |  | 
 |  private: | 
 |   bool HandleSynchronousFrame(); | 
 |  | 
 |   void UnwindAwaiterFrame(); | 
 |  | 
 |   // Returns either the `onData` or the Future awaiter. | 
 |   ObjectPtr FindCallerInAsyncStarStreamController( | 
 |       const Object& async_star_stream_controller); | 
 |  | 
 |   void InitializeAwaiterFrameFromSuspendState(); | 
 |  | 
 |   void InitializeAwaiterFrameFromFutureListener(const Object& listener); | 
 |  | 
 |   void UnwindToAwaiter(); | 
 |  | 
 |   // |frame.next| is a |_Future| instance. Unwind to the next frame. | 
 |   void UnwindFrameToFutureListener(); | 
 |  | 
 |   // |frame.next| is an |_AsyncStarStreamController| instance corresponding to | 
 |   // an async* function. Unwind to the next frame. | 
 |   void UnwindFrameToStreamListener(); | 
 |  | 
 |   ObjectPtr GetReceiver() const; | 
 |  | 
 | #define USED_CLASS_LIST(V)                                                     \ | 
 |   V(_AsyncStarStreamController)                                                \ | 
 |   V(_BufferingStreamSubscription)                                              \ | 
 |   V(_Completer)                                                                \ | 
 |   V(_AsyncCompleter)                                                           \ | 
 |   V(_SyncCompleter)                                                            \ | 
 |   V(_ControllerSubscription)                                                   \ | 
 |   V(_Future)                                                                   \ | 
 |   V(_FutureListener)                                                           \ | 
 |   V(_StreamController)                                                         \ | 
 |   V(_StreamIterator)                                                           \ | 
 |   V(_SyncStreamController) | 
 |  | 
 |   enum ClassId { | 
 | #define DECLARE_CONSTANT(symbol) k##symbol, | 
 |     USED_CLASS_LIST(DECLARE_CONSTANT) | 
 | #undef DECLARE_CONSTANT | 
 |   }; | 
 |  | 
 | #define USED_FIELD_LIST(V)                                                     \ | 
 |   V(_AsyncStarStreamController, controller)                                    \ | 
 |   V(_BufferingStreamSubscription, _onData)                                     \ | 
 |   V(_Completer, future)                                                        \ | 
 |   V(_Future, _resultOrListeners)                                               \ | 
 |   V(_FutureListener, callback)                                                 \ | 
 |   V(_FutureListener, result)                                                   \ | 
 |   V(_FutureListener, state)                                                    \ | 
 |   V(_StreamController, _state)                                                 \ | 
 |   V(_StreamController, _varData)                                               \ | 
 |   V(_StreamIterator, _hasValue)                                                \ | 
 |   V(_StreamIterator, _stateData) | 
 |  | 
 |   enum FieldId { | 
 | #define DECLARE_CONSTANT(class_symbol, field_symbol)                           \ | 
 |   k##class_symbol##_##field_symbol, | 
 |     USED_FIELD_LIST(DECLARE_CONSTANT) | 
 | #undef DECLARE_CONSTANT | 
 |   }; | 
 |  | 
 | #define PLUS_ONE(...) +1 | 
 |   static constexpr intptr_t kUsedClassCount = 0 USED_CLASS_LIST(PLUS_ONE); | 
 |   static constexpr intptr_t kUsedFieldCount = 0 USED_FIELD_LIST(PLUS_ONE); | 
 | #undef PLUS_ONE | 
 |  | 
 | #define DECLARE_GETTER(symbol)                                                 \ | 
 |   const Class& symbol() {                                                      \ | 
 |     auto& cls = classes_[k##symbol];                                           \ | 
 |     if (cls == nullptr) {                                                      \ | 
 |       cls = &Class::Handle(                                                    \ | 
 |           zone_, async_lib_.LookupClassAllowPrivate(Symbols::symbol()));       \ | 
 |       ASSERT(!cls->IsNull());                                                  \ | 
 |     }                                                                          \ | 
 |     return *cls;                                                               \ | 
 |   } | 
 |   USED_CLASS_LIST(DECLARE_GETTER) | 
 | #undef DECLARE_GETTER | 
 |  | 
 | #define DECLARE_GETTER(class_symbol, field_symbol)                             \ | 
 |   ObjectPtr Get##class_symbol##_##field_symbol(const Object& obj) {            \ | 
 |     auto& field = fields_[k##class_symbol##_##field_symbol];                   \ | 
 |     if (field == nullptr) {                                                    \ | 
 |       field = &Field::Handle(zone_, class_symbol().LookupFieldAllowPrivate(    \ | 
 |                                         Symbols::field_symbol()));             \ | 
 |       ASSERT(!field->IsNull());                                                \ | 
 |     }                                                                          \ | 
 |     return Instance::Cast(obj).GetField(*field);                               \ | 
 |   } | 
 |   USED_FIELD_LIST(DECLARE_GETTER) | 
 | #undef DECLARE_GETTER | 
 |  | 
 |   struct AwaiterFrame { | 
 |     Closure& closure; | 
 |     Object& next; | 
 |     bool has_catch_error; | 
 |   }; | 
 |  | 
 |   Zone* zone_; | 
 |   DartFrameIterator sync_frames_; | 
 |  | 
 |   StackFrame* sync_frame_; | 
 |   AwaiterFrame awaiter_frame_; | 
 |  | 
 |   Closure& closure_; | 
 |   Code& code_; | 
 |   Context& context_; | 
 |   Function& function_; | 
 |   Function& parent_function_; | 
 |   Object& object_; | 
 |   SuspendState& suspend_state_; | 
 |  | 
 |   Object& controller_; | 
 |   Object& subscription_; | 
 |   Object& stream_iterator_; | 
 |  | 
 |   const Library& async_lib_; | 
 |   Class* classes_[kUsedClassCount] = {}; | 
 |   Field* fields_[kUsedFieldCount] = {}; | 
 |  | 
 |   const Closure& null_closure_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(AsyncAwareStackUnwinder); | 
 | }; | 
 |  | 
 | bool AsyncAwareStackUnwinder::Unwind( | 
 |     intptr_t skip_frames, | 
 |     std::function<void(const StackTraceUtils::Frame&)> handle_frame) { | 
 |   // First skip the given number of synchronous frames. | 
 |   sync_frame_ = sync_frames_.NextFrame(); | 
 |   while (skip_frames > 0 && sync_frame_ != nullptr) { | 
 |     sync_frame_ = sync_frames_.NextFrame(); | 
 |     skip_frames--; | 
 |   } | 
 |  | 
 |   // Continue unwinding synchronous portion of the stack looking for | 
 |   // a synchronous frame which has an awaiter. | 
 |   while (sync_frame_ != nullptr && awaiter_frame_.closure.IsNull()) { | 
 |     const bool was_handled = HandleSynchronousFrame(); | 
 |     if (!was_handled) { | 
 |       code_ = sync_frame_->LookupDartCode(); | 
 |       const uword pc_offset = sync_frame_->pc() - code_.PayloadStart(); | 
 |       handle_frame({sync_frame_, code_, pc_offset, null_closure_, false}); | 
 |     } | 
 |     sync_frame_ = sync_frames_.NextFrame(); | 
 |   } | 
 |  | 
 |   // Traverse awaiter frames. | 
 |   bool any_async = false; | 
 |   for (; !awaiter_frame_.closure.IsNull(); UnwindToAwaiter()) { | 
 |     function_ = awaiter_frame_.closure.function(); | 
 |     if (function_.IsNull()) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     any_async = true; | 
 |     if (awaiter_frame_.next.IsSuspendState()) { | 
 |       const uword pc = SuspendState::Cast(awaiter_frame_.next).pc(); | 
 |       if (pc == 0) { | 
 |         // Async function is already resumed. | 
 |         continue; | 
 |       } | 
 |  | 
 |       code_ = SuspendState::Cast(awaiter_frame_.next).GetCodeObject(); | 
 |       const uword pc_offset = pc - code_.PayloadStart(); | 
 |       handle_frame({nullptr, code_, pc_offset, awaiter_frame_.closure, | 
 |                     awaiter_frame_.has_catch_error}); | 
 |     } else { | 
 |       // This is an asynchronous continuation represented by a closure which | 
 |       // will handle successful completion. This function is not yet executing | 
 |       // so we have to use artificial marker offset (1). | 
 |       code_ = function_.EnsureHasCode(); | 
 |       RELEASE_ASSERT(!code_.IsNull()); | 
 |       const uword pc_offset = | 
 |           (function_.entry_point() + StackTraceUtils::kFutureListenerPcOffset) - | 
 |           code_.PayloadStart(); | 
 |       handle_frame({nullptr, code_, pc_offset, awaiter_frame_.closure, | 
 |                     awaiter_frame_.has_catch_error}); | 
 |     } | 
 |   } | 
 |   return any_async; | 
 | } | 
 |  | 
 | ObjectPtr AsyncAwareStackUnwinder::GetReceiver() const { | 
 |   return *(reinterpret_cast<ObjectPtr*>(sync_frame_->GetCallerSp()) + | 
 |            function_.num_fixed_parameters() - 1); | 
 | } | 
 |  | 
 | bool AsyncAwareStackUnwinder::HandleSynchronousFrame() { | 
 |   function_ = sync_frame_->LookupDartFunction(); | 
 |   if (function_.IsNull()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // This is an invocation of an `async` or `async*` function. | 
 |   if (function_.IsAsyncFunction() || function_.IsAsyncGenerator()) { | 
 |     InitializeAwaiterFrameFromSuspendState(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // This is an invocation of a closure which has a variable marked | 
 |   // with `@pragma('vm:awaiter-link')` which points to the awaiter. | 
 |   if (function_.HasAwaiterLink()) { | 
 |     object_ = GetReceiver(); | 
 |     if (object_.IsClosure() && | 
 |         StackTraceUtils::GetSuspendState(Closure::Cast(object_), | 
 |                                          &awaiter_frame_.next)) { | 
 |       awaiter_frame_.closure ^= object_.ptr(); | 
 |       return true;  // Hide this frame from the stack trace. | 
 |     } | 
 |   } | 
 |  | 
 |   // This is `_FutureListener.handleValue(...)` invocation. | 
 |   if (function_.recognized_kind() == | 
 |       MethodRecognizer::kFutureListenerHandleValue) { | 
 |     object_ = GetReceiver(); | 
 |     InitializeAwaiterFrameFromFutureListener(object_); | 
 |     UnwindToAwaiter(); | 
 |     return true;  // Hide this frame from the stack trace. | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | void AsyncAwareStackUnwinder::InitializeAwaiterFrameFromSuspendState() { | 
 |   if (function_.IsAsyncFunction() || function_.IsAsyncGenerator()) { | 
 |     // Check if we reached a resumed asynchronous function. In this case we | 
 |     // are going to start following async frames after we emit this frame. | 
 |     object_ = *reinterpret_cast<ObjectPtr*>(LocalVarAddress( | 
 |         sync_frame_->fp(), runtime_frame_layout.FrameSlotForVariableIndex( | 
 |                                SuspendState::kSuspendStateVarIndex))); | 
 |  | 
 |     awaiter_frame_.closure = Closure::null(); | 
 |     if (WasPreviouslySuspended(function_, object_)) { | 
 |       awaiter_frame_.next = object_.ptr(); | 
 |       UnwindToAwaiter(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void AsyncAwareStackUnwinder::UnwindToAwaiter() { | 
 |   awaiter_frame_.has_catch_error = false; | 
 |   do { | 
 |     UnwindAwaiterFrame(); | 
 |   } while (awaiter_frame_.closure.IsNull() && !awaiter_frame_.next.IsNull()); | 
 | } | 
 |  | 
 | void AsyncAwareStackUnwinder::UnwindAwaiterFrame() { | 
 |   if (awaiter_frame_.next.IsSuspendState()) { | 
 |     awaiter_frame_.next = | 
 |         SuspendState::Cast(awaiter_frame_.next).function_data(); | 
 |   } else if (awaiter_frame_.next.GetClassId() == _SyncCompleter().id() || | 
 |              awaiter_frame_.next.GetClassId() == _AsyncCompleter().id()) { | 
 |     awaiter_frame_.next = Get_Completer_future(awaiter_frame_.next); | 
 |   } | 
 |  | 
 |   if (awaiter_frame_.next.GetClassId() == _Future().id()) { | 
 |     UnwindFrameToFutureListener(); | 
 |   } else if (awaiter_frame_.next.GetClassId() == | 
 |              _AsyncStarStreamController().id()) { | 
 |     UnwindFrameToStreamListener(); | 
 |   } else { | 
 |     awaiter_frame_.closure = Closure::null(); | 
 |     awaiter_frame_.next = Object::null(); | 
 |     return; | 
 |   } | 
 |  | 
 |   while (!awaiter_frame_.closure.IsNull()) { | 
 |     function_ = awaiter_frame_.closure.function(); | 
 |     context_ = awaiter_frame_.closure.context(); | 
 |  | 
 |     const auto awaiter_link = function_.awaiter_link(); | 
 |     if (awaiter_link.depth != ClosureData::kNoAwaiterLinkDepth) { | 
 |       intptr_t depth = awaiter_link.depth; | 
 |       while (depth-- > 0) { | 
 |         context_ = context_.parent(); | 
 |       } | 
 |  | 
 |       const Object& object = Object::Handle(context_.At(awaiter_link.index)); | 
 |       if (object.IsClosure()) { | 
 |         awaiter_frame_.closure ^= object.ptr(); | 
 |         continue; | 
 |       } else { | 
 |         awaiter_frame_.next = object.ptr(); | 
 |         return; | 
 |       } | 
 |     } | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | void AsyncAwareStackUnwinder::UnwindFrameToFutureListener() { | 
 |   object_ = Get_Future__resultOrListeners(awaiter_frame_.next); | 
 |   if (object_.GetClassId() == _FutureListener().id()) { | 
 |     InitializeAwaiterFrameFromFutureListener(object_); | 
 |   } else { | 
 |     awaiter_frame_.closure = Closure::null(); | 
 |     awaiter_frame_.next = Object::null(); | 
 |   } | 
 | } | 
 |  | 
 | void AsyncAwareStackUnwinder::InitializeAwaiterFrameFromFutureListener( | 
 |     const Object& listener) { | 
 |   if (listener.GetClassId() != _FutureListener().id()) { | 
 |     awaiter_frame_.closure = Closure::null(); | 
 |     awaiter_frame_.next = Object::null(); | 
 |     return; | 
 |   } | 
 |  | 
 |   const auto state = | 
 |       Smi::Value(Smi::RawCast(Get_FutureListener_state(listener))); | 
 |   if (state == k_FutureListener_stateThen || | 
 |       state == k_FutureListener_stateCatchError || | 
 |       state == k_FutureListener_stateWhenComplete || | 
 |       state == | 
 |           (k_FutureListener_stateThen | k_FutureListener_stateCatchError) || | 
 |       state == (k_FutureListener_stateThen | k_FutureListener_stateCatchError | | 
 |                 k_FutureListener_maskAwait)) { | 
 |     awaiter_frame_.next = Get_FutureListener_result(listener); | 
 |   } else { | 
 |     awaiter_frame_.next = Object::null(); | 
 |   } | 
 |   awaiter_frame_.closure = | 
 |       Closure::RawCast(Get_FutureListener_callback(listener)); | 
 |  | 
 |   // If the Future has catchError callback attached through either | 
 |   // `Future.onError` or `Future.then(..., onError: ...)` then we should | 
 |   // treat this listener as a catch all exception handler. However we should | 
 |   // ignore the case when these callbacks are simply forwarding errors into a | 
 |   // suspended async function, because it will be represented by its own async | 
 |   // frame. | 
 |   if ((state & | 
 |        (k_FutureListener_stateCatchError | k_FutureListener_maskAwait)) == | 
 |       k_FutureListener_stateCatchError) { | 
 |     awaiter_frame_.has_catch_error = true; | 
 |   } | 
 | } | 
 |  | 
 | void AsyncAwareStackUnwinder::UnwindFrameToStreamListener() { | 
 |   controller_ = Get_AsyncStarStreamController_controller(awaiter_frame_.next); | 
 |  | 
 |   // Clear the frame. | 
 |   awaiter_frame_.closure = Closure::null(); | 
 |   awaiter_frame_.next = Object::null(); | 
 |  | 
 |   const auto state = | 
 |       Smi::Value(Smi::RawCast(Get_StreamController__state(controller_))); | 
 |   if (state != k_StreamController__STATE_SUBSCRIBED) { | 
 |     return; | 
 |   } | 
 |  | 
 |   subscription_ = Get_StreamController__varData(controller_); | 
 |   closure_ ^= Get_BufferingStreamSubscription__onData(subscription_); | 
 |  | 
 |   // If this is not the "_StreamIterator._onData" tear-off, we return the | 
 |   // callback we found. | 
 |   function_ = closure_.function(); | 
 |   if (!function_.IsImplicitInstanceClosureFunction() || | 
 |       function_.Owner() != _StreamIterator().ptr()) { | 
 |     awaiter_frame_.closure = closure_.ptr(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // All implicit closure functions (tear-offs) have the "this" receiver | 
 |   // captured. | 
 |   context_ = closure_.context(); | 
 |   ASSERT(context_.num_variables() == 1); | 
 |   stream_iterator_ = context_.At(0); | 
 |   ASSERT(stream_iterator_.IsInstance()); | 
 |  | 
 |   if (stream_iterator_.GetClassId() != _StreamIterator().id()) { | 
 |     UNREACHABLE(); | 
 |   } | 
 |  | 
 |   // If `_hasValue` is true then the `StreamIterator._stateData` field | 
 |   // contains the iterator's value. In that case we cannot unwind anymore. | 
 |   // | 
 |   // Notice: With correct async* semantics this may never be true: The async* | 
 |   // generator should only be invoked to produce a value if there's an | 
 |   // in-progress `await streamIterator.moveNext()` call. Once such call has | 
 |   // finished the async* generator should be paused/yielded until the next | 
 |   // such call - and being paused/yielded means it should not appear in stack | 
 |   // traces. | 
 |   // | 
 |   // See dartbug.com/48695. | 
 |   if (Get_StreamIterator__hasValue(stream_iterator_) == | 
 |       Object::bool_true().ptr()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // If we have an await'er for `await streamIterator.moveNext()` we continue | 
 |   // unwinding there. | 
 |   // | 
 |   // Notice: With correct async* semantics this may always contain a Future | 
 |   // See also comment above as well as dartbug.com/48695. | 
 |   object_ = Get_StreamIterator__stateData(stream_iterator_); | 
 |   if (object_.GetClassId() == _Future().id()) { | 
 |     awaiter_frame_.next = object_.ptr(); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool StackTraceUtils::IsNeededForAsyncAwareUnwinding(const Function& function) { | 
 |   // If this is a closure function which specifies an awaiter-link then | 
 |   // we need to retain both function and the corresponding code. | 
 |   if (function.HasAwaiterLink()) { | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (function.recognized_kind() == | 
 |       MethodRecognizer::kFutureListenerHandleValue) { | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | void StackTraceUtils::CollectFrames( | 
 |     Thread* thread, | 
 |     int skip_frames, | 
 |     const std::function<void(const StackTraceUtils::Frame&)>& handle_frame) { | 
 |   const Closure& null_closure = Closure::Handle(thread->zone()); | 
 |  | 
 |   const Frame gap_frame = {nullptr, StubCode::AsynchronousGapMarker(), | 
 |                            /*pc_offset=*/0, null_closure, false}; | 
 |  | 
 |   const Frame gap_frame_with_catch = {nullptr, | 
 |                                       StubCode::AsynchronousGapMarker(), | 
 |                                       /*pc_offset=*/0, null_closure, true}; | 
 |  | 
 |   AsyncAwareStackUnwinder it(thread); | 
 |   const bool any_async = it.Unwind(skip_frames, [&](const Frame& frame) { | 
 |     if (frame.frame == nullptr) { | 
 |       handle_frame(frame.has_async_catch_error ? gap_frame_with_catch | 
 |                                                : gap_frame); | 
 |     } | 
 |  | 
 |     handle_frame(frame); | 
 |   }); | 
 |  | 
 |   if (any_async) { | 
 |     handle_frame(gap_frame); | 
 |   } | 
 | } | 
 |  | 
 | bool StackTraceUtils::GetSuspendState(const Closure& closure, | 
 |                                       Object* suspend_state) { | 
 |   const Function& function = Function::Handle(closure.function()); | 
 |   const auto awaiter_link = function.awaiter_link(); | 
 |   if (awaiter_link.depth != ClosureData::kNoAwaiterLinkDepth) { | 
 |     Context& context = Context::Handle(closure.context()); | 
 |     intptr_t depth = awaiter_link.depth; | 
 |     while (depth-- > 0) { | 
 |       context = context.parent(); | 
 |     } | 
 |  | 
 |     const Object& link = Object::Handle(context.At(awaiter_link.index)); | 
 |     if (link.IsSuspendState()) { | 
 |       *suspend_state = link.ptr(); | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace dart |