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