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:
parent
607c2c3293
commit
8d5a267b19
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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(
|
||||||
|
@ -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 ", "{}"},
|
||||||
|
@ -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', {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user