Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index 62a6a071305fee4fc0a3b29cd3063d186b444fd0..f7db62fa973ae6feac63732acdd0e390194e6428 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -5425,7 +5425,6 @@ void Parser::OpenFunctionBlock(const Function& func) { |
void Parser::OpenAsyncClosure() { |
TRACE_PARSER("OpenAsyncClosure"); |
parsed_function()->set_await_temps_scope(current_block_->scope); |
- // TODO(mlippautz): Set up explicit jump table for await continuations. |
} |
@@ -5489,6 +5488,16 @@ SequenceNode* Parser::CloseBlock() { |
} |
+static inline String& BuildAsyncSavedTryContextName(Isolate* isolate, |
+ int16_t id) { |
+ const char* async_saved_prefix = ":async_saved_try_ctx_var_"; |
+ // Can be a regular handle since we only use it to build an actual symbol. |
+ const String& cnt_str = String::Handle( |
+ String::NewFormatted("%s%d", async_saved_prefix, id)); |
+ return String::ZoneHandle(isolate, Symbols::New(cnt_str)); |
+} |
+ |
+ |
SequenceNode* Parser::CloseAsyncFunction(const Function& closure, |
SequenceNode* closure_body) { |
TRACE_PARSER("CloseAsyncFunction"); |
@@ -5501,8 +5510,6 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure, |
// Create and return a new future that executes a closure with the current |
// body. |
- bool found = false; |
- |
// No need to capture parameters or other variables, since they have already |
// been captured in the corresponding scope as the body has been parsed within |
// a nested block (contained in the async funtion's block). |
@@ -5519,28 +5526,49 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure, |
completer.LookupFunction(Symbols::CompleterConstructor())); |
ASSERT(!completer_constructor.IsNull()); |
+ bool found = false; |
// Add to AST: |
// var :async_op; |
// var :async_completer; |
+ // var :await_jump_var; |
+ // var :await_ctx_var; |
+ // Add as many variables to saved the try block ctx as there were try blocks. |
+ // var :async_saved_try_ctx_var_<x>; |
+ const Type& dynamic_type = Type::ZoneHandle(I, Type::DynamicType()); |
LocalVariable* async_op_var = new (I) LocalVariable( |
- Scanner::kNoSourcePos, |
- Symbols::AsyncOperation(), |
- Type::ZoneHandle(I, Type::DynamicType())); |
+ Scanner::kNoSourcePos, Symbols::AsyncOperation(), dynamic_type); |
current_block_->scope->AddVariable(async_op_var); |
found = closure_body->scope()->CaptureVariable(Symbols::AsyncOperation()); |
ASSERT(found); |
LocalVariable* async_completer = new (I) LocalVariable( |
- Scanner::kNoSourcePos, |
- Symbols::AsyncCompleter(), |
- Type::ZoneHandle(I, Type::DynamicType())); |
+ Scanner::kNoSourcePos, Symbols::AsyncCompleter(), dynamic_type); |
current_block_->scope->AddVariable(async_completer); |
found = closure_body->scope()->CaptureVariable(Symbols::AsyncCompleter()); |
ASSERT(found); |
+ LocalVariable* await_jump_var = new (I) LocalVariable( |
+ Scanner::kNoSourcePos, Symbols::AwaitJumpVar(), dynamic_type); |
+ current_block_->scope->AddVariable(await_jump_var); |
+ found = closure_body->scope()->CaptureVariable(Symbols::AwaitJumpVar()); |
+ ASSERT(found); |
+ LocalVariable* await_ctx_var = new (I) LocalVariable( |
+ Scanner::kNoSourcePos, Symbols::AwaitContextVar(), dynamic_type); |
+ current_block_->scope->AddVariable(await_ctx_var); |
+ found = closure_body->scope()->CaptureVariable(Symbols::AwaitContextVar()); |
+ ASSERT(found); |
+ LocalVariable* async_saved_try_ctx_var; |
+ for (int16_t i = 0; i < last_used_try_index_; i++) { |
+ String& async_saved_try_ctx_name = BuildAsyncSavedTryContextName(I, i); |
+ async_saved_try_ctx_var = new (I) LocalVariable( |
+ Scanner::kNoSourcePos, async_saved_try_ctx_name, dynamic_type); |
+ current_block_->scope->AddVariable(async_saved_try_ctx_var); |
+ found = closure_body->scope()->CaptureVariable(async_saved_try_ctx_name); |
+ ASSERT(found); |
+ } |
// Add to AST: |
// :async_completer = new Completer(); |
- ArgumentListNode* empty_args = new (I) ArgumentListNode( |
- Scanner::kNoSourcePos); |
+ ArgumentListNode* empty_args = |
+ new (I) ArgumentListNode(Scanner::kNoSourcePos); |
ConstructorCallNode* completer_constructor_node = new (I) ConstructorCallNode( |
Scanner::kNoSourcePos, |
TypeArguments::ZoneHandle(I), |
@@ -5591,6 +5619,13 @@ void Parser::CloseAsyncClosure(SequenceNode* body) { |
TRACE_PARSER("CloseAsyncClosure"); |
// We need a temporary expression to store intermediate return values. |
parsed_function()->EnsureExpressionTemp(); |
+ // Implicitly mark those variables below as captured. We currently mark all |
+ // variables of all scopes as captured (below), but as soon as we do something |
+ // smarter we rely on these internal variables to be available. |
+ body->scope()->LookupVariable(Symbols::Completer(), false); |
+ body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false); |
+ body->scope()->LookupVariable(Symbols::AwaitContextVar(), false); |
+ body->scope()->RecursivelyCaptureAllVariables(); |
} |
@@ -7125,6 +7160,19 @@ SequenceNode* Parser::ParseFinallyBlock() { |
TRACE_PARSER("ParseFinallyBlock"); |
OpenBlock(); |
ExpectToken(Token::kLBRACE); |
+ |
+ // In case of async closures we need to restore the saved try index of an |
+ // outer try block (if it exists). The current try block has already been |
+ // removed from the stack of try blocks. |
+ if (current_function().is_async_closure() && (try_blocks_list_ != NULL)) { |
+ // We need two unchain two scopes: finally clause, and the try block level. |
+ SetupSavedTryContext(current_block_->scope->parent()->parent(), |
+ try_blocks_list_->try_index(), |
+ current_block_->statements); |
+ } else { |
+ parsed_function()->reset_saved_try_ctx_vars(); |
+ } |
+ |
ParseStatementSequence(); |
ExpectToken(Token::kRBRACE); |
SequenceNode* finally_block = CloseBlock(); |
@@ -7270,6 +7318,21 @@ SequenceNode* Parser::ParseCatchClauses( |
// Add nested block with user-defined code. This blocks allows |
// declarations in the body to shadow the catch parameters. |
CheckToken(Token::kLBRACE); |
+ |
+ // In case of async closures we need to restore the saved try index of an |
+ // outer try block (if it exists). |
+ ASSERT(try_blocks_list_ != NULL); |
+ if (current_function().is_async_closure() && |
+ (try_blocks_list_->outer_try_block() != NULL)) { |
+ // We need to unchain three scope levels: catch clause, catch parameters, |
+ // and the general try block. |
+ SetupSavedTryContext(current_block_->scope->parent()->parent()->parent(), |
+ try_blocks_list_->outer_try_block()->try_index(), |
+ current_block_->statements); |
+ } else { |
+ parsed_function()->reset_saved_try_ctx_vars(); |
+ } |
+ |
current_block_->statements->Add(ParseNestedStatement(false, NULL)); |
catch_blocks.Add(CloseBlock()); |
@@ -7365,6 +7428,33 @@ SequenceNode* Parser::ParseCatchClauses( |
} |
+// Set up the currently relevant :saved_try_context_var on the stack: |
+// * Try blocks: Set the context variable for this try block. |
+// * Catch/finally blocks: Set the context variable for any outer try block (if |
+// existent). |
+// |
+// Also save the captured variable and the stack variable to be able to set |
+// it after a function continues execution (await). |
+void Parser::SetupSavedTryContext(LocalScope* saved_try_context_scope, |
+ int16_t try_index, |
+ SequenceNode* target) { |
+ LocalVariable* saved_try_ctx = saved_try_context_scope->LookupVariable( |
+ Symbols::SavedTryContextVar(), false); |
+ ASSERT((saved_try_ctx != NULL) && !saved_try_ctx->is_captured()); |
+ const String& async_saved_try_ctx_name = |
+ BuildAsyncSavedTryContextName(I, try_index); |
+ LocalVariable* async_saved_try_ctx = |
+ target->scope()->LookupVariable(async_saved_try_ctx_name, false); |
+ ASSERT((async_saved_try_ctx != NULL) && async_saved_try_ctx->is_captured()); |
+ target->Add(new (I) StoreLocalNode( |
+ Scanner::kNoSourcePos, saved_try_ctx, new (I) LoadLocalNode( |
+ Scanner::kNoSourcePos, async_saved_try_ctx))); |
+ |
+ parsed_function()->set_saved_try_ctx(saved_try_ctx); |
+ parsed_function()->set_async_saved_try_ctx(async_saved_try_ctx); |
+} |
+ |
+ |
AstNode* Parser::ParseTryStatement(String* label_name) { |
TRACE_PARSER("ParseTryStatement"); |
@@ -7423,6 +7513,22 @@ AstNode* Parser::ParseTryStatement(String* label_name) { |
OpenBlock(); |
PushTryBlock(current_block_); |
ExpectToken(Token::kLBRACE); |
+ |
+ if (current_function().is_async_closure()) { |
+ const String& async_saved_try_ctx_name = |
+ BuildAsyncSavedTryContextName(I, last_used_try_index_ - 1); |
+ LocalVariable* async_saved_try_ctx = |
+ current_block_->scope->LookupVariable( |
+ async_saved_try_ctx_name, false); |
+ ASSERT(async_saved_try_ctx != NULL); |
+ ASSERT(context_var != NULL); |
+ current_block_->statements->Add(new (I) StoreLocalNode( |
+ Scanner::kNoSourcePos, async_saved_try_ctx, new (I) LoadLocalNode( |
+ Scanner::kNoSourcePos, context_var))); |
+ parsed_function()->set_saved_try_ctx(context_var); |
+ parsed_function()->set_async_saved_try_ctx(async_saved_try_ctx); |
+ } |
+ |
ParseStatementSequence(); |
ExpectToken(Token::kRBRACE); |
SequenceNode* try_block = CloseBlock(); |
@@ -7495,6 +7601,21 @@ AstNode* Parser::ParseTryStatement(String* label_name) { |
sequence->set_label(try_label); |
try_catch_node = sequence; |
} |
+ |
+ // In case of async closures we need to restore the saved try index of an |
+ // outer try block (if it exists). |
+ if (current_function().is_async_closure() && |
+ (outer_try_index != CatchClauseNode::kInvalidTryIndex)) { |
+ SequenceNode* try_catch_and_restore_try_ctx = new (I) SequenceNode( |
+ Scanner::kNoSourcePos, current_block_->scope); |
+ try_catch_and_restore_try_ctx->Add(try_catch_node); |
+ SetupSavedTryContext( |
+ current_block_->scope, outer_try_index, try_catch_and_restore_try_ctx); |
+ return try_catch_and_restore_try_ctx; |
+ } else { |
+ parsed_function()->reset_saved_try_ctx_vars(); |
+ } |
+ |
return try_catch_node; |
} |