Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index 923526885ecea9685b32b527aa03bcd12b6b9592..25d83e7bd725a320e01f56268f37f15562bb131b 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -3468,6 +3468,9 @@ SequenceNode* Parser::ParseFunc(const Function& func, bool check_semicolon) { |
ASSERT(!func.is_generated_body()); |
// The code of an async function is synthesized. Disable debugging. |
func.set_is_debuggable(false); |
+ // In order to produce sane asynchronous stacks we rely on this function |
+ // not being inlined. |
+ func.set_is_inlinable(false); |
generated_body_closure = OpenAsyncFunction(func.token_pos()); |
} else if (func.IsAsyncClosure()) { |
// The closure containing the body of an async function is debuggable. |
@@ -6808,6 +6811,9 @@ RawFunction* Parser::OpenAsyncFunction(TokenPosition async_func_pos) { |
closure = Function::NewClosureFunction(closure_name, innermost_function(), |
async_func_pos); |
closure.set_is_generated_body(true); |
+ // In order to generate sane asynchronous stacks we rely on this function |
+ // not being inlined. |
+ closure.set_is_inlinable(false); |
closure.set_result_type(Object::dynamic_type()); |
is_new_closure = true; |
} |
@@ -6861,6 +6867,7 @@ void Parser::AddAsyncClosureVariables() { |
// var :async_then_callback; |
// var :async_catch_error_callback; |
// var :async_completer; |
+ // var :async_stack_trace; |
LocalVariable* async_op_var = |
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
Symbols::AsyncOperation(), Object::dynamic_type()); |
@@ -6877,6 +6884,10 @@ void Parser::AddAsyncClosureVariables() { |
new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
Symbols::AsyncCompleter(), Object::dynamic_type()); |
current_block_->scope->AddVariable(async_completer); |
+ LocalVariable* async_stack_trace = new (Z) |
+ LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
+ Symbols::AsyncStackTraceVar(), Object::dynamic_type()); |
+ current_block_->scope->AddVariable(async_stack_trace); |
} |
@@ -6889,6 +6900,8 @@ void Parser::AddAsyncGeneratorVariables() { |
// var :async_op; |
// var :async_then_callback; |
// var :async_catch_error_callback; |
+ // var :async_stack_trace; |
+ // var :controller_stream |
// These variables are used to store the async generator closure containing |
// the body of the async* function. They are used by the await operator. |
LocalVariable* controller_var = |
@@ -6907,6 +6920,14 @@ void Parser::AddAsyncGeneratorVariables() { |
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
Symbols::AsyncCatchErrorCallback(), Object::dynamic_type()); |
current_block_->scope->AddVariable(async_catch_error_callback_var); |
+ LocalVariable* async_stack_trace = new (Z) |
+ LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
+ Symbols::AsyncStackTraceVar(), Object::dynamic_type()); |
+ current_block_->scope->AddVariable(async_stack_trace); |
+ LocalVariable* controller_stream = new (Z) |
+ LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
+ Symbols::ControllerStream(), Object::dynamic_type()); |
+ current_block_->scope->AddVariable(controller_stream); |
} |
@@ -6936,6 +6957,7 @@ RawFunction* Parser::OpenAsyncGeneratorFunction(TokenPosition async_func_pos) { |
closure = Function::NewClosureFunction(closure_name, innermost_function(), |
async_func_pos); |
closure.set_is_generated_body(true); |
+ closure.set_is_inlinable(false); |
closure.set_result_type(Object::dynamic_type()); |
is_new_closure = true; |
} |
@@ -6976,7 +6998,8 @@ RawFunction* Parser::OpenAsyncGeneratorFunction(TokenPosition async_func_pos) { |
// var :async_then_callback = _asyncThenWrapperHelper(:async_op); |
// var :async_catch_error_callback = _asyncCatchErrorWrapperHelper(:async_op); |
// :controller = new _AsyncStarStreamController(:async_op); |
-// return :controller.stream; |
+// var :controller_stream = :controller.stream; |
+// return :controller_stream; |
// } |
SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func, |
SequenceNode* closure_body) { |
@@ -7004,6 +7027,12 @@ SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func, |
existing_var = closure_body->scope()->LookupVariable( |
Symbols::AsyncCatchErrorCallback(), false); |
ASSERT((existing_var != NULL) && existing_var->is_captured()); |
+ existing_var = closure_body->scope()->LookupVariable( |
+ Symbols::AsyncStackTraceVar(), false); |
+ ASSERT((existing_var != NULL) && existing_var->is_captured()); |
+ existing_var = |
+ closure_body->scope()->LookupVariable(Symbols::ControllerStream(), false); |
+ ASSERT((existing_var != NULL) && existing_var->is_captured()); |
const Library& async_lib = Library::Handle(Library::AsyncLibrary()); |
@@ -7015,6 +7044,8 @@ SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func, |
Z, controller_class.LookupConstructorAllowPrivate( |
Symbols::_AsyncStarStreamControllerConstructor())); |
+ TokenPosition token_pos = TokenPosition::kNoSource; |
+ |
// :await_jump_var = -1; |
LocalVariable* jump_var = |
current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false); |
@@ -7034,6 +7065,24 @@ SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func, |
current_block_->statements->Add(store_async_op); |
+ // Add to AST: |
+ // :async_stack_trace = _asyncStackTraceHelper(:async_op); |
+ const Function& async_stack_trace_helper = Function::ZoneHandle( |
+ Z, |
+ async_lib.LookupFunctionAllowPrivate(Symbols::AsyncStackTraceHelper())); |
+ ASSERT(!async_stack_trace_helper.IsNull()); |
+ ArgumentListNode* async_stack_trace_helper_args = |
+ new (Z) ArgumentListNode(TokenPosition::kNoSource); |
+ async_stack_trace_helper_args->Add( |
+ new (Z) LoadLocalNode(TokenPosition::kNoSource, async_op_var)); |
+ StaticCallNode* async_stack_trace_helper_call = new (Z) StaticCallNode( |
+ token_pos, async_stack_trace_helper, async_stack_trace_helper_args); |
+ LocalVariable* async_stack_trace_var = current_block_->scope->LookupVariable( |
+ Symbols::AsyncStackTraceVar(), false); |
+ StoreLocalNode* store_async_stack_trace = new (Z) StoreLocalNode( |
+ token_pos, async_stack_trace_var, async_stack_trace_helper_call); |
+ current_block_->statements->Add(store_async_stack_trace); |
+ |
// :async_then_callback = _asyncThenWrapperHelper(:async_op) |
const Function& async_then_wrapper_helper = Function::ZoneHandle( |
Z, |
@@ -7090,13 +7139,28 @@ SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func, |
TokenPosition::kNoSource, controller_var, controller_constructor_call); |
current_block_->statements->Add(store_controller); |
- // return :controller.stream; |
+ // Grab :controller.stream |
+ InstanceGetterNode* controller_stream = new (Z) InstanceGetterNode( |
+ TokenPosition::kNoSource, |
+ new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var), |
+ Symbols::Stream()); |
+ |
+ // Store :controller.stream into :controller_stream inside the closure. |
+ // We have to remember the stream because a new instance is generated for |
+ // each getter invocation and in order to recreate the linkage, we need the |
+ // awaited on instance. |
+ LocalVariable* controller_stream_var = |
+ current_block_->scope->LookupVariable(Symbols::ControllerStream(), false); |
+ ASSERT(controller_stream_var != NULL); |
+ |
+ StoreLocalNode* store_controller_stream = new (Z) StoreLocalNode( |
+ TokenPosition::kNoSource, controller_stream_var, controller_stream); |
+ current_block_->statements->Add(store_controller_stream); |
+ |
+ // return :controller_stream; |
ReturnNode* return_node = new (Z) ReturnNode( |
TokenPosition::kNoSource, |
- new (Z) InstanceGetterNode( |
- TokenPosition::kNoSource, |
- new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var), |
- Symbols::Stream())); |
+ new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_stream_var)); |
current_block_->statements->Add(return_node); |
return CloseBlock(); |
} |
@@ -7166,6 +7230,9 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure, |
existing_var = |
closure_body->scope()->LookupVariable(Symbols::AsyncCompleter(), false); |
ASSERT((existing_var != NULL) && existing_var->is_captured()); |
+ existing_var = closure_body->scope()->LookupVariable( |
+ Symbols::AsyncStackTraceVar(), false); |
+ ASSERT((existing_var != NULL) && existing_var->is_captured()); |
// Create and return a new future that executes a closure with the current |
// body. |
@@ -7185,6 +7252,8 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure, |
Z, completer.LookupFunction(Symbols::CompleterSyncConstructor())); |
ASSERT(!completer_constructor.IsNull()); |
+ const Library& async_lib = Library::Handle(Library::AsyncLibrary()); |
+ |
LocalVariable* async_completer = |
current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false); |
@@ -7217,7 +7286,24 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure, |
new (Z) StoreLocalNode(token_pos, async_op_var, cn); |
current_block_->statements->Add(store_async_op); |
- const Library& async_lib = Library::Handle(Library::AsyncLibrary()); |
+ // Add to AST: |
+ // :async_stack_trace = _asyncStackTraceHelper(:async_op); |
+ const Function& async_stack_trace_helper = Function::ZoneHandle( |
+ Z, |
+ async_lib.LookupFunctionAllowPrivate(Symbols::AsyncStackTraceHelper())); |
+ ASSERT(!async_stack_trace_helper.IsNull()); |
+ ArgumentListNode* async_stack_trace_helper_args = |
+ new (Z) ArgumentListNode(token_pos); |
+ async_stack_trace_helper_args->Add( |
+ new (Z) LoadLocalNode(token_pos, async_op_var)); |
+ StaticCallNode* async_stack_trace_helper_call = new (Z) StaticCallNode( |
+ token_pos, async_stack_trace_helper, async_stack_trace_helper_args); |
+ LocalVariable* async_stack_trace_var = current_block_->scope->LookupVariable( |
+ Symbols::AsyncStackTraceVar(), false); |
+ StoreLocalNode* store_async_stack_trace = new (Z) StoreLocalNode( |
+ token_pos, async_stack_trace_var, async_stack_trace_helper_call); |
+ current_block_->statements->Add(store_async_stack_trace); |
+ |
// :async_then_callback = _asyncThenWrapperHelper(:async_op) |
const Function& async_then_wrapper_helper = Function::ZoneHandle( |
Z, |
@@ -8779,6 +8865,36 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) { |
ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); |
ExpectToken(Token::kRPAREN); |
+ // Create :stream to store the stream into temporarily. |
+ LocalVariable* stream_var = |
+ new (Z) LocalVariable(stream_expr_pos, stream_expr_pos, |
+ Symbols::ColonStream(), Object::dynamic_type()); |
+ current_block_->scope->AddVariable(stream_var); |
+ |
+ // Store the stream expression into a variable. |
+ StoreLocalNode* store_stream_var = |
+ new (Z) StoreLocalNode(stream_expr_pos, stream_var, stream_expr); |
+ current_block_->statements->Add(store_stream_var); |
+ |
+ // Register the awaiter on the stream by invoking `_asyncStarListenHelper`. |
+ const Library& async_lib = Library::Handle(Library::AsyncLibrary()); |
+ const Function& async_star_listen_helper = Function::ZoneHandle( |
+ Z, |
+ async_lib.LookupFunctionAllowPrivate(Symbols::_AsyncStarListenHelper())); |
+ ASSERT(!async_star_listen_helper.IsNull()); |
+ LocalVariable* async_op_var = |
+ current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false); |
+ ASSERT(async_op_var != NULL); |
+ ArgumentListNode* async_star_listen_helper_args = |
+ new (Z) ArgumentListNode(stream_expr_pos); |
+ async_star_listen_helper_args->Add( |
+ new (Z) LoadLocalNode(stream_expr_pos, stream_var)); |
+ async_star_listen_helper_args->Add( |
+ new (Z) LoadLocalNode(stream_expr_pos, async_op_var)); |
+ StaticCallNode* async_star_listen_helper_call = new (Z) StaticCallNode( |
+ stream_expr_pos, async_star_listen_helper, async_star_listen_helper_args); |
+ current_block_->statements->Add(async_star_listen_helper_call); |
+ |
// Build creation of implicit StreamIterator. |
// var :for-in-iter = new StreamIterator(stream_expr). |
const Class& stream_iterator_cls = |
@@ -8789,7 +8905,7 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) { |
stream_iterator_cls.LookupFunction(Symbols::StreamIteratorConstructor())); |
ASSERT(!iterator_ctor.IsNull()); |
ArgumentListNode* ctor_args = new (Z) ArgumentListNode(stream_expr_pos); |
- ctor_args->Add(stream_expr); |
+ ctor_args->Add(new (Z) LoadLocalNode(stream_expr_pos, stream_var)); |
ConstructorCallNode* ctor_call = new (Z) ConstructorCallNode( |
stream_expr_pos, TypeArguments::ZoneHandle(Z), iterator_ctor, ctor_args); |
const AbstractType& iterator_type = Object::dynamic_type(); |
@@ -8834,7 +8950,23 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) { |
AstNode* iterator_moveNext = new (Z) InstanceCallNode( |
stream_expr_pos, new (Z) LoadLocalNode(stream_expr_pos, iterator_var), |
Symbols::MoveNext(), no_args); |
+ |
OpenBlock(); |
+ // Give the debugger an opportunity to 'step into' the generator by |
+ // calling '_asyncStarMoveNextHelper'. |
+ const Function& async_star_move_next_helper = |
+ Function::ZoneHandle(Z, async_lib.LookupFunctionAllowPrivate( |
+ Symbols::_AsyncStarMoveNextHelper())); |
+ ASSERT(!async_star_move_next_helper.IsNull()); |
+ ASSERT(async_op_var != NULL); |
+ ArgumentListNode* async_star_move_next_helper_args = |
+ new (Z) ArgumentListNode(stream_expr_pos); |
+ async_star_move_next_helper_args->Add( |
+ new (Z) LoadLocalNode(stream_expr_pos, stream_var)); |
+ StaticCallNode* async_star_move_next_helper_call = |
+ new (Z) StaticCallNode(stream_expr_pos, async_star_move_next_helper, |
+ async_star_move_next_helper_args); |
+ current_block_->statements->Add(async_star_move_next_helper_call); |
AstNode* await_moveNext = new (Z) AwaitNode( |
stream_expr_pos, iterator_moveNext, saved_try_ctx, async_saved_try_ctx, |
outer_saved_try_ctx, outer_async_saved_try_ctx, current_block_->scope); |