Commit c04ef1ff authored by adamk's avatar adamk Committed by Commit bot

Fix handling of escaped "let" and "static" tokens

The old handling of escaped keywords erroneously treated escaped versions
of "let" and "static" as ESCAPED_KEYWORD, leading to erroneous errors in
sloppy mode. Moreover, though the class literal parsing code attempted
to fix up the parsing of escaped versions of "static" to allow it in the
right places, that code wasn't complete.

Fixing the scanner to mark escaped "static" as ESCAPED_STRICT_RESERVED_WORD
allows simplifying the class literal parsing code. A little extra code
was needed to properly handle the new treatment of escaped "let".

Note that "yield" is still broken (that is, we're overly restrictive of
escaped "yield" in sloppy mode).

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

Cr-Commit-Position: refs/heads/master@{#33396}
parent 1e0fd2a5
......@@ -733,7 +733,6 @@ class ParserBase : public Traits {
ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok);
ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
bool* is_static, bool* is_computed_name,
bool* is_identifier, bool* is_escaped_keyword,
ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseObjectLiteral(ExpressionClassifier* classifier, bool* ok);
ObjectLiteralPropertyT ParsePropertyDefinition(
......@@ -1116,7 +1115,9 @@ ParserBase<Traits>::ParseAndClassifyIdentifier(ExpressionClassifier* classifier,
*ok = false;
return Traits::EmptyIdentifier();
}
if (next == Token::LET) {
if (next == Token::LET ||
(next == Token::ESCAPED_STRICT_RESERVED_WORD &&
scanner()->is_literal_contextual_keyword(CStrVector("let")))) {
classifier->RecordLetPatternError(scanner()->location(),
MessageTemplate::kLetInLexicalBinding);
}
......@@ -1557,8 +1558,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
template <class Traits>
typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName(
IdentifierT* name, bool* is_get, bool* is_set, bool* is_static,
bool* is_computed_name, bool* is_identifier, bool* is_escaped_keyword,
ExpressionClassifier* classifier, bool* ok) {
bool* is_computed_name, ExpressionClassifier* classifier, bool* ok) {
Token::Value token = peek();
int pos = peek_position();
......@@ -1601,17 +1601,11 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName(
return expression;
}
case Token::ESCAPED_KEYWORD:
*is_escaped_keyword = true;
*name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK);
break;
case Token::STATIC:
*is_static = true;
// Fall through.
default:
*is_identifier = true;
*name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK);
break;
}
......@@ -1639,21 +1633,14 @@ ParserBase<Traits>::ParsePropertyDefinition(
Token::Value name_token = peek();
int next_beg_pos = scanner()->peek_location().beg_pos;
int next_end_pos = scanner()->peek_location().end_pos;
bool is_identifier = false;
bool is_escaped_keyword = false;
ExpressionT name_expression = ParsePropertyName(
name, &is_get, &is_set, &name_is_static, is_computed_name, &is_identifier,
&is_escaped_keyword, classifier,
name, &is_get, &is_set, &name_is_static, is_computed_name, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (fni_ != nullptr && !*is_computed_name) {
this->PushLiteralName(fni_, *name);
}
bool escaped_static =
is_escaped_keyword &&
scanner()->is_literal_contextual_keyword(CStrVector("static"));
if (!in_class && !is_generator) {
DCHECK(!is_static);
......@@ -1673,7 +1660,8 @@ ParserBase<Traits>::ParsePropertyDefinition(
*is_computed_name);
}
if ((is_identifier || is_escaped_keyword) &&
if (Token::IsIdentifier(name_token, language_mode(),
this->is_generator()) &&
(peek() == Token::COMMA || peek() == Token::RBRACE ||
peek() == Token::ASSIGN)) {
// PropertyDefinition
......@@ -1682,14 +1670,6 @@ ParserBase<Traits>::ParsePropertyDefinition(
//
// CoverInitializedName
// IdentifierReference Initializer?
if (!Token::IsIdentifier(name_token, language_mode(),
this->is_generator())) {
if (!escaped_static) {
ReportUnexpectedTokenAt(scanner()->location(), name_token);
*ok = false;
return this->EmptyObjectLiteralProperty();
}
}
if (classifier->duplicate_finder() != nullptr &&
scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) {
classifier->RecordDuplicateFormalParameterError(scanner()->location());
......@@ -1727,12 +1707,6 @@ ParserBase<Traits>::ParsePropertyDefinition(
}
}
if (in_class && escaped_static && !is_static) {
ReportUnexpectedTokenAt(scanner()->location(), name_token);
*ok = false;
return this->EmptyObjectLiteralProperty();
}
// Method definitions are never valid in patterns.
classifier->RecordPatternError(
Scanner::Location(next_beg_pos, scanner()->location().end_pos),
......@@ -1791,8 +1765,8 @@ ParserBase<Traits>::ParsePropertyDefinition(
name_token = peek();
name_expression = ParsePropertyName(
name, &dont_care, &dont_care, &dont_care, is_computed_name, &dont_care,
&dont_care, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
name, &dont_care, &dont_care, &dont_care, is_computed_name, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (!*is_computed_name) {
checker->CheckProperty(name_token, kAccessorProperty, is_static,
......
......@@ -1206,7 +1206,9 @@ static Token::Value KeywordOrIdentifierToken(const uint8_t* input,
(keyword_length <= 8 || input[8] == keyword[8]) && \
(keyword_length <= 9 || input[9] == keyword[9])) { \
if (escaped) { \
return token == Token::FUTURE_STRICT_RESERVED_WORD \
/* TODO(adamk): YIELD should be handled specially. */ \
return (token == Token::FUTURE_STRICT_RESERVED_WORD || \
token == Token::LET || token == Token::STATIC) \
? Token::ESCAPED_STRICT_RESERVED_WORD \
: Token::ESCAPED_KEYWORD; \
} \
......
......@@ -4426,6 +4426,7 @@ TEST(ClassDeclarationNoErrors) {
TEST(ClassBodyNoErrors) {
// clang-format off
// Tests that parser and preparser accept valid class syntax.
const char* context_data[][2] = {{"(class {", "});"},
{"(class extends Base {", "});"},
......@@ -4448,6 +4449,8 @@ TEST(ClassBodyNoErrors) {
"; *g() {}",
"*g() {}; *h(x) {}",
"static() {}",
"get static() {}",
"set static(v) {}",
"static m() {}",
"static get x() {}",
"static set x(v) {}",
......@@ -4457,10 +4460,23 @@ TEST(ClassBodyNoErrors) {
"static get static() {}",
"static set static(v) {}",
"*static() {}",
"static *static() {}",
"*get() {}",
"*set() {}",
"static *g() {}",
// Escaped 'static' should be allowed anywhere
// static-as-PropertyName is.
"st\\u0061tic() {}",
"get st\\u0061tic() {}",
"set st\\u0061tic(v) {}",
"static st\\u0061tic() {}",
"static get st\\u0061tic() {}",
"static set st\\u0061tic(v) {}",
"*st\\u0061tic() {}",
"static *st\\u0061tic() {}",
NULL};
// clang-format on
static const ParserFlag always_flags[] = {
kAllowHarmonySloppy
......@@ -7678,6 +7694,13 @@ TEST(LetSloppyOnly) {
"for (const [let] = 1; let < 1; let++) {}",
"for (const [let] in {}) {}",
"for (const [let] of []) {}",
// Sprinkle in the escaped version too.
"let l\\u0065t = 1",
"const l\\u0065t = 1",
"let [l\\u0065t] = 1",
"const [l\\u0065t] = 1",
"for (let l\\u0065t in {}) {}",
NULL
};
// clang-format on
......@@ -7736,6 +7759,7 @@ TEST(EscapedKeywords) {
"wh\\u0069le (true) { }",
"w\\u0069th (this.scope) { }",
"(function*() { y\\u0069eld 1; })()",
"(function*() { var y\\u0069eld = 1; })()",
"var \\u0065num = 1;",
"var { \\u0065num } = {}",
......@@ -7773,7 +7797,11 @@ TEST(EscapedKeywords) {
"do { ; } wh\\u0069le (true) { }",
"(function*() { return (n++, y\\u0069eld 1); })()",
"class C { st\\u0061tic bar() {} }",
"class C { st\\u0061tic *bar() {} }",
"class C { st\\u0061tic get bar() {} }",
"class C { st\\u0061tic set bar() {} }",
// TODO(adamk): These should not be errors in sloppy mode.
"(y\\u0069eld);",
"var y\\u0069eld = 1;",
"var { y\\u0069eld } = {};",
......@@ -7799,14 +7827,14 @@ TEST(EscapedKeywords) {
};
// clang-format on
RunParserSyncTest(sloppy_context_data, let_data, kError, NULL, 0,
RunParserSyncTest(sloppy_context_data, let_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, let_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
static const ParserFlag sloppy_let_flags[] = {
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kAllowHarmonyDestructuring};
RunParserSyncTest(sloppy_context_data, let_data, kError, NULL, 0,
RunParserSyncTest(sloppy_context_data, let_data, kSuccess, NULL, 0,
sloppy_let_flags, arraysize(sloppy_let_flags));
// Non-errors in sloppy mode
......@@ -7828,6 +7856,9 @@ TEST(EscapedKeywords) {
"(publ\\u0069c);",
"var publ\\u0069c = 1;",
"var { publ\\u0069c } = {};",
"(st\\u0061tic);",
"var st\\u0061tic = 1;",
"var { st\\u0061tic } = {};",
NULL};
RunParserSyncTest(sloppy_context_data, valid_data, kSuccess, NULL, 0,
always_flags, arraysize(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