dart-sdk/runtime/vm/stack_trace.h
Siva Annamalai 94bc01240c Revert "[vm] Fix some async* semantics issues: Only run generator if there's active subscription (not paused/cancelled)"
This reverts commit 837ee17b43.

Reason for revert: Please see https://github.com/flutter/flutter/issues/101514

Original change's description:
> [vm] Fix some async* semantics issues: Only run generator if there's active subscription (not paused/cancelled)
>
> This fixes an issue where VM would run the async* generator after a
> `yield` / `yield*` even though the subscription may be paused or
> cancelled.
>
> Furthermore this fixes an issue where `StackTrace.current` used
> in async* generator crashes VM and/or produces truncated stack
> trace.
>
> This fixes the following existing tests that were failing before:
>
>   * co19/Language/Statements/Yield_and_Yield_Each/Yield_Each/execution_async_t08
>   * co19/Language/Statements/Yield_and_Yield_Each/Yield_Each/execution_async_t09
>   * co19/Language/Statements/Yield_and_Yield_Each/Yield_Each/execution_async_t10
>   * language/async_star/async_star_cancel_test
>   * language/async_star/pause_test
>
> Issue https://github.com/flutter/flutter/issues/100441
> Issue https://github.com/dart-lang/sdk/issues/48695
> Issue https://github.com/dart-lang/sdk/issues/34775
>
> TEST=vm/dart{,_2}/causal_stacks/flutter_regress_100441_test
>
> Change-Id: I73f7d0b70937a3e3766b992740fa6fe6e6d57cec
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239421
> Reviewed-by: Lasse Nielsen <lrn@google.com>
> Commit-Queue: Martin Kustermann <kustermann@google.com>

# Not skipping CQ checks because original CL landed > 1 day ago.

Change-Id: Ic3d9c0508310a33a2aaee67860c0bb2ec83bab4a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240506
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Siva Annamalai <asiva@google.com>
2022-04-07 21:46:38 +00:00

169 lines
6.1 KiB
C++

// 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.
#ifndef RUNTIME_VM_STACK_TRACE_H_
#define RUNTIME_VM_STACK_TRACE_H_
#include <functional>
#include "vm/allocation.h"
#include "vm/flag_list.h"
#include "vm/object.h"
#include "vm/symbols.h"
namespace dart {
// Helper class for finding the closure of the caller.
class CallerClosureFinder {
public:
explicit CallerClosureFinder(Zone* zone);
// Recursively follow any `_FutureListener.result`.
// If no `result`, then return (bottom) `_FutureListener.callback`
ClosurePtr GetCallerInFutureImpl(const Object& future_);
// Get caller closure from _FutureListener.
// Returns closure found either via the `result` Future, or the `callback`.
ClosurePtr GetCallerInFutureListener(const Object& future_listener);
// Find caller closure from an async* function receiver context.
// Returns either the `onData` or the Future awaiter.
ClosurePtr FindCallerInAsyncGenClosure(const Context& receiver_context);
// Find caller closure from a function receiver closure.
// For async* functions, async functions, `Future.timeout` and `Future.wait`,
// we can do this by finding and following their awaited Futures.
ClosurePtr FindCaller(const Closure& receiver_closure);
// Finds the awaited Future from an async function receiver closure.
ObjectPtr GetAsyncFuture(const Closure& receiver_closure);
// Get sdk/lib/async/future_impl.dart:_FutureListener.state.
intptr_t GetFutureListenerState(const Object& future_listener);
// Get sdk/lib/async/future_impl.dart:_FutureListener.callback.
ClosurePtr GetFutureListenerCallback(const Object& future_listener);
// Get sdk/lib/async/future_impl.dart:_FutureListener.result.
ObjectPtr GetFutureListenerResult(const Object& future_listener);
// Get sdk/lib/async/future_impl.dart:_Future._resultOrListeners.
ObjectPtr GetFutureFutureListener(const Object& future);
bool HasCatchError(const Object& future_listener);
static bool IsRunningAsync(const Closure& receiver_closure);
private:
ClosurePtr FindCallerInternal(const Closure& receiver_closure);
ClosurePtr GetCallerInFutureListenerInternal(const Object& future_listener);
ClosurePtr UnwrapAsyncThen(const Closure& closure);
Closure& closure_;
Context& receiver_context_;
Function& receiver_function_;
Function& parent_function_;
Object& context_entry_;
Object& future_;
Object& listener_;
Object& callback_;
Object& controller_;
Object& state_;
Object& var_data_;
Object& callback_instance_;
Class& future_impl_class;
Class& future_listener_class;
Class& async_start_stream_controller_class;
Class& stream_controller_class;
Class& async_stream_controller_class;
Class& controller_subscription_class;
Class& buffering_stream_subscription_class;
Class& stream_iterator_class;
Field& future_result_or_listeners_field;
Field& callback_field;
Field& future_listener_state_field;
Field& future_listener_result_field;
Field& controller_controller_field;
Field& var_data_field;
Field& state_field;
Field& on_data_field;
Field& state_data_field;
Field& has_value_field;
DISALLOW_COPY_AND_ASSIGN(CallerClosureFinder);
};
class StackTraceUtils : public AllStatic {
public:
// Find the async_op closure from the stack frame.
static ClosurePtr FindClosureInFrame(ObjectPtr* last_object_in_caller,
const Function& function);
static ClosurePtr ClosureFromFrameFunction(
Zone* zone,
CallerClosureFinder* caller_closure_finder,
const DartFrameIterator& frames,
StackFrame* frame,
bool* skip_frame,
bool* is_async);
static void UnwindAwaiterChain(Zone* zone,
const GrowableObjectArray& code_array,
GrowableArray<uword>* pc_offset_array,
CallerClosureFinder* caller_closure_finder,
const Closure& leaf_closure);
/// Collects all frames on the current stack until an async/async* frame is
/// hit which has yielded before (i.e. is not in sync-async case).
///
/// From there on finds the closure of the async/async* frame and starts
/// traversing the listeners:
/// while (closure != null) {
/// yield_index = closure.context[Context::kAsyncJumpVarIndex]
/// pc = closure.function.code.pc_descriptors.LookupPcFromYieldIndex(
/// yield_index);
/// <emit pc in frame>
/// closure = closure.context[Context::kAsyncCompleterVarIndex]._future
/// ._resultOrListeners.callback;
/// }
///
/// If [on_sync_frames] is non-nullptr, it will be called for every
/// synchronous frame which is collected.
static void CollectFramesLazy(
Thread* thread,
const GrowableObjectArray& code_array,
GrowableArray<uword>* pc_offset_array,
int skip_frames,
std::function<void(StackFrame*)>* on_sync_frames = nullptr,
bool* has_async = nullptr);
/// Counts the number of stack frames.
/// Skips over the first |skip_frames|.
/// If |async_function| is not null, stops at the function that has
/// |async_function| as its parent, and records in 'sync_async_end' whether
/// |async_function| was called synchronously.
static intptr_t CountFrames(Thread* thread,
int skip_frames,
const Function& async_function,
bool* sync_async_end);
/// Collects |count| frames into |code_array| and |pc_offset_array|.
/// Writing begins at |array_offset|.
/// Skips over the first |skip_frames|.
/// Returns the number of frames collected.
static intptr_t CollectFrames(Thread* thread,
const Array& code_array,
const TypedData& pc_offset_array,
intptr_t array_offset,
intptr_t count,
int skip_frames);
};
} // namespace dart
#endif // RUNTIME_VM_STACK_TRACE_H_