Commit 18f41e46 authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[es6] support AssignmentPattern as LHS in for-in/of loops

BUG=v8:811, v8:4599
LOG=N
R=adamk@chromium.org, rossberg@chromium.org

Review URL: https://codereview.chromium.org/1517973002

Cr-Commit-Position: refs/heads/master@{#32814}
parent bf24486b
...@@ -137,6 +137,10 @@ static void AssignVectorSlots(Expression* expr, FeedbackVectorSpec* spec, ...@@ -137,6 +137,10 @@ static void AssignVectorSlots(Expression* expr, FeedbackVectorSpec* spec,
void ForEachStatement::AssignFeedbackVectorSlots( void ForEachStatement::AssignFeedbackVectorSlots(
Isolate* isolate, FeedbackVectorSpec* spec, Isolate* isolate, FeedbackVectorSpec* spec,
FeedbackVectorSlotCache* cache) { FeedbackVectorSlotCache* cache) {
// TODO(adamk): for-of statements do not make use of this feedback slot.
// The each_slot_ should be specific to ForInStatement, and this work moved
// there.
if (IsForOfStatement()) return;
AssignVectorSlots(each(), spec, &each_slot_); AssignVectorSlots(each(), spec, &each_slot_);
} }
......
...@@ -3327,9 +3327,10 @@ Expression* Parser::BuildIteratorNextResult(Expression* iterator, ...@@ -3327,9 +3327,10 @@ Expression* Parser::BuildIteratorNextResult(Expression* iterator,
void Parser::InitializeForEachStatement(ForEachStatement* stmt, void Parser::InitializeForEachStatement(ForEachStatement* stmt,
Expression* each, Expression* each, Expression* subject,
Expression* subject, Statement* body,
Statement* body) { bool is_destructuring) {
DCHECK(!is_destructuring || allow_harmony_destructuring_assignment());
ForOfStatement* for_of = stmt->AsForOfStatement(); ForOfStatement* for_of = stmt->AsForOfStatement();
if (for_of != NULL) { if (for_of != NULL) {
...@@ -3380,6 +3381,10 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt, ...@@ -3380,6 +3381,10 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt,
result_proxy, value_literal, RelocInfo::kNoPosition); result_proxy, value_literal, RelocInfo::kNoPosition);
assign_each = factory()->NewAssignment(Token::ASSIGN, each, result_value, assign_each = factory()->NewAssignment(Token::ASSIGN, each, result_value,
RelocInfo::kNoPosition); RelocInfo::kNoPosition);
if (is_destructuring) {
assign_each = PatternRewriter::RewriteDestructuringAssignment(
this, assign_each->AsAssignment(), scope_);
}
} }
for_of->Initialize(each, subject, body, for_of->Initialize(each, subject, body,
...@@ -3388,6 +3393,23 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt, ...@@ -3388,6 +3393,23 @@ void Parser::InitializeForEachStatement(ForEachStatement* stmt,
result_done, result_done,
assign_each); assign_each);
} else { } else {
if (is_destructuring) {
Variable* temp =
scope_->NewTemporary(ast_value_factory()->empty_string());
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
Expression* assign_each = PatternRewriter::RewriteDestructuringAssignment(
this, factory()->NewAssignment(Token::ASSIGN, each, temp_proxy,
RelocInfo::kNoPosition),
scope_);
auto block =
factory()->NewBlock(nullptr, 2, false, RelocInfo::kNoPosition);
block->statements()->Add(factory()->NewExpressionStatement(
assign_each, RelocInfo::kNoPosition),
zone());
block->statements()->Add(body, zone());
body = block;
each = factory()->NewVariableProxy(temp);
}
stmt->Initialize(each, subject, body); stmt->Initialize(each, subject, body);
} }
} }
...@@ -3772,7 +3794,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3772,7 +3794,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
body_block->statements()->Add(body, zone()); body_block->statements()->Add(body, zone());
VariableProxy* temp_proxy = VariableProxy* temp_proxy =
factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos); factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
InitializeForEachStatement(loop, temp_proxy, enumerable, body_block); InitializeForEachStatement(loop, temp_proxy, enumerable, body_block,
false);
scope_ = for_scope; scope_ = for_scope;
body_scope->set_end_position(scanner()->location().end_pos); body_scope->set_end_position(scanner()->location().end_pos);
body_scope = body_scope->FinalizeBlockScope(); body_scope = body_scope->FinalizeBlockScope();
...@@ -3823,7 +3846,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3823,7 +3846,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
} }
} else { } else {
int lhs_beg_pos = peek_position(); int lhs_beg_pos = peek_position();
Expression* expression = ParseExpression(false, CHECK_OK); ExpressionClassifier classifier;
Expression* expression = ParseExpression(false, &classifier, CHECK_OK);
int lhs_end_pos = scanner()->location().end_pos; int lhs_end_pos = scanner()->location().end_pos;
ForEachStatement::VisitMode mode; ForEachStatement::VisitMode mode;
is_let_identifier_expression = is_let_identifier_expression =
...@@ -3831,11 +3855,24 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3831,11 +3855,24 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
expression->AsVariableProxy()->raw_name() == expression->AsVariableProxy()->raw_name() ==
ast_value_factory()->let_string(); ast_value_factory()->let_string();
if (CheckInOrOf(&mode, ok)) { bool is_for_each = CheckInOrOf(&mode, ok);
if (!*ok) return nullptr; if (!*ok) return nullptr;
bool is_destructuring =
is_for_each && allow_harmony_destructuring_assignment() &&
(expression->IsArrayLiteral() || expression->IsObjectLiteral());
if (is_destructuring) {
ValidateAssignmentPattern(&classifier, CHECK_OK);
} else {
ValidateExpression(&classifier, CHECK_OK);
}
if (is_for_each) {
if (!is_destructuring) {
expression = this->CheckAndRewriteReferenceExpression( expression = this->CheckAndRewriteReferenceExpression(
expression, lhs_beg_pos, lhs_end_pos, expression, lhs_beg_pos, lhs_end_pos,
MessageTemplate::kInvalidLhsInFor, kSyntaxError, CHECK_OK); MessageTemplate::kInvalidLhsInFor, kSyntaxError, CHECK_OK);
}
ForEachStatement* loop = ForEachStatement* loop =
factory()->NewForEachStatement(mode, labels, stmt_pos); factory()->NewForEachStatement(mode, labels, stmt_pos);
...@@ -3856,7 +3893,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3856,7 +3893,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition); factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
Statement* body = ParseSubStatement(NULL, CHECK_OK); Statement* body = ParseSubStatement(NULL, CHECK_OK);
block->statements()->Add(body, zone()); block->statements()->Add(body, zone());
InitializeForEachStatement(loop, expression, enumerable, block); InitializeForEachStatement(loop, expression, enumerable, block,
is_destructuring);
scope_ = saved_scope; scope_ = saved_scope;
body_scope->set_end_position(scanner()->location().end_pos); body_scope->set_end_position(scanner()->location().end_pos);
body_scope = body_scope->FinalizeBlockScope(); body_scope = body_scope->FinalizeBlockScope();
...@@ -4556,10 +4594,8 @@ class InitializerRewriter : public AstExpressionVisitor { ...@@ -4556,10 +4594,8 @@ class InitializerRewriter : public AstExpressionVisitor {
expr->AsRewritableAssignmentExpression(); expr->AsRewritableAssignmentExpression();
if (to_rewrite == nullptr || to_rewrite->is_rewritten()) return; if (to_rewrite == nullptr || to_rewrite->is_rewritten()) return;
bool ok = true;
Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite, Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite,
scope_, &ok); scope_);
DCHECK(ok);
} }
private: private:
...@@ -6529,10 +6565,7 @@ void Parser::RewriteDestructuringAssignments() { ...@@ -6529,10 +6565,7 @@ void Parser::RewriteDestructuringAssignments() {
Scope* scope = pair.scope; Scope* scope = pair.scope;
DCHECK_NOT_NULL(to_rewrite); DCHECK_NOT_NULL(to_rewrite);
if (!to_rewrite->is_rewritten()) { if (!to_rewrite->is_rewritten()) {
bool ok = true; PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope);
PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope,
&ok);
DCHECK(ok);
} }
} }
} }
......
...@@ -1047,8 +1047,11 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -1047,8 +1047,11 @@ class Parser : public ParserBase<ParserTraits> {
ZoneList<const AstRawString*>* names, bool* ok); ZoneList<const AstRawString*>* names, bool* ok);
static void RewriteDestructuringAssignment( static void RewriteDestructuringAssignment(
Parser* parser, RewritableAssignmentExpression* expr, Scope* Scope, Parser* parser, RewritableAssignmentExpression* expr, Scope* Scope);
bool* ok);
static Expression* RewriteDestructuringAssignment(Parser* parser,
Assignment* assignment,
Scope* scope);
void set_initializer_position(int pos) { initializer_position_ = pos; } void set_initializer_position(int pos) { initializer_position_ = pos; }
...@@ -1144,10 +1147,9 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -1144,10 +1147,9 @@ class Parser : public ParserBase<ParserTraits> {
// Initialize the components of a for-in / for-of statement. // Initialize the components of a for-in / for-of statement.
void InitializeForEachStatement(ForEachStatement* stmt, void InitializeForEachStatement(ForEachStatement* stmt, Expression* each,
Expression* each, Expression* subject, Statement* body,
Expression* subject, bool is_destructuring);
Statement* body);
Statement* DesugarLexicalBindingsInForStatement( Statement* DesugarLexicalBindingsInForStatement(
Scope* inner_scope, bool is_const, ZoneList<const AstRawString*>* names, Scope* inner_scope, bool is_const, ZoneList<const AstRawString*>* names,
ForStatement* loop, Statement* init, Expression* cond, Statement* next, ForStatement* loop, Statement* init, Expression* cond, Statement* next,
......
...@@ -32,12 +32,12 @@ void Parser::PatternRewriter::DeclareAndInitializeVariables( ...@@ -32,12 +32,12 @@ void Parser::PatternRewriter::DeclareAndInitializeVariables(
void Parser::PatternRewriter::RewriteDestructuringAssignment( void Parser::PatternRewriter::RewriteDestructuringAssignment(
Parser* parser, RewritableAssignmentExpression* to_rewrite, Scope* scope, Parser* parser, RewritableAssignmentExpression* to_rewrite, Scope* scope) {
bool* ok) {
PatternRewriter rewriter; PatternRewriter rewriter;
DCHECK(!to_rewrite->is_rewritten()); DCHECK(!to_rewrite->is_rewritten());
bool ok = true;
rewriter.scope_ = scope; rewriter.scope_ = scope;
rewriter.parser_ = parser; rewriter.parser_ = parser;
rewriter.context_ = ASSIGNMENT; rewriter.context_ = ASSIGNMENT;
...@@ -45,9 +45,21 @@ void Parser::PatternRewriter::RewriteDestructuringAssignment( ...@@ -45,9 +45,21 @@ void Parser::PatternRewriter::RewriteDestructuringAssignment(
rewriter.block_ = nullptr; rewriter.block_ = nullptr;
rewriter.descriptor_ = nullptr; rewriter.descriptor_ = nullptr;
rewriter.names_ = nullptr; rewriter.names_ = nullptr;
rewriter.ok_ = ok; rewriter.ok_ = &ok;
rewriter.RecurseIntoSubpattern(rewriter.pattern_, nullptr); rewriter.RecurseIntoSubpattern(rewriter.pattern_, nullptr);
DCHECK(ok);
}
Expression* Parser::PatternRewriter::RewriteDestructuringAssignment(
Parser* parser, Assignment* assignment, Scope* scope) {
DCHECK_NOT_NULL(assignment);
DCHECK_EQ(Token::ASSIGN, assignment->op());
auto to_rewrite =
parser->factory()->NewRewritableAssignmentExpression(assignment);
RewriteDestructuringAssignment(parser, to_rewrite, scope);
return to_rewrite->expression();
} }
......
...@@ -956,15 +956,29 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { ...@@ -956,15 +956,29 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
} }
} else { } else {
int lhs_beg_pos = peek_position(); int lhs_beg_pos = peek_position();
Expression lhs = ParseExpression(false, CHECK_OK); ExpressionClassifier classifier;
Expression lhs = ParseExpression(false, &classifier, CHECK_OK);
int lhs_end_pos = scanner()->location().end_pos; int lhs_end_pos = scanner()->location().end_pos;
is_let_identifier_expression = is_let_identifier_expression =
lhs.IsIdentifier() && lhs.AsIdentifier().IsLet(); lhs.IsIdentifier() && lhs.AsIdentifier().IsLet();
if (CheckInOrOf(&mode, ok)) { bool is_for_each = CheckInOrOf(&mode, ok);
if (!*ok) return Statement::Default(); if (!*ok) return Statement::Default();
bool is_destructuring = is_for_each &&
allow_harmony_destructuring_assignment() &&
(lhs->IsArrayLiteral() || lhs->IsObjectLiteral());
if (is_destructuring) {
ValidateAssignmentPattern(&classifier, CHECK_OK);
} else {
ValidateExpression(&classifier, CHECK_OK);
}
if (is_for_each) {
if (!is_destructuring) {
lhs = CheckAndRewriteReferenceExpression( lhs = CheckAndRewriteReferenceExpression(
lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor, lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor,
kSyntaxError, CHECK_OK); kSyntaxError, CHECK_OK);
}
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK); ParseSubStatement(CHECK_OK);
......
...@@ -6828,6 +6828,25 @@ TEST(DestructuringAssignmentPositiveTests) { ...@@ -6828,6 +6828,25 @@ TEST(DestructuringAssignmentPositiveTests) {
{"'use strict'; let x, y, z; for (x of ", " = {});"}, {"'use strict'; let x, y, z; for (x of ", " = {});"},
{"var x, y, z; for (x in ", " = {});"}, {"var x, y, z; for (x in ", " = {});"},
{"var x, y, z; for (x of ", " = {});"}, {"var x, y, z; for (x of ", " = {});"},
{"var x, y, z; for (", " in {});"},
{"var x, y, z; for (", " of {});"},
{"'use strict'; var x, y, z; for (", " in {});"},
{"'use strict'; var x, y, z; for (", " of {});"},
{NULL, NULL}};
const char* mixed_assignments_context_data[][2] = {
{"'use strict'; let x, y, z; (", " = z = {});"},
{"var x, y, z; (", " = z = {});"},
{"'use strict'; let x, y, z; (x = ", " = z = {});"},
{"var x, y, z; (x = ", " = z = {});"},
{"'use strict'; let x, y, z; for (x in ", " = z = {});"},
{"'use strict'; let x, y, z; for (x in x = ", " = z = {});"},
{"'use strict'; let x, y, z; for (x of ", " = z = {});"},
{"'use strict'; let x, y, z; for (x of x = ", " = z = {});"},
{"var x, y, z; for (x in ", " = z = {});"},
{"var x, y, z; for (x in x = ", " = z = {});"},
{"var x, y, z; for (x of ", " = z = {});"},
{"var x, y, z; for (x of x = ", " = z = {});"},
{NULL, NULL}}; {NULL, NULL}};
// clang-format off // clang-format off
...@@ -6901,8 +6920,6 @@ TEST(DestructuringAssignmentPositiveTests) { ...@@ -6901,8 +6920,6 @@ TEST(DestructuringAssignmentPositiveTests) {
"[ [ foo()[x] = 10 ] = {} ]", "[ [ foo()[x] = 10 ] = {} ]",
"[ [ x.y = 10 ] = {} ]", "[ [ x.y = 10 ] = {} ]",
"[ [ x[y] = 10 ] = {} ]", "[ [ x[y] = 10 ] = {} ]",
"{ x : y }",
"{ x : y = 1 }", "{ x : y = 1 }",
"{ x }", "{ x }",
"{ x, y, z }", "{ x, y, z }",
...@@ -6945,12 +6962,8 @@ TEST(DestructuringAssignmentPositiveTests) { ...@@ -6945,12 +6962,8 @@ TEST(DestructuringAssignmentPositiveTests) {
"[...x]", "[...x]",
"[x,y,...z]", "[x,y,...z]",
"[x,,...z]", "[x,,...z]",
"{ x: y } = z", "{ x: y }",
"[x, y] = z", "[x, y]",
"{ x: y } = { z }",
"[x, y] = { z }",
"{ x: y } = [ z ]",
"[x, y] = [ z ]",
"[((x, y) => z).x]", "[((x, y) => z).x]",
"{x: ((y, z) => z).x}", "{x: ((y, z) => z).x}",
"[((x, y) => z)['x']]", "[((x, y) => z)['x']]",
...@@ -6966,6 +6979,9 @@ TEST(DestructuringAssignmentPositiveTests) { ...@@ -6966,6 +6979,9 @@ TEST(DestructuringAssignmentPositiveTests) {
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags, RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags)); arraysize(always_flags));
RunParserSyncTest(mixed_assignments_context_data, data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
const char* empty_context_data[][2] = { const char* empty_context_data[][2] = {
{"'use strict';", ""}, {"", ""}, {NULL, NULL}}; {"'use strict';", ""}, {"", ""}, {NULL, NULL}};
...@@ -7755,3 +7771,12 @@ TEST(EscapedKeywords) { ...@@ -7755,3 +7771,12 @@ TEST(EscapedKeywords) {
RunModuleParserSyncTest(strict_context_data, valid_data, kError, NULL, 0, RunModuleParserSyncTest(strict_context_data, valid_data, kError, NULL, 0,
always_flags, arraysize(always_flags)); always_flags, arraysize(always_flags));
} }
TEST(MiscSyntaxErrors) {
const char* context_data[][2] = {
{"'use strict'", ""}, {"", ""}, {NULL, NULL}};
const char* error_data[] = {"for (();;) {}", NULL};
RunParserSyncTest(context_data, error_data, kError, NULL, 0, NULL, 0);
}
...@@ -428,3 +428,55 @@ assertEquals(oz, [1, 2, 3, 4, 5]); ...@@ -428,3 +428,55 @@ assertEquals(oz, [1, 2, 3, 4, 5]);
assertThrows(() => { ({ a: [ c ] } = { a: [ "nope!" ] }); }, TypeError); assertThrows(() => { ({ a: [ c ] } = { a: [ "nope!" ] }); }, TypeError);
assertEquals("untouchable", c); assertEquals("untouchable", c);
})(); })();
(function testForIn() {
var log = [];
var x = {};
var object = {
"Apenguin": 1,
"\u{1F382}cake": 2,
"Bpuppy": 3,
"Cspork": 4
};
for ([x.firstLetter, ...x.rest] in object) {
if (x.firstLetter === "A") {
assertEquals(["p", "e", "n", "g", "u", "i", "n"], x.rest);
continue;
}
if (x.firstLetter === "C") {
assertEquals(["s", "p", "o", "r", "k"], x.rest);
break;
}
log.push({ firstLetter: x.firstLetter, rest: x.rest });
}
assertEquals([
{ firstLetter: "\u{1F382}", rest: ["c", "a", "k", "e"] },
{ firstLetter: "B", rest: ["p", "u", "p", "p", "y"] },
], log);
})();
(function testForOf() {
var log = [];
var x = {};
var names = [
"Apenguin",
"\u{1F382}cake",
"Bpuppy",
"Cspork"
];
for ([x.firstLetter, ...x.rest] of names) {
if (x.firstLetter === "A") {
assertEquals(["p", "e", "n", "g", "u", "i", "n"], x.rest);
continue;
}
if (x.firstLetter === "C") {
assertEquals(["s", "p", "o", "r", "k"], x.rest);
break;
}
log.push({ firstLetter: x.firstLetter, rest: x.rest });
}
assertEquals([
{ firstLetter: "\u{1F382}", rest: ["c", "a", "k", "e"] },
{ firstLetter: "B", rest: ["p", "u", "p", "p", "y"] },
], log);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment