Commit a13e2298 authored by arv@chromium.org's avatar arv@chromium.org

Allow duplicate property names in classes

ES6 no longer makes duplicate properties an error. However, we
continue to treat duplicate properties in strict mode object
literals as errors. With this change we allow duplicate properties
in class bodies. We continue to flag duplicate constructors as an
error as required by ES6.

BUG=v8:3570
LOG=Y
R=marja@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#24933}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24933 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 11a6681b
...@@ -176,7 +176,8 @@ var kMessages = { ...@@ -176,7 +176,8 @@ var kMessages = {
module_export_undefined: ["Export '", "%0", "' is not defined in module"], module_export_undefined: ["Export '", "%0", "' is not defined in module"],
unexpected_super: ["'super' keyword unexpected here"], unexpected_super: ["'super' keyword unexpected here"],
extends_value_not_a_function: ["Class extends value ", "%0", " is not a function or null"], extends_value_not_a_function: ["Class extends value ", "%0", " is not a function or null"],
prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"] prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"],
duplicate_constructor: ["A class may only have one constructor"]
}; };
......
...@@ -476,6 +476,7 @@ class ParserBase : public Traits { ...@@ -476,6 +476,7 @@ class ParserBase : public Traits {
ExpressionT ParseObjectLiteral(bool* ok); ExpressionT ParseObjectLiteral(bool* ok);
ObjectLiteralPropertyT ParsePropertyDefinition(ObjectLiteralChecker* checker, ObjectLiteralPropertyT ParsePropertyDefinition(ObjectLiteralChecker* checker,
bool in_class, bool is_static, bool in_class, bool is_static,
bool* has_seen_constructor,
bool* ok); bool* ok);
typename Traits::Type::ExpressionList ParseArguments(bool* ok); typename Traits::Type::ExpressionList ParseArguments(bool* ok);
ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok); ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
...@@ -1183,10 +1184,7 @@ class PreParserTraits { ...@@ -1183,10 +1184,7 @@ class PreParserTraits {
return false; return false;
} }
bool IsConstructorProperty(PreParserExpression property) { return false; }
static PreParserExpression GetPropertyValue(PreParserExpression property) { static PreParserExpression GetPropertyValue(PreParserExpression property) {
UNREACHABLE();
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
...@@ -1925,7 +1923,9 @@ typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParsePropertyName( ...@@ -1925,7 +1923,9 @@ typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParsePropertyName(
template <class Traits> template <class Traits>
typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase< typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker, Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker,
bool in_class, bool is_static, bool* ok) { bool in_class, bool is_static,
bool* has_seen_constructor, bool* ok) {
DCHECK(!in_class || is_static || has_seen_constructor != NULL);
ExpressionT value = this->EmptyExpression(); ExpressionT value = this->EmptyExpression();
bool is_get = false; bool is_get = false;
bool is_set = false; bool is_set = false;
...@@ -1942,8 +1942,10 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase< ...@@ -1942,8 +1942,10 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
if (!in_class && !is_generator && peek() == Token::COLON) { if (!in_class && !is_generator && peek() == Token::COLON) {
// PropertyDefinition : PropertyName ':' AssignmentExpression // PropertyDefinition : PropertyName ':' AssignmentExpression
checker->CheckProperty(name_token, kValueProperty, if (checker != NULL) {
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); checker->CheckProperty(name_token, kValueProperty,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
}
Consume(Token::COLON); Consume(Token::COLON);
value = this->ParseAssignmentExpression( value = this->ParseAssignmentExpression(
true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
...@@ -1968,11 +1970,20 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase< ...@@ -1968,11 +1970,20 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
return this->EmptyObjectLiteralProperty(); return this->EmptyObjectLiteralProperty();
} }
if (*has_seen_constructor) {
ReportMessageAt(scanner()->location(), "duplicate_constructor");
*ok = false;
return this->EmptyObjectLiteralProperty();
}
*has_seen_constructor = true;
kind = FunctionKind::kNormalFunction; kind = FunctionKind::kNormalFunction;
} }
checker->CheckProperty(name_token, kValueProperty, if (checker != NULL) {
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); checker->CheckProperty(name_token, kValueProperty,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
}
value = this->ParseFunctionLiteral( value = this->ParseFunctionLiteral(
name, scanner()->location(), name, scanner()->location(),
...@@ -1983,7 +1994,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase< ...@@ -1983,7 +1994,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
} else if (in_class && name_is_static && !is_static) { } else if (in_class && name_is_static && !is_static) {
// static MethodDefinition // static MethodDefinition
return ParsePropertyDefinition(checker, true, true, ok); return ParsePropertyDefinition(checker, true, true, NULL, ok);
} else if (is_get || is_set) { } else if (is_get || is_set) {
// Accessor // Accessor
...@@ -1998,16 +2009,15 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase< ...@@ -1998,16 +2009,15 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
*ok = false; *ok = false;
return this->EmptyObjectLiteralProperty(); return this->EmptyObjectLiteralProperty();
} else if (in_class && !is_static && this->IsConstructor(name)) { } else if (in_class && !is_static && this->IsConstructor(name)) {
// ES6, spec draft rev 27, treats static get constructor as an error too.
// https://bugs.ecmascript.org/show_bug.cgi?id=3223
// TODO(arv): Update when bug is resolved.
ReportMessageAt(scanner()->location(), "constructor_special_method"); ReportMessageAt(scanner()->location(), "constructor_special_method");
*ok = false; *ok = false;
return this->EmptyObjectLiteralProperty(); return this->EmptyObjectLiteralProperty();
} }
checker->CheckProperty(name_token, if (checker != NULL) {
is_get ? kGetterProperty : kSetterProperty, checker->CheckProperty(name_token,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); is_get ? kGetterProperty : kSetterProperty,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
}
typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral( typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral(
name, scanner()->location(), name, scanner()->location(),
...@@ -2061,8 +2071,8 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral( ...@@ -2061,8 +2071,8 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
const bool in_class = false; const bool in_class = false;
const bool is_static = false; const bool is_static = false;
ObjectLiteralPropertyT property = ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
this->ParsePropertyDefinition(&checker, in_class, is_static, CHECK_OK); &checker, in_class, is_static, NULL, CHECK_OK);
// Mark top-level object literals that contain function literals and // Mark top-level object literals that contain function literals and
// pretenure the literal so it can be added as a constant function // pretenure the literal so it can be added as a constant function
...@@ -2744,22 +2754,22 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral( ...@@ -2744,22 +2754,22 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral(
scope_->SetStrictMode(STRICT); scope_->SetStrictMode(STRICT);
scope_->SetScopeName(name); scope_->SetScopeName(name);
ObjectLiteralChecker checker(this, STRICT);
typename Traits::Type::PropertyList properties = typename Traits::Type::PropertyList properties =
this->NewPropertyList(4, zone_); this->NewPropertyList(4, zone_);
ExpressionT constructor = this->EmptyExpression(); ExpressionT constructor = this->EmptyExpression();
bool has_seen_constructor = false;
Expect(Token::LBRACE, CHECK_OK); Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) { while (peek() != Token::RBRACE) {
if (Check(Token::SEMICOLON)) continue; if (Check(Token::SEMICOLON)) continue;
if (fni_ != NULL) fni_->Enter(); if (fni_ != NULL) fni_->Enter();
const bool in_class = true; const bool in_class = true;
const bool is_static = false; const bool is_static = false;
ObjectLiteralPropertyT property = bool old_has_seen_constructor = has_seen_constructor;
this->ParsePropertyDefinition(&checker, in_class, is_static, CHECK_OK); ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
NULL, in_class, is_static, &has_seen_constructor, CHECK_OK);
if (this->IsConstructorProperty(property)) { if (has_seen_constructor != old_has_seen_constructor) {
constructor = this->GetPropertyValue(property); constructor = this->GetPropertyValue(property);
} else { } else {
properties->Add(property, zone()); properties->Add(property, zone());
......
...@@ -4041,9 +4041,6 @@ TEST(ClassConstructorNoErrors) { ...@@ -4041,9 +4041,6 @@ TEST(ClassConstructorNoErrors) {
TEST(ClassMultipleConstructorErrors) { TEST(ClassMultipleConstructorErrors) {
// We currently do not allow any duplicate properties in class bodies. This
// test ensures that when we change that we still throw on duplicate
// constructors.
const char* context_data[][2] = {{"class C {", "}"}, const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"}, {"(class {", "});"},
{NULL, NULL}}; {NULL, NULL}};
...@@ -4061,9 +4058,7 @@ TEST(ClassMultipleConstructorErrors) { ...@@ -4061,9 +4058,7 @@ TEST(ClassMultipleConstructorErrors) {
} }
// TODO(arv): We should allow duplicate property names. TEST(ClassMultiplePropertyNamesNoErrors) {
// https://code.google.com/p/v8/issues/detail?id=3570
DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) {
const char* context_data[][2] = {{"class C {", "}"}, const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"}, {"(class {", "});"},
{NULL, NULL}}; {NULL, NULL}};
...@@ -4072,6 +4067,8 @@ DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) { ...@@ -4072,6 +4067,8 @@ DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) {
"constructor() {}; static constructor() {}", "constructor() {}; static constructor() {}",
"m() {}; static m() {}", "m() {}; static m() {}",
"m() {}; m() {}", "m() {}; m() {}",
"static m() {}; static m() {}",
"get m() {}; set m(_) {}; get m() {}; set m(_) {};",
NULL}; NULL};
static const ParserFlag always_flags[] = { static const ParserFlag always_flags[] = {
......
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