Commit fcb75915 authored by neis's avatar neis Committed by Commit bot

[parsing] Fix maybe-assigned flag in some cases.

This CL attempts to set the maybe-assigned flag for variables that are written
to as part of a destructuring or loop header.

For instance, in the following two cases we now mark x as maybe-assigned.

a) [x] = [1];
b) for (x of [1,2,3]) {};

There's more work to do here, this is just a first step.

R=adamk@chromium.org, mstarzinger@chromium.org
BUG=v8:5636

Review-Url: https://codereview.chromium.org/2562443003
Cr-Commit-Position: refs/heads/master@{#41582}
parent 4cfe91cf
...@@ -1634,8 +1634,10 @@ class VariableProxy final : public Expression { ...@@ -1634,8 +1634,10 @@ class VariableProxy final : public Expression {
bool is_assigned() const { return IsAssignedField::decode(bit_field_); } bool is_assigned() const { return IsAssignedField::decode(bit_field_); }
void set_is_assigned() { void set_is_assigned() {
DCHECK(!is_resolved());
bit_field_ = IsAssignedField::update(bit_field_, true); bit_field_ = IsAssignedField::update(bit_field_, true);
if (is_resolved()) {
var()->set_maybe_assigned();
}
} }
bool is_resolved() const { return IsResolvedField::decode(bit_field_); } bool is_resolved() const { return IsResolvedField::decode(bit_field_); }
...@@ -3455,9 +3457,13 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3455,9 +3457,13 @@ class AstNodeFactory final BASE_EMBEDDED {
Expression* value, Expression* value,
int pos) { int pos) {
DCHECK(Token::IsAssignmentOp(op)); DCHECK(Token::IsAssignmentOp(op));
if (op != Token::INIT && target->IsVariableProxy()) {
target->AsVariableProxy()->set_is_assigned();
}
Assignment* assign = new (zone_) Assignment(op, target, value, pos); Assignment* assign = new (zone_) Assignment(op, target, value, pos);
if (assign->is_compound()) { if (assign->is_compound()) {
DCHECK(Token::IsAssignmentOp(op));
assign->binary_operation_ = assign->binary_operation_ =
NewBinaryOperation(assign->binary_op(), target, value, pos + 1); NewBinaryOperation(assign->binary_op(), target, value, pos + 1);
} }
......
...@@ -1909,6 +1909,7 @@ Statement* Parser::InitializeForEachStatement(ForEachStatement* stmt, ...@@ -1909,6 +1909,7 @@ Statement* Parser::InitializeForEachStatement(ForEachStatement* stmt,
body = block; body = block;
each = factory()->NewVariableProxy(temp); each = factory()->NewVariableProxy(temp);
} }
MarkExpressionAsAssigned(each);
stmt->AsForInStatement()->Initialize(each, subject, body); stmt->AsForInStatement()->Initialize(each, subject, body);
} }
return stmt; return stmt;
...@@ -2058,8 +2059,8 @@ Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of, ...@@ -2058,8 +2059,8 @@ Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of,
const int nopos = kNoSourcePosition; const int nopos = kNoSourcePosition;
auto avfactory = ast_value_factory(); auto avfactory = ast_value_factory();
Variable* iterator = NewTemporary(ast_value_factory()->dot_iterator_string()); Variable* iterator = NewTemporary(avfactory->dot_iterator_string());
Variable* result = NewTemporary(ast_value_factory()->dot_result_string()); Variable* result = NewTemporary(avfactory->dot_result_string());
Variable* completion = NewTemporary(avfactory->empty_string()); Variable* completion = NewTemporary(avfactory->empty_string());
// iterator = GetIterator(iterable) // iterator = GetIterator(iterable)
......
...@@ -219,6 +219,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { ...@@ -219,6 +219,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
// But for var declarations we need to do a new lookup. // But for var declarations we need to do a new lookup.
if (descriptor_->mode == VAR) { if (descriptor_->mode == VAR) {
proxy = var_init_scope->NewUnresolved(factory(), name); proxy = var_init_scope->NewUnresolved(factory(), name);
// TODO(neis): Set is_assigned on proxy.
} else { } else {
DCHECK_NOT_NULL(proxy); DCHECK_NOT_NULL(proxy);
DCHECK_NOT_NULL(proxy->var()); DCHECK_NOT_NULL(proxy->var());
......
...@@ -3273,6 +3273,7 @@ TEST(InnerAssignment) { ...@@ -3273,6 +3273,7 @@ TEST(InnerAssignment) {
{"var x = 5; function x() {}", true, false}, {"var x = 5; function x() {}", true, false},
{"var x = 4; var x = 5;", true, false}, {"var x = 4; var x = 5;", true, false},
{"var [x, x] = [4, 5];", true, false}, {"var [x, x] = [4, 5];", true, false},
{"var x; [x, x] = [4, 5];", true, false},
{"var {a: x, b: x} = {a: 4, b: 5};", true, false}, {"var {a: x, b: x} = {a: 4, b: 5};", true, false},
{"var x = {a: 4, b: (x = 5)};", true, false}, {"var x = {a: 4, b: (x = 5)};", true, false},
{"var {x=1} = {a: 4, b: (x = 5)};", true, false}, {"var {x=1} = {a: 4, b: (x = 5)};", true, false},
...@@ -3301,53 +3302,63 @@ TEST(InnerAssignment) { ...@@ -3301,53 +3302,63 @@ TEST(InnerAssignment) {
{"function x() {}; var x;", true, false}, {"function x() {}; var x;", true, false},
{"var x; try {} catch (x) { var x = 5; }", true, false}, {"var x; try {} catch (x) { var x = 5; }", true, false},
}; };
struct { const char* source; bool assigned; bool with; } inners[] = { struct {
// Actual assignments. const char* source;
{ "x = 1;", true, false }, bool assigned;
{ "x++;", true, false }, bool with;
{ "++x;", true, false }, } inners[] = {
{ "x--;", true, false }, // Actual assignments.
{ "--x;", true, false }, {"x = 1;", true, false},
{ "{ x = 1; }", true, false }, {"x++;", true, false},
{ "'use strict'; { let x; }; x = 0;", true, false }, {"++x;", true, false},
{ "'use strict'; { const x = 1; }; x = 0;", true, false }, {"x--;", true, false},
{ "'use strict'; { function x() {} }; x = 0;", true, false }, {"--x;", true, false},
{ "with ({}) { x = 1; }", true, true }, {"{ x = 1; }", true, false},
{ "eval('');", true, false }, {"'use strict'; { let x; }; x = 0;", true, false},
{ "'use strict'; { let y; eval('') }", true, false }, {"'use strict'; { const x = 1; }; x = 0;", true, false},
{ "function h() { x = 0; }", true, false }, {"'use strict'; { function x() {} }; x = 0;", true, false},
{ "(function() { x = 0; })", true, false }, {"with ({}) { x = 1; }", true, true},
{ "(function() { x = 0; })", true, false }, {"eval('');", true, false},
{ "with ({}) (function() { x = 0; })", true, true }, {"'use strict'; { let y; eval('') }", true, false},
// Actual non-assignments. {"function h() { x = 0; }", true, false},
{ "", false, false }, {"(function() { x = 0; })", true, false},
{ "x;", false, false }, {"(function() { x = 0; })", true, false},
{ "var x;", false, false }, {"with ({}) (function() { x = 0; })", true, true},
{ "var x = 8;", false, false }, {"for (x of [1,2,3]) {}", true, false},
{ "var x; x = 8;", false, false }, {"for (x in {a: 1}) {}", true, false},
{ "'use strict'; let x;", false, false }, {"for ([x] of [[1],[2],[3]]) {}", true, false},
{ "'use strict'; let x = 8;", false, false }, {"for ([x] in {ab: 1}) {}", true, false},
{ "'use strict'; let x; x = 8;", false, false }, {"for ([...x] in {ab: 1}) {}", true, false},
{ "'use strict'; const x = 8;", false, false }, {"[x] = [1]", true, false},
{ "function x() {}", false, false }, // Actual non-assignments.
{ "function x() { x = 0; }", false, false }, {"", false, false},
{ "function h(x) { x = 0; }", false, false }, {"x;", false, false},
{ "'use strict'; { let x; x = 0; }", false, false }, {"var x;", false, false},
{ "{ var x; }; x = 0;", false, false }, {"var x = 8;", false, false},
{ "with ({}) {}", false, true }, {"var x; x = 8;", false, false},
{ "var x; { with ({}) { x = 1; } }", false, true }, {"'use strict'; let x;", false, false},
{ "try {} catch(x) { x = 0; }", false, false }, {"'use strict'; let x = 8;", false, false},
{ "try {} catch(x) { with ({}) { x = 1; } }", false, true }, {"'use strict'; let x; x = 8;", false, false},
// Eval approximation. {"'use strict'; const x = 8;", false, false},
{ "eval('');", true, false }, {"function x() {}", false, false},
{ "function h() { eval(''); }", true, false }, {"function x() { x = 0; }", false, false},
{ "(function() { eval(''); })", true, false }, {"function h(x) { x = 0; }", false, false},
// Shadowing not recognized because of eval approximation. {"'use strict'; { let x; x = 0; }", false, false},
{ "var x; eval('');", true, false }, {"{ var x; }; x = 0;", false, false},
{ "'use strict'; let x; eval('');", true, false }, {"with ({}) {}", false, true},
{ "try {} catch(x) { eval(''); }", true, false }, {"var x; { with ({}) { x = 1; } }", false, true},
{ "function x() { eval(''); }", true, false }, {"try {} catch(x) { x = 0; }", false, false},
{ "(function(x) { eval(''); })", true, false }, {"try {} catch(x) { with ({}) { x = 1; } }", false, true},
// Eval approximation.
{"eval('');", true, false},
{"function h() { eval(''); }", true, false},
{"(function() { eval(''); })", true, false},
// Shadowing not recognized because of eval approximation.
{"var x; eval('');", true, false},
{"'use strict'; let x; eval('');", true, false},
{"try {} catch(x) { eval(''); }", true, false},
{"function x() { eval(''); }", true, false},
{"(function(x) { eval(''); })", true, false},
}; };
int prefix_len = Utf8LengthHelper(prefix); int prefix_len = Utf8LengthHelper(prefix);
...@@ -3410,7 +3421,7 @@ TEST(InnerAssignment) { ...@@ -3410,7 +3421,7 @@ TEST(InnerAssignment) {
// maybe_assigned. // maybe_assigned.
CHECK(is_maybe_assigned || (is_maybe_assigned == expected)); CHECK(is_maybe_assigned || (is_maybe_assigned == expected));
} else { } else {
CHECK(is_maybe_assigned == expected); CHECK_EQ(is_maybe_assigned, expected);
} }
} }
} }
......
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