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