[vm] New async*/yield/yield* implementation based on suspend/resume stubs

TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/48378
Change-Id: I0c2ca9269b2c8f008a79c139a0ce10231996732d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242923
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Alexander Markov 2022-05-02 20:53:01 +00:00 committed by Commit Bot
parent 0d5d34a2ed
commit 4075e8b3f9
48 changed files with 1288 additions and 765 deletions

View file

@ -1371,7 +1371,7 @@ type TryFinally extends Statement {
type YieldStatement extends Statement {
Byte tag = 77;
FileOffset fileOffset;
Byte flags (isYieldStar);
Byte flags (isYieldStar, isNative);
Expression expression;
}

View file

@ -158,9 +158,16 @@ class RecursiveContinuationRewriter extends RemovingTransformer {
return node;
}
case AsyncMarker.AsyncStar:
return new AsyncStarFunctionRewriter(
helper, node, staticTypeContext, desugarAsync)
.rewrite();
if (desugarAsync) {
return new AsyncStarFunctionRewriter(
helper, node, staticTypeContext, desugarAsync)
.rewrite();
} else {
node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
helper, staticTypeContext, desugarAsync,
desugarAwaitFor: true));
return node;
}
}
}

View file

@ -77,7 +77,9 @@ class ExceptionHandlerList : public ZoneAllocated {
};
explicit ExceptionHandlerList(const Function& function)
: list_(), has_async_handler_(function.IsCompactAsyncFunction()) {}
: list_(),
has_async_handler_(function.IsCompactAsyncFunction() ||
function.IsCompactAsyncStarFunction()) {}
intptr_t Length() const { return list_.length(); }

View file

@ -376,7 +376,7 @@ void FlowGraphCompiler::EmitPrologue() {
} else if (parsed_function().suspend_state_var() != nullptr) {
// Initialize synthetic :suspend_state variable early
// as it may be accessed by GC and exception handling before
// InitAsync stub is called.
// InitSuspendableFunction stub is called.
const intptr_t slot_index =
compiler::target::frame_layout.FrameSlotForVariable(
parsed_function().suspend_state_var());

View file

@ -366,7 +366,7 @@ void FlowGraphCompiler::EmitPrologue() {
} else if (parsed_function().suspend_state_var() != nullptr) {
// Initialize synthetic :suspend_state variable early
// as it may be accessed by GC and exception handling before
// InitAsync stub is called.
// InitSuspendableFunction stub is called.
const intptr_t slot_index =
compiler::target::frame_layout.FrameSlotForVariable(
parsed_function().suspend_state_var());

View file

@ -453,7 +453,7 @@ void FlowGraphCompiler::EmitPrologue() {
} else if (parsed_function().suspend_state_var() != nullptr) {
// Initialize synthetic :suspend_state variable early
// as it may be accessed by GC and exception handling before
// InitAsync stub is called.
// InitSuspendableFunction stub is called.
const intptr_t slot_index =
compiler::target::frame_layout.FrameSlotForVariable(
parsed_function().suspend_state_var());

View file

@ -360,7 +360,7 @@ void FlowGraphCompiler::EmitPrologue() {
} else if (parsed_function().suspend_state_var() != nullptr) {
// Initialize synthetic :suspend_state variable early
// as it may be accessed by GC and exception handling before
// InitAsync stub is called.
// InitSuspendableFunction stub is called.
const intptr_t slot_index =
compiler::target::frame_layout.FrameSlotForVariable(
parsed_function().suspend_state_var());

View file

@ -372,7 +372,7 @@ void FlowGraphCompiler::EmitPrologue() {
} else if (parsed_function().suspend_state_var() != nullptr) {
// Initialize synthetic :suspend_state variable early
// as it may be accessed by GC and exception handling before
// InitAsync stub is called.
// InitSuspendableFunction stub is called.
const intptr_t slot_index =
compiler::target::frame_layout.FrameSlotForVariable(
parsed_function().suspend_state_var());

View file

@ -6901,16 +6901,25 @@ void RawStoreFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
const Code& ReturnInstr::GetReturnStub(FlowGraphCompiler* compiler) const {
ASSERT(compiler->parsed_function().function().IsCompactAsyncFunction());
if (!value()->Type()->CanBeFuture()) {
return Code::ZoneHandle(compiler->zone(),
compiler->isolate_group()
->object_store()
->return_async_not_future_stub());
const Function& function = compiler->parsed_function().function();
ASSERT(function.IsSuspendableFunction());
if (function.IsCompactAsyncFunction()) {
if (!value()->Type()->CanBeFuture()) {
return Code::ZoneHandle(compiler->zone(),
compiler->isolate_group()
->object_store()
->return_async_not_future_stub());
}
return Code::ZoneHandle(
compiler->zone(),
compiler->isolate_group()->object_store()->return_async_stub());
} else if (function.IsCompactAsyncStarFunction()) {
return Code::ZoneHandle(
compiler->zone(),
compiler->isolate_group()->object_store()->return_async_star_stub());
} else {
UNREACHABLE();
}
return Code::ZoneHandle(
compiler->zone(),
compiler->isolate_group()->object_store()->return_async_stub());
}
void NativeReturnInstr::EmitReturnMoves(FlowGraphCompiler* compiler) {
@ -7230,10 +7239,12 @@ LocationSummary* Call1ArgStubInstr::MakeLocationSummary(Zone* zone,
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
switch (stub_id_) {
case StubId::kInitAsync:
case StubId::kInitAsyncStar:
locs->set_in(0, Location::RegisterLocation(
InitSuspendableFunctionStubABI::kTypeArgsReg));
break;
case StubId::kAwaitAsync:
case StubId::kAwait:
case StubId::kYieldAsyncStar:
locs->set_in(0, Location::RegisterLocation(SuspendStubABI::kArgumentReg));
break;
}
@ -7248,15 +7259,21 @@ void Call1ArgStubInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
case StubId::kInitAsync:
stub = object_store->init_async_stub();
break;
case StubId::kAwaitAsync:
stub = object_store->await_async_stub();
case StubId::kAwait:
stub = object_store->await_stub();
break;
case StubId::kInitAsyncStar:
stub = object_store->init_async_star_stub();
break;
case StubId::kYieldAsyncStar:
stub = object_store->yield_async_star_stub();
break;
}
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
locs(), deopt_id(), env());
#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32)
if (stub_id_ == StubId::kAwaitAsync) {
if ((stub_id_ == StubId::kAwait) || (stub_id_ == StubId::kYieldAsyncStar)) {
// On x86 (X64 and IA32) mismatch between calls and returns
// significantly regresses performance. So suspend stub
// does not return directly to the caller. Instead, a small

View file

@ -9567,8 +9567,10 @@ class SimdOpInstr : public Definition {
class Call1ArgStubInstr : public TemplateDefinition<1, Throws> {
public:
enum class StubId {
kAwait,
kInitAsync,
kAwaitAsync,
kInitAsyncStar,
kYieldAsyncStar,
};
Call1ArgStubInstr(const InstructionSource& source,

View file

@ -484,7 +484,7 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(result == CallingConventions::kReturnFpuReg);
}
if (compiler->parsed_function().function().IsCompactAsyncFunction()) {
if (compiler->parsed_function().function().IsSuspendableFunction()) {
ASSERT(compiler->flow_graph().graph_entry()->NeedsFrame());
const Code& stub = GetReturnStub(compiler);
compiler->EmitJumpToStub(stub);

View file

@ -411,7 +411,7 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(result == CallingConventions::kReturnFpuReg);
}
if (compiler->parsed_function().function().IsCompactAsyncFunction()) {
if (compiler->parsed_function().function().IsSuspendableFunction()) {
ASSERT(compiler->flow_graph().graph_entry()->NeedsFrame());
const Code& stub = GetReturnStub(compiler);
compiler->EmitJumpToStub(stub);

View file

@ -233,7 +233,7 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register result = locs()->in(0).reg();
ASSERT(result == EAX);
if (compiler->parsed_function().function().IsCompactAsyncFunction()) {
if (compiler->parsed_function().function().IsSuspendableFunction()) {
ASSERT(compiler->flow_graph().graph_entry()->NeedsFrame());
const Code& stub = GetReturnStub(compiler);
compiler->EmitJumpToStub(stub);

View file

@ -1362,8 +1362,14 @@ void Call1ArgStubInstr::PrintOperandsTo(BaseTextBuffer* f) const {
case StubId::kInitAsync:
name = "InitAsync";
break;
case StubId::kAwaitAsync:
name = "AwaitAsync";
case StubId::kAwait:
name = "Await";
break;
case StubId::kInitAsyncStar:
name = "InitAsyncStar";
break;
case StubId::kYieldAsyncStar:
name = "YieldAsyncStar";
break;
}
f->Printf("%s(", name);

View file

@ -464,7 +464,7 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(result == CallingConventions::kReturnFpuReg);
}
if (compiler->parsed_function().function().IsCompactAsyncFunction()) {
if (compiler->parsed_function().function().IsSuspendableFunction()) {
ASSERT(compiler->flow_graph().graph_entry()->NeedsFrame());
const Code& stub = GetReturnStub(compiler);
compiler->EmitJumpToStub(stub);

View file

@ -338,7 +338,7 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(result == CallingConventions::kReturnFpuReg);
}
if (compiler->parsed_function().function().IsCompactAsyncFunction()) {
if (compiler->parsed_function().function().IsSuspendableFunction()) {
ASSERT(compiler->flow_graph().graph_entry()->NeedsFrame());
const Code& stub = GetReturnStub(compiler);
compiler->EmitJumpToStub(stub);

View file

@ -2814,7 +2814,7 @@ void LoadFieldInstr::InferRange(RangeAnalysis* analysis, Range* range) {
case Slot::Kind::kFunctionType_parameter_types:
case Slot::Kind::kFunctionType_type_parameters:
case Slot::Kind::kInstance_native_fields_array:
case Slot::Kind::kSuspendState_future:
case Slot::Kind::kSuspendState_function_data:
case Slot::Kind::kSuspendState_then_callback:
case Slot::Kind::kSuspendState_error_callback:
case Slot::Kind::kTypedDataView_typed_data:

View file

@ -243,7 +243,7 @@ bool Slot::IsImmutableLengthSlot() const {
case Slot::Kind::kFunctionType_named_parameter_names:
case Slot::Kind::kFunctionType_parameter_types:
case Slot::Kind::kFunctionType_type_parameters:
case Slot::Kind::kSuspendState_future:
case Slot::Kind::kSuspendState_function_data:
case Slot::Kind::kSuspendState_then_callback:
case Slot::Kind::kSuspendState_error_callback:
case Slot::Kind::kType_arguments:

View file

@ -73,7 +73,7 @@ class ParsedFunction;
V(ImmutableLinkedHashBase, UntaggedLinkedHashBase, index, \
TypedDataUint32Array, VAR) \
V(Instance, UntaggedInstance, native_fields_array, Dynamic, VAR) \
V(SuspendState, UntaggedSuspendState, future, Dynamic, VAR) \
V(SuspendState, UntaggedSuspendState, function_data, Dynamic, VAR) \
V(SuspendState, UntaggedSuspendState, then_callback, Closure, VAR) \
V(SuspendState, UntaggedSuspendState, error_callback, Closure, VAR) \
V(Type, UntaggedType, arguments, TypeArguments, FINAL) \

View file

@ -684,6 +684,24 @@ Fragment StreamingFlowGraphBuilder::InitSuspendableFunction(
body += B->Call1ArgStub(TokenPosition::kNoSource,
Call1ArgStubInstr::StubId::kInitAsync);
body += Drop();
} else if (dart_function.IsCompactAsyncStarFunction()) {
const auto& result_type =
AbstractType::Handle(Z, dart_function.result_type());
auto& type_args = TypeArguments::ZoneHandle(Z);
if (result_type.IsType() &&
(result_type.type_class() == IG->object_store()->stream_class())) {
ASSERT(result_type.IsFinalized());
type_args = result_type.arguments();
}
body += TranslateInstantiatedTypeArguments(type_args);
body += B->Call1ArgStub(TokenPosition::kNoSource,
Call1ArgStubInstr::StubId::kInitAsyncStar);
body += Drop();
body += NullConstant();
body += B->Call1ArgStub(TokenPosition::kNoSource,
Call1ArgStubInstr::StubId::kYieldAsyncStar);
body += Drop();
}
return body;
}
@ -4322,7 +4340,8 @@ Fragment StreamingFlowGraphBuilder::BuildLibraryPrefixAction(
Fragment StreamingFlowGraphBuilder::BuildAwaitExpression(
TokenPosition* position) {
ASSERT(parsed_function()->function().IsCompactAsyncFunction());
ASSERT(parsed_function()->function().IsCompactAsyncFunction() ||
parsed_function()->function().IsCompactAsyncStarFunction());
Fragment instructions;
const TokenPosition pos = ReadPosition(); // read file offset.
@ -4330,7 +4349,7 @@ Fragment StreamingFlowGraphBuilder::BuildAwaitExpression(
instructions += BuildExpression(); // read operand.
instructions += B->Call1ArgStub(pos, Call1ArgStubInstr::StubId::kAwaitAsync);
instructions += B->Call1ArgStub(pos, Call1ArgStubInstr::StubId::kAwait);
return instructions;
}
@ -5228,8 +5247,83 @@ Fragment StreamingFlowGraphBuilder::BuildYieldStatement(
const TokenPosition pos = ReadPosition(); // read position.
if (position != nullptr) *position = pos;
uint8_t flags = ReadByte(); // read flags.
ASSERT(flags == kNativeYieldFlags); // Must have been desugared.
const uint8_t flags = ReadByte(); // read flags.
if ((flags & kYieldStatementFlagNative) == 0) {
Fragment instructions;
// Generate the following code for yield <expr>:
//
// _AsyncStarStreamController controller = :suspend_state._functionData;
// if (controller.add(<expr>)) {
// return;
// }
// suspend();
//
// Generate the following code for yield* <expr>:
//
// _AsyncStarStreamController controller = :suspend_state._functionData;
// controller.addStream(<expr>);
// if (suspend()) {
// return;
// }
//
// Load :suspend_state variable using low-level FP-relative load
// in order to avoid confusing SSA construction (which cannot
// track its value as it is modified implicitly by stubs).
LocalVariable* suspend_state = parsed_function()->suspend_state_var();
ASSERT(suspend_state != nullptr);
instructions += IntConstant(0);
instructions += B->LoadFpRelativeSlot(
compiler::target::frame_layout.FrameSlotForVariable(suspend_state) *
compiler::target::kWordSize,
CompileType::Dynamic(), kTagged);
instructions += LoadNativeField(Slot::SuspendState_function_data());
instructions += BuildExpression(); // read expression.
auto& add_method = Function::ZoneHandle(Z);
const bool is_yield_star = (flags & kYieldStatementFlagYieldStar) != 0;
if (is_yield_star) {
add_method =
IG->object_store()->async_star_stream_controller_add_stream();
} else {
add_method = IG->object_store()->async_star_stream_controller_add();
}
instructions += StaticCall(pos, add_method, 2, ICData::kNoRebind);
if (is_yield_star) {
// Discard result of _AsyncStarStreamController.addStream().
instructions += Drop();
// Suspend and test value passed to the resumed async* body.
instructions += NullConstant();
instructions +=
B->Call1ArgStub(pos, Call1ArgStubInstr::StubId::kYieldAsyncStar);
} else {
// Test value returned by _AsyncStarStreamController.add().
}
TargetEntryInstr* exit;
TargetEntryInstr* continue_execution;
instructions += BranchIfTrue(&exit, &continue_execution, false);
Fragment do_exit(exit);
do_exit += TranslateFinallyFinalizers(nullptr, -1);
do_exit += NullConstant();
do_exit += Return(TokenPosition::kNoSource);
instructions = Fragment(instructions.entry, continue_execution);
if (!is_yield_star) {
instructions += NullConstant();
instructions +=
B->Call1ArgStub(pos, Call1ArgStubInstr::StubId::kYieldAsyncStar);
instructions += Drop();
}
return instructions;
}
ASSERT(flags == kYieldStatementFlagNative); // Must have been desugared.
// Setup yield/continue point:
//
@ -5476,6 +5570,16 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
function.set_is_inlinable(false);
function.set_is_visible(true);
ASSERT(function.IsCompactAsyncFunction());
} else if (function_node_helper.async_marker_ ==
FunctionNodeHelper::kAsyncStar) {
if (!FLAG_precompiled_mode) {
FATAL("Compact async* functions are only supported in AOT mode.");
}
function.set_modifier(UntaggedFunction::kAsyncGen);
function.set_is_debuggable(true);
function.set_is_inlinable(false);
function.set_is_visible(true);
ASSERT(function.IsCompactAsyncStarFunction());
} else {
ASSERT((function_node_helper.async_marker_ ==
FunctionNodeHelper::kSync) ||
@ -5514,6 +5618,7 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
function.set_is_inlinable(!FLAG_lazy_async_stacks);
}
ASSERT(!function.IsCompactAsyncFunction());
ASSERT(!function.IsCompactAsyncStarFunction());
}
// If the start token position is synthetic, the end token position

View file

@ -834,7 +834,7 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
V(LinkedHashBase_getIndex, LinkedHashBase_index) \
V(LinkedHashBase_getUsedData, LinkedHashBase_used_data) \
V(ObjectArrayLength, Array_length) \
V(SuspendState_getFuture, SuspendState_future) \
V(SuspendState_getFunctionData, SuspendState_function_data) \
V(SuspendState_getThenCallback, SuspendState_then_callback) \
V(SuspendState_getErrorCallback, SuspendState_error_callback) \
V(TypedDataViewOffsetInBytes, TypedDataView_offset_in_bytes) \
@ -852,7 +852,7 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
V(NativeFinalizer_setCallback, NativeFinalizer_callback) \
V(LinkedHashBase_setData, LinkedHashBase_data) \
V(LinkedHashBase_setIndex, LinkedHashBase_index) \
V(SuspendState_setFuture, SuspendState_future) \
V(SuspendState_setFunctionData, SuspendState_function_data) \
V(SuspendState_setThenCallback, SuspendState_then_callback) \
V(SuspendState_setErrorCallback, SuspendState_error_callback) \
V(WeakProperty_setKey, WeakProperty_key) \

View file

@ -89,7 +89,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
scope_->set_begin_token_pos(function.token_pos());
scope_->set_end_token_pos(function.end_token_pos());
if (function.IsCompactAsyncFunction()) {
if (function.IsSuspendableFunction()) {
LocalVariable* suspend_state_var =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::SuspendStateVar(), AbstractType::dynamic_type());
@ -1276,15 +1276,16 @@ void ScopeBuilder::VisitStatement() {
word flags = helper_.ReadByte(); // read flags.
VisitExpression(); // read expression.
ASSERT(flags == kNativeYieldFlags);
if (depth_.function_ == 0) {
AddSwitchVariable();
// Promote all currently visible local variables into the context.
// TODO(27590) CaptureLocalVariables promotes to many variables into
// the scope. Mark those variables as stack_local.
// TODO(27590) we don't need to promote those variables that are
// not used across yields.
scope_->CaptureLocalVariables(current_function_scope_);
if ((flags & kYieldStatementFlagNative) != 0) {
if (depth_.function_ == 0) {
AddSwitchVariable();
// Promote all currently visible local variables into the context.
// TODO(27590) CaptureLocalVariables promotes to many variables into
// the scope. Mark those variables as stack_local.
// TODO(27590) we don't need to promote those variables that are
// not used across yields.
scope_->CaptureLocalVariables(current_function_scope_);
}
}
return;
}

View file

@ -83,8 +83,10 @@ namespace dart {
V(::, copyRangeFromUint8ListToOneByteString, \
CopyRangeFromUint8ListToOneByteString, 0x19a1bf41) \
V(_StringBase, _interpolate, StringBaseInterpolate, 0x7da2a580) \
V(_SuspendState, get:_future, SuspendState_getFuture, 0x0e2a7e73) \
V(_SuspendState, set:_future, SuspendState_setFuture, 0x179923b0) \
V(_SuspendState, get:_functionData, SuspendState_getFunctionData, \
0x7290026e) \
V(_SuspendState, set:_functionData, SuspendState_setFunctionData, \
0x2b6668ab) \
V(_SuspendState, get:_thenCallback, SuspendState_getThenCallback, \
0xff1dccec) \
V(_SuspendState, set:_thenCallback, SuspendState_setThenCallback, \
@ -95,7 +97,9 @@ namespace dart {
0x4935f88c) \
V(_SuspendState, _createAsyncCallbacks, SuspendState_createAsyncCallbacks, \
0x4add6c13) \
V(_SuspendState, _resume, SuspendState_resume, 0x93d8c5e8) \
V(_SuspendState, _createAsyncStarCallback, \
SuspendState_createAsyncStarCallback, 0xfa7537e4) \
V(_SuspendState, _resume, SuspendState_resume, 0xc738e9d2) \
V(_IntegerImplementation, toDouble, IntegerToDouble, 0x97728b46) \
V(_Double, _add, DoubleAdd, 0xea666327) \
V(_Double, _sub, DoubleSub, 0x28474c2e) \

View file

@ -992,7 +992,7 @@ class SuspendState : public AllStatic {
public:
static word frame_size_offset();
static word pc_offset();
static word future_offset();
static word function_data_offset();
static word then_callback_offset();
static word error_callback_offset();
static word payload_offset();
@ -1239,9 +1239,14 @@ class Thread : public AllStatic {
static word random_offset();
static word suspend_state_init_async_entry_point_offset();
static word suspend_state_await_async_entry_point_offset();
static word suspend_state_await_entry_point_offset();
static word suspend_state_return_async_entry_point_offset();
static word suspend_state_return_async_not_future_entry_point_offset();
static word suspend_state_init_async_star_entry_point_offset();
static word suspend_state_yield_async_star_entry_point_offset();
static word suspend_state_return_async_star_entry_point_offset();
static word suspend_state_handle_exception_entry_point_offset();
static word OffsetFromThread(const dart::Object& object);

File diff suppressed because it is too large Load diff

View file

@ -192,7 +192,7 @@
FIELD(SubtypeTestCache, cache_offset) \
FIELD(SuspendState, error_callback_offset) \
FIELD(SuspendState, frame_size_offset) \
FIELD(SuspendState, future_offset) \
FIELD(SuspendState, function_data_offset) \
FIELD(SuspendState, payload_offset) \
FIELD(SuspendState, pc_offset) \
FIELD(SuspendState, then_callback_offset) \
@ -281,10 +281,13 @@
\
FIELD(Thread, stack_overflow_shared_without_fpu_regs_stub_offset) \
FIELD(Thread, store_buffer_block_offset) \
FIELD(Thread, suspend_state_await_async_entry_point_offset) \
FIELD(Thread, suspend_state_await_entry_point_offset) \
FIELD(Thread, suspend_state_init_async_entry_point_offset) \
FIELD(Thread, suspend_state_return_async_entry_point_offset) \
FIELD(Thread, suspend_state_return_async_not_future_entry_point_offset) \
FIELD(Thread, suspend_state_init_async_star_entry_point_offset) \
FIELD(Thread, suspend_state_yield_async_star_entry_point_offset) \
FIELD(Thread, suspend_state_return_async_star_entry_point_offset) \
FIELD(Thread, suspend_state_handle_exception_entry_point_offset) \
FIELD(Thread, top_exit_frame_info_offset) \
FIELD(Thread, top_offset) \

View file

@ -1287,7 +1287,7 @@ void StubCodeCompiler::GenerateSuspendStub(
const Register kTemp = SuspendStubABI::kTempReg;
const Register kFrameSize = SuspendStubABI::kFrameSizeReg;
const Register kSuspendState = SuspendStubABI::kSuspendStateReg;
const Register kFuture = SuspendStubABI::kFutureReg;
const Register kFunctionData = SuspendStubABI::kFunctionDataReg;
const Register kSrcFrame = SuspendStubABI::kSrcFrameReg;
const Register kDstFrame = SuspendStubABI::kDstFrameReg;
Label alloc_slow_case, alloc_done, init_done, old_gen_object, call_await;
@ -1308,7 +1308,7 @@ void StubCodeCompiler::GenerateSuspendStub(
__ CompareClassId(kSuspendState, kSuspendStateCid, kTemp);
__ BranchIf(EQUAL, &init_done);
__ MoveRegister(kFuture, kSuspendState);
__ MoveRegister(kFunctionData, kSuspendState);
__ Comment("Allocate SuspendState");
// Check for allocation tracing.
@ -1361,8 +1361,8 @@ void StubCodeCompiler::GenerateSuspendStub(
FieldAddress(kSuspendState, target::SuspendState::frame_size_offset()));
__ StoreCompressedIntoObjectNoBarrier(
kSuspendState,
FieldAddress(kSuspendState, target::SuspendState::future_offset()),
kFuture);
FieldAddress(kSuspendState, target::SuspendState::function_data_offset()),
kFunctionData);
{
#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \
@ -1474,8 +1474,8 @@ void StubCodeCompiler::GenerateSuspendStub(
__ PushRegister(kFrameSize); // Save frame size.
__ PushObject(NullObject()); // Make space on stack for the return value.
__ SmiTag(kFrameSize);
__ PushRegister(kFrameSize); // Pass frame size to runtime entry.
__ PushRegister(kFuture); // Pass future.
__ PushRegister(kFrameSize); // Pass frame size to runtime entry.
__ PushRegister(kFunctionData); // Pass function data.
__ CallRuntime(kAllocateSuspendStateRuntimeEntry, 2);
__ Drop(2); // Drop arguments
__ PopRegister(kSuspendState); // Get result.
@ -1502,10 +1502,15 @@ void StubCodeCompiler::GenerateSuspendStub(
__ Jump(&call_await);
}
void StubCodeCompiler::GenerateAwaitAsyncStub(Assembler* assembler) {
void StubCodeCompiler::GenerateAwaitStub(Assembler* assembler) {
GenerateSuspendStub(assembler,
target::Thread::suspend_state_await_entry_point_offset());
}
void StubCodeCompiler::GenerateYieldAsyncStarStub(Assembler* assembler) {
GenerateSuspendStub(
assembler,
target::Thread::suspend_state_await_async_entry_point_offset());
target::Thread::suspend_state_yield_async_star_entry_point_offset());
}
void StubCodeCompiler::GenerateInitSuspendableFunctionStub(
@ -1531,6 +1536,12 @@ void StubCodeCompiler::GenerateInitAsyncStub(Assembler* assembler) {
assembler, target::Thread::suspend_state_init_async_entry_point_offset());
}
void StubCodeCompiler::GenerateInitAsyncStarStub(Assembler* assembler) {
GenerateInitSuspendableFunctionStub(
assembler,
target::Thread::suspend_state_init_async_star_entry_point_offset());
}
void StubCodeCompiler::GenerateResumeStub(Assembler* assembler) {
const Register kSuspendState = ResumeStubABI::kSuspendStateReg;
const Register kTemp = ResumeStubABI::kTempReg;
@ -1674,6 +1685,12 @@ void StubCodeCompiler::GenerateReturnAsyncNotFutureStub(Assembler* assembler) {
suspend_state_return_async_not_future_entry_point_offset());
}
void StubCodeCompiler::GenerateReturnAsyncStarStub(Assembler* assembler) {
GenerateReturnStub(
assembler,
target::Thread::suspend_state_return_async_star_entry_point_offset());
}
void StubCodeCompiler::GenerateAsyncExceptionHandlerStub(Assembler* assembler) {
const Register kSuspendState = AsyncExceptionHandlerStubABI::kSuspendStateReg;
ASSERT(kSuspendState != kExceptionObjectReg);

View file

@ -532,18 +532,18 @@ struct DoubleToIntegerStubABI {
static const Register kResultReg = R0;
};
// ABI for SuspendStub (AwaitAsyncStub).
// ABI for SuspendStub (AwaitStub, YieldAsyncStarStub).
struct SuspendStubABI {
static const Register kArgumentReg = R0;
static const Register kTempReg = R1;
static const Register kFrameSizeReg = R2;
static const Register kSuspendStateReg = R3;
static const Register kFutureReg = R4;
static const Register kFunctionDataReg = R4;
static const Register kSrcFrameReg = R8;
static const Register kDstFrameReg = R9;
};
// ABI for InitSuspendableFunctionStub (InitAsyncStub).
// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub).
struct InitSuspendableFunctionStubABI {
static const Register kTypeArgsReg = R0;
};
@ -563,7 +563,8 @@ struct ResumeStubABI {
static const Register kStackTraceReg = R4;
};
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub).
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
static const Register kSuspendStateReg = R2;
};

View file

@ -366,18 +366,18 @@ struct DoubleToIntegerStubABI {
static const Register kResultReg = R0;
};
// ABI for SuspendStub (AwaitAsyncStub).
// ABI for SuspendStub (AwaitStub, YieldAsyncStarStub).
struct SuspendStubABI {
static const Register kArgumentReg = R0;
static const Register kTempReg = R1;
static const Register kFrameSizeReg = R2;
static const Register kSuspendStateReg = R3;
static const Register kFutureReg = R4;
static const Register kFunctionDataReg = R4;
static const Register kSrcFrameReg = R5;
static const Register kDstFrameReg = R6;
};
// ABI for InitSuspendableFunctionStub (InitAsyncStub).
// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub).
struct InitSuspendableFunctionStubABI {
static const Register kTypeArgsReg = R0;
};
@ -397,7 +397,8 @@ struct ResumeStubABI {
static const Register kStackTraceReg = R4;
};
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub).
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
static const Register kSuspendStateReg = R2;
};

View file

@ -260,16 +260,16 @@ struct DoubleToIntegerStubABI {
static const Register kResultReg = EAX;
};
// ABI for SuspendStub (AwaitAsyncStub).
// ABI for SuspendStub (AwaitStub, YieldAsyncStarStub).
struct SuspendStubABI {
static const Register kArgumentReg = EAX;
static const Register kTempReg = EDX;
static const Register kFrameSizeReg = ECX;
static const Register kSuspendStateReg = EBX;
static const Register kFutureReg = EDI;
static const Register kFunctionDataReg = EDI;
// Can reuse THR.
static const Register kSrcFrameReg = ESI;
// Can reuse kFutureReg.
// Can reuse kFunctionDataReg.
static const Register kDstFrameReg = EDI;
// Number of bytes to skip after
@ -278,7 +278,7 @@ struct SuspendStubABI {
static const intptr_t kResumePcDistance = 5;
};
// ABI for InitSuspendableFunctionStub (InitAsyncStub).
// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub).
struct InitSuspendableFunctionStubABI {
static const Register kTypeArgsReg = EAX;
};
@ -298,7 +298,8 @@ struct ResumeStubABI {
static const Register kStackTraceReg = EDI;
};
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub).
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
static const Register kSuspendStateReg = EBX;
};

View file

@ -378,18 +378,18 @@ struct DoubleToIntegerStubABI {
static constexpr Register kResultReg = A0;
};
// ABI for SuspendStub (AwaitAsyncStub).
// ABI for SuspendStub (AwaitStub, YieldAsyncStarStub).
struct SuspendStubABI {
static const Register kArgumentReg = A0;
static const Register kTempReg = T0;
static const Register kFrameSizeReg = T1;
static const Register kSuspendStateReg = T2;
static const Register kFutureReg = T3;
static const Register kFunctionDataReg = T3;
static const Register kSrcFrameReg = T4;
static const Register kDstFrameReg = T5;
};
// ABI for InitSuspendableFunctionStub (InitAsyncStub).
// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub).
struct InitSuspendableFunctionStubABI {
static const Register kTypeArgsReg = A0;
};
@ -409,7 +409,8 @@ struct ResumeStubABI {
static const Register kStackTraceReg = T4;
};
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub).
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
static const Register kSuspendStateReg = T1;
};

View file

@ -335,13 +335,13 @@ struct DoubleToIntegerStubABI {
static const Register kResultReg = RAX;
};
// ABI for SuspendStub (AwaitAsyncStub).
// ABI for SuspendStub (AwaitStub, YieldAsyncStarStub).
struct SuspendStubABI {
static const Register kArgumentReg = RAX;
static const Register kTempReg = RDX;
static const Register kFrameSizeReg = RCX;
static const Register kSuspendStateReg = RBX;
static const Register kFutureReg = R8;
static const Register kFunctionDataReg = R8;
static const Register kSrcFrameReg = RSI;
static const Register kDstFrameReg = RDI;
@ -351,7 +351,7 @@ struct SuspendStubABI {
static const intptr_t kResumePcDistance = 5;
};
// ABI for InitSuspendableFunctionStub (InitAsyncStub).
// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub).
struct InitSuspendableFunctionStubABI {
static const Register kTypeArgsReg = RAX;
};
@ -371,7 +371,8 @@ struct ResumeStubABI {
static const Register kStackTraceReg = RDI;
};
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub).
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
static const Register kSuspendStateReg = RBX;
};

View file

@ -56,8 +56,6 @@ class StringIndex {
int value_;
};
const uint8_t kNativeYieldFlags = 0x2;
enum LogicalOperator { kAnd, kOr };
struct ProgramBinary {

View file

@ -212,6 +212,12 @@ enum InstanceInvocationFlags {
kInstanceInvocationFlagBoundsSafe = 1 << 1,
};
// Keep in sync with package:kernel/lib/ast.dart
enum YieldStatementFlags {
kYieldStatementFlagYieldStar = 1 << 0,
kYieldStatementFlagNative = 1 << 1,
};
// Keep in sync with package:kernel/lib/ast.dart
enum class NamedTypeFlags : uint8_t {
kIsRequired = 1 << 0,

View file

@ -2039,6 +2039,16 @@ void KernelLoader::LoadProcedure(const Library& library,
function.set_is_inlinable(false);
function.set_is_visible(true);
ASSERT(function.IsCompactAsyncFunction());
} else if (function_node_helper.async_marker_ ==
FunctionNodeHelper::kAsyncStar) {
if (!FLAG_precompiled_mode) {
FATAL("Compact async* functions are only supported in AOT mode.");
}
function.set_modifier(UntaggedFunction::kAsyncGen);
function.set_is_debuggable(true);
function.set_is_inlinable(false);
function.set_is_visible(true);
ASSERT(function.IsCompactAsyncStarFunction());
} else {
ASSERT(function_node_helper.async_marker_ == FunctionNodeHelper::kSync);
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
@ -2063,6 +2073,7 @@ void KernelLoader::LoadProcedure(const Library& library,
break;
}
ASSERT(!function.IsCompactAsyncFunction());
ASSERT(!function.IsCompactAsyncStarFunction());
}
if (!native_name.IsNull()) {

View file

@ -26036,7 +26036,7 @@ DEFINE_FLAG_HANDLER(DwarfStackTracesHandler,
"symbolize stack traces in the precompiled runtime.");
SuspendStatePtr SuspendState::New(intptr_t frame_size,
const Instance& future,
const Instance& function_data,
Heap::Space space) {
SuspendState& result = SuspendState::Handle();
{
@ -26047,7 +26047,7 @@ SuspendStatePtr SuspendState::New(intptr_t frame_size,
result ^= raw;
result.set_frame_size(frame_size);
result.set_pc(0);
result.set_future(future);
result.set_function_data(function_data);
}
return result.ptr();
}
@ -26061,8 +26061,8 @@ void SuspendState::set_pc(uword pc) const {
StoreNonPointer(&untag()->pc_, pc);
}
void SuspendState::set_future(const Instance& future) const {
untag()->set_future(future.ptr());
void SuspendState::set_function_data(const Instance& function_data) const {
untag()->set_function_data(function_data.ptr());
}
const char* SuspendState::ToCString() const {

View file

@ -3171,7 +3171,7 @@ class Function : public Object {
// Returns true if parameters of this function are copied into the frame
// in the function prologue.
bool MakesCopyOfParameters() const {
return HasOptionalParameters() || IsCompactAsyncFunction();
return HasOptionalParameters() || IsSuspendableFunction();
}
#if defined(DART_PRECOMPILED_RUNTIME)
@ -3570,12 +3570,25 @@ class Function : public Object {
return modifier() == UntaggedFunction::kAsync;
}
// TODO(alexmarkov): replace this predicate with IsAsyncFunction() after
// old async functions are removed.
// TODO(dartbug.com/48378): replace this predicate with IsAsyncFunction()
// after old async functions are removed.
bool IsCompactAsyncFunction() const {
return IsAsyncFunction() && is_debuggable();
}
// TODO(dartbug.com/48378): replace this predicate with IsAsyncGenerator()
// after old async* functions are removed.
bool IsCompactAsyncStarFunction() const {
return IsAsyncGenerator() && is_debuggable();
}
// Returns true for functions which execution can be suspended
// using Suspend/Resume stubs. Such functions have an artificial
// :suspend_state local variable at the fixed location of the frame.
bool IsSuspendableFunction() const {
return IsCompactAsyncFunction() || IsCompactAsyncStarFunction();
}
// Recognise synthetic sync-yielding functions like the inner-most:
// user_func /* was async */ {
// :async_op(..) yielding {
@ -11804,8 +11817,8 @@ class SuspendState : public Instance {
return OFFSET_OF(UntaggedSuspendState, frame_size_);
}
static intptr_t pc_offset() { return OFFSET_OF(UntaggedSuspendState, pc_); }
static intptr_t future_offset() {
return OFFSET_OF(UntaggedSuspendState, future_);
static intptr_t function_data_offset() {
return OFFSET_OF(UntaggedSuspendState, function_data_);
}
static intptr_t then_callback_offset() {
return OFFSET_OF(UntaggedSuspendState, then_callback_);
@ -11818,10 +11831,10 @@ class SuspendState : public Instance {
}
static SuspendStatePtr New(intptr_t frame_size,
const Instance& future,
const Instance& function_data,
Heap::Space space = Heap::kNew);
InstancePtr future() const { return untag()->future(); }
InstancePtr function_data() const { return untag()->function_data(); }
uword pc() const { return untag()->pc_; }
// Returns Code object corresponding to the suspended function.
@ -11830,7 +11843,7 @@ class SuspendState : public Instance {
private:
void set_frame_size(intptr_t frame_size) const;
void set_pc(uword pc) const;
void set_future(const Instance& future) const;
void set_function_data(const Instance& function_data) const;
FINAL_HEAP_OBJECT_IMPLEMENTATION(SuspendState, Instance);
friend class Class;

View file

@ -286,8 +286,17 @@ void ObjectStore::InitKnownObjects() {
cls =
async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController());
ASSERT(!cls.IsNull());
RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null());
set_async_star_stream_controller(cls);
function = cls.LookupFunctionAllowPrivate(Symbols::add());
ASSERT(!function.IsNull());
set_async_star_stream_controller_add(function);
function = cls.LookupFunctionAllowPrivate(Symbols::addStream());
ASSERT(!function.IsNull());
set_async_star_stream_controller_add_stream(function);
if (FLAG_async_debugger) {
// Disable debugging and inlining of all functions on the
// _AsyncStarStreamController class.
@ -301,6 +310,10 @@ void ObjectStore::InitKnownObjects() {
}
}
cls = async_lib.LookupClassAllowPrivate(Symbols::Stream());
ASSERT(!cls.IsNull());
set_stream_class(cls);
cls = async_lib.LookupClassAllowPrivate(Symbols::_SuspendState());
ASSERT(!cls.IsNull());
const auto& error = cls.EnsureIsFinalized(thread);
@ -310,9 +323,9 @@ void ObjectStore::InitKnownObjects() {
ASSERT(!function.IsNull());
set_suspend_state_init_async(function);
function = cls.LookupFunctionAllowPrivate(Symbols::_awaitAsync());
function = cls.LookupFunctionAllowPrivate(Symbols::_await());
ASSERT(!function.IsNull());
set_suspend_state_await_async(function);
set_suspend_state_await(function);
function = cls.LookupFunctionAllowPrivate(Symbols::_returnAsync());
ASSERT(!function.IsNull());
@ -322,6 +335,18 @@ void ObjectStore::InitKnownObjects() {
ASSERT(!function.IsNull());
set_suspend_state_return_async_not_future(function);
function = cls.LookupFunctionAllowPrivate(Symbols::_initAsyncStar());
ASSERT(!function.IsNull());
set_suspend_state_init_async_star(function);
function = cls.LookupFunctionAllowPrivate(Symbols::_yieldAsyncStar());
ASSERT(!function.IsNull());
set_suspend_state_yield_async_star(function);
function = cls.LookupFunctionAllowPrivate(Symbols::_returnAsyncStar());
ASSERT(!function.IsNull());
set_suspend_state_return_async_star(function);
function = cls.LookupFunctionAllowPrivate(Symbols::_handleException());
ASSERT(!function.IsNull());
set_suspend_state_handle_exception(function);

View file

@ -165,15 +165,21 @@ class ObjectPointerVisitor;
RW(Function, simple_instance_of_true_function) \
RW(Function, simple_instance_of_false_function) \
RW(Function, async_star_move_next_helper) \
RW(Function, async_star_stream_controller_add) \
RW(Function, async_star_stream_controller_add_stream) \
RW(Function, complete_on_async_return) \
RW(Function, complete_with_no_future_on_async_return) \
RW(Function, complete_on_async_error) \
RW(Function, suspend_state_init_async) \
RW(Function, suspend_state_await_async) \
RW(Function, suspend_state_await) \
RW(Function, suspend_state_return_async) \
RW(Function, suspend_state_return_async_not_future) \
RW(Function, suspend_state_init_async_star) \
RW(Function, suspend_state_yield_async_star) \
RW(Function, suspend_state_return_async_star) \
RW(Function, suspend_state_handle_exception) \
RW(Class, async_star_stream_controller) \
RW(Class, stream_class) \
ARW_RELAXED(Smi, future_timeout_future_index) \
ARW_RELAXED(Smi, future_wait_future_index) \
RW(CompressedStackMaps, canonicalized_stack_map_entries) \
@ -244,11 +250,14 @@ class ObjectPointerVisitor;
RW(Code, type_parameter_tts_stub) \
RW(Code, unreachable_tts_stub) \
RW(Code, slow_tts_stub) \
RW(Code, await_async_stub) \
RW(Code, await_stub) \
RW(Code, init_async_stub) \
RW(Code, resume_stub) \
RW(Code, return_async_stub) \
RW(Code, return_async_not_future_stub) \
RW(Code, init_async_star_stub) \
RW(Code, yield_async_star_stub) \
RW(Code, return_async_star_stub) \
RW(Array, dispatch_table_code_entries) \
RW(GrowableObjectArray, instructions_tables) \
RW(Array, obfuscation_map) \
@ -324,11 +333,14 @@ class ObjectPointerVisitor;
DO(init_instance_field_stub, InitInstanceField) \
DO(init_late_instance_field_stub, InitLateInstanceField) \
DO(init_late_final_instance_field_stub, InitLateFinalInstanceField) \
DO(await_async_stub, AwaitAsync) \
DO(await_stub, Await) \
DO(init_async_stub, InitAsync) \
DO(resume_stub, Resume) \
DO(return_async_stub, ReturnAsync) \
DO(return_async_not_future_stub, ReturnAsyncNotFuture) \
DO(init_async_star_stub, InitAsyncStar) \
DO(yield_async_star_stub, YieldAsyncStar) \
DO(return_async_star_stub, ReturnAsyncStar) \
DO(instance_of_stub, InstanceOf)
#define ISOLATE_OBJECT_STORE_FIELD_LIST(R_, RW) \

View file

@ -3292,10 +3292,15 @@ class UntaggedSuspendState : public UntaggedInstance {
intptr_t frame_size_;
uword pc_;
COMPRESSED_POINTER_FIELD(InstancePtr, future)
// Holds function-specific object which is returned from
// SuspendState.init* method.
// For async functions: _Future instance.
// For async* functions: _AsyncStarStreamController instance.
COMPRESSED_POINTER_FIELD(InstancePtr, function_data)
COMPRESSED_POINTER_FIELD(ClosurePtr, then_callback)
COMPRESSED_POINTER_FIELD(ClosurePtr, error_callback)
VISIT_FROM(future)
VISIT_FROM(function_data)
VISIT_TO(error_callback)
public:

View file

@ -191,7 +191,7 @@ namespace dart {
F(RegExp, two_byte_sticky_) \
F(RegExp, external_one_byte_sticky_) \
F(RegExp, external_two_byte_sticky_) \
F(SuspendState, future_) \
F(SuspendState, function_data_) \
F(SuspendState, then_callback_) \
F(SuspendState, error_callback_) \
F(WeakProperty, key_) \

View file

@ -711,13 +711,14 @@ DEFINE_RUNTIME_ENTRY(CloneContext, 1) {
// Allocate a SuspendState object.
// Arg0: frame size.
// Arg1: future.
// Arg1: function data.
// Return value: newly allocated object.
DEFINE_RUNTIME_ENTRY(AllocateSuspendState, 2) {
const Smi& frame_size = Smi::CheckedHandle(zone, arguments.ArgAt(0));
const Instance& future = Instance::CheckedHandle(zone, arguments.ArgAt(1));
const Instance& function_data =
Instance::CheckedHandle(zone, arguments.ArgAt(1));
const SuspendState& result = SuspendState::Handle(
zone, SuspendState::New(frame_size.Value(), future,
zone, SuspendState::New(frame_size.Value(), function_data,
SpaceForRuntimeAllocation()));
arguments.SetReturn(result);
}

View file

@ -70,7 +70,7 @@ CallerClosureFinder::CallerClosureFinder(Zone* zone)
callback_instance_(Object::Handle(zone)),
future_impl_class(Class::Handle(zone)),
future_listener_class(Class::Handle(zone)),
async_start_stream_controller_class(Class::Handle(zone)),
async_star_stream_controller_class(Class::Handle(zone)),
stream_controller_class(Class::Handle(zone)),
sync_stream_controller_class(Class::Handle(zone)),
controller_subscription_class(Class::Handle(zone)),
@ -95,9 +95,9 @@ CallerClosureFinder::CallerClosureFinder(Zone* zone)
async_lib.LookupClassAllowPrivate(Symbols::_FutureListener());
ASSERT(!future_listener_class.IsNull());
// - async*:
async_start_stream_controller_class =
async_star_stream_controller_class =
async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController());
ASSERT(!async_start_stream_controller_class.IsNull());
ASSERT(!async_star_stream_controller_class.IsNull());
stream_controller_class =
async_lib.LookupClassAllowPrivate(Symbols::_StreamController());
ASSERT(!stream_controller_class.IsNull());
@ -130,7 +130,7 @@ CallerClosureFinder::CallerClosureFinder(Zone* zone)
ASSERT(!future_listener_result_field.IsNull());
// - async*:
controller_controller_field =
async_start_stream_controller_class.LookupFieldAllowPrivate(
async_star_stream_controller_class.LookupFieldAllowPrivate(
Symbols::controller());
ASSERT(!controller_controller_field.IsNull());
state_field =
@ -163,14 +163,19 @@ ClosurePtr CallerClosureFinder::GetCallerInFutureImpl(const Object& future) {
ClosurePtr CallerClosureFinder::FindCallerInAsyncGenClosure(
const Context& receiver_context) {
// Get the async* _StreamController.
// Get the async* _AsyncStarStreamController.
context_entry_ = receiver_context.At(Context::kControllerIndex);
ASSERT(context_entry_.IsInstance());
ASSERT(context_entry_.GetClassId() ==
async_start_stream_controller_class.id());
return FindCallerInAsyncStarStreamController(context_entry_);
}
const Instance& controller = Instance::Cast(context_entry_);
controller_ = controller.GetField(controller_controller_field);
ClosurePtr CallerClosureFinder::FindCallerInAsyncStarStreamController(
const Object& async_star_stream_controller) {
ASSERT(async_star_stream_controller.IsInstance());
ASSERT(async_star_stream_controller.GetClassId() ==
async_star_stream_controller_class.id());
controller_ = Instance::Cast(async_star_stream_controller)
.GetField(controller_controller_field);
ASSERT(!controller_.IsNull());
ASSERT(controller_.GetClassId() == sync_stream_controller_class.id());
@ -269,8 +274,15 @@ ClosurePtr CallerClosureFinder::FindCaller(const Closure& receiver_closure) {
ClosurePtr CallerClosureFinder::FindCallerFromSuspendState(
const SuspendState& suspend_state) {
future_ = suspend_state.future();
return GetCallerInFutureImpl(future_);
context_entry_ = suspend_state.function_data();
if (context_entry_.GetClassId() == future_impl_class.id()) {
return GetCallerInFutureImpl(context_entry_);
} else if (context_entry_.GetClassId() ==
async_star_stream_controller_class.id()) {
return FindCallerInAsyncStarStreamController(context_entry_);
} else {
UNREACHABLE();
}
}
ClosurePtr CallerClosureFinder::UnwrapAsyncThen(const Closure& closure) {
@ -289,14 +301,15 @@ ClosurePtr CallerClosureFinder::UnwrapAsyncThen(const Closure& closure) {
bool CallerClosureFinder::IsCompactAsyncCallback(const Function& function) {
parent_function_ = function.parent_function();
return parent_function_.recognized_kind() ==
MethodRecognizer::kSuspendState_createAsyncCallbacks;
auto kind = parent_function_.recognized_kind();
return (kind == MethodRecognizer::kSuspendState_createAsyncCallbacks) ||
(kind == MethodRecognizer::kSuspendState_createAsyncStarCallback);
}
SuspendStatePtr CallerClosureFinder::GetSuspendStateFromAsyncCallback(
const Closure& closure) {
ASSERT(IsCompactAsyncCallback(Function::Handle(closure.function())));
// Async handler only captures the receiver (SuspendState).
// Async/async* handler only captures the receiver (SuspendState).
receiver_context_ = closure.context();
RELEASE_ASSERT(receiver_context_.num_variables() == 1);
return SuspendState::RawCast(receiver_context_.At(0));
@ -469,7 +482,8 @@ ClosurePtr StackTraceUtils::ClosureFromFrameFunction(
return Closure::null();
}
if (function.IsCompactAsyncFunction()) {
if (function.IsCompactAsyncFunction() ||
function.IsCompactAsyncStarFunction()) {
auto& suspend_state = Object::Handle(
zone, *reinterpret_cast<ObjectPtr*>(LocalVarAddress(
frame->fp(), runtime_frame_layout.FrameSlotForVariableIndex(

View file

@ -31,6 +31,12 @@ class CallerClosureFinder {
// 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.
ClosurePtr FindCallerInAsyncStarStreamController(
const Object& async_star_stream_controller);
// 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.
@ -40,11 +46,11 @@ class CallerClosureFinder {
ClosurePtr FindCallerFromSuspendState(const SuspendState& suspend_state);
// Returns true if given closure function is a Future callback
// corresponding to an async function.
// corresponding to an async/async* function or async* body callback.
bool IsCompactAsyncCallback(const Function& function);
// Returns SuspendState from the given Future callback which corresponds
// to an async 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.
@ -88,7 +94,7 @@ class CallerClosureFinder {
Class& future_impl_class;
Class& future_listener_class;
Class& async_start_stream_controller_class;
Class& async_star_stream_controller_class;
Class& stream_controller_class;
Class& sync_stream_controller_class;
Class& controller_subscription_class;

View file

@ -149,11 +149,14 @@ namespace dart {
V(InstantiateTypeArgumentsMayShareInstantiatorTA) \
V(InstantiateTypeArgumentsMayShareFunctionTA) \
V(NoSuchMethodDispatcher) \
V(AwaitAsync) \
V(Await) \
V(InitAsync) \
V(Resume) \
V(ReturnAsync) \
V(ReturnAsyncNotFuture) \
V(InitAsyncStar) \
V(YieldAsyncStar) \
V(ReturnAsyncStar) \
V(AsyncExceptionHandler) \
V(UnknownDartCode)

View file

@ -241,6 +241,7 @@ class ObjectPointerVisitor;
V(SpaceWhereNewLine, " where\n") \
V(StackOverflowError, "StackOverflowError") \
V(StackTraceParameter, ":stack_trace") \
V(Stream, "Stream") \
V(StringBase, "_StringBase") \
V(Struct, "Struct") \
V(SubtypeTestCache, "SubtypeTestCache") \
@ -410,7 +411,7 @@ class ObjectPointerVisitor;
V(_WeakProperty, "_WeakProperty") \
V(_WeakReferenceImpl, "_WeakReferenceImpl") \
V(_typedDataBase, "_typedDataBase") \
V(_awaitAsync, "_awaitAsync") \
V(_await, "_await") \
V(_classRangeCheck, "_classRangeCheck") \
V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \
V(_future, "_future") \
@ -420,6 +421,7 @@ class ObjectPointerVisitor;
V(_handleNativeFinalizerMessage, "_handleNativeFinalizerMessage") \
V(_hasValue, "_hasValue") \
V(_initAsync, "_initAsync") \
V(_initAsyncStar, "_initAsyncStar") \
V(_instanceOf, "_instanceOf") \
V(_listGetAt, "_listGetAt") \
V(_listLength, "_listLength") \
@ -439,6 +441,7 @@ class ObjectPointerVisitor;
V(_resultOrListeners, "_resultOrListeners") \
V(_returnAsync, "_returnAsync") \
V(_returnAsyncNotFuture, "_returnAsyncNotFuture") \
V(_returnAsyncStar, "_returnAsyncStar") \
V(_runExtension, "_runExtension") \
V(_runPendingImmediateCallback, "_runPendingImmediateCallback") \
V(_scanFlags, "_scanFlags") \
@ -451,6 +454,9 @@ class ObjectPointerVisitor;
V(_toString, "_toString") \
V(_varData, "_varData") \
V(_wordCharacterMap, "_wordCharacterMap") \
V(_yieldAsyncStar, "_yieldAsyncStar") \
V(add, "add") \
V(addStream, "addStream") \
V(callback, "callback") \
V(capture_length, ":capture_length") \
V(capture_start_index, ":capture_start_index") \

View file

@ -168,9 +168,12 @@ class Thread;
#define CACHED_FUNCTION_ENTRY_POINTS_LIST(V) \
V(suspend_state_init_async) \
V(suspend_state_await_async) \
V(suspend_state_await) \
V(suspend_state_return_async) \
V(suspend_state_return_async_not_future) \
V(suspend_state_init_async_star) \
V(suspend_state_yield_async_star) \
V(suspend_state_return_async_star) \
V(suspend_state_handle_exception)
// This assertion marks places which assume that boolean false immediate

View file

@ -110,7 +110,7 @@ void _asyncStarMoveNextHelper(var stream) {
class _AsyncStarStreamController<T> {
@pragma("vm:entry-point")
StreamController<T> controller;
Function asyncStarBody;
Function? asyncStarBody;
bool isAdding = false;
bool onListenReceived = false;
bool isScheduled = false;
@ -127,7 +127,7 @@ class _AsyncStarStreamController<T> {
Stream<T> get stream {
final Stream<T> local = controller.stream;
if (local is _StreamImpl<T>) {
local._generator = asyncStarBody;
local._generator = asyncStarBody!;
}
return local;
}
@ -137,7 +137,7 @@ class _AsyncStarStreamController<T> {
isSuspendedAtYield = false;
final bool? argument = continuationArgument;
continuationArgument = null;
asyncStarBody(argument, null);
asyncStarBody!(argument, null);
}
void scheduleGenerator() {
@ -158,6 +158,7 @@ class _AsyncStarStreamController<T> {
// controller.add(e);
// suspend;
// if (controller.isCancelled) return;
@pragma("vm:entry-point", "call")
bool add(T event) {
if (!onListenReceived) _fatal("yield before stream is listened to");
if (isSuspendedAtYield) _fatal("unexpected yield");
@ -174,6 +175,7 @@ class _AsyncStarStreamController<T> {
// Adds the elements of stream into this controller's stream.
// The generator will be scheduled again when all of the
// elements of the added stream have been consumed.
@pragma("vm:entry-point", "call")
void addStream(Stream<T> stream) {
if (!onListenReceived) _fatal("yield before stream is listened to");
@ -327,7 +329,7 @@ class _SuspendState {
@pragma("vm:invisible")
static Object? _initAsync<T>() {
if (_trace) print('_initAsync<$T>');
return new _Future<T>();
return _Future<T>();
}
@pragma("vm:invisible")
@ -371,14 +373,14 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
Object? _awaitAsync(Object? object) {
Object? _await(Object? object) {
if (_trace) print('_awaitAsync (object=$object)');
if (_thenCallback == null) {
_createAsyncCallbacks();
}
_awaitHelper(object, unsafeCast<dynamic Function(dynamic)>(_thenCallback),
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
return _future;
return _functionData;
}
@pragma("vm:entry-point", "call")
@ -391,7 +393,7 @@ class _SuspendState {
_Future future;
bool isSync = true;
if (suspendState is _SuspendState) {
future = suspendState._future;
future = unsafeCast<_Future>(suspendState._functionData);
} else {
future = unsafeCast<_Future>(suspendState);
isSync = false;
@ -411,7 +413,7 @@ class _SuspendState {
_Future future;
bool isSync = true;
if (suspendState is _SuspendState) {
future = suspendState._future;
future = unsafeCast<_Future>(suspendState._functionData);
} else {
future = unsafeCast<_Future>(suspendState);
isSync = false;
@ -422,31 +424,79 @@ class _SuspendState {
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static Future _handleException(
static Object? _initAsyncStar<T>() {
if (_trace) print('_initAsyncStar<$T>');
return _AsyncStarStreamController<T>(null);
}
@pragma("vm:invisible")
@pragma("vm:recognized", "other")
_createAsyncStarCallback(_AsyncStarStreamController controller) {
controller.asyncStarBody = (value, _) {
if (_trace) print('asyncStarBody callback (value=$value)');
_resume(value, null, null);
};
}
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
Object? _yieldAsyncStar(Object? object) {
final controller = unsafeCast<_AsyncStarStreamController>(_functionData);
if (controller.asyncStarBody == null) {
_createAsyncStarCallback(controller);
return controller.stream;
}
return null;
}
@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();
}
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
static Object? _handleException(
Object suspendState, Object exception, StackTrace stackTrace) {
if (_trace) {
print('_handleException (suspendState=$suspendState, '
'exception=$exception, stackTrace=$stackTrace)');
}
_Future future;
Object? functionData;
bool isSync = true;
if (suspendState is _SuspendState) {
future = suspendState._future;
functionData = suspendState._functionData;
} else {
future = unsafeCast<_Future>(suspendState);
functionData = suspendState;
isSync = false;
}
_completeOnAsyncError(future, exception, stackTrace, isSync);
return future;
if (functionData is _Future) {
// async function.
_completeOnAsyncError(functionData, exception, stackTrace, isSync);
} else if (functionData is _AsyncStarStreamController) {
// async* function.
functionData.addError(exception, stackTrace);
functionData.close();
} else {
throw 'Unexpected function data ${functionData.runtimeType} $functionData';
}
return functionData;
}
@pragma("vm:recognized", "other")
@pragma("vm:prefer-inline")
external set _future(_Future value);
external set _functionData(Object value);
@pragma("vm:recognized", "other")
@pragma("vm:prefer-inline")
external _Future get _future;
external Object get _functionData;
@pragma("vm:recognized", "other")
@pragma("vm:prefer-inline")
@ -467,5 +517,5 @@ class _SuspendState {
@pragma("vm:recognized", "other")
@pragma("vm:never-inline")
external void _resume(
dynamic value, Object? exception, StackTrace? stackTrace);
Object? value, Object? exception, StackTrace? stackTrace);
}