Revert "[SDK] Switch to is_sync to identify sync/async running."
This reverts commit 886615d0f9.
Reason for revert:
There was an unexpected slowdown in some async benchmarks, e.g. Calls.AwaitAsyncCall.
Will revert for now and investigate next year.
Original change's description:
> [SDK] Switch to is_sync to identify sync/async running.
>
> This should address the regression introduced by https://dart-review.googlesource.com/c/sdk/+/124988
>
> Bug: https://github.com/dart-lang/sdk/issues/39525
> Change-Id: Id163b649bdd0363297c186559fa84ff87f908e4b
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129062
> Reviewed-by: Clement Skau <cskau@google.com>
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Commit-Queue: Clement Skau <cskau@google.com>
TBR=kustermann@google.com,cskau@google.com
Change-Id: I5cda795cbccc01f22e0f8192473c171a4e9fca4b
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: https://github.com/dart-lang/sdk/issues/39525
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129285
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
ffc17eba93
commit
72f4a831aa
+132
-117
@@ -2313,10 +2313,10 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Causal async stacks are not supported in the AOT runtime.
|
||||
ASSERT(!FLAG_async_debugger);
|
||||
return nullptr;
|
||||
return NULL;
|
||||
#else
|
||||
if (!FLAG_async_debugger) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Thread* thread = Thread::Current();
|
||||
@@ -2337,148 +2337,163 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() {
|
||||
Closure& async_activation = Closure::Handle(zone);
|
||||
Object& next_async_activation = Object::Handle(zone);
|
||||
Array& deopt_frame = Array::Handle(zone);
|
||||
// Note: 'class' since Debugger declares a method by the same name.
|
||||
class StackTrace& async_stack_trace = StackTrace::Handle(zone);
|
||||
bool stack_has_async_function = false;
|
||||
Closure& closure = Closure::Handle(zone);
|
||||
CallerClosureFinder caller_closure_finder(zone);
|
||||
|
||||
for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
|
||||
// Number of frames we are trying to skip that form "sync async" entry.
|
||||
int skip_sync_async_frames_count = -1;
|
||||
String& function_name = String::Handle(zone);
|
||||
for (StackFrame* frame = iterator.NextFrame(); frame != NULL;
|
||||
frame = iterator.NextFrame()) {
|
||||
ASSERT(frame->IsValid());
|
||||
if (FLAG_trace_debugger_stacktrace) {
|
||||
OS::PrintErr("CollectAwaiterReturnStackTrace: visiting frame:\n\t%s\n",
|
||||
frame->ToCString());
|
||||
}
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frame->is_interpreted()) {
|
||||
bytecode = frame->LookupDartBytecode();
|
||||
function = bytecode.function();
|
||||
if (function.IsNull()) {
|
||||
continue; // Skip bytecode stub frame.
|
||||
}
|
||||
|
||||
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
|
||||
ActivationFrame* activation =
|
||||
CollectDartFrame(isolate, frame->pc(), frame, bytecode,
|
||||
ActivationFrame::kAsyncActivation);
|
||||
ASSERT(activation != nullptr);
|
||||
stack_trace->AddActivation(activation);
|
||||
stack_has_async_function = true;
|
||||
// Grab the awaiter.
|
||||
async_activation ^= activation->GetAsyncAwaiter();
|
||||
async_stack_trace ^= activation->GetCausalStack();
|
||||
|
||||
// Bail if we've reach the end of the sync execution stack.
|
||||
RawObject** last_caller_obj =
|
||||
reinterpret_cast<RawObject**>(frame->GetCallerSp());
|
||||
closure = StackTraceUtils::FindClosureInFrame(last_caller_obj, function,
|
||||
frame->is_interpreted());
|
||||
if (caller_closure_finder.IsRunningAsync(closure)) {
|
||||
// We've reached the end of the stack of the sync execution.
|
||||
break;
|
||||
if (frame->IsDartFrame()) {
|
||||
if (frame->is_interpreted()) {
|
||||
bytecode = frame->LookupDartBytecode();
|
||||
function = bytecode.function();
|
||||
if (function.IsNull()) {
|
||||
continue; // Skip bytecode stub frame.
|
||||
}
|
||||
} else {
|
||||
stack_trace->AddActivation(
|
||||
CollectDartFrame(isolate, frame->pc(), frame, bytecode));
|
||||
}
|
||||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
if (code.is_optimized()) {
|
||||
if (code.is_force_optimized()) {
|
||||
if (FLAG_trace_debugger_stacktrace) {
|
||||
function = code.function();
|
||||
ASSERT(!function.IsNull());
|
||||
OS::PrintErr(
|
||||
"CollectAwaiterReturnStackTrace: "
|
||||
"skipping force-optimized function: %s\n",
|
||||
function.ToFullyQualifiedCString());
|
||||
}
|
||||
// Skip frame of force-optimized (and non-debuggable) function.
|
||||
continue;
|
||||
}
|
||||
deopt_frame = DeoptimizeToArray(thread, frame, code);
|
||||
bool found_async_awaiter = false;
|
||||
bool abort_attempt_to_navigate_through_sync_async = false;
|
||||
for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
|
||||
it.Advance()) {
|
||||
inlined_code = it.code();
|
||||
function = it.function();
|
||||
|
||||
if (FLAG_trace_debugger_stacktrace) {
|
||||
ASSERT(!function.IsNull());
|
||||
OS::PrintErr(
|
||||
"CollectAwaiterReturnStackTrace: "
|
||||
"visiting inlined function: %s\n ",
|
||||
function.ToFullyQualifiedCString());
|
||||
if (skip_sync_async_frames_count > 0) {
|
||||
function_name = function.QualifiedScrubbedName();
|
||||
if (!StackTraceUtils::CheckAndSkipAsync(&skip_sync_async_frames_count,
|
||||
function_name)) {
|
||||
// Unexpected function in synchronous call of async function.
|
||||
break;
|
||||
}
|
||||
intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
|
||||
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
|
||||
ActivationFrame* activation = CollectDartFrame(
|
||||
isolate, it.pc(), frame, inlined_code, deopt_frame,
|
||||
deopt_frame_offset, ActivationFrame::kAsyncActivation);
|
||||
stack_trace->AddActivation(activation);
|
||||
stack_has_async_function = true;
|
||||
// Grab the awaiter.
|
||||
async_activation ^= activation->GetAsyncAwaiter();
|
||||
found_async_awaiter = true;
|
||||
|
||||
// Bail if we've reach the end of the sync execution stack.
|
||||
RawObject** last_caller_obj =
|
||||
reinterpret_cast<RawObject**>(frame->GetCallerSp());
|
||||
closure = StackTraceUtils::FindClosureInFrame(
|
||||
last_caller_obj, function, frame->is_interpreted());
|
||||
if (caller_closure_finder.IsRunningAsync(closure)) {
|
||||
abort_attempt_to_navigate_through_sync_async = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stack_trace->AddActivation(
|
||||
CollectDartFrame(isolate, it.pc(), frame, inlined_code,
|
||||
deopt_frame, deopt_frame_offset));
|
||||
}
|
||||
} // for (InlinedFunctionsIterator)
|
||||
// Break out of outer loop.
|
||||
if (found_async_awaiter ||
|
||||
abort_attempt_to_navigate_through_sync_async) {
|
||||
break;
|
||||
}
|
||||
} else { // code.is_optimized()
|
||||
function = code.function();
|
||||
|
||||
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
|
||||
ActivationFrame* activation = CollectDartFrame(
|
||||
isolate, frame->pc(), frame, code, Object::null_array(), 0,
|
||||
ActivationFrame::kAsyncActivation);
|
||||
ASSERT(activation != nullptr);
|
||||
ActivationFrame* activation =
|
||||
CollectDartFrame(isolate, frame->pc(), frame, bytecode,
|
||||
ActivationFrame::kAsyncActivation);
|
||||
ASSERT(activation != NULL);
|
||||
stack_trace->AddActivation(activation);
|
||||
stack_has_async_function = true;
|
||||
// Grab the awaiter.
|
||||
async_activation ^= activation->GetAsyncAwaiter();
|
||||
async_stack_trace ^= activation->GetCausalStack();
|
||||
// Interpreted bytecode does not invoke _ClosureCall().
|
||||
// Skip _AsyncAwaitCompleterStart() only.
|
||||
skip_sync_async_frames_count = 1;
|
||||
} else {
|
||||
stack_trace->AddActivation(
|
||||
CollectDartFrame(isolate, frame->pc(), frame, bytecode));
|
||||
}
|
||||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
if (code.is_optimized()) {
|
||||
if (code.is_force_optimized()) {
|
||||
if (FLAG_trace_debugger_stacktrace) {
|
||||
function = code.function();
|
||||
ASSERT(!function.IsNull());
|
||||
OS::PrintErr(
|
||||
"CollectAwaiterReturnStackTrace: "
|
||||
"skipping force-optimized function: %s\n",
|
||||
function.ToFullyQualifiedCString());
|
||||
}
|
||||
// Skip frame of force-optimized (and non-debuggable) function.
|
||||
continue;
|
||||
}
|
||||
deopt_frame = DeoptimizeToArray(thread, frame, code);
|
||||
bool found_async_awaiter = false;
|
||||
bool abort_attempt_to_navigate_through_sync_async = false;
|
||||
for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
|
||||
it.Advance()) {
|
||||
inlined_code = it.code();
|
||||
function = it.function();
|
||||
|
||||
// Bail if we've reach the end of sync execution stack.
|
||||
RawObject** last_caller_obj =
|
||||
reinterpret_cast<RawObject**>(frame->GetCallerSp());
|
||||
closure = StackTraceUtils::FindClosureInFrame(
|
||||
last_caller_obj, function, frame->is_interpreted());
|
||||
if (caller_closure_finder.IsRunningAsync(closure)) {
|
||||
if (skip_sync_async_frames_count > 0) {
|
||||
function_name ^= function.QualifiedScrubbedName();
|
||||
if (!StackTraceUtils::CheckAndSkipAsync(
|
||||
&skip_sync_async_frames_count, function_name)) {
|
||||
// Unexpected function in sync async call
|
||||
skip_sync_async_frames_count = -1;
|
||||
abort_attempt_to_navigate_through_sync_async = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG_trace_debugger_stacktrace) {
|
||||
ASSERT(!function.IsNull());
|
||||
OS::PrintErr(
|
||||
"CollectAwaiterReturnStackTrace: "
|
||||
"visiting inlined function: %s\n ",
|
||||
function.ToFullyQualifiedCString());
|
||||
}
|
||||
intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
|
||||
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
|
||||
ActivationFrame* activation = CollectDartFrame(
|
||||
isolate, it.pc(), frame, inlined_code, deopt_frame,
|
||||
deopt_frame_offset, ActivationFrame::kAsyncActivation);
|
||||
ASSERT(activation != NULL);
|
||||
stack_trace->AddActivation(activation);
|
||||
stack_has_async_function = true;
|
||||
// Grab the awaiter.
|
||||
async_activation ^= activation->GetAsyncAwaiter();
|
||||
found_async_awaiter = true;
|
||||
// async function might have been called synchronously, in which
|
||||
// case we need to keep going down the stack.
|
||||
// To determine how we are called we peek few more frames further
|
||||
// expecting to see Closure_call followed by
|
||||
// AsyncAwaitCompleter_start.
|
||||
// If we are able to see those functions we continue going down
|
||||
// thestack, if we are not, we break out of the loop as we are
|
||||
// not interested in exploring rest of the stack - there is only
|
||||
// dart-internal code left.
|
||||
skip_sync_async_frames_count = 2;
|
||||
} else {
|
||||
stack_trace->AddActivation(
|
||||
CollectDartFrame(isolate, it.pc(), frame, inlined_code,
|
||||
deopt_frame, deopt_frame_offset));
|
||||
}
|
||||
}
|
||||
// Break out of outer loop.
|
||||
if (found_async_awaiter ||
|
||||
abort_attempt_to_navigate_through_sync_async) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stack_trace->AddActivation(CollectDartFrame(
|
||||
isolate, frame->pc(), frame, code, Object::null_array(), 0));
|
||||
function = code.function();
|
||||
|
||||
if (skip_sync_async_frames_count > 0) {
|
||||
function_name ^= function.QualifiedScrubbedName();
|
||||
if (!StackTraceUtils::CheckAndSkipAsync(
|
||||
&skip_sync_async_frames_count, function_name)) {
|
||||
// Unexpected function in synchronous call of async function.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
|
||||
ActivationFrame* activation = CollectDartFrame(
|
||||
isolate, frame->pc(), frame, code, Object::null_array(), 0,
|
||||
ActivationFrame::kAsyncActivation);
|
||||
ASSERT(activation != NULL);
|
||||
stack_trace->AddActivation(activation);
|
||||
stack_has_async_function = true;
|
||||
// Grab the awaiter.
|
||||
async_activation ^= activation->GetAsyncAwaiter();
|
||||
async_stack_trace ^= activation->GetCausalStack();
|
||||
// see comment regarding skipping frames of async functions called
|
||||
// synchronously above.
|
||||
skip_sync_async_frames_count = 2;
|
||||
} else {
|
||||
stack_trace->AddActivation(CollectDartFrame(
|
||||
isolate, frame->pc(), frame, code, Object::null_array(), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for (frame in iterator)
|
||||
}
|
||||
|
||||
// If the stack doesn't have any async functions on it, return nullptr.
|
||||
// If the stack doesn't have any async functions on it, return NULL.
|
||||
if (!stack_has_async_function) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Append the awaiter return call stack.
|
||||
@@ -2499,7 +2514,7 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() {
|
||||
break;
|
||||
}
|
||||
async_activation = Closure::RawCast(next_async_activation.raw());
|
||||
} // while (!async_activation.IsNull())
|
||||
}
|
||||
|
||||
// Now we append the asynchronous causal stack trace. These are not active
|
||||
// frames but a historical record of how this asynchronous function was
|
||||
@@ -2554,7 +2569,7 @@ DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() {
|
||||
}
|
||||
// Follow the link.
|
||||
async_stack_trace = async_stack_trace.async_link();
|
||||
} // while (!async_stack_trace.IsNull())
|
||||
}
|
||||
|
||||
return stack_trace;
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
@@ -132,12 +132,6 @@ class StackFrame : public ValueObject {
|
||||
|
||||
static void DumpCurrentTrace();
|
||||
|
||||
uword GetCallerSp() const {
|
||||
return fp() +
|
||||
((is_interpreted() ? kKBCCallerSpSlotFromFp : kCallerSpSlotFromFp) *
|
||||
kWordSize);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit StackFrame(Thread* thread)
|
||||
: fp_(0), sp_(0), pc_(0), thread_(thread), is_interpreted_(false) {}
|
||||
@@ -157,6 +151,12 @@ class StackFrame : public ValueObject {
|
||||
RawCode* GetCodeObject() const;
|
||||
RawBytecode* GetBytecodeObject() const;
|
||||
|
||||
uword GetCallerSp() const {
|
||||
return fp() +
|
||||
((is_interpreted() ? kKBCCallerSpSlotFromFp : kCallerSpSlotFromFp) *
|
||||
kWordSize);
|
||||
}
|
||||
|
||||
uword GetCallerFp() const {
|
||||
return *(reinterpret_cast<uword*>(
|
||||
fp() + ((is_interpreted() ? kKBCSavedCallerFpSlotFromFp
|
||||
|
||||
+274
-213
@@ -11,6 +11,35 @@
|
||||
|
||||
namespace dart {
|
||||
|
||||
// Keep in sync with
|
||||
// sdk/lib/async/stream_controller.dart:_StreamController._STATE_SUBSCRIBED.
|
||||
const intptr_t kStreamController_StateSubscribed = 1;
|
||||
|
||||
RawClosure* FindClosureInFrame(RawObject** last_object_in_caller,
|
||||
const Function& function,
|
||||
bool is_interpreted) {
|
||||
NoSafepointScope nsp;
|
||||
|
||||
// The callee has function signature
|
||||
// :async_op([result, exception, stack])
|
||||
// So we are guaranteed to
|
||||
// a) have only tagged arguments on the stack until we find the :async_op
|
||||
// closure, and
|
||||
// b) find the async closure.
|
||||
auto& closure = Closure::Handle();
|
||||
for (intptr_t i = 0; i < 4; i++) {
|
||||
// KBC builds the stack upwards instead of the usual downwards stack.
|
||||
RawObject* arg = last_object_in_caller[(is_interpreted ? -i : i)];
|
||||
if (arg->IsHeapObject() && arg->GetClassId() == kClosureCid) {
|
||||
closure = Closure::RawCast(arg);
|
||||
if (closure.function() == function.raw()) {
|
||||
return closure.raw();
|
||||
}
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Find current yield index from async closure.
|
||||
// Async closures contains a variable, :await_jump_var that holds the index into
|
||||
// async wrapper.
|
||||
@@ -59,6 +88,222 @@ intptr_t FindPcOffset(const Bytecode& bytecode, intptr_t yield_index) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Helper class for finding the closure of the caller.
|
||||
// This is done via the _AsyncAwaitCompleter which holds a
|
||||
// FutureResultOrListeners which in turn holds a callback.
|
||||
class CallerClosureFinder {
|
||||
public:
|
||||
// Instance caches library and field references.
|
||||
// This way we don't have to do the look-ups for every frame in the stack.
|
||||
explicit CallerClosureFinder(Zone* zone)
|
||||
: receiver_context_(Context::Handle(zone)),
|
||||
receiver_function_(Function::Handle(zone)),
|
||||
context_entry_(Object::Handle(zone)),
|
||||
is_sync(Object::Handle(zone)),
|
||||
future_(Object::Handle(zone)),
|
||||
listener_(Object::Handle(zone)),
|
||||
callback_(Object::Handle(zone)),
|
||||
controller_(Object::Handle(zone)),
|
||||
state_(Object::Handle(zone)),
|
||||
var_data_(Object::Handle(zone)),
|
||||
future_impl_class(Class::Handle(zone)),
|
||||
async_await_completer_class(Class::Handle(zone)),
|
||||
future_listener_class(Class::Handle(zone)),
|
||||
async_start_stream_controller_class(Class::Handle(zone)),
|
||||
stream_controller_class(Class::Handle(zone)),
|
||||
controller_subscription_class(Class::Handle(zone)),
|
||||
buffering_stream_subscription_class(Class::Handle(zone)),
|
||||
async_stream_controller_class(Class::Handle(zone)),
|
||||
completer_is_sync_field(Field::Handle(zone)),
|
||||
completer_future_field(Field::Handle(zone)),
|
||||
future_result_or_listeners_field(Field::Handle(zone)),
|
||||
callback_field(Field::Handle(zone)),
|
||||
controller_controller_field(Field::Handle(zone)),
|
||||
var_data_field(Field::Handle(zone)),
|
||||
state_field(Field::Handle(zone)),
|
||||
on_data_field(Field::Handle(zone)) {
|
||||
const auto& async_lib = Library::Handle(zone, Library::AsyncLibrary());
|
||||
// Look up classes:
|
||||
// - async:
|
||||
future_impl_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::FutureImpl());
|
||||
ASSERT(!future_impl_class.IsNull());
|
||||
async_await_completer_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_AsyncAwaitCompleter());
|
||||
ASSERT(!async_await_completer_class.IsNull());
|
||||
future_listener_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_FutureListener());
|
||||
ASSERT(!future_listener_class.IsNull());
|
||||
// - async*:
|
||||
async_start_stream_controller_class = async_lib.LookupClassAllowPrivate(
|
||||
Symbols::_AsyncStarStreamController());
|
||||
ASSERT(!async_start_stream_controller_class.IsNull());
|
||||
stream_controller_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_StreamController());
|
||||
ASSERT(!stream_controller_class.IsNull());
|
||||
async_stream_controller_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_AsyncStreamController());
|
||||
ASSERT(!async_stream_controller_class.IsNull());
|
||||
controller_subscription_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_ControllerSubscription());
|
||||
ASSERT(!controller_subscription_class.IsNull());
|
||||
buffering_stream_subscription_class = async_lib.LookupClassAllowPrivate(
|
||||
Symbols::_BufferingStreamSubscription());
|
||||
ASSERT(!buffering_stream_subscription_class.IsNull());
|
||||
|
||||
// Look up fields:
|
||||
// - async:
|
||||
completer_is_sync_field =
|
||||
async_await_completer_class.LookupFieldAllowPrivate(Symbols::isSync());
|
||||
ASSERT(!completer_is_sync_field.IsNull());
|
||||
completer_future_field =
|
||||
async_await_completer_class.LookupFieldAllowPrivate(Symbols::_future());
|
||||
ASSERT(!completer_future_field.IsNull());
|
||||
future_result_or_listeners_field =
|
||||
future_impl_class.LookupFieldAllowPrivate(
|
||||
Symbols::_resultOrListeners());
|
||||
ASSERT(!future_result_or_listeners_field.IsNull());
|
||||
callback_field =
|
||||
future_listener_class.LookupFieldAllowPrivate(Symbols::callback());
|
||||
ASSERT(!callback_field.IsNull());
|
||||
// - async*:
|
||||
controller_controller_field =
|
||||
async_start_stream_controller_class.LookupFieldAllowPrivate(
|
||||
Symbols::controller());
|
||||
ASSERT(!controller_controller_field.IsNull());
|
||||
state_field =
|
||||
stream_controller_class.LookupFieldAllowPrivate(Symbols::_state());
|
||||
ASSERT(!state_field.IsNull());
|
||||
var_data_field =
|
||||
stream_controller_class.LookupFieldAllowPrivate(Symbols::_varData());
|
||||
ASSERT(!var_data_field.IsNull());
|
||||
on_data_field = buffering_stream_subscription_class.LookupFieldAllowPrivate(
|
||||
Symbols::_onData());
|
||||
ASSERT(!on_data_field.IsNull());
|
||||
}
|
||||
|
||||
RawClosure* FindCallerInAsyncClosure(const Context& receiver_context) {
|
||||
context_entry_ = receiver_context.At(Context::kAsyncCompleterIndex);
|
||||
ASSERT(context_entry_.IsInstance());
|
||||
ASSERT(context_entry_.GetClassId() == async_await_completer_class.id());
|
||||
|
||||
const Instance& completer = Instance::Cast(context_entry_);
|
||||
future_ = completer.GetField(completer_future_field);
|
||||
ASSERT(!future_.IsNull());
|
||||
ASSERT(future_.GetClassId() == future_impl_class.id());
|
||||
|
||||
listener_ =
|
||||
Instance::Cast(future_).GetField(future_result_or_listeners_field);
|
||||
if (listener_.GetClassId() != future_listener_class.id()) {
|
||||
return Closure::null();
|
||||
}
|
||||
|
||||
callback_ = Instance::Cast(listener_).GetField(callback_field);
|
||||
// This happens for e.g.: await f().catchError(..);
|
||||
if (callback_.IsNull()) {
|
||||
return Closure::null();
|
||||
}
|
||||
ASSERT(callback_.IsClosure());
|
||||
|
||||
return Closure::Cast(callback_).raw();
|
||||
}
|
||||
|
||||
RawClosure* FindCallerInAsyncGenClosure(const Context& receiver_context) {
|
||||
context_entry_ = receiver_context.At(Context::kControllerIndex);
|
||||
ASSERT(context_entry_.IsInstance());
|
||||
ASSERT(context_entry_.GetClassId() ==
|
||||
async_start_stream_controller_class.id());
|
||||
|
||||
const Instance& controller = Instance::Cast(context_entry_);
|
||||
controller_ = controller.GetField(controller_controller_field);
|
||||
ASSERT(!controller_.IsNull());
|
||||
ASSERT(controller_.GetClassId() == async_stream_controller_class.id());
|
||||
|
||||
state_ = Instance::Cast(controller_).GetField(state_field);
|
||||
ASSERT(state_.IsSmi());
|
||||
if (Smi::Cast(state_).Value() != kStreamController_StateSubscribed) {
|
||||
return Closure::null();
|
||||
}
|
||||
|
||||
var_data_ = Instance::Cast(controller_).GetField(var_data_field);
|
||||
ASSERT(var_data_.GetClassId() == controller_subscription_class.id());
|
||||
|
||||
callback_ = Instance::Cast(var_data_).GetField(on_data_field);
|
||||
ASSERT(callback_.IsClosure());
|
||||
|
||||
return Closure::Cast(callback_).raw();
|
||||
}
|
||||
|
||||
RawClosure* FindCaller(const Closure& receiver_closure) {
|
||||
receiver_function_ = receiver_closure.function();
|
||||
receiver_context_ = receiver_closure.context();
|
||||
|
||||
if (receiver_function_.IsAsyncClosure()) {
|
||||
return FindCallerInAsyncClosure(receiver_context_);
|
||||
} else if (receiver_function_.IsAsyncGenClosure()) {
|
||||
return FindCallerInAsyncGenClosure(receiver_context_);
|
||||
}
|
||||
|
||||
return Closure::null();
|
||||
}
|
||||
|
||||
bool IsRunningAsync(const Closure& receiver_closure) {
|
||||
receiver_function_ = receiver_closure.function();
|
||||
receiver_context_ = receiver_closure.context();
|
||||
|
||||
// The async* functions are never started synchronously, they start running
|
||||
// after the first `listen()` call to its returned `Stream`.
|
||||
if (receiver_function_.IsAsyncGenClosure()) {
|
||||
return true;
|
||||
}
|
||||
ASSERT(receiver_function_.IsAsyncClosure());
|
||||
|
||||
context_entry_ = receiver_context_.At(Context::kAsyncCompleterIndex);
|
||||
ASSERT(context_entry_.IsInstance());
|
||||
ASSERT(context_entry_.GetClassId() == async_await_completer_class.id());
|
||||
|
||||
const Instance& completer = Instance::Cast(context_entry_);
|
||||
is_sync = completer.GetField(completer_is_sync_field);
|
||||
ASSERT(!is_sync.IsNull());
|
||||
ASSERT(is_sync.IsBool());
|
||||
// _AsyncAwaitCompleter.isSync indicates whether the future should be
|
||||
// completed async. or sync., based on whether it has yielded yet.
|
||||
// isSync is true when the :async_op is running async.
|
||||
return Bool::Cast(is_sync).value();
|
||||
}
|
||||
|
||||
private:
|
||||
Context& receiver_context_;
|
||||
Function& receiver_function_;
|
||||
|
||||
Object& context_entry_;
|
||||
Object& is_sync;
|
||||
Object& future_;
|
||||
Object& listener_;
|
||||
Object& callback_;
|
||||
Object& controller_;
|
||||
Object& state_;
|
||||
Object& var_data_;
|
||||
|
||||
Class& future_impl_class;
|
||||
Class& async_await_completer_class;
|
||||
Class& future_listener_class;
|
||||
Class& async_start_stream_controller_class;
|
||||
Class& stream_controller_class;
|
||||
Class& controller_subscription_class;
|
||||
Class& buffering_stream_subscription_class;
|
||||
Class& async_stream_controller_class;
|
||||
|
||||
Field& completer_is_sync_field;
|
||||
Field& completer_future_field;
|
||||
Field& future_result_or_listeners_field;
|
||||
Field& callback_field;
|
||||
Field& controller_controller_field;
|
||||
Field& var_data_field;
|
||||
Field& state_field;
|
||||
Field& on_data_field;
|
||||
};
|
||||
|
||||
void StackTraceUtils::CollectFramesLazy(
|
||||
Thread* thread,
|
||||
const GrowableObjectArray& code_array,
|
||||
@@ -106,15 +351,15 @@ void StackTraceUtils::CollectFramesLazy(
|
||||
|
||||
// Next, look up caller's closure on the stack and walk backwards through
|
||||
// the yields.
|
||||
RawObject** last_caller_obj =
|
||||
reinterpret_cast<RawObject**>(frame->GetCallerSp());
|
||||
frame = frames.NextFrame();
|
||||
RawObject** last_caller_obj = reinterpret_cast<RawObject**>(frame->sp());
|
||||
closure = FindClosureInFrame(last_caller_obj, function,
|
||||
frame->is_interpreted());
|
||||
|
||||
// If this async function hasn't yielded yet, we're still dealing with a
|
||||
// normal stack. Continue to next frame as usual.
|
||||
if (!caller_closure_finder.IsRunningAsync(closure)) {
|
||||
frame = frames.NextFrame();
|
||||
// Don't advance frame since we already did so just above.
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -190,22 +435,26 @@ intptr_t StackTraceUtils::CountFrames(Thread* thread,
|
||||
bool* sync_async_end) {
|
||||
Zone* zone = thread->zone();
|
||||
intptr_t frame_count = 0;
|
||||
DartFrameIterator frames(thread, StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != nullptr); // We expect to find a dart invocation frame.
|
||||
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
||||
Function& function = Function::Handle(zone);
|
||||
Code& code = Code::Handle(zone);
|
||||
Bytecode& bytecode = Bytecode::Handle(zone);
|
||||
Closure& closure = Closure::Handle(zone);
|
||||
CallerClosureFinder caller_closure_finder(zone);
|
||||
String& function_name = String::Handle(zone);
|
||||
const bool async_function_is_null = async_function.IsNull();
|
||||
ASSERT(async_function_is_null || sync_async_end != nullptr);
|
||||
for (; frame != nullptr; frame = frames.NextFrame()) {
|
||||
int sync_async_gap_frames = -1;
|
||||
ASSERT(async_function_is_null || sync_async_end != NULL);
|
||||
for (; frame != NULL && sync_async_gap_frames != 0;
|
||||
frame = frames.NextFrame()) {
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
}
|
||||
if (skip_frames > 0) {
|
||||
skip_frames--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frame->is_interpreted()) {
|
||||
bytecode = frame->LookupDartBytecode();
|
||||
function = bytecode.function();
|
||||
@@ -216,35 +465,22 @@ intptr_t StackTraceUtils::CountFrames(Thread* thread,
|
||||
code = frame->LookupDartCode();
|
||||
function = code.function();
|
||||
}
|
||||
|
||||
frame_count++;
|
||||
|
||||
// If we're only walking the stack for a particular async func. (for
|
||||
// --causal-async-stacks) return whether it's running sync-async.
|
||||
if (!async_function_is_null &&
|
||||
(async_function.raw() == function.parent_function())) {
|
||||
if (function.IsAsyncGenClosure()) {
|
||||
// async* is always running async.
|
||||
if (sync_async_gap_frames > 0) {
|
||||
function_name = function.QualifiedScrubbedName();
|
||||
if (!CheckAndSkipAsync(&sync_async_gap_frames, function_name)) {
|
||||
*sync_async_end = false;
|
||||
return frame_count;
|
||||
} else if (function.IsAsyncClosure()) {
|
||||
// Extract closure from next frame to determine if we're running async.
|
||||
RawObject** last_caller_obj =
|
||||
reinterpret_cast<RawObject**>(frame->GetCallerSp());
|
||||
closure = FindClosureInFrame(last_caller_obj, function,
|
||||
frame->is_interpreted());
|
||||
if (caller_closure_finder.IsRunningAsync(closure)) {
|
||||
*sync_async_end = false;
|
||||
return frame_count;
|
||||
}
|
||||
|
||||
*sync_async_end = true;
|
||||
return frame_count;
|
||||
}
|
||||
} else {
|
||||
frame_count++;
|
||||
}
|
||||
if (!async_function_is_null &&
|
||||
(async_function.raw() == function.parent_function())) {
|
||||
sync_async_gap_frames = kSyncAsyncFrameGap;
|
||||
}
|
||||
}
|
||||
if (!async_function_is_null) {
|
||||
*sync_async_end = true;
|
||||
*sync_async_end = sync_async_gap_frames == 0;
|
||||
}
|
||||
return frame_count;
|
||||
}
|
||||
@@ -256,7 +492,8 @@ intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
||||
intptr_t count,
|
||||
int skip_frames) {
|
||||
Zone* zone = thread->zone();
|
||||
DartFrameIterator frames(thread, StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
||||
Function& function = Function::Handle(zone);
|
||||
@@ -266,6 +503,9 @@ intptr_t StackTraceUtils::CollectFrames(Thread* thread,
|
||||
intptr_t collected_frames_count = 0;
|
||||
for (; (frame != NULL) && (collected_frames_count < count);
|
||||
frame = frames.NextFrame()) {
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
}
|
||||
if (skip_frames > 0) {
|
||||
skip_frames--;
|
||||
continue;
|
||||
@@ -327,183 +567,4 @@ intptr_t StackTraceUtils::ExtractAsyncStackTraceInfo(
|
||||
return async_stack_trace_length;
|
||||
}
|
||||
|
||||
CallerClosureFinder::CallerClosureFinder(Zone* zone)
|
||||
: receiver_context_(Context::Handle(zone)),
|
||||
receiver_function_(Function::Handle(zone)),
|
||||
context_entry_(Object::Handle(zone)),
|
||||
is_sync(Object::Handle(zone)),
|
||||
future_(Object::Handle(zone)),
|
||||
listener_(Object::Handle(zone)),
|
||||
callback_(Object::Handle(zone)),
|
||||
controller_(Object::Handle(zone)),
|
||||
state_(Object::Handle(zone)),
|
||||
var_data_(Object::Handle(zone)),
|
||||
future_impl_class(Class::Handle(zone)),
|
||||
async_await_completer_class(Class::Handle(zone)),
|
||||
future_listener_class(Class::Handle(zone)),
|
||||
async_start_stream_controller_class(Class::Handle(zone)),
|
||||
stream_controller_class(Class::Handle(zone)),
|
||||
controller_subscription_class(Class::Handle(zone)),
|
||||
buffering_stream_subscription_class(Class::Handle(zone)),
|
||||
async_stream_controller_class(Class::Handle(zone)),
|
||||
completer_is_sync_field(Field::Handle(zone)),
|
||||
completer_future_field(Field::Handle(zone)),
|
||||
future_result_or_listeners_field(Field::Handle(zone)),
|
||||
callback_field(Field::Handle(zone)),
|
||||
controller_controller_field(Field::Handle(zone)),
|
||||
var_data_field(Field::Handle(zone)),
|
||||
state_field(Field::Handle(zone)),
|
||||
on_data_field(Field::Handle(zone)) {
|
||||
// Instance caches library and field references.
|
||||
// This way we don't have to do the look-ups for every frame in the stack.
|
||||
const auto& async_lib = Library::Handle(zone, Library::AsyncLibrary());
|
||||
// Look up classes:
|
||||
// - async:
|
||||
future_impl_class = async_lib.LookupClassAllowPrivate(Symbols::FutureImpl());
|
||||
ASSERT(!future_impl_class.IsNull());
|
||||
async_await_completer_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_AsyncAwaitCompleter());
|
||||
ASSERT(!async_await_completer_class.IsNull());
|
||||
future_listener_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_FutureListener());
|
||||
ASSERT(!future_listener_class.IsNull());
|
||||
// - async*:
|
||||
async_start_stream_controller_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController());
|
||||
ASSERT(!async_start_stream_controller_class.IsNull());
|
||||
stream_controller_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_StreamController());
|
||||
ASSERT(!stream_controller_class.IsNull());
|
||||
async_stream_controller_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_AsyncStreamController());
|
||||
ASSERT(!async_stream_controller_class.IsNull());
|
||||
controller_subscription_class =
|
||||
async_lib.LookupClassAllowPrivate(Symbols::_ControllerSubscription());
|
||||
ASSERT(!controller_subscription_class.IsNull());
|
||||
buffering_stream_subscription_class = async_lib.LookupClassAllowPrivate(
|
||||
Symbols::_BufferingStreamSubscription());
|
||||
ASSERT(!buffering_stream_subscription_class.IsNull());
|
||||
|
||||
// Look up fields:
|
||||
// - async:
|
||||
completer_is_sync_field =
|
||||
async_await_completer_class.LookupFieldAllowPrivate(Symbols::isSync());
|
||||
ASSERT(!completer_is_sync_field.IsNull());
|
||||
completer_future_field =
|
||||
async_await_completer_class.LookupFieldAllowPrivate(Symbols::_future());
|
||||
ASSERT(!completer_future_field.IsNull());
|
||||
future_result_or_listeners_field =
|
||||
future_impl_class.LookupFieldAllowPrivate(Symbols::_resultOrListeners());
|
||||
ASSERT(!future_result_or_listeners_field.IsNull());
|
||||
callback_field =
|
||||
future_listener_class.LookupFieldAllowPrivate(Symbols::callback());
|
||||
ASSERT(!callback_field.IsNull());
|
||||
// - async*:
|
||||
controller_controller_field =
|
||||
async_start_stream_controller_class.LookupFieldAllowPrivate(
|
||||
Symbols::controller());
|
||||
ASSERT(!controller_controller_field.IsNull());
|
||||
state_field =
|
||||
stream_controller_class.LookupFieldAllowPrivate(Symbols::_state());
|
||||
ASSERT(!state_field.IsNull());
|
||||
var_data_field =
|
||||
stream_controller_class.LookupFieldAllowPrivate(Symbols::_varData());
|
||||
ASSERT(!var_data_field.IsNull());
|
||||
on_data_field = buffering_stream_subscription_class.LookupFieldAllowPrivate(
|
||||
Symbols::_onData());
|
||||
ASSERT(!on_data_field.IsNull());
|
||||
}
|
||||
|
||||
RawClosure* CallerClosureFinder::FindCallerInAsyncClosure(
|
||||
const Context& receiver_context) {
|
||||
context_entry_ = receiver_context.At(Context::kAsyncCompleterIndex);
|
||||
ASSERT(context_entry_.IsInstance());
|
||||
ASSERT(context_entry_.GetClassId() == async_await_completer_class.id());
|
||||
|
||||
const Instance& completer = Instance::Cast(context_entry_);
|
||||
future_ = completer.GetField(completer_future_field);
|
||||
ASSERT(!future_.IsNull());
|
||||
ASSERT(future_.GetClassId() == future_impl_class.id());
|
||||
|
||||
listener_ =
|
||||
Instance::Cast(future_).GetField(future_result_or_listeners_field);
|
||||
if (listener_.GetClassId() != future_listener_class.id()) {
|
||||
return Closure::null();
|
||||
}
|
||||
|
||||
callback_ = Instance::Cast(listener_).GetField(callback_field);
|
||||
// This happens for e.g.: await f().catchError(..);
|
||||
if (callback_.IsNull()) {
|
||||
return Closure::null();
|
||||
}
|
||||
ASSERT(callback_.IsClosure());
|
||||
|
||||
return Closure::Cast(callback_).raw();
|
||||
}
|
||||
|
||||
RawClosure* CallerClosureFinder::FindCallerInAsyncGenClosure(
|
||||
const Context& receiver_context) {
|
||||
context_entry_ = receiver_context.At(Context::kControllerIndex);
|
||||
ASSERT(context_entry_.IsInstance());
|
||||
ASSERT(context_entry_.GetClassId() ==
|
||||
async_start_stream_controller_class.id());
|
||||
|
||||
const Instance& controller = Instance::Cast(context_entry_);
|
||||
controller_ = controller.GetField(controller_controller_field);
|
||||
ASSERT(!controller_.IsNull());
|
||||
ASSERT(controller_.GetClassId() == async_stream_controller_class.id());
|
||||
|
||||
state_ = Instance::Cast(controller_).GetField(state_field);
|
||||
ASSERT(state_.IsSmi());
|
||||
if (Smi::Cast(state_).Value() != kStreamController_StateSubscribed) {
|
||||
return Closure::null();
|
||||
}
|
||||
|
||||
var_data_ = Instance::Cast(controller_).GetField(var_data_field);
|
||||
ASSERT(var_data_.GetClassId() == controller_subscription_class.id());
|
||||
|
||||
callback_ = Instance::Cast(var_data_).GetField(on_data_field);
|
||||
ASSERT(callback_.IsClosure());
|
||||
|
||||
return Closure::Cast(callback_).raw();
|
||||
}
|
||||
|
||||
RawClosure* CallerClosureFinder::FindCaller(const Closure& receiver_closure) {
|
||||
receiver_function_ = receiver_closure.function();
|
||||
receiver_context_ = receiver_closure.context();
|
||||
|
||||
if (receiver_function_.IsAsyncClosure()) {
|
||||
return FindCallerInAsyncClosure(receiver_context_);
|
||||
} else if (receiver_function_.IsAsyncGenClosure()) {
|
||||
return FindCallerInAsyncGenClosure(receiver_context_);
|
||||
}
|
||||
|
||||
return Closure::null();
|
||||
}
|
||||
|
||||
bool CallerClosureFinder::IsRunningAsync(const Closure& receiver_closure) {
|
||||
receiver_function_ = receiver_closure.function();
|
||||
receiver_context_ = receiver_closure.context();
|
||||
|
||||
// The async* functions are never started synchronously, they start running
|
||||
// after the first `listen()` call to its returned `Stream`.
|
||||
if (receiver_function_.IsAsyncGenClosure()) {
|
||||
return true;
|
||||
}
|
||||
ASSERT(receiver_function_.IsAsyncClosure());
|
||||
|
||||
context_entry_ = receiver_context_.At(Context::kAsyncCompleterIndex);
|
||||
ASSERT(context_entry_.IsInstance());
|
||||
ASSERT(context_entry_.GetClassId() == async_await_completer_class.id());
|
||||
|
||||
const Instance& completer = Instance::Cast(context_entry_);
|
||||
is_sync = completer.GetField(completer_is_sync_field);
|
||||
ASSERT(!is_sync.IsNull());
|
||||
ASSERT(is_sync.IsBool());
|
||||
// _AsyncAwaitCompleter.isSync indicates whether the future should be
|
||||
// completed async. or sync., based on whether it has yielded yet.
|
||||
// isSync is true when the :async_op is running async.
|
||||
return Bool::Cast(is_sync).value();
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
+26
-76
@@ -64,87 +64,37 @@ class StackTraceUtils : public AllStatic {
|
||||
Array* async_code_array,
|
||||
Array* async_pc_offset_array);
|
||||
|
||||
// Find the closure corresponding to `function` in (presumably) its parent
|
||||
// stack frame (based on the frame's SP).
|
||||
static RawClosure* FindClosureInFrame(RawObject** last_object_in_caller,
|
||||
const Function& function,
|
||||
bool is_interpreted) {
|
||||
NoSafepointScope nsp;
|
||||
// The number of frames involved in a "sync-async" gap: a synchronous initial
|
||||
// invocation of an asynchronous function. See CheckAndSkipAsync.
|
||||
static constexpr intptr_t kSyncAsyncFrameGap = 2;
|
||||
|
||||
// The callee has function signature
|
||||
// :async_op([result, exception, stack])
|
||||
// So we are guaranteed to
|
||||
// a) have only (up to three) tagged arguments on the stack until we find
|
||||
// the :async_op closure, and
|
||||
// b) find the async closure.
|
||||
auto& closure = Closure::Handle();
|
||||
for (intptr_t i = 0; i < 4; i++) {
|
||||
// KBC builds the stack upwards instead of the usual downwards stack.
|
||||
RawObject* arg = last_object_in_caller[(is_interpreted ? -i : i)];
|
||||
if (arg->IsHeapObject() && arg->GetClassId() == kClosureCid) {
|
||||
closure = Closure::RawCast(arg);
|
||||
if (closure.function() == function.raw()) {
|
||||
return closure.raw();
|
||||
}
|
||||
}
|
||||
// A synchronous invocation of an async function involves the following
|
||||
// frames:
|
||||
// <async function>__<anonymous_closure> (0)
|
||||
// _Closure.call (1)
|
||||
// _AsyncAwaitCompleter.start (2)
|
||||
// <async_function> (3)
|
||||
//
|
||||
// Alternatively, for bytecode or optimized frames, we may see:
|
||||
// <async function>__<anonymous_closure> (0)
|
||||
// _AsyncAwaitCompleter.start (1)
|
||||
// <async_function> (2)
|
||||
static bool CheckAndSkipAsync(int* skip_sync_async_frames_count,
|
||||
const String& function_name) {
|
||||
ASSERT(*skip_sync_async_frames_count > 0);
|
||||
if (function_name.Equals(Symbols::_AsyncAwaitCompleterStart())) {
|
||||
*skip_sync_async_frames_count = 0;
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE();
|
||||
if (function_name.Equals(Symbols::_ClosureCall()) &&
|
||||
*skip_sync_async_frames_count == 2) {
|
||||
(*skip_sync_async_frames_count)--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class for finding the closure of the caller.
|
||||
// This is done via the _AsyncAwaitCompleter which holds a
|
||||
// FutureResultOrListeners which in turn holds a callback.
|
||||
class CallerClosureFinder {
|
||||
public:
|
||||
// Instance caches library and field references.
|
||||
// This way we don't have to do the look-ups for every frame in the stack.
|
||||
explicit CallerClosureFinder(Zone* zone);
|
||||
|
||||
RawClosure* FindCallerInAsyncClosure(const Context& receiver_context);
|
||||
|
||||
RawClosure* FindCallerInAsyncGenClosure(const Context& receiver_context);
|
||||
|
||||
RawClosure* FindCaller(const Closure& receiver_closure);
|
||||
|
||||
bool IsRunningAsync(const Closure& receiver_closure);
|
||||
|
||||
private:
|
||||
// Keep in sync with
|
||||
// sdk/lib/async/stream_controller.dart:_StreamController._STATE_SUBSCRIBED.
|
||||
const intptr_t kStreamController_StateSubscribed = 1;
|
||||
|
||||
Context& receiver_context_;
|
||||
Function& receiver_function_;
|
||||
|
||||
Object& context_entry_;
|
||||
Object& is_sync;
|
||||
Object& future_;
|
||||
Object& listener_;
|
||||
Object& callback_;
|
||||
Object& controller_;
|
||||
Object& state_;
|
||||
Object& var_data_;
|
||||
|
||||
Class& future_impl_class;
|
||||
Class& async_await_completer_class;
|
||||
Class& future_listener_class;
|
||||
Class& async_start_stream_controller_class;
|
||||
Class& stream_controller_class;
|
||||
Class& controller_subscription_class;
|
||||
Class& buffering_stream_subscription_class;
|
||||
Class& async_stream_controller_class;
|
||||
|
||||
Field& completer_is_sync_field;
|
||||
Field& completer_future_field;
|
||||
Field& future_result_or_listeners_field;
|
||||
Field& callback_field;
|
||||
Field& controller_controller_field;
|
||||
Field& var_data_field;
|
||||
Field& state_field;
|
||||
Field& on_data_field;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_STACK_TRACE_H_
|
||||
|
||||
@@ -800,7 +800,6 @@ abstract class _AsyncStreamControllerDispatch<T>
|
||||
// TODO(lrn): Use common superclass for callback-controllers when VM supports
|
||||
// constructors in mixin superclasses.
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
class _AsyncStreamController<T> = _StreamController<T>
|
||||
with _AsyncStreamControllerDispatch<T>;
|
||||
|
||||
|
||||
@@ -807,7 +807,6 @@ abstract class _AsyncStreamControllerDispatch<T>
|
||||
// TODO(lrn): Use common superclass for callback-controllers when VM supports
|
||||
// constructors in mixin superclasses.
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
class _AsyncStreamController<T> = _StreamController<T>
|
||||
with _AsyncStreamControllerDispatch<T>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user