Chromium Code Reviews| Index: runtime/vm/parser.cc |
| =================================================================== |
| --- runtime/vm/parser.cc (revision 44677) |
| +++ runtime/vm/parser.cc (working copy) |
| @@ -6855,6 +6855,7 @@ |
| TRACE_PARSER("CloseAsyncFunction"); |
| ASSERT(!closure.IsNull()); |
| ASSERT(closure_body != NULL); |
| + |
| // The block for the async closure body has already been closed. Close the |
| // corresponding function block. |
| CloseBlock(); |
| @@ -8335,7 +8336,10 @@ |
| ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); |
| ExpectToken(Token::kRPAREN); |
| + // Open a block for the iterator variable and the try-finally |
| + // statement that contains the loop. |
| OpenBlock(); |
| + const Block* loop_block = current_block_; |
| // Build creation of implicit StreamIterator. |
| // var :for-in-iter = new StreamIterator(stream_expr). |
| @@ -8361,6 +8365,33 @@ |
| new(Z) StoreLocalNode(stream_pos, iterator_var, ctor_call); |
| current_block_->statements->Add(iterator_init); |
| + // We need to ensure that the stream is cancelled after the loop. |
| + // Thus, wrap the loop in a try-finally that calls :for-in-iter.close() |
| + // in the finally clause. It is harmless to call close() if the stream |
| + // is already cancelled (when moveNext() returns false). |
| + // Note: even though this is async code, we do not need to set up |
| + // the closurized saved_exception_var and saved_stack_trace_var because |
| + // there can not be a suspend/resume event before the exception is |
| + // rethrown in the catch clause. The catch block of the implicit |
| + // try-finally is empty. |
| + LocalVariable* context_var = NULL; |
| + LocalVariable* exception_var = NULL; |
| + LocalVariable* stack_trace_var = NULL; |
| + LocalVariable* saved_exception_var = NULL; |
| + LocalVariable* saved_stack_trace_var = NULL; |
| + SetupExceptionVariables(current_block_->scope, |
| + false, // Do not create the saved_ vars. |
| + &context_var, |
| + &exception_var, |
| + &stack_trace_var, |
| + &saved_exception_var, |
| + &saved_stack_trace_var); |
| + OpenBlock(); // try block. |
| + PushTry(current_block_); |
| + SetupSavedTryContext(context_var); |
| + |
| + // Build while loop condition. |
| + // while (await :for-in-iter.moveNext()) |
| LocalScope* try_scope; |
| int16_t try_index; |
| LocalScope* outer_try_scope; |
| @@ -8368,23 +8399,22 @@ |
| CheckAsyncOpInTryBlock(&try_scope, &try_index, |
| &outer_try_scope, &outer_try_index); |
| - // Build while loop condition. |
| - // while (await :for-in-iter.moveNext()) |
| ArgumentListNode* no_args = new(Z) ArgumentListNode(stream_pos); |
| AstNode* iterator_moveNext = new(Z) InstanceCallNode( |
| stream_pos, |
| new(Z) LoadLocalNode(stream_pos, iterator_var), |
| Symbols::MoveNext(), |
| no_args); |
| - AstNode* await_moveNext = new (Z) AwaitNode(stream_pos, |
| - iterator_moveNext, |
| - try_scope, |
| - try_index, |
| - outer_try_scope, |
| - outer_try_index); |
| + AstNode* await_moveNext = |
| + new(Z) AwaitNode(stream_pos, |
| + iterator_moveNext, |
| + try_scope, |
| + try_index, |
| + outer_try_scope, |
| + outer_try_index); |
| OpenBlock(); |
| AwaitTransformer at(current_block_->statements, async_temp_scope_); |
| - AstNode* transformed_await = at.Transform(await_moveNext); |
| + await_moveNext = at.Transform(await_moveNext); |
| SequenceNode* await_preamble = CloseBlock(); |
| // Parse the for loop body. Ideally, we would use ParseNestedStatement() |
| @@ -8392,6 +8422,7 @@ |
| // variable assignment and potentially a variable declaration in the |
| // loop body. |
| OpenLoopBlock(); |
| + |
| SourceLabel* label = |
| SourceLabel::New(await_for_pos, label_name, SourceLabel::kFor); |
| current_block_->scope->AddLabel(label); |
| @@ -8429,6 +8460,7 @@ |
| loop_var_assignment_pos); |
| ASSERT(loop_var_assignment != NULL); |
| } |
| + |
| current_block_->statements->Add(loop_var_assignment); |
| // Now parse the for-in loop statement or block. |
| @@ -8442,15 +8474,103 @@ |
| current_block_->statements->Add(statement); |
| } |
| } |
| - SequenceNode* for_loop_statement = CloseBlock(); |
| + SequenceNode* for_loop_block = CloseBlock(); |
| WhileNode* while_node = new (Z) WhileNode(await_for_pos, |
| label, |
| - transformed_await, |
| + await_moveNext, |
| await_preamble, |
| - for_loop_statement); |
| + for_loop_block); |
| + // Add the while loop to the try block. |
| current_block_->statements->Add(while_node); |
| + SequenceNode* try_block = CloseBlock(); |
| + |
| + |
|
regis
2015/03/24 23:46:27
remove extra blank line
hausner
2015/03/24 23:57:29
Done.
|
| + // Create an empty "catch all" block that rethrows the current |
| + // exception and stacktrace. |
| + try_stack_->enter_catch(); |
| + SequenceNode* catch_block = new(Z) SequenceNode(await_for_pos, NULL); |
| + |
| + if (outer_try_scope != NULL) { |
| + // TODO(hausner): Do we need to restore the saved try context here? The |
| + // code does not touch any captured variables, it just rethrows the |
| + // current exception. |
|
regis
2015/03/24 23:46:27
The code may not access captured variables, but if
hausner
2015/03/24 23:57:29
True, thank you. Removed comment.
|
| + catch_block->Add(AwaitTransformer::RestoreSavedTryContext( |
| + Z, outer_try_scope, outer_try_index)); |
| + } |
| + |
| + // We don't need to copy the current execption and stack trace variables |
|
regis
2015/03/24 23:46:27
typo
hausner
2015/03/24 23:57:29
Done.
|
| + // into :saved_exception_var and :saved_stack_trace_var here because there |
| + // is no code in the catch clause that could suspend the function. |
| + |
| + // Rethrow the exception. |
| + catch_block->Add(new(Z) ThrowNode( |
| + await_for_pos, |
| + new(Z) LoadLocalNode(await_for_pos, exception_var), |
| + new(Z) LoadLocalNode(await_for_pos, stack_trace_var))); |
| + |
| + TryStack* try_statement = PopTry(); |
| + ASSERT(try_index == try_statement->try_index()); |
| + |
| + // The finally block contains a call to cancel the stream. |
| + // :for-in-iter.cancel() |
| + |
| + // Inline the finally block to the exit points in the try block. |
| + intptr_t node_index = 0; |
| + SequenceNode* finally_clause = NULL; |
| + do { |
| + OpenBlock(); |
| + ArgumentListNode* no_args = |
| + new(Z) ArgumentListNode(Scanner::kNoSourcePos); |
| + current_block_->statements->Add( |
| + new(Z) InstanceCallNode(Scanner::kNoSourcePos, |
| + new(Z) LoadLocalNode(Scanner::kNoSourcePos, iterator_var), |
| + Symbols::Cancel(), |
| + no_args)); |
| + finally_clause = CloseBlock(); |
| + AstNode* node_to_inline = try_statement->GetNodeToInlineFinally(node_index); |
| + if (node_to_inline != NULL) { |
| + InlinedFinallyNode* node = |
| + new(Z) InlinedFinallyNode(Scanner::kNoSourcePos, |
| + finally_clause, |
| + context_var, |
| + outer_try_index); |
| + finally_clause = NULL; |
| + AddFinallyBlockToNode(true, node_to_inline, node); |
| + node_index++; |
| + } |
| + } while (finally_clause == NULL); |
| + |
| + // Create the try-statement and add to the current sequence, which is |
| + // the block around the loop statement. |
| + |
| + const Type& dynamic_type = Type::ZoneHandle(Z, Type::DynamicType()); |
| + const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); |
| + handler_types.SetAt(0, dynamic_type); // Catch block handles all exceptions. |
| + |
| + CatchClauseNode* catch_clause = new(Z) CatchClauseNode(await_for_pos, |
| + catch_block, |
| + handler_types, |
| + context_var, |
| + exception_var, |
| + stack_trace_var, |
| + exception_var, |
| + stack_trace_var, |
| + AllocateTryIndex(), |
| + true); // Needs stack trace. |
| + |
| + AstNode* try_catch_node = |
| + new(Z) TryCatchNode(await_for_pos, |
| + try_block, |
| + context_var, |
| + catch_clause, |
| + finally_clause, |
| + try_index); |
| + |
| + ASSERT(current_block_ == loop_block); |
| + loop_block->statements->Add(try_catch_node); |
| + |
| return CloseBlock(); // Implicit block around while loop. |
| } |