| Index: src/parser.cc
|
| diff --git a/src/parser.cc b/src/parser.cc
|
| index 5efcba6119225365a42fc50172420ae121757857..76501c448d2f190c3de9cea0201e20098fe5087b 100644
|
| --- a/src/parser.cc
|
| +++ b/src/parser.cc
|
| @@ -2837,12 +2837,176 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt,
|
| }
|
|
|
|
|
| +
|
| +Statement* Parser::DesugarLetBindingsInForStatement(
|
| + Scope* inner_scope, ZoneStringList* names, ForStatement* loop,
|
| + Statement* init, Expression* cond, Statement* next, Statement* body,
|
| + bool* ok) {
|
| + // ES6 13.6.3.4 specifies that on each loop iteration the let variables are
|
| + // copied into a new environment. After copying, the "next" statement of the
|
| + // loop is executed to update the loop variables. The loop condition is
|
| + // checked and the loop body is executed.
|
| + //
|
| + // We rewrite a for statement of the form
|
| + //
|
| + // for (let x = i; cond; next) body
|
| + //
|
| + // into
|
| + //
|
| + // {
|
| + // let x = i;
|
| + // temp_x = x;
|
| + // flag = 1;
|
| + // for (;;) {
|
| + // let x = temp_x;
|
| + // if (flag == 1) {
|
| + // flag = 0;
|
| + // } else {
|
| + // next;
|
| + // }
|
| + // if (cond) {
|
| + // <empty>
|
| + // } else {
|
| + // break;
|
| + // }
|
| + // b
|
| + // temp_x = x;
|
| + // }
|
| + // }
|
| +
|
| + ASSERT(names->length() > 0);
|
| + Scope* for_scope = scope_;
|
| + ZoneList<Variable*> temps(names->length(), zone());
|
| +
|
| + Block* outer_block = factory()->NewBlock(NULL, names->length() + 3, false,
|
| + RelocInfo::kNoPosition);
|
| + outer_block->AddStatement(init, zone());
|
| +
|
| + Handle<String> temp_name = isolate()->factory()->dot_for_string();
|
| + Handle<Smi> smi0 = handle(Smi::FromInt(0), isolate());
|
| + Handle<Smi> smi1 = handle(Smi::FromInt(1), isolate());
|
| +
|
| +
|
| + // For each let variable x:
|
| + // make statement: temp_x = x.
|
| + for (int i = 0; i < names->length(); i++) {
|
| + VariableProxy* proxy =
|
| + NewUnresolved(names->at(i), LET, Interface::NewValue());
|
| + Variable* temp = scope_->DeclarationScope()->NewTemporary(temp_name);
|
| + VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
| + Assignment* assignment = factory()->NewAssignment(
|
| + Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition);
|
| + Statement* assignment_statement = factory()->NewExpressionStatement(
|
| + assignment, RelocInfo::kNoPosition);
|
| + outer_block->AddStatement(assignment_statement, zone());
|
| + temps.Add(temp, zone());
|
| + }
|
| +
|
| + Variable* flag = scope_->DeclarationScope()->NewTemporary(temp_name);
|
| + // Make statement: flag = 1.
|
| + {
|
| + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
| + Expression* const1 = factory()->NewLiteral(smi1, RelocInfo::kNoPosition);
|
| + Assignment* assignment = factory()->NewAssignment(
|
| + Token::ASSIGN, flag_proxy, const1, RelocInfo::kNoPosition);
|
| + Statement* assignment_statement = factory()->NewExpressionStatement(
|
| + assignment, RelocInfo::kNoPosition);
|
| + outer_block->AddStatement(assignment_statement, zone());
|
| + }
|
| +
|
| + outer_block->AddStatement(loop, zone());
|
| + outer_block->set_scope(for_scope);
|
| + scope_ = inner_scope;
|
| +
|
| + Block* inner_block = factory()->NewBlock(NULL, 2 * names->length() + 3,
|
| + false, RelocInfo::kNoPosition);
|
| + int pos = scanner()->location().beg_pos;
|
| + ZoneList<Variable*> inner_vars(names->length(), zone());
|
| +
|
| + // For each let variable x:
|
| + // make statement: let x = temp_x.
|
| + for (int i = 0; i < names->length(); i++) {
|
| + VariableProxy* proxy =
|
| + NewUnresolved(names->at(i), LET, Interface::NewValue());
|
| + Declaration* declaration =
|
| + factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
|
| + Declare(declaration, true, CHECK_OK);
|
| + inner_vars.Add(declaration->proxy()->var(), zone());
|
| + VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
| + Assignment* assignment = factory()->NewAssignment(
|
| + Token::INIT_LET, proxy, temp_proxy, pos);
|
| + Statement* assignment_statement = factory()->NewExpressionStatement(
|
| + assignment, pos);
|
| + proxy->var()->set_initializer_position(pos);
|
| + inner_block->AddStatement(assignment_statement, zone());
|
| + }
|
| +
|
| + // Make statement: if (flag == 1) { flag = 0; } else { next; }.
|
| + {
|
| + Expression* compare = NULL;
|
| + // Make compare expresion: flag == 1.
|
| + {
|
| + Expression* const1 = factory()->NewLiteral(smi1, RelocInfo::kNoPosition);
|
| + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
| + compare = factory()->NewCompareOperation(
|
| + Token::EQ, flag_proxy, const1, RelocInfo::kNoPosition);
|
| + }
|
| + Statement* clear_flag = NULL;
|
| + // Make statement: flag = 0.
|
| + {
|
| + VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
| + Expression* const0 = factory()->NewLiteral(smi0, RelocInfo::kNoPosition);
|
| + Assignment* assignment = factory()->NewAssignment(
|
| + Token::ASSIGN, flag_proxy, const0, RelocInfo::kNoPosition);
|
| + clear_flag = factory()->NewExpressionStatement(assignment, pos);
|
| + }
|
| + Statement* clear_flag_or_next = factory()->NewIfStatement(
|
| + compare, clear_flag, next, RelocInfo::kNoPosition);
|
| + inner_block->AddStatement(clear_flag_or_next, zone());
|
| + }
|
| +
|
| +
|
| + // Make statement: if (cond) { } else { break; }.
|
| + {
|
| + Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
| + BreakableStatement* t = LookupBreakTarget(Handle<String>(), CHECK_OK);
|
| + Statement* stop = factory()->NewBreakStatement(t, RelocInfo::kNoPosition);
|
| + Statement* if_not_cond_break = factory()->NewIfStatement(
|
| + cond, empty, stop, RelocInfo::kNoPosition);
|
| + inner_block->AddStatement(if_not_cond_break, zone());
|
| + }
|
| +
|
| + inner_block->AddStatement(body, zone());
|
| +
|
| + // For each let variable x:
|
| + // make statement: temp_x = x;
|
| + for (int i = 0; i < names->length(); i++) {
|
| + VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
| + int pos = scanner()->location().end_pos;
|
| + VariableProxy* proxy = factory()->NewVariableProxy(inner_vars.at(i), pos);
|
| + Assignment* assignment = factory()->NewAssignment(
|
| + Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition);
|
| + Statement* assignment_statement = factory()->NewExpressionStatement(
|
| + assignment, RelocInfo::kNoPosition);
|
| + inner_block->AddStatement(assignment_statement, zone());
|
| + }
|
| +
|
| + inner_scope->set_end_position(scanner()->location().end_pos);
|
| + inner_block->set_scope(inner_scope);
|
| + scope_ = for_scope;
|
| +
|
| + loop->Initialize(NULL, NULL, NULL, inner_block);
|
| + return outer_block;
|
| +}
|
| +
|
| +
|
| Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
|
| // ForStatement ::
|
| // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
|
|
|
| int pos = peek_position();
|
| Statement* init = NULL;
|
| + ZoneStringList let_bindings(1, zone());
|
|
|
| // Create an in-between scope for let-bound iteration variables.
|
| Scope* saved_scope = scope_;
|
| @@ -2894,8 +3058,8 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
|
| Handle<String> name;
|
| VariableDeclarationProperties decl_props = kHasNoInitializers;
|
| Block* variable_statement =
|
| - ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name,
|
| - CHECK_OK);
|
| + ParseVariableDeclarations(kForStatement, &decl_props, &let_bindings,
|
| + &name, CHECK_OK);
|
| bool accept_IN = !name.is_null() && decl_props != kHasInitializers;
|
| bool accept_OF = decl_props == kHasNoInitializers;
|
| ForEachStatement::VisitMode mode;
|
| @@ -2998,6 +3162,15 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
|
| // Parsed initializer at this point.
|
| Expect(Token::SEMICOLON, CHECK_OK);
|
|
|
| + // If there are let bindings, then condition and the next statement of the
|
| + // for loop must be parsed in a new scope.
|
| + Scope* inner_scope = NULL;
|
| + if (let_bindings.length() > 0) {
|
| + inner_scope = NewScope(for_scope, BLOCK_SCOPE);
|
| + inner_scope->set_start_position(scanner()->location().beg_pos);
|
| + scope_ = inner_scope;
|
| + }
|
| +
|
| Expression* cond = NULL;
|
| if (peek() != Token::SEMICOLON) {
|
| cond = ParseExpression(true, CHECK_OK);
|
| @@ -3012,31 +3185,22 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
|
| Expect(Token::RPAREN, CHECK_OK);
|
|
|
| Statement* body = ParseStatement(NULL, CHECK_OK);
|
| - scope_ = saved_scope;
|
| - for_scope->set_end_position(scanner()->location().end_pos);
|
| - for_scope = for_scope->FinalizeBlockScope();
|
| - if (for_scope != NULL) {
|
| - // Rewrite a for statement of the form
|
| - //
|
| - // for (let x = i; c; n) b
|
| - //
|
| - // into
|
| - //
|
| - // {
|
| - // let x = i;
|
| - // for (; c; n) b
|
| - // }
|
| - ASSERT(init != NULL);
|
| - Block* result = factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
|
| - result->AddStatement(init, zone());
|
| - result->AddStatement(loop, zone());
|
| - result->set_scope(for_scope);
|
| - loop->Initialize(NULL, cond, next, body);
|
| - return result;
|
| +
|
| + Statement* result = NULL;
|
| + if (let_bindings.length() > 0) {
|
| + scope_ = for_scope;
|
| + result = DesugarLetBindingsInForStatement(inner_scope, &let_bindings, loop,
|
| + init, cond, next, body, CHECK_OK);
|
| + scope_ = saved_scope;
|
| + for_scope->set_end_position(scanner()->location().end_pos);
|
| } else {
|
| loop->Initialize(init, cond, next, body);
|
| - return loop;
|
| + result = loop;
|
| + scope_ = saved_scope;
|
| + for_scope->set_end_position(scanner()->location().end_pos);
|
| + for_scope->FinalizeBlockScope();
|
| }
|
| + return result;
|
| }
|
|
|
|
|
|
|