Commit 7ffdb519 authored by dslomov's avatar dslomov Committed by Commit bot

[destructuring] Grand for statement parsing unification.

Also support patterns in ``for (var p in/of ...)``

This CL extends the rewriting we used to do for ``for (let p in/of...)`` to
``for (var p in/of ...)``. For all for..in/of loop declaring variable,
we rewrite
   for (var/let/const pattern in/of e) b
into
   for (x' in/of e) { var/let/const pattern = e; b }

This adds a small complication for debugger: for a statement
   for (var v in/of e) ...
we used to have
   var v;
   for (v in/of e) ...
and there was a separate breakpoint on ``var v`` line.
This breakpoint is actually useless since it is immediately followed by
a breakpoint on evaluation of ``e``, so this CL removes that breakpoint
location.

Similiraly, for let, it used to be that
  for (let v in/of e) ...
became
  for (x' in/of e) { let v; v  = x'; ... }
``let v``generetaed a useless breakpoint (with the location at the
loop's head. This CL removes that breakpoint as well.

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

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

Cr-Commit-Position: refs/heads/master@{#28565}
parent a40e85d6
......@@ -2295,8 +2295,8 @@ const AstRawString* Parser::DeclarationParsingResult::SingleName() const {
Block* Parser::DeclarationParsingResult::BuildInitializationBlock(
ZoneList<const AstRawString*>* names, bool* ok) {
Block* result =
descriptor.parser->factory()->NewBlock(NULL, 1, true, descriptor.pos);
Block* result = descriptor.parser->factory()->NewBlock(
NULL, 1, true, descriptor.declaration_pos);
for (auto declaration : declarations) {
PatternRewriter::DeclareAndInitializeVariables(
result, &descriptor, &declaration, names, CHECK_OK);
......@@ -2350,7 +2350,8 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
// BindingPattern '=' AssignmentExpression
parsing_result->descriptor.parser = this;
parsing_result->descriptor.pos = peek_position();
parsing_result->descriptor.declaration_pos = peek_position();
parsing_result->descriptor.initialization_pos = peek_position();
parsing_result->descriptor.mode = VAR;
// True if the binding needs initialization. 'let' and 'const' declared
// bindings are created uninitialized by their declaration nodes and
......@@ -3417,11 +3418,10 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
bool is_let_identifier_expression = false;
DeclarationParsingResult parsing_result;
if (peek() != Token::SEMICOLON) {
if (peek() == Token::VAR ||
(peek() == Token::CONST && is_sloppy(language_mode()))) {
if (peek() == Token::VAR || peek() == Token::CONST ||
(peek() == Token::LET && is_strict(language_mode()))) {
ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK);
Block* variable_statement =
parsing_result.BuildInitializationBlock(nullptr, CHECK_OK);
is_const = parsing_result.descriptor.mode == CONST;
int num_decl = parsing_result.declarations.length();
bool accept_IN = num_decl >= 1;
......@@ -3454,124 +3454,100 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
*ok = false;
return nullptr;
}
ForEachStatement* loop =
factory()->NewForEachStatement(mode, labels, stmt_pos);
Target target(&this->target_stack_, loop);
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
VariableProxy* each =
scope_->NewUnresolved(factory(), parsing_result.SingleName(),
Variable::NORMAL, each_beg_pos, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
InitializeForEachStatement(loop, each, enumerable, body);
Block* result =
factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
result->AddStatement(variable_statement, zone());
result->AddStatement(loop, zone());
scope_ = saved_scope;
for_scope->set_end_position(scanner()->location().end_pos);
for_scope = for_scope->FinalizeBlockScope();
DCHECK(for_scope == NULL);
// Parsed for-in loop w/ variable/const declaration.
return result;
} else {
init = variable_statement;
}
} else if ((peek() == Token::LET || peek() == Token::CONST) &&
is_strict(language_mode())) {
is_const = peek() == Token::CONST;
ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK);
DCHECK(parsing_result.descriptor.pos != RelocInfo::kNoPosition);
int num_decl = parsing_result.declarations.length();
bool accept_IN = num_decl >= 1;
bool accept_OF = true;
ForEachStatement::VisitMode mode;
int each_beg_pos = scanner()->location().beg_pos;
int each_end_pos = scanner()->location().end_pos;
if (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return nullptr;
if (num_decl != 1) {
const char* loop_type =
mode == ForEachStatement::ITERATE ? "for-of" : "for-in";
ParserTraits::ReportMessageAt(
parsing_result.bindings_loc,
MessageTemplate::kForInOfLoopMultiBindings, loop_type);
*ok = false;
return nullptr;
}
if (parsing_result.first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(parsing_result.first_initializer_loc,
MessageTemplate::kForOfLoopInitializer);
} else {
ReportMessageAt(parsing_result.first_initializer_loc,
MessageTemplate::kForInLoopInitializer);
}
*ok = false;
return nullptr;
DCHECK(parsing_result.declarations.length() == 1);
Block* init_block = nullptr;
// special case for legacy for (var/const x =.... in)
if (is_sloppy(language_mode()) &&
!IsLexicalVariableMode(parsing_result.descriptor.mode) &&
parsing_result.declarations[0].initializer != nullptr) {
VariableProxy* single_var = scope_->NewUnresolved(
factory(), parsing_result.SingleName(), Variable::NORMAL,
each_beg_pos, each_end_pos);
init_block = factory()->NewBlock(
nullptr, 2, true, parsing_result.descriptor.declaration_pos);
init_block->AddStatement(
factory()->NewExpressionStatement(
factory()->NewAssignment(
Token::ASSIGN, single_var,
parsing_result.declarations[0].initializer,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
}
// Rewrite a for-in statement of the form
// Rewrite a for-in/of statement of the form
//
// for (let/const x in e) b
// for (let/const/var x in/of e) b
//
// into
//
// <let x' be a temporary variable>
// for (x' in e) {
// let/const x;
// for (x' in/of e) {
// let/const/var x;
// x = x';
// b;
// }
// TODO(keuchel): Move the temporary variable to the block scope, after
// implementing stack allocated block scoped variables.
Variable* temp = scope_->DeclarationScope()->NewTemporary(
ast_value_factory()->dot_for_string());
VariableProxy* temp_proxy =
factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
ForEachStatement* loop =
factory()->NewForEachStatement(mode, labels, stmt_pos);
Target target(&this->target_stack_, loop);
// The expression does not see the loop variable.
// The expression does not see the lexical loop variables.
scope_ = saved_scope;
Expression* enumerable = ParseExpression(true, CHECK_OK);
scope_ = for_scope;
Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Block* body_block =
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
auto each_initialization_block = factory()->NewBlock(
nullptr, 1, true, parsing_result.descriptor.pos);
auto each_initialization_block =
factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition);
{
DCHECK(parsing_result.declarations.length() == 1);
DeclarationParsingResult::Declaration decl =
parsing_result.declarations[0];
decl.initializer = temp_proxy;
auto descriptor = parsing_result.descriptor;
descriptor.declaration_pos = RelocInfo::kNoPosition;
decl.initializer = factory()->NewVariableProxy(temp);
PatternRewriter::DeclareAndInitializeVariables(
each_initialization_block, &parsing_result.descriptor, &decl,
&lexical_bindings, CHECK_OK);
each_initialization_block, &descriptor, &decl,
IsLexicalVariableMode(descriptor.mode) ? &lexical_bindings
: nullptr,
CHECK_OK);
}
body_block->AddStatement(each_initialization_block, zone());
body_block->AddStatement(body, zone());
VariableProxy* temp_proxy =
factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
InitializeForEachStatement(loop, temp_proxy, enumerable, body_block);
scope_ = saved_scope;
for_scope->set_end_position(scanner()->location().end_pos);
for_scope = for_scope->FinalizeBlockScope();
body_block->set_scope(for_scope);
// Parsed for-in loop w/ let declaration.
return loop;
if (for_scope != nullptr) {
body_block->set_scope(for_scope);
}
// Parsed for-in loop w/ variable declarations.
if (init_block != nullptr) {
init_block->AddStatement(loop, zone());
return init_block;
} else {
return loop;
}
} else {
init = parsing_result.BuildInitializationBlock(&lexical_bindings,
CHECK_OK);
init = parsing_result.BuildInitializationBlock(
IsLexicalVariableMode(parsing_result.descriptor.mode)
? &lexical_bindings
: nullptr,
CHECK_OK);
}
} else {
Scanner::Location lhs_location = scanner()->peek_location();
......@@ -3579,9 +3555,9 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
ForEachStatement::VisitMode mode;
bool accept_OF = expression->IsVariableProxy();
is_let_identifier_expression =
expression->IsVariableProxy() &&
expression->AsVariableProxy()->raw_name() ==
ast_value_factory()->let_string();
expression->IsVariableProxy() &&
expression->AsVariableProxy()->raw_name() ==
ast_value_factory()->let_string();
if (CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return nullptr;
......
......@@ -950,7 +950,8 @@ class Parser : public ParserBase<ParserTraits> {
VariableMode mode;
bool is_const;
bool needs_init;
int pos;
int declaration_pos;
int initialization_pos;
Token::Value init_op;
};
......
......@@ -51,7 +51,8 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
const AstRawString* name = pattern->raw_name();
VariableProxy* proxy = parser->NewUnresolved(name, descriptor_->mode);
Declaration* declaration = factory()->NewVariableDeclaration(
proxy, descriptor_->mode, descriptor_->scope, descriptor_->pos);
proxy, descriptor_->mode, descriptor_->scope,
descriptor_->declaration_pos);
Variable* var = parser->Declare(declaration, descriptor_->mode != VAR, ok_);
if (!*ok_) return;
DCHECK_NOT_NULL(var);
......@@ -126,7 +127,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
ZoneList<Expression*>* arguments =
new (zone()) ZoneList<Expression*>(3, zone());
// We have at least 1 parameter.
arguments->Add(factory()->NewStringLiteral(name, descriptor_->pos), zone());
arguments->Add(
factory()->NewStringLiteral(name, descriptor_->declaration_pos),
zone());
CallRuntime* initialize;
if (descriptor_->is_const) {
......@@ -140,13 +143,14 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
initialize = factory()->NewCallRuntime(
ast_value_factory()->initialize_const_global_string(),
Runtime::FunctionForId(Runtime::kInitializeConstGlobal), arguments,
descriptor_->pos);
descriptor_->initialization_pos);
} else {
// Add language mode.
// We may want to pass singleton to avoid Literal allocations.
LanguageMode language_mode = initialization_scope->language_mode();
arguments->Add(
factory()->NewNumberLiteral(language_mode, descriptor_->pos), zone());
arguments->Add(factory()->NewNumberLiteral(language_mode,
descriptor_->declaration_pos),
zone());
// Be careful not to assign a value to the global variable if
// we're in a with. The initialization value should not
......@@ -160,7 +164,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
initialize = factory()->NewCallRuntime(
ast_value_factory()->initialize_var_global_string(),
Runtime::FunctionForId(Runtime::kInitializeVarGlobal), arguments,
descriptor_->pos);
descriptor_->declaration_pos);
} else {
initialize = NULL;
}
......@@ -184,7 +188,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
DCHECK_NOT_NULL(proxy->var());
DCHECK_NOT_NULL(value);
Assignment* assignment = factory()->NewAssignment(
descriptor_->init_op, proxy, value, descriptor_->pos);
descriptor_->init_op, proxy, value, descriptor_->initialization_pos);
block_->AddStatement(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
zone());
......@@ -200,7 +204,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
// property).
VariableProxy* proxy = initialization_scope->NewUnresolved(factory(), name);
Assignment* assignment = factory()->NewAssignment(
descriptor_->init_op, proxy, value, descriptor_->pos);
descriptor_->init_op, proxy, value, descriptor_->initialization_pos);
block_->AddStatement(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
zone());
......
......@@ -6525,6 +6525,32 @@ TEST(DestructuringNegativeTests) {
}
TEST(DestructuringDisallowPatternsInForVarIn) {
i::FLAG_harmony_destructuring = true;
static const ParserFlag always_flags[] = {kAllowHarmonyDestructuring};
const char* context_data[][2] = {
{"", ""}, {"function f() {", "}"}, {NULL, NULL}};
// clang-format off
const char* error_data[] = {
"for (var {x} = {} in null);",
"for (var {x} = {} of null);",
"for (let x = {} in null);",
"for (let x = {} of null);",
NULL};
// clang-format on
RunParserSyncTest(context_data, error_data, kError, NULL, 0, always_flags,
arraysize(always_flags));
// clang-format off
const char* success_data[] = {
"for (var x = {} in null);",
NULL};
// clang-format on
RunParserSyncTest(context_data, success_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(SpreadArray) {
i::FLAG_harmony_spread_arrays = true;
......
......@@ -81,28 +81,28 @@ Debug.setListener(listener);
f();
Debug.setListener(null); // Break z
print(JSON.stringify(log));
print("log:\n"+ JSON.stringify(log));
// The let declaration differs from var in that the loop variable
// is declared in every iteration.
var expected = [
// Entry
"a2","b2",
// Empty for-in-var: var decl, get enumerable
"c7","c16",
// Empty for-in-var: get enumerable
"c16",
// Empty for-in: get enumerable
"d12",
// For-in-var: var decl, get enumerable, assign, body, assign, body, ...
"e7","e16","e11","E4","e11","E4","e11","E4","e11",
// For-in-var: get enumerable, assign, body, assign, body, ...
"e16","e11","E4","e11","E4","e11","E4","e11",
// For-in: get enumerable, assign, body, assign, body, ...
"f12","f7","F4","f7","F4","f7","F4","f7",
// For-in-let: get enumerable, next, new let, body, next, new let, ...
"g16","g11","g7","G4","g11","g7","G4","g11","g7","G4","g11",
// For-of-var: var decl, next(), body, next(), body, ...
"h7","h16","H4","h16","H4","h16","H4","h16",
// For-in-let: get enumerable, next, body, next, ...
"g16","g11","G4","g11","G4","g11","G4","g11",
// For-of-var: next(), body, next(), body, ...
"h16","H4","h16","H4","h16","H4","h16",
// For-of: next(), body, next(), body, ...
"i12","I4","i12","I4","i12","I4","i12",
// For-of-let: next(), new let, body, next(), new let, ...
"j16","j7","J4","j16","j7","J4","j16","j7","J4","j16",
// For-of-let: next(), body, next(), ...
"j16","J4","j16","J4","j16","J4","j16",
// For-var: var decl, condition, body, next, condition, body, ...
"k7","k20","K4","k23","k20","K4","k23","k20","K4","k23","k20",
// For: init, condition, body, next, condition, body, ...
......@@ -110,6 +110,7 @@ var expected = [
// Exit.
"y0","z0",
]
print("expected:\n"+ JSON.stringify(log));
assertArrayEquals(expected, log);
assertEquals(48, s);
......
......@@ -645,8 +645,7 @@
assertSame(-(i+1), fy());
}
var o = { 'a1':1, 'b2':2 };
o.__proto__ = null;
var o = { __proto__:null, 'a1':1, 'b2':2 };
let sx = '';
let sy = '';
for (let [x,y] in o) {
......@@ -656,3 +655,34 @@
assertEquals('ab', sx);
assertEquals('12', sy);
}());
(function TestForEachVars() {
var a = [{x:1, y:-1}, {x:2,y:-2}, {x:3,y:-3}];
var sumX = 0;
var sumY = 0;
var fs = [];
for (var {x,y} of a) {
sumX += x;
sumY += y;
fs.push({fx : function() { return x; }, fy : function() { return y }});
}
assertSame(6, sumX);
assertSame(-6, sumY);
assertSame(3, fs.length);
for (var i = 0; i < fs.length; i++) {
var {fx,fy} = fs[i];
assertSame(3, fx());
assertSame(-3, fy());
}
var o = { __proto__:null, 'a1':1, 'b2':2 };
var sx = '';
var sy = '';
for (var [x,y] in o) {
sx += x;
sy += y;
}
assertEquals('ab', sx);
assertEquals('12', sy);
}());
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