Index: src/parsing/parser.cc |
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc |
index dd9744e7f6314d72f1b3b91ad4b28218da7d1225..5e88b58fefce96eecbd887e82005aee15decc139 100644 |
--- a/src/parsing/parser.cc |
+++ b/src/parsing/parser.cc |
@@ -4655,35 +4655,72 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( |
{ |
BlockState block_state(&scope_, inner_scope); |
- // For generators, allocate and yield an iterator on function entry. |
if (IsGeneratorFunction(kind)) { |
- ZoneList<Expression*>* arguments = |
- new(zone()) ZoneList<Expression*>(0, zone()); |
- CallRuntime* allocation = factory()->NewCallRuntime( |
- Runtime::kCreateJSGeneratorObject, arguments, pos); |
- VariableProxy* init_proxy = factory()->NewVariableProxy( |
- function_state_->generator_object_variable()); |
- Assignment* assignment = factory()->NewAssignment( |
- Token::INIT, init_proxy, allocation, RelocInfo::kNoPosition); |
- VariableProxy* get_proxy = factory()->NewVariableProxy( |
- function_state_->generator_object_variable()); |
- Yield* yield = factory()->NewYield( |
- get_proxy, assignment, Yield::kInitial, RelocInfo::kNoPosition); |
- body->Add(factory()->NewExpressionStatement( |
- yield, RelocInfo::kNoPosition), zone()); |
- } |
+ // We produce: |
+ // |
+ // try { InitialYield; ...body...; FinalYield } |
+ // finally { %GeneratorClose(generator) } |
+ // |
+ // - InitialYield yields the actual generator object. |
+ // - FinalYield yields {value: foo, done: true} where foo is the |
+ // completion value of body. (This is needed here in case the body |
+ // falls through without an explicit return.) |
+ // - Any return statement inside the body will be converted into a similar |
+ // FinalYield. |
+ // - If the generator terminates for whatever reason, we must close it. |
+ // Hence the finally clause. |
+ |
+ Block* try_block = |
+ factory()->NewBlock(nullptr, 3, false, RelocInfo::kNoPosition); |
- ParseStatementList(body, Token::RBRACE, CHECK_OK); |
+ { |
+ ZoneList<Expression*>* arguments = |
+ new (zone()) ZoneList<Expression*>(0, zone()); |
+ CallRuntime* allocation = factory()->NewCallRuntime( |
+ Runtime::kCreateJSGeneratorObject, arguments, pos); |
+ VariableProxy* init_proxy = factory()->NewVariableProxy( |
+ function_state_->generator_object_variable()); |
+ Assignment* assignment = factory()->NewAssignment( |
+ Token::INIT, init_proxy, allocation, RelocInfo::kNoPosition); |
+ VariableProxy* get_proxy = factory()->NewVariableProxy( |
+ function_state_->generator_object_variable()); |
+ Yield* yield = factory()->NewYield( |
+ get_proxy, assignment, Yield::kInitial, RelocInfo::kNoPosition); |
+ try_block->statements()->Add( |
+ factory()->NewExpressionStatement(yield, RelocInfo::kNoPosition), |
+ zone()); |
+ } |
+ |
+ ParseStatementList(try_block->statements(), Token::RBRACE, CHECK_OK); |
- if (IsGeneratorFunction(kind)) { |
VariableProxy* get_proxy = factory()->NewVariableProxy( |
function_state_->generator_object_variable()); |
Expression* undefined = |
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition); |
Yield* yield = factory()->NewYield(get_proxy, undefined, Yield::kFinal, |
RelocInfo::kNoPosition); |
- body->Add(factory()->NewExpressionStatement( |
- yield, RelocInfo::kNoPosition), zone()); |
+ try_block->statements()->Add( |
+ factory()->NewExpressionStatement(yield, RelocInfo::kNoPosition), |
+ zone()); |
+ |
+ Block* finally_block = |
+ factory()->NewBlock(nullptr, 1, false, RelocInfo::kNoPosition); |
+ ZoneList<Expression*>* args = |
+ new (zone()) ZoneList<Expression*>(1, zone()); |
+ VariableProxy* call_proxy = factory()->NewVariableProxy( |
+ function_state_->generator_object_variable()); |
+ args->Add(call_proxy, zone()); |
+ Expression* call = factory()->NewCallRuntime( |
+ Runtime::kGeneratorClose, args, RelocInfo::kNoPosition); |
+ finally_block->statements()->Add( |
+ factory()->NewExpressionStatement(call, RelocInfo::kNoPosition), |
+ zone()); |
+ |
+ body->Add(factory()->NewTryFinallyStatement(try_block, finally_block, |
+ RelocInfo::kNoPosition), |
+ zone()); |
+ } else { |
+ ParseStatementList(body, Token::RBRACE, CHECK_OK); |
} |
if (IsSubclassConstructor(kind)) { |