Chromium Code Reviews| Index: runtime/vm/parser.cc |
| diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
| index 62a6a071305fee4fc0a3b29cd3063d186b444fd0..9d8d088db127f06e1f95a5342086704835cbb10f 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. |
| } |
| @@ -5501,8 +5500,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 +5516,52 @@ 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 :await_saved_try_ctx_var_<x>; |
|
hausner
2014/08/25 20:26:14
is the name :await_saved_try_ctx_var_ or :async_sa
Michael Lippautz (Google)
2014/08/26 16:45:48
Done. Using :async_...
|
| + Type& dynamic_type = Type::ZoneHandle(I, Type::DynamicType()); |
|
hausner
2014/08/25 20:26:14
const
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| 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; |
| + const char* async_saved_prefix = ":async_saved_try_ctx_var_"; |
| + for (int16_t i = 0; i < last_used_try_index_; i++) { |
| + const String& cnt_str = String::ZoneHandle( |
|
hausner
2014/08/25 20:26:14
cnt_str can be a regular handle, need not be a zon
Michael Lippautz (Google)
2014/08/26 16:45:48
Done: BuildAsyncSavedTryContextName()
|
| + I, String::NewFormatted("%s%d", async_saved_prefix, i)); |
| + const String& symbol = String::ZoneHandle(I, Symbols::New(cnt_str)); |
| + async_saved_try_ctx_var = new (I) LocalVariable( |
| + Scanner::kNoSourcePos, symbol, dynamic_type); |
| + current_block_->scope->AddVariable(async_saved_try_ctx_var); |
| + found = closure_body->scope()->CaptureVariable(symbol); |
| + 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 +5612,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 +7153,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 +7311,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 +7421,30 @@ SequenceNode* Parser::ParseCatchClauses( |
| } |
|
hausner
2014/08/25 20:26:14
Could you add a short comment describing what this
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| +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 char* async_saved_prefix = ":async_saved_try_ctx_var_"; |
| + const String& cnt_str = String::ZoneHandle( |
|
hausner
2014/08/25 20:26:14
cnt_str probably doesn't need to be a ZoneHandle;
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| + I, String::NewFormatted("%s%d", async_saved_prefix, try_index)); |
| + const String& current_await_saved_try_ctx_symbol = String::ZoneHandle( |
| + I, Symbols::New(cnt_str)); |
| + LocalVariable* async_saved_try_ctx = |
| + target->scope()->LookupVariable( |
| + current_await_saved_try_ctx_symbol, 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 +7503,26 @@ AstNode* Parser::ParseTryStatement(String* label_name) { |
| OpenBlock(); |
| PushTryBlock(current_block_); |
| ExpectToken(Token::kLBRACE); |
| + |
| + const char* async_saved_prefix = ":async_saved_try_ctx_var_"; |
| + if (current_function().is_async_closure()) { |
| + const String& cnt_str = String::ZoneHandle( |
|
srdjan
2014/08/26 15:54:33
Regular handle: use ZoneHandle-s only if the varia
Michael Lippautz (Google)
2014/08/26 16:45:48
Done. Code is now refactored but uses a regular ha
|
| + I, String::NewFormatted("%s%d", |
| + async_saved_prefix, last_used_try_index_-1)); |
| + const String& current_await_saved_try_ctx_symbol = |
| + String::ZoneHandle(I, Symbols::New(cnt_str)); |
| + LocalVariable* async_saved_try_ctx = |
| + current_block_->scope->LookupVariable( |
| + current_await_saved_try_ctx_symbol, false); |
| + ASSERT(async_saved_try_ctx != NULL); |
| + ASSERT(context_var); |
| + 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 +7595,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; |
| } |