Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(492)

Unified Diff: runtime/vm/parser.cc

Issue 1014273003: Make await for cancel the stream when breaking out of the loop (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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.
}

Powered by Google App Engine
This is Rietveld 408576698