Commit 5cdaa3e6 authored by dslomov's avatar dslomov Committed by Commit bot

[destructuring] Implement initializers in patterns.

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

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

Cr-Commit-Position: refs/heads/master@{#28482}
parent dd26fbf2
......@@ -1007,15 +1007,14 @@ class Parser : public ParserBase<ParserTraits> {
current_value_ = old_value;
}
Variable* CreateTempVar(Expression* value);
AstNodeFactory* factory() const { return descriptor_->parser->factory(); }
AstValueFactory* ast_value_factory() const {
return descriptor_->parser->ast_value_factory();
}
bool inside_with() const { return descriptor_->parser->inside_with(); }
Zone* zone() const { return descriptor_->parser->zone(); }
Scope* TemporaryDeclarationScope() const {
return descriptor_->parser->scope_->DeclarationScope();
}
Expression* pattern_;
int initializer_position_;
......
......@@ -208,17 +208,22 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
}
void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern) {
auto temp = TemporaryDeclarationScope()->NewTemporary(
ast_value_factory()->empty_string());
Variable* Parser::PatternRewriter::CreateTempVar(Expression* value) {
auto temp_scope = descriptor_->parser->scope_->DeclarationScope();
auto temp = temp_scope->NewTemporary(ast_value_factory()->empty_string());
auto assignment =
factory()->NewAssignment(Token::ASSIGN, factory()->NewVariableProxy(temp),
current_value_, RelocInfo::kNoPosition);
value, RelocInfo::kNoPosition);
block_->AddStatement(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
zone());
return temp;
}
void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern) {
auto temp = CreateTempVar(current_value_);
if (pattern->properties()->length() == 0) {
block_->AddStatement(descriptor_->parser->BuildAssertIsCoercible(temp),
zone());
......@@ -240,7 +245,20 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
void Parser::PatternRewriter::VisitAssignment(Assignment* node) {
// TODO(dslomov): implement.
// let {<pattern> = <init>} = <value>
// becomes
// temp = <value>;
// <pattern> = temp === undefined ? <init> : temp;
DCHECK(node->op() == Token::ASSIGN);
auto temp = CreateTempVar(current_value_);
Expression* is_undefined = factory()->NewCompareOperation(
Token::EQ_STRICT, factory()->NewVariableProxy(temp),
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
Expression* value = factory()->NewConditional(
is_undefined, node->value(), factory()->NewVariableProxy(temp),
RelocInfo::kNoPosition);
RecurseIntoSubpattern(node->target(), value);
}
......
......@@ -691,8 +691,10 @@ class ParserBase : public Traits {
ArrowFormalParametersProduction = 1 << 4,
StandardProductions = (ExpressionProduction | BindingPatternProduction |
AssignmentPatternProduction),
PatternProductions =
BindingPatternProduction | AssignmentPatternProduction,
AllProductions = (StandardProductions | FormalParametersProduction |
ArrowFormalParametersProduction)
ArrowFormalParametersProduction),
};
void Accumulate(const ExpressionClassifier& inner,
......@@ -730,6 +732,18 @@ class ParserBase : public Traits {
}
}
void AccumulateReclassifyingAsPattern(const ExpressionClassifier& inner) {
Accumulate(inner, AllProductions & ~PatternProductions);
if (!inner.is_valid_expression()) {
if (is_valid_binding_pattern()) {
binding_pattern_error_ = inner.expression_error();
}
if (is_valid_assignment_pattern()) {
assignment_pattern_error_ = inner.expression_error();
}
}
}
private:
Error expression_error_;
Error binding_pattern_error_;
......@@ -803,6 +817,12 @@ class ParserBase : public Traits {
}
}
void ExpressionUnexpectedToken(ExpressionClassifier* classifier) {
classifier->RecordExpressionError(scanner()->peek_location(),
MessageTemplate::kUnexpectedToken,
Token::String(peek()));
}
void BindingPatternUnexpectedToken(ExpressionClassifier* classifier) {
classifier->RecordBindingPatternError(scanner()->peek_location(),
MessageTemplate::kUnexpectedToken,
......@@ -2673,8 +2693,21 @@ ParserBase<Traits>::ParsePropertyDefinition(
this->is_generator())) {
DCHECK(!*is_computed_name);
DCHECK(!is_static);
value = this->ExpressionFromIdentifier(name, next_beg_pos, next_end_pos,
scope_, factory());
ExpressionT lhs = this->ExpressionFromIdentifier(
name, next_beg_pos, next_end_pos, scope_, factory());
if (peek() == Token::ASSIGN) {
this->ExpressionUnexpectedToken(classifier);
Consume(Token::ASSIGN);
ExpressionClassifier rhs_classifier;
ExpressionT rhs = this->ParseAssignmentExpression(
true, &rhs_classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
classifier->AccumulateReclassifyingAsPattern(rhs_classifier);
value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs,
RelocInfo::kNoPosition);
} else {
value = lhs;
}
return factory()->NewObjectLiteralProperty(
name_expression, value, ObjectLiteralProperty::COMPUTED, false, false);
......@@ -2895,9 +2928,17 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
expression = this->MarkExpressionAsAssigned(expression);
Token::Value op = Next(); // Get assignment operator.
if (op != Token::ASSIGN) {
classifier->RecordBindingPatternError(scanner()->location(),
MessageTemplate::kUnexpectedToken,
Token::String(op));
}
int pos = position();
ExpressionClassifier rhs_classifier;
ExpressionT right =
this->ParseAssignmentExpression(accept_IN, classifier, CHECK_OK);
this->ParseAssignmentExpression(accept_IN, &rhs_classifier, CHECK_OK);
classifier->AccumulateReclassifyingAsPattern(rhs_classifier);
// TODO(1231235): We try to estimate the set of properties set by
// constructors. We define a new property whenever there is an
......
......@@ -6367,21 +6367,35 @@ TEST(DestructuringPositiveTests) {
const char* data[] = {
"a",
"{ x : y }",
"{ x : y = 1 }",
"[a]",
"[a = 1]",
"[a,b,c]",
"[a, b = 42, c]",
"{ x : x, y : y }",
"{ x : x = 1, y : y }",
"{ x : x, y : y = 42 }",
"[]",
"{}",
"[{x:x, y:y}, [a,b,c]]",
"[{x:x = 1, y:y = 2}, [a = 3, b = 4, c = 5]]",
"{x}",
"{x, y}",
"{x = 42, y = 15}",
"[a,,b]",
"{42 : x}",
"{42 : x = 42}",
"{42e-2 : x}",
"{42e-2 : x = 42}",
"{'hi' : x}",
"{'hi' : x = 42}",
"{var: x}",
"{var: x = 42}",
"{}",
NULL};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyDestructuring};
static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals,
kAllowHarmonyDestructuring};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
......@@ -6389,7 +6403,8 @@ TEST(DestructuringPositiveTests) {
TEST(DestructuringNegativeTests) {
i::FLAG_harmony_destructuring = true;
static const ParserFlag always_flags[] = {kAllowHarmonyDestructuring};
static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals,
kAllowHarmonyDestructuring};
{ // All modes.
const char* context_data[][2] = {{"'use strict'; let ", " = {};"},
......@@ -6441,6 +6456,10 @@ TEST(DestructuringNegativeTests) {
"var",
"[var]",
"{x : {y : var}}",
"{x : x = a+}",
"{x : x = (a+)}",
"{x : x += a}",
"{m() {} = 0}",
NULL};
// clang-format on
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
......
......@@ -14,7 +14,7 @@
var sum = 0;
for(var {z} = { z : 3 }; z != 0; z--) {
for (var {z} = { z : 3 }; z != 0; z--) {
sum += z;
}
assertEquals(6, sum);
......@@ -41,6 +41,79 @@
}());
(function TestObjectLiteralPatternInitializers() {
var { x : x, y : y = 2 } = { x : 1 };
assertEquals(1, x);
assertEquals(2, y);
var {z = 3} = {};
assertEquals(3, z);
var sum = 0;
for (var {z = 3} = {}; z != 0; z--) {
sum += z;
}
assertEquals(6, sum);
var log = [];
var o = {
get x() {
log.push("x");
return undefined;
},
get y() {
log.push("y");
return {
get z() { log.push("z"); return undefined; }
}
}
};
var { x : x0 = 0, y : { z : z1 = 1}, x : x1 = 0} = o;
assertSame(0, x0);
assertSame(1, z1);
assertSame(0, x1);
assertArrayEquals(["x", "y", "z", "x"], log);
}());
(function TestObjectLiteralPatternLexicalInitializers() {
'use strict';
let { x : x, y : y = 2 } = { x : 1 };
assertEquals(1, x);
assertEquals(2, y);
let {z = 3} = {};
assertEquals(3, z);
let log = [];
let o = {
get x() {
log.push("x");
return undefined;
},
get y() {
log.push("y");
return {
get z() { log.push("z"); return undefined; }
}
}
};
let { x : x0 = 0, y : { z : z1 = 1 }, x : x1 = 5} = o;
assertSame(0, x0);
assertSame(1, z1);
assertSame(5, x1);
assertArrayEquals(["x", "y", "z", "x"], log);
let sum = 0;
for (let {x = 0, z = 3} = {}; z != 0; z--) {
assertEquals(0, x);
sum += z;
}
assertEquals(6, sum);
}());
(function TestObjectLiteralPatternLexical() {
'use strict';
let { x : x, y : y } = { x : 1, y : 2 };
......@@ -70,7 +143,7 @@
assertArrayEquals(["x", "y", "z", "x"], log);
let sum = 0;
for(let {x, z} = { x : 0, z : 3 }; z != 0; z--) {
for (let {x, z} = { x : 0, z : 3 }; z != 0; z--) {
assertEquals(0, x);
sum += z;
}
......@@ -90,8 +163,7 @@
const {z} = { z : 3 };
assertEquals(3, z);
for(const {x, z} = { x : 0, z : 3 }; z != 3 || x != 0;) {
for (const {x, z} = { x : 0, z : 3 }; z != 3 || x != 0;) {
assertTrue(false);
}
}());
......@@ -130,6 +202,87 @@
}());
(function TestTDZInIntializers() {
'use strict';
{
let {x, y = x} = {x : 42, y : 27};
assertSame(42, x);
assertSame(27, y);
}
{
let {x, y = x + 1} = { x : 42 };
assertSame(42, x);
assertSame(43, y);
}
assertThrows(function() {
let {x = y, y} = { y : 42 };
}, ReferenceError);
{
let {x, y = eval("x+1")} = {x:42};
assertEquals(42, x);
assertEquals(43, y);
}
{
let {x = function() {return y+1;}, y} = {y:42};
assertEquals(43, x());
assertEquals(42, y);
}
{
let {x = function() {return eval("y+1");}, y} = {y:42};
assertEquals(43, x());
assertEquals(42, y);
}
}());
(function TestSideEffectsInInitializers() {
var callCount = 0;
function f(v) { callCount++; return v; }
callCount = 0;
var { x = f(42) } = { x : 27 };
assertSame(27, x);
assertEquals(0, callCount);
callCount = 0;
var { x = f(42) } = {};
assertSame(42, x);
assertEquals(1, callCount);
}());
(function TestMultipleAccesses() {
assertThrows(
"'use strict';"+
"const {x,x} = {x:1};",
SyntaxError);
assertThrows(
"'use strict';"+
"let {x,x} = {x:1};",
SyntaxError);
(function() {
var {x,x = 2} = {x : 1};
assertSame(1, x);
}());
assertThrows(function () {
'use strict';
let {x = (function() { x = 2; }())} = {};
}, ReferenceError);
(function() {
'use strict';
let {x = (function() { x = 2; }())} = {x:1};
assertSame(1, x);
}());
}());
(function TestExceptions() {
for (var val of [null, undefined]) {
assertThrows(function() { var {} = val; }, TypeError);
......
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