Refactor object/class literal property name parsing

This patch arranges that property names are parsed in a single pass,
reporting the name as well as the type of the property, instead of
parsing qualifiers like 'static' or 'get' initially as names and then
re-parsing. This change is easier to reason about, very slightly (4%)
faster in some cases (although slower in other, less common ones, though
this slowdown will be fixed in an upcoming patch), and is a prerequisite
for separating the parsing of object and class literal properties, which
will become increasingly important as ECMAScript adds more class features.

This is a reland of https://codereview.chromium.org/2278153004/,
which fixes the issue causing the revert and adds more tests.

Review-Url: https://codereview.chromium.org/2300503002
Cr-Commit-Position: refs/heads/master@{#39056}
This commit is contained in:
bakkot 2016-08-31 14:13:11 -07:00 committed by Commit bot
parent 607c2c3293
commit 8d5a267b19
5 changed files with 412 additions and 148 deletions

View File

@ -1108,13 +1108,25 @@ class ParserBase {
ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier, ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier,
bool* ok); bool* ok);
ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok); ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok);
ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
enum class PropertyKind {
kAccessorProperty,
kValueProperty,
kShorthandProperty,
kMethodProperty,
kNotSet
};
bool SetPropertyKindFromToken(Token::Value token, PropertyKind* kind);
ExpressionT ParsePropertyName(IdentifierT* name, PropertyKind* kind,
bool in_class, bool* is_generator, bool* is_get,
bool* is_set, bool* is_async, bool* is_static,
bool* is_computed_name, bool* is_computed_name,
ExpressionClassifier* classifier, bool* ok); ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseObjectLiteral(ExpressionClassifier* classifier, bool* ok); ExpressionT ParseObjectLiteral(ExpressionClassifier* classifier, bool* ok);
ObjectLiteralPropertyT ParsePropertyDefinition( ObjectLiteralPropertyT ParsePropertyDefinition(
ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
MethodKind kind, bool* is_computed_name, bool* has_seen_constructor, bool* is_computed_name, bool* has_seen_constructor,
ExpressionClassifier* classifier, IdentifierT* name, bool* ok); ExpressionClassifier* classifier, IdentifierT* name, bool* ok);
typename Types::ExpressionList ParseArguments( typename Types::ExpressionList ParseArguments(
Scanner::Location* first_spread_pos, bool maybe_arrow, Scanner::Location* first_spread_pos, bool maybe_arrow,
@ -1213,13 +1225,6 @@ class ParserBase {
return Call::NOT_EVAL; return Call::NOT_EVAL;
} }
// Used to validate property names in object literals and class literals
enum PropertyKind {
kAccessorProperty,
kValueProperty,
kMethodProperty
};
class ObjectLiteralCheckerBase { class ObjectLiteralCheckerBase {
public: public:
explicit ObjectLiteralCheckerBase(ParserBase* parser) : parser_(parser) {} explicit ObjectLiteralCheckerBase(ParserBase* parser) : parser_(parser) {}
@ -1853,13 +1858,104 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral(
return result; return result;
} }
template <class Impl>
bool ParserBase<Impl>::SetPropertyKindFromToken(Token::Value token,
PropertyKind* kind) {
// This returns true, setting the property kind, iff the given token is one
// which must occur after a property name, indicating that the previous token
// was in fact a name and not a modifier (like the "get" in "get x").
switch (token) {
case Token::COLON:
*kind = PropertyKind::kValueProperty;
return true;
case Token::COMMA:
case Token::RBRACE:
case Token::ASSIGN:
*kind = PropertyKind::kShorthandProperty;
return true;
case Token::LPAREN:
*kind = PropertyKind::kMethodProperty;
return true;
default:
break;
}
return false;
}
template <class Impl> template <class Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
IdentifierT* name, bool* is_get, bool* is_set, bool* is_computed_name, IdentifierT* name, PropertyKind* kind, bool in_class, bool* is_generator,
ExpressionClassifier* classifier, bool* ok) { bool* is_get, bool* is_set, bool* is_async, bool* is_static,
bool* is_computed_name, ExpressionClassifier* classifier, bool* ok) {
DCHECK(*kind == PropertyKind::kNotSet);
DCHECK(!*is_generator);
DCHECK(!*is_get);
DCHECK(!*is_set);
DCHECK(!*is_async);
DCHECK(!*is_static);
DCHECK(!*is_computed_name);
*is_generator = Check(Token::MUL);
if (*is_generator) {
*kind = PropertyKind::kMethodProperty;
}
Token::Value token = peek(); Token::Value token = peek();
int pos = peek_position(); int pos = peek_position();
if (in_class && !*is_generator && token == Token::STATIC) {
Consume(Token::STATIC);
*kind = PropertyKind::kMethodProperty;
if (peek() == Token::LPAREN) {
*name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static'
return factory()->NewStringLiteral(*name, pos);
}
*is_generator = Check(Token::MUL);
*is_static = true;
token = peek();
pos = peek_position();
}
if (allow_harmony_async_await() && !*is_generator && token == Token::ASYNC &&
!scanner()->HasAnyLineTerminatorAfterNext()) {
Consume(Token::ASYNC);
token = peek();
if (SetPropertyKindFromToken(token, kind)) {
*name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'async'
if (fni_ != nullptr) {
impl()->PushLiteralName(fni_, *name);
}
return factory()->NewStringLiteral(*name, pos);
}
*kind = PropertyKind::kMethodProperty;
*is_async = true;
pos = peek_position();
}
if (token == Token::IDENTIFIER && !*is_generator && !*is_async) {
// This is checking for 'get' and 'set' in particular. Any other identifier
// must be the property name, and so SetPropertyKindFromToken will return
// true (unless there's a syntax error).
Consume(Token::IDENTIFIER);
token = peek();
if (SetPropertyKindFromToken(token, kind)) {
*name = impl()->GetSymbol();
if (fni_ != nullptr) {
impl()->PushLiteralName(fni_, *name);
}
return factory()->NewStringLiteral(*name, pos);
}
scanner()->IsGetOrSet(is_get, is_set);
if (!*is_get && !*is_set) { // TODO(bakkot) have IsGetOrSet return a bool
Token::Value next = Next();
ReportUnexpectedToken(next);
*ok = false;
return impl()->EmptyExpression();
}
*kind = PropertyKind::kAccessorProperty;
pos = peek_position();
}
// For non computed property names we normalize the name a bit: // For non computed property names we normalize the name a bit:
// //
// "12" -> 12 // "12" -> 12
@ -1869,6 +1965,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
// //
// This is important because we use the property name as a key in a hash // This is important because we use the property name as a key in a hash
// table when we compute constant properties. // table when we compute constant properties.
ExpressionT expression = impl()->EmptyExpression();
switch (token) { switch (token) {
case Token::STRING: case Token::STRING:
Consume(Token::STRING); Consume(Token::STRING);
@ -1886,24 +1983,45 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
break; break;
case Token::LBRACK: { case Token::LBRACK: {
*name = impl()->EmptyIdentifier();
*is_computed_name = true; *is_computed_name = true;
Consume(Token::LBRACK); Consume(Token::LBRACK);
ExpressionClassifier computed_name_classifier(this); ExpressionClassifier computed_name_classifier(this);
ExpressionT expression = expression =
ParseAssignmentExpression(true, &computed_name_classifier, CHECK_OK); ParseAssignmentExpression(true, &computed_name_classifier, CHECK_OK);
impl()->RewriteNonPattern(&computed_name_classifier, CHECK_OK); impl()->RewriteNonPattern(&computed_name_classifier, CHECK_OK);
classifier->AccumulateFormalParameterContainmentErrors( classifier->AccumulateFormalParameterContainmentErrors(
&computed_name_classifier); &computed_name_classifier);
Expect(Token::RBRACK, CHECK_OK); Expect(Token::RBRACK, CHECK_OK);
return expression; break;
} }
default: default:
*name = ParseIdentifierName(CHECK_OK); *name = ParseIdentifierName(CHECK_OK);
scanner()->IsGetOrSet(is_get, is_set);
break; break;
} }
if (*kind == PropertyKind::kNotSet) {
if (!SetPropertyKindFromToken(peek(), kind) ||
(*kind == PropertyKind::kShorthandProperty &&
!Token::IsIdentifier(token, language_mode(), this->is_generator(),
parsing_module_ || is_async_function()))) {
Token::Value next = Next();
ReportUnexpectedToken(next);
*ok = false;
return impl()->EmptyExpression();
}
}
DCHECK(*kind != PropertyKind::kNotSet);
if (*is_computed_name) {
return expression;
}
if (fni_ != nullptr) {
impl()->PushLiteralName(fni_, *name);
}
uint32_t index; uint32_t index;
return impl()->IsArrayIndex(*name, &index) return impl()->IsArrayIndex(*name, &index)
? factory()->NewNumberLiteral(index, pos) ? factory()->NewNumberLiteral(index, pos)
@ -1912,46 +2030,43 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
template <typename Impl> template <typename Impl>
typename ParserBase<Impl>::ObjectLiteralPropertyT typename ParserBase<Impl>::ObjectLiteralPropertyT
ParserBase<Impl>::ParsePropertyDefinition( ParserBase<Impl>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, bool in_class, bool has_extends,
MethodKind method_kind, bool* is_computed_name, bool* has_seen_constructor, bool* is_computed_name,
ExpressionClassifier* classifier, IdentifierT* name, bool* ok) { bool* has_seen_constructor,
DCHECK(!in_class || IsStaticMethod(method_kind) || ExpressionClassifier* classifier,
has_seen_constructor != nullptr); IdentifierT* name, bool* ok) {
DCHECK(!in_class || has_seen_constructor != nullptr);
bool is_get = false; bool is_get = false;
bool is_set = false; bool is_set = false;
bool is_generator = Check(Token::MUL); bool is_generator = false;
bool is_async = false; bool is_async = false;
const bool is_static = IsStaticMethod(method_kind); bool is_static = false;
PropertyKind kind = PropertyKind::kNotSet;
Token::Value name_token = peek(); Token::Value name_token = peek();
if (is_generator) {
method_kind |= MethodKind::kGenerator;
} else if (allow_harmony_async_await() && name_token == Token::ASYNC &&
!scanner()->HasAnyLineTerminatorAfterNext() &&
PeekAhead() != Token::LPAREN && PeekAhead()) {
is_async = true;
}
int next_beg_pos = scanner()->peek_location().beg_pos; int next_beg_pos = scanner()->peek_location().beg_pos;
int next_end_pos = scanner()->peek_location().end_pos; int next_end_pos = scanner()->peek_location().end_pos;
ExpressionT name_expression = ExpressionT name_expression =
ParsePropertyName(name, &is_get, &is_set, is_computed_name, classifier, ParsePropertyName(name, &kind, in_class, &is_generator, &is_get, &is_set,
&is_async, &is_static, is_computed_name, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (fni_ != nullptr && !*is_computed_name) { switch (kind) {
impl()->PushLiteralName(fni_, *name); case PropertyKind::kValueProperty: {
if (in_class) {
Token::Value next = Next();
ReportUnexpectedToken(next);
*ok = false;
return impl()->EmptyObjectLiteralProperty();
} }
if (!in_class && !is_generator) { DCHECK(!is_get && !is_set && !is_generator && !is_async && !is_static);
DCHECK(!IsStaticMethod(method_kind));
if (peek() == Token::COLON) {
// PropertyDefinition
// PropertyName ':' AssignmentExpression
if (!*is_computed_name) { if (!*is_computed_name) {
checker->CheckProperty(name_token, kValueProperty, MethodKind::kNormal, checker->CheckProperty(name_token, PropertyKind::kValueProperty,
classifier, MethodKind::kNormal, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
} }
Consume(Token::COLON); Consume(Token::COLON);
@ -1965,16 +2080,23 @@ ParserBase<Impl>::ParsePropertyDefinition(
is_static, *is_computed_name); is_static, *is_computed_name);
} }
if (Token::IsIdentifier(name_token, language_mode(), this->is_generator(), case PropertyKind::kShorthandProperty: {
parsing_module_ || is_async_function()) &&
(peek() == Token::COMMA || peek() == Token::RBRACE ||
peek() == Token::ASSIGN)) {
// PropertyDefinition // PropertyDefinition
// IdentifierReference // IdentifierReference
// CoverInitializedName // CoverInitializedName
// //
// CoverInitializedName // CoverInitializedName
// IdentifierReference Initializer? // IdentifierReference Initializer?
if (in_class) {
Token::Value next = Next();
ReportUnexpectedToken(next);
*ok = false;
return impl()->EmptyObjectLiteralProperty();
}
DCHECK(!is_get && !is_set && !is_generator && !is_async && !is_static &&
!*is_computed_name);
if (classifier->duplicate_finder() != nullptr && if (classifier->duplicate_finder() != nullptr &&
scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) { scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) {
classifier->RecordDuplicateFormalParameterError(scanner()->location()); classifier->RecordDuplicateFormalParameterError(scanner()->location());
@ -2023,30 +2145,32 @@ ParserBase<Impl>::ParsePropertyDefinition(
name_expression, value, ObjectLiteralProperty::COMPUTED, is_static, name_expression, value, ObjectLiteralProperty::COMPUTED, is_static,
false); false);
} }
case PropertyKind::kMethodProperty: {
DCHECK(!is_get && !is_set);
MethodKind method_kind = MethodKind::kNormal;
if (is_generator) {
method_kind |= MethodKind::kGenerator;
}
if (is_async) {
method_kind |= MethodKind::kAsync;
}
if (is_static) {
method_kind |= MethodKind::kStatic;
} }
// Method definitions are never valid in patterns. // MethodDefinition
// PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
// '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
classifier->RecordPatternError( classifier->RecordPatternError(
Scanner::Location(next_beg_pos, scanner()->location().end_pos), Scanner::Location(next_beg_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget); MessageTemplate::kInvalidDestructuringTarget);
if (is_async && !IsSpecialMethod(method_kind)) {
DCHECK(!is_get);
DCHECK(!is_set);
bool dont_care;
name_expression = ParsePropertyName(
name, &dont_care, &dont_care, is_computed_name, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
method_kind |= MethodKind::kAsync;
}
if (is_generator || peek() == Token::LPAREN) {
// MethodDefinition
// PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
// '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
if (!*is_computed_name) { if (!*is_computed_name) {
checker->CheckProperty(name_token, kMethodProperty, method_kind, checker->CheckProperty(name_token, PropertyKind::kMethodProperty,
classifier, method_kind, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
} }
@ -2055,8 +2179,7 @@ ParserBase<Impl>::ParsePropertyDefinition(
: is_async ? FunctionKind::kAsyncConciseMethod : is_async ? FunctionKind::kAsyncConciseMethod
: FunctionKind::kConciseMethod; : FunctionKind::kConciseMethod;
if (in_class && !IsStaticMethod(method_kind) && if (in_class && !is_static && impl()->IsConstructor(*name)) {
impl()->IsConstructor(*name)) {
*has_seen_constructor = true; *has_seen_constructor = true;
kind = has_extends ? FunctionKind::kSubclassConstructor kind = has_extends ? FunctionKind::kSubclassConstructor
: FunctionKind::kBaseConstructor; : FunctionKind::kBaseConstructor;
@ -2064,48 +2187,38 @@ ParserBase<Impl>::ParsePropertyDefinition(
ExpressionT value = impl()->ParseFunctionLiteral( ExpressionT value = impl()->ParseFunctionLiteral(
*name, scanner()->location(), kSkipFunctionNameCheck, kind, *name, scanner()->location(), kSkipFunctionNameCheck, kind,
kNoSourcePosition, FunctionLiteral::kAccessorOrMethod, language_mode(), kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
return factory()->NewObjectLiteralProperty(name_expression, value, return factory()->NewObjectLiteralProperty(
ObjectLiteralProperty::COMPUTED, name_expression, value, ObjectLiteralProperty::COMPUTED, is_static,
is_static, *is_computed_name); *is_computed_name);
} }
if (in_class && name_token == Token::STATIC && IsNormalMethod(method_kind)) { case PropertyKind::kAccessorProperty: {
// ClassElement (static) DCHECK((is_get || is_set) && !is_generator && !is_async);
// 'static' MethodDefinition
*name = impl()->EmptyIdentifier(); MethodKind method_kind = MethodKind::kNormal;
ObjectLiteralPropertyT property = ParsePropertyDefinition( if (is_static) {
checker, true, has_extends, MethodKind::kStatic, is_computed_name, DCHECK(in_class);
nullptr, classifier, name, ok); method_kind |= MethodKind::kStatic;
impl()->RewriteNonPattern(classifier, ok);
return property;
} }
if (is_get || is_set) { classifier->RecordPatternError(
// MethodDefinition (Accessors) Scanner::Location(next_beg_pos, scanner()->location().end_pos),
// get PropertyName '(' ')' '{' FunctionBody '}' MessageTemplate::kInvalidDestructuringTarget);
// set PropertyName '(' PropertySetParameterList ')' '{' FunctionBody '}'
*name = impl()->EmptyIdentifier();
bool dont_care = false;
name_token = peek();
name_expression = ParsePropertyName(
name, &dont_care, &dont_care, is_computed_name, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (!*is_computed_name) { if (!*is_computed_name) {
checker->CheckProperty(name_token, kAccessorProperty, method_kind, checker->CheckProperty(name_token, PropertyKind::kAccessorProperty,
classifier, method_kind, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
} }
FunctionLiteralT value = impl()->ParseFunctionLiteral( typename Types::FunctionLiteral value = impl()->ParseFunctionLiteral(
*name, scanner()->location(), kSkipFunctionNameCheck, *name, scanner()->location(), kSkipFunctionNameCheck,
is_get ? FunctionKind::kGetterFunction : FunctionKind::kSetterFunction, is_get ? FunctionKind::kGetterFunction
kNoSourcePosition, FunctionLiteral::kAccessorOrMethod, language_mode(), : FunctionKind::kSetterFunction,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
// Make sure the name expression is a string since we need a Name for // Make sure the name expression is a string since we need a Name for
// Runtime_DefineAccessorPropertyUnchecked and since we can determine this // Runtime_DefineAccessorPropertyUnchecked and since we can determine this
@ -2116,14 +2229,15 @@ ParserBase<Impl>::ParsePropertyDefinition(
} }
return factory()->NewObjectLiteralProperty( return factory()->NewObjectLiteralProperty(
name_expression, value, name_expression, value, is_get ? ObjectLiteralProperty::GETTER
is_get ? ObjectLiteralProperty::GETTER : ObjectLiteralProperty::SETTER, : ObjectLiteralProperty::SETTER,
is_static, *is_computed_name); is_static, *is_computed_name);
} }
Token::Value next = Next(); case PropertyKind::kNotSet:
ReportUnexpectedToken(next); UNREACHABLE();
*ok = false; }
UNREACHABLE();
return impl()->EmptyObjectLiteralProperty(); return impl()->EmptyObjectLiteralProperty();
} }
@ -2149,8 +2263,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral(
bool is_computed_name = false; bool is_computed_name = false;
IdentifierT name = impl()->EmptyIdentifier(); IdentifierT name = impl()->EmptyIdentifier();
ObjectLiteralPropertyT property = ParsePropertyDefinition( ObjectLiteralPropertyT property = ParsePropertyDefinition(
&checker, in_class, has_extends, MethodKind::kNormal, &is_computed_name, &checker, in_class, has_extends, &is_computed_name, nullptr, classifier,
NULL, classifier, &name, CHECK_OK); &name, CHECK_OK);
if (is_computed_name) { if (is_computed_name) {
has_computed_names = true; has_computed_names = true;
@ -3640,11 +3754,12 @@ void ParserBase<Impl>::ObjectLiteralChecker::CheckProperty(
Token::Value property, PropertyKind type, MethodKind method_type, Token::Value property, PropertyKind type, MethodKind method_type,
ExpressionClassifier* classifier, bool* ok) { ExpressionClassifier* classifier, bool* ok) {
DCHECK(!IsStaticMethod(method_type)); DCHECK(!IsStaticMethod(method_type));
DCHECK(!IsSpecialMethod(method_type) || type == kMethodProperty); DCHECK(!IsSpecialMethod(method_type) ||
type == PropertyKind::kMethodProperty);
if (property == Token::SMI || property == Token::NUMBER) return; if (property == Token::SMI || property == Token::NUMBER) return;
if (type == kValueProperty && IsProto()) { if (type == PropertyKind::kValueProperty && IsProto()) {
if (has_seen_proto_) { if (has_seen_proto_) {
classifier->RecordExpressionError(this->scanner()->location(), classifier->RecordExpressionError(this->scanner()->location(),
MessageTemplate::kDuplicateProto); MessageTemplate::kDuplicateProto);
@ -3658,7 +3773,8 @@ template <typename Impl>
void ParserBase<Impl>::ClassLiteralChecker::CheckProperty( void ParserBase<Impl>::ClassLiteralChecker::CheckProperty(
Token::Value property, PropertyKind type, MethodKind method_type, Token::Value property, PropertyKind type, MethodKind method_type,
ExpressionClassifier* classifier, bool* ok) { ExpressionClassifier* classifier, bool* ok) {
DCHECK(type == kMethodProperty || type == kAccessorProperty); DCHECK(type == PropertyKind::kMethodProperty ||
type == PropertyKind::kAccessorProperty);
if (property == Token::SMI || property == Token::NUMBER) return; if (property == Token::SMI || property == Token::NUMBER) return;
@ -3671,7 +3787,7 @@ void ParserBase<Impl>::ClassLiteralChecker::CheckProperty(
} else if (IsConstructor()) { } else if (IsConstructor()) {
const bool is_generator = IsGeneratorMethod(method_type); const bool is_generator = IsGeneratorMethod(method_type);
const bool is_async = IsAsyncMethod(method_type); const bool is_async = IsAsyncMethod(method_type);
if (is_generator || is_async || type == kAccessorProperty) { if (is_generator || is_async || type == PropertyKind::kAccessorProperty) {
MessageTemplate::Template msg = MessageTemplate::Template msg =
is_generator ? MessageTemplate::kConstructorIsGenerator is_generator ? MessageTemplate::kConstructorIsGenerator
: is_async ? MessageTemplate::kConstructorIsAsync : is_async ? MessageTemplate::kConstructorIsAsync

View File

@ -4707,7 +4707,7 @@ Expression* Parser::ParseClassLiteral(ExpressionClassifier* classifier,
ExpressionClassifier property_classifier(this); ExpressionClassifier property_classifier(this);
const AstRawString* property_name = nullptr; const AstRawString* property_name = nullptr;
ObjectLiteral::Property* property = ParsePropertyDefinition( ObjectLiteral::Property* property = ParsePropertyDefinition(
&checker, in_class, has_extends, MethodKind::kNormal, &is_computed_name, &checker, in_class, has_extends, &is_computed_name,
&has_seen_constructor, &property_classifier, &property_name, CHECK_OK); &has_seen_constructor, &property_classifier, &property_name, CHECK_OK);
RewriteNonPattern(&property_classifier, CHECK_OK); RewriteNonPattern(&property_classifier, CHECK_OK);
if (classifier != nullptr) { if (classifier != nullptr) {

View File

@ -1184,9 +1184,9 @@ PreParserExpression PreParser::ParseClassLiteral(
// property names here. // property names here.
Identifier name; Identifier name;
ExpressionClassifier property_classifier(this); ExpressionClassifier property_classifier(this);
ParsePropertyDefinition( ParsePropertyDefinition(&checker, in_class, has_extends, &is_computed_name,
&checker, in_class, has_extends, MethodKind::kNormal, &is_computed_name, &has_seen_constructor, &property_classifier, &name,
&has_seen_constructor, &property_classifier, &name, CHECK_OK); CHECK_OK);
ValidateExpression(&property_classifier, CHECK_OK); ValidateExpression(&property_classifier, CHECK_OK);
if (classifier != nullptr) { if (classifier != nullptr) {
classifier->AccumulateFormalParameterContainmentErrors( classifier->AccumulateFormalParameterContainmentErrors(

View File

@ -2896,6 +2896,7 @@ TEST(StrictObjectLiteralChecking) {
TEST(ErrorsObjectLiteralChecking) { TEST(ErrorsObjectLiteralChecking) {
// clang-format off
const char* context_data[][2] = { const char* context_data[][2] = {
{"\"use strict\"; var myobject = {", "};"}, {"\"use strict\"; var myobject = {", "};"},
{"var myobject = {", "};"}, {"var myobject = {", "};"},
@ -2912,14 +2913,60 @@ TEST(ErrorsObjectLiteralChecking) {
// Parsing FunctionLiteral for getter or setter fails // Parsing FunctionLiteral for getter or setter fails
"get foo( +", "get foo( +",
"get foo() \"error\"", "get foo() \"error\"",
// Various forbidden forms
"static x: 0",
"static x(){}",
"static async x(){}",
"static get x(){}",
"static get x : 0",
"static x",
"static 0",
"*x: 0",
"*x",
"*get x(){}",
"*set x(y){}",
"get *x(){}",
"set *x(y){}",
"get x*(){}",
"set x*(y){}",
"x = 0",
"* *x(){}",
"x*(){}",
// This should fail without --harmony-async-await
"async x(){}",
NULL NULL
}; };
// clang-format on
RunParserSyncTest(context_data, statement_data, kError); RunParserSyncTest(context_data, statement_data, kError);
// clang-format off
const char* async_data[] = {
"static async x(){}",
"static async x : 0",
"static async get x : 0",
"async static x(){}",
"*async x(){}",
"async *x(){}",
"async x*(){}",
"async x : 0",
"async 0 : 0",
"async get x(){}",
"async get *x(){}",
"async set x(y){}",
"async get : 0",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncAwait};
RunParserSyncTest(context_data, async_data, kError, NULL, 0, always_flags,
arraysize(always_flags));
} }
TEST(NoErrorsObjectLiteralChecking) { TEST(NoErrorsObjectLiteralChecking) {
// clang-format off
const char* context_data[][2] = { const char* context_data[][2] = {
{"var myobject = {", "};"}, {"var myobject = {", "};"},
{"var myobject = {", ",};"}, {"var myobject = {", ",};"},
@ -2968,6 +3015,19 @@ TEST(NoErrorsObjectLiteralChecking) {
"1: 1, set 2(v) {}", "1: 1, set 2(v) {}",
"get: 1, get foo() {}", "get: 1, get foo() {}",
"set: 1, set foo(_) {}", "set: 1, set foo(_) {}",
// Potentially confusing cases
"get(){}",
"set(){}",
"static(){}",
"async(){}",
"*get() {}",
"*set() {}",
"*static() {}",
"*async(){}",
"get : 0",
"set : 0",
"static : 0",
"async : 0",
// Keywords, future reserved and strict future reserved are also allowed as // Keywords, future reserved and strict future reserved are also allowed as
// property names. // property names.
"if: 4", "if: 4",
@ -2977,8 +3037,28 @@ TEST(NoErrorsObjectLiteralChecking) {
"arguments: 8", "arguments: 8",
NULL NULL
}; };
// clang-format on
RunParserSyncTest(context_data, statement_data, kSuccess); RunParserSyncTest(context_data, statement_data, kSuccess);
// clang-format off
const char* async_data[] = {
"async x(){}",
"async 0(){}",
"async get(){}",
"async set(){}",
"async static(){}",
"async async(){}",
"async : 0",
"async(){}",
"*async(){}",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncAwait};
RunParserSyncTest(context_data, async_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
} }
@ -4394,6 +4474,10 @@ TEST(ClassBodyNoErrors) {
"*get() {}", "*get() {}",
"*set() {}", "*set() {}",
"static *g() {}", "static *g() {}",
"async(){}",
"*async(){}",
"static async(){}",
"static *async(){}",
// Escaped 'static' should be allowed anywhere // Escaped 'static' should be allowed anywhere
// static-as-PropertyName is. // static-as-PropertyName is.
@ -4409,6 +4493,27 @@ TEST(ClassBodyNoErrors) {
// clang-format on // clang-format on
RunParserSyncTest(context_data, class_body_data, kSuccess); RunParserSyncTest(context_data, class_body_data, kSuccess);
// clang-format off
const char* async_data[] = {
"static async x(){}",
"static async(){}",
"static *async(){}",
"async x(){}",
"async 0(){}",
"async get(){}",
"async set(){}",
"async static(){}",
"async async(){}",
"async(){}",
"*async(){}",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncAwait};
RunParserSyncTest(context_data, async_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
} }
@ -4522,6 +4627,43 @@ TEST(ClassDeclarationErrors) {
RunParserSyncTest(context_data, class_data, kError); RunParserSyncTest(context_data, class_data, kError);
} }
TEST(ClassAsyncErrors) {
// clang-format off
const char* context_data[][2] = {{"(class {", "});"},
{"(class extends Base {", "});"},
{"class C {", "}"},
{"class C extends Base {", "}"},
{NULL, NULL}};
const char* async_data[] = {
"*async x(){}",
"async *(){}",
"async *x(){}",
"async get x(){}",
"async set x(y){}",
"async x : 0",
"async : 0",
"async static x(){}",
"static *async x(){}",
"static async *(){}",
"static async *x(){}",
"static async get x(){}",
"static async set x(y){}",
"static async x : 0",
"static async : 0",
NULL
};
// clang-format on
// All of these are illegal whether or not async functions are permitted,
// although for different reasons.
RunParserSyncTest(context_data, async_data, kError);
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncAwait};
RunParserSyncTest(context_data, async_data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(ClassNameErrors) { TEST(ClassNameErrors) {
const char* context_data[][2] = {{"class ", "{}"}, const char* context_data[][2] = {{"class ", "{}"},

View File

@ -530,6 +530,12 @@
'es6/collections': [PASS, ['arch == ia32', FAST_VARIANTS]], 'es6/collections': [PASS, ['arch == ia32', FAST_VARIANTS]],
}], # 'system == windows' }], # 'system == windows'
##############################################################################
['system == macos', {
# BUG(v8:5333)
'big-object-literal': [SKIP],
}], # 'system == macos'
############################################################################## ##############################################################################
['arch == s390 or arch == s390x', { ['arch == s390 or arch == s390x', {