[vm] Cleanup more async-related code

This change continues cleanup of async implementation in the VM.

TEST=ci

Issue: https://github.com/dart-lang/sdk/issues/48378
Change-Id: Icdaeab18bcdc0d6974bc45841b630822cd1ac114
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251441
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2022-07-14 16:04:54 +00:00 committed by Commit Bot
parent 9391343387
commit 2ad557a892
12 changed files with 44 additions and 151 deletions

View file

@ -698,22 +698,14 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionBody(
return body;
}
Fragment StreamingFlowGraphBuilder::BuildEveryTimePrologue(
Fragment StreamingFlowGraphBuilder::BuildRegularFunctionPrologue(
const Function& dart_function,
TokenPosition token_position,
intptr_t type_parameters_offset) {
LocalVariable* first_parameter) {
Fragment F;
F += CheckStackOverflowInPrologue(dart_function);
F += DebugStepCheckInPrologue(dart_function, token_position);
F += B->InitConstantParameters();
return F;
}
Fragment StreamingFlowGraphBuilder::BuildFirstTimePrologue(
const Function& dart_function,
LocalVariable* first_parameter,
intptr_t type_parameters_offset) {
Fragment F;
F += SetupCapturedParameters(dart_function);
F += ShortcutForUserDefinedEquals(dart_function, first_parameter);
return F;
@ -747,8 +739,7 @@ Fragment StreamingFlowGraphBuilder::ClearRawParameters(
UncheckedEntryPointStyle StreamingFlowGraphBuilder::ChooseEntryPointStyle(
const Function& dart_function,
const Fragment& implicit_type_checks,
const Fragment& first_time_prologue,
const Fragment& every_time_prologue,
const Fragment& regular_function_prologue,
const Fragment& type_args_handling) {
ASSERT(!dart_function.IsImplicitClosureFunction());
if (!dart_function.MayHaveUncheckedEntryPoint() ||
@ -761,17 +752,21 @@ UncheckedEntryPointStyle StreamingFlowGraphBuilder::ChooseEntryPointStyle(
//
// 1. There is a non-empty PrologueBuilder-prologue.
//
// 2. There is a non-empty "first-time" prologue.
// 2. The regular function prologue has more than two instructions
// (DebugStepCheck and CheckStackOverflow).
//
// 3. The "every-time" prologue has more than two instructions (DebugStepCheck
// and CheckStackOverflow).
//
// TODO(#34162): For regular closures we can often avoid the
// PrologueBuilder-prologue on non-dynamic invocations.
if (!PrologueBuilder::HasEmptyPrologue(dart_function) ||
!type_args_handling.is_empty() || !first_time_prologue.is_empty() ||
!(every_time_prologue.entry == every_time_prologue.current ||
every_time_prologue.current->previous() == every_time_prologue.entry)) {
!type_args_handling.is_empty()) {
return UncheckedEntryPointStyle::kSharedWithVariable;
}
Instruction* instr = regular_function_prologue.entry;
if (instr != nullptr && instr->IsCheckStackOverflow()) {
instr = instr->next();
}
if (instr != nullptr && instr->IsDebugStepCheck()) {
instr = instr->next();
}
if (instr != nullptr) {
return UncheckedEntryPointStyle::kSharedWithVariable;
}
@ -782,15 +777,11 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
bool is_constructor) {
const Function& dart_function = parsed_function()->function();
intptr_t type_parameters_offset = 0;
LocalVariable* first_parameter = nullptr;
TokenPosition token_position = TokenPosition::kNoSource;
{
AlternativeReadingScope alt(&reader_);
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kTypeParameters);
type_parameters_offset = ReaderOffset();
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
intptr_t list_length = ReadListLength(); // read number of positionals.
@ -811,15 +802,8 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
BlockEntryInstr* instruction_cursor =
flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info);
// The 'every_time_prologue' runs first and is run when resuming from yield
// points.
const Fragment every_time_prologue = BuildEveryTimePrologue(
dart_function, token_position, type_parameters_offset);
// The 'first_time_prologue' run after 'every_time_prologue' and is *not* run
// when resuming from yield points.
const Fragment first_time_prologue = BuildFirstTimePrologue(
dart_function, first_parameter, type_parameters_offset);
const Fragment regular_prologue = BuildRegularFunctionPrologue(
dart_function, token_position, first_parameter);
// TODO(#34162): We can remove the default type handling (and
// shorten the prologue type handling sequence) for non-dynamic invocations of
@ -847,30 +831,28 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
InitSuspendableFunction(dart_function) +
BuildFunctionBody(dart_function, first_parameter, is_constructor);
auto extra_entry_point_style = ChooseEntryPointStyle(
dart_function, implicit_type_checks, first_time_prologue,
every_time_prologue, type_args_handling);
auto extra_entry_point_style =
ChooseEntryPointStyle(dart_function, implicit_type_checks,
regular_prologue, type_args_handling);
Fragment function(instruction_cursor);
FunctionEntryInstr* extra_entry = nullptr;
switch (extra_entry_point_style) {
case UncheckedEntryPointStyle::kNone: {
function += every_time_prologue + first_time_prologue +
type_args_handling + implicit_type_checks +
function += regular_prologue + type_args_handling + implicit_type_checks +
explicit_type_checks + body;
break;
}
case UncheckedEntryPointStyle::kSeparate: {
ASSERT(instruction_cursor == normal_entry);
ASSERT(first_time_prologue.is_empty());
ASSERT(type_args_handling.is_empty());
const Fragment prologue_copy = BuildEveryTimePrologue(
dart_function, token_position, type_parameters_offset);
const Fragment prologue_copy = BuildRegularFunctionPrologue(
dart_function, token_position, first_parameter);
extra_entry = B->BuildSeparateUncheckedEntryPoint(
normal_entry,
/*normal_prologue=*/every_time_prologue + implicit_type_checks,
/*normal_prologue=*/regular_prologue + implicit_type_checks,
/*extra_prologue=*/prologue_copy,
/*shared_prologue=*/explicit_type_checks,
/*body=*/body);
@ -878,8 +860,7 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
}
case UncheckedEntryPointStyle::kSharedWithVariable: {
Fragment prologue(normal_entry, instruction_cursor);
prologue += every_time_prologue;
prologue += first_time_prologue;
prologue += regular_prologue;
prologue += type_args_handling;
prologue += explicit_type_checks;
extra_entry = B->BuildSharedUncheckedEntryPoint(

View file

@ -82,16 +82,12 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
bool constructor);
// Pieces of the prologue. They are all agnostic to the current Kernel offset.
Fragment BuildEveryTimePrologue(const Function& dart_function,
TokenPosition token_position,
intptr_t type_parameters_offset);
Fragment BuildFirstTimePrologue(const Function& dart_function,
LocalVariable* first_parameter,
intptr_t type_parameters_offset);
Fragment BuildRegularFunctionPrologue(const Function& dart_function,
TokenPosition token_position,
LocalVariable* first_parameter);
Fragment ClearRawParameters(const Function& dart_function);
Fragment DebugStepCheckInPrologue(const Function& dart_function,
TokenPosition position);
Fragment SetAsyncStackTrace(const Function& dart_function);
Fragment CheckStackOverflowInPrologue(const Function& dart_function);
Fragment SetupCapturedParameters(const Function& dart_function);
Fragment InitSuspendableFunction(const Function& dart_function);
@ -102,8 +98,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
static UncheckedEntryPointStyle ChooseEntryPointStyle(
const Function& dart_function,
const Fragment& implicit_type_checks,
const Fragment& first_time_prologue,
const Fragment& every_time_prologue,
const Fragment& regular_function_prologue,
const Fragment& type_args_handling);
void loop_depth_inc();

View file

@ -148,7 +148,6 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
helper_.ReadUntilFunctionNode();
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
current_function_async_marker_ = function_node_helper.async_marker_;
// NOTE: FunctionNode is read further below the if.
intptr_t pos = 0;
@ -368,7 +367,6 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
scope_->InsertParameterAt(pos++, parsed_function_->receiver_var());
// Create all positional and named parameters.
current_function_async_marker_ = FunctionNodeHelper::kSync;
AddPositionalAndNamedParameters(
pos, kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
attrs);
@ -393,7 +391,6 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
// Callbacks and calls with handles need try/catch variables.
if ((function.FfiCallbackTarget() != Function::null() ||
function.FfiCSignatureContainsHandles())) {
current_function_async_marker_ = FunctionNodeHelper::kSync;
++depth_.try_;
AddTryVariables();
--depth_.try_;
@ -1462,13 +1459,10 @@ void ScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
LocalScope* saved_function_scope = current_function_scope_;
FunctionNodeHelper::AsyncMarker saved_function_async_marker =
current_function_async_marker_;
DepthState saved_depth_state = depth_;
depth_ = DepthState(depth_.function_ + 1);
EnterScope(parent_kernel_offset);
current_function_scope_ = scope_;
current_function_async_marker_ = function_node_helper.async_marker_;
if (depth_.function_ == 1) {
FunctionScope function_scope = {offset, scope_};
result_->function_scopes.Add(function_scope);
@ -1516,7 +1510,6 @@ void ScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
ExitScope(function_node_helper.position_, function_node_helper.end_position_);
depth_ = saved_depth_state;
current_function_scope_ = saved_function_scope;
current_function_async_marker_ = saved_function_async_marker;
}
void ScopeBuilder::EnterScope(intptr_t kernel_offset) {

View file

@ -154,7 +154,6 @@ class ScopeBuilder {
TranslationHelper translation_helper_;
Zone* zone_;
FunctionNodeHelper::AsyncMarker current_function_async_marker_;
LocalScope* current_function_scope_;
LocalScope* scope_;
DepthState depth_;

View file

@ -97,9 +97,9 @@ namespace dart {
0xc40903ac) \
V(_SuspendState, _clone, SuspendState_clone, 0xae1a40a0) \
V(_SuspendState, _createAsyncCallbacks, SuspendState_createAsyncCallbacks, \
0x68be1bf3) \
0x967521b1) \
V(_SuspendState, _createAsyncStarCallback, \
SuspendState_createAsyncStarCallback, 0xfa7537e4) \
SuspendState_createAsyncStarCallback, 0xa50f923c) \
V(_SuspendState, _resume, SuspendState_resume, 0x5d7a8489) \
V(_IntegerImplementation, toDouble, IntegerToDouble, 0x97728b46) \
V(_Double, _add, DoubleAdd, 0xea666327) \

View file

@ -306,7 +306,7 @@ ActivationFrame::ActivationFrame(const Closure& async_activation,
pc_desc_(PcDescriptors::ZoneHandle()) {
// Extract the function and the code from the asynchronous activation.
function_ = async_activation.function();
if (caller_closure_finder->IsCompactAsyncCallback(function_)) {
if (caller_closure_finder->IsAsyncCallback(function_)) {
const auto& suspend_state = SuspendState::Handle(
caller_closure_finder->GetSuspendStateFromAsyncCallback(
async_activation));
@ -2944,16 +2944,6 @@ Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure,
return bpt_location->AddPerClosure(this, closure, for_over_await);
}
Breakpoint* Debugger::SetBreakpointAtAsyncOp(const Function& async_op) {
const Script& script = Script::Handle(async_op.script());
BreakpointLocation* bpt_location =
SetBreakpoint(script, async_op.token_pos(), async_op.end_token_pos(), -1,
-1 /* no line/col */, async_op);
auto bpt = bpt_location->AddSingleShot(this);
bpt->set_is_synthetic_async(true);
return bpt;
}
Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
if (!closure.IsClosure()) {
return NULL;
@ -4422,7 +4412,7 @@ void Debugger::MaybeAsyncStepInto(const Closure& async_op) {
void Debugger::AsyncStepInto(const Closure& async_op) {
Zone* zone = Thread::Current()->zone();
CallerClosureFinder caller_closure_finder(zone);
if (caller_closure_finder.IsCompactAsyncCallback(
if (caller_closure_finder.IsAsyncCallback(
Function::Handle(zone, async_op.function()))) {
const auto& suspend_state = SuspendState::Handle(
zone, caller_closure_finder.GetSuspendStateFromAsyncCallback(async_op));

View file

@ -719,10 +719,6 @@ class Debugger {
intptr_t line_number,
intptr_t column_number);
// Sets synthetic breakpoint at async_op to step over the synthetic part of
// the stack trace.
Breakpoint* SetBreakpointAtAsyncOp(const Function& async_op);
BreakpointLocation* BreakpointLocationAtLineCol(const String& script_url,
intptr_t line_number,
intptr_t column_number);

View file

@ -6996,13 +6996,6 @@ class Context : public Object {
static const intptr_t kBytesPerElement = kCompressedWordSize;
static const intptr_t kMaxElements = kSmiMax / kBytesPerElement;
static const intptr_t kAwaitJumpVarIndex = 0;
static const intptr_t kAsyncFutureIndex = 1;
static const intptr_t kControllerIndex = 1;
// Expected context index of chained futures in recognized async functions.
// These are used to unwind async stacks.
static const intptr_t kIsSyncIndex = 2;
struct ArrayTraits {
static intptr_t elements_start_offset() { return sizeof(UntaggedContext); }
static constexpr intptr_t kElementSize = kBytesPerElement;

View file

@ -133,13 +133,6 @@ ClosurePtr CallerClosureFinder::GetCallerInFutureImpl(const Object& future) {
return GetCallerInFutureListener(listener);
}
ClosurePtr CallerClosureFinder::FindCallerInAsyncGenClosure(
const Context& receiver_context) {
// Get the async* _AsyncStarStreamController.
context_entry_ = receiver_context.At(Context::kControllerIndex);
return FindCallerInAsyncStarStreamController(context_entry_);
}
ClosurePtr CallerClosureFinder::FindCallerInAsyncStarStreamController(
const Object& async_star_stream_controller) {
ASSERT(async_star_stream_controller.IsInstance());
@ -246,7 +239,7 @@ ClosurePtr CallerClosureFinder::FindCallerFromSuspendState(
}
}
bool CallerClosureFinder::IsCompactAsyncCallback(const Function& function) {
bool CallerClosureFinder::IsAsyncCallback(const Function& function) {
parent_function_ = function.parent_function();
auto kind = parent_function_.recognized_kind();
return (kind == MethodRecognizer::kSuspendState_createAsyncCallbacks) ||
@ -255,7 +248,7 @@ bool CallerClosureFinder::IsCompactAsyncCallback(const Function& function) {
SuspendStatePtr CallerClosureFinder::GetSuspendStateFromAsyncCallback(
const Closure& closure) {
ASSERT(IsCompactAsyncCallback(Function::Handle(closure.function())));
ASSERT(IsAsyncCallback(Function::Handle(closure.function())));
// Async/async* handler only captures the receiver (SuspendState).
receiver_context_ = closure.context();
RELEASE_ASSERT(receiver_context_.num_variables() == 1);
@ -266,7 +259,7 @@ ClosurePtr CallerClosureFinder::FindCaller(const Closure& receiver_closure) {
receiver_function_ = receiver_closure.function();
receiver_context_ = receiver_closure.context();
if (IsCompactAsyncCallback(receiver_function_)) {
if (IsAsyncCallback(receiver_function_)) {
suspend_state_ = GetSuspendStateFromAsyncCallback(receiver_closure);
return FindCallerFromSuspendState(suspend_state_);
}
@ -302,12 +295,6 @@ ClosurePtr CallerClosureFinder::FindCaller(const Closure& receiver_closure) {
return Closure::null();
}
ObjectPtr CallerClosureFinder::GetAsyncFuture(const Closure& receiver_closure) {
// Closure -> Context -> _Future.
receiver_context_ = receiver_closure.context();
return receiver_context_.At(Context::kAsyncFutureIndex);
}
ObjectPtr CallerClosureFinder::GetFutureFutureListener(const Object& future) {
ASSERT(future.GetClassId() == future_impl_class.id());
auto& listener = Object::Handle(
@ -462,7 +449,7 @@ void StackTraceUtils::UnwindAwaiterChain(
if (function.IsNull()) {
continue;
}
if (caller_closure_finder->IsCompactAsyncCallback(function)) {
if (caller_closure_finder->IsAsyncCallback(function)) {
suspend_state =
caller_closure_finder->GetSuspendStateFromAsyncCallback(closure);
const uword pc = suspend_state.pc();

View file

@ -27,10 +27,6 @@ class CallerClosureFinder {
// 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 an _AsyncStarStreamController instance
// corresponding to async* function.
// Returns either the `onData` or the Future awaiter.
@ -47,15 +43,12 @@ class CallerClosureFinder {
// Returns true if given closure function is a Future callback
// corresponding to an async/async* function or async* body callback.
bool IsCompactAsyncCallback(const Function& function);
bool IsAsyncCallback(const Function& function);
// Returns SuspendState from the given callback which corresponds
// to an async/async* function.
SuspendStatePtr GetSuspendStateFromAsyncCallback(const Closure& 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);

View file

@ -235,7 +235,6 @@ class ObjectPointerVisitor;
V(SpaceOfSpace, " of ") \
V(SpaceWhereNewLine, " where\n") \
V(StackOverflowError, "StackOverflowError") \
V(StackTraceParameter, ":stack_trace") \
V(Stream, "Stream") \
V(StringBase, "_StringBase") \
V(Struct, "Struct") \
@ -436,7 +435,6 @@ class ObjectPointerVisitor;
V(_returnAsync, "_returnAsync") \
V(_returnAsyncNotFuture, "_returnAsyncNotFuture") \
V(_returnAsyncStar, "_returnAsyncStar") \
V(_returnSyncStar, "_returnSyncStar") \
V(_runExtension, "_runExtension") \
V(_runPendingImmediateCallback, "_runPendingImmediateCallback") \
V(_scanFlags, "_scanFlags") \

View file

@ -39,7 +39,7 @@ class _AsyncStarStreamController<T> {
@pragma("vm:entry-point")
StreamController<T> controller;
@pragma("vm:entry-point")
Function? asyncStarBody;
void Function(Object?)? asyncStarBody;
bool isAdding = false;
bool onListenReceived = false;
bool isScheduled = false;
@ -66,7 +66,7 @@ class _AsyncStarStreamController<T> {
isSuspendedAtYield = false;
final bool? argument = continuationArgument;
continuationArgument = null;
asyncStarBody!(argument, null);
asyncStarBody!(argument);
}
void scheduleGenerator() {
@ -169,8 +169,7 @@ class _AsyncStarStreamController<T> {
controller.close();
}
_AsyncStarStreamController(this.asyncStarBody)
: controller = new StreamController(sync: true) {
_AsyncStarStreamController() : controller = new StreamController(sync: true) {
controller.onListen = this.onListen;
controller.onResume = this.onResume;
controller.onCancel = this.onCancel;
@ -217,32 +216,22 @@ external void _moveNextDebuggerStepCheck(Function async_op);
@pragma("vm:entry-point")
class _SuspendState {
static const bool _trace = false;
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static Object? _initAsync<T>() {
if (_trace) print('_initAsync<$T>');
return _Future<T>();
}
@pragma("vm:invisible")
@pragma("vm:recognized", "other")
void _createAsyncCallbacks() {
if (_trace) print('_createAsyncCallbacks');
@pragma("vm:invisible")
thenCallback(value) {
if (_trace) print('thenCallback (this=$this, value=$value)');
_resume(value, null, null);
}
@pragma("vm:invisible")
errorCallback(Object exception, StackTrace stackTrace) {
if (_trace) {
print('errorCallback (this=$this, '
'exception=$exception, stackTrace=$stackTrace)');
}
_resume(null, exception, stackTrace);
}
@ -335,7 +324,6 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
Object? _await(Object? object) {
if (_trace) print('_await (object=$object)');
if (_thenCallback == null) {
_createAsyncCallbacks();
}
@ -358,10 +346,6 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static Future _returnAsync(Object suspendState, Object? returnValue) {
if (_trace) {
print('_returnAsync (suspendState=$suspendState, '
'returnValue=$returnValue)');
}
_Future future;
if (suspendState is _SuspendState) {
future = unsafeCast<_Future>(suspendState._functionData);
@ -380,10 +364,6 @@ class _SuspendState {
@pragma("vm:invisible")
static Future _returnAsyncNotFuture(
Object suspendState, Object? returnValue) {
if (_trace) {
print('_returnAsyncNotFuture (suspendState=$suspendState, '
'returnValue=$returnValue)');
}
_Future future;
if (suspendState is _SuspendState) {
future = unsafeCast<_Future>(suspendState._functionData);
@ -397,15 +377,13 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static Object? _initAsyncStar<T>() {
if (_trace) print('_initAsyncStar<$T>');
return _AsyncStarStreamController<T>(null);
return _AsyncStarStreamController<T>();
}
@pragma("vm:invisible")
@pragma("vm:recognized", "other")
_createAsyncStarCallback(_AsyncStarStreamController controller) {
controller.asyncStarBody = (value, _) {
if (_trace) print('asyncStarBody callback (value=$value)');
controller.asyncStarBody = (value) {
_resume(value, null, null);
};
}
@ -424,10 +402,6 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static void _returnAsyncStar(Object suspendState, Object? returnValue) {
if (_trace) {
print('_returnAsyncStar (suspendState=$suspendState, '
'returnValue=$returnValue)');
}
final controller = unsafeCast<_AsyncStarStreamController>(
unsafeCast<_SuspendState>(suspendState)._functionData);
controller.close();
@ -437,10 +411,6 @@ class _SuspendState {
@pragma("vm:invisible")
static Object? _handleException(
Object suspendState, Object exception, StackTrace stackTrace) {
if (_trace) {
print('_handleException (suspendState=$suspendState, '
'exception=$exception, stackTrace=$stackTrace)');
}
Object? functionData;
bool isSync = true;
if (suspendState is _SuspendState) {
@ -469,14 +439,12 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static Object? _initSyncStar<T>() {
if (_trace) print('_initSyncStar<$T>');
return _SyncStarIterable<T>();
}
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
Object? _suspendSyncStarAtStart(Object? object) {
if (_trace) print('_suspendSyncStarAtStart($object)');
final data = _functionData;
unsafeCast<_SyncStarIterable>(data)._stateAtStart = this;
return data;