[parser] Relex restriction on reserved words
Some IdentifierNames are only included in the set of FutureReservedWords for strict mode code. Outside of strict mode, these IdentifierNames may be used as Identifiers. Notably, this includes their use as BindingIdentifiers in LexicalBindings. From ES2015 12.1.1 Static Semantics: Early Errors (Identifiers): > It is a Syntax Error if this phrase is contained in strict mode code > and the StringValue of IdentifierName is: "implements", "interface", > "let", "package", "private", "protected", "public", "static", or > "yield". http://www.ecma-international.org/ecma-262/6.0/#sec-identifiers-static-semantics-early-errors Due to a error in its heuristic for disambiguating the `let` token, V8 does not currently allow any of the strict-mode-only FutureReservedWords to be used as a BindingIdentifier outside of strict mode. Update V8's heuristic for disambiguating the `let` keyword to account for strict mode, enabling these IdentifierNames to be used BUG=v8:4918 LOG=N R=adamk@chromium.org Review-Url: https://codereview.chromium.org/1891453005 Cr-Commit-Position: refs/heads/master@{#36296}
This commit is contained in:
parent
8352ad50e6
commit
d0c65f93bf
@ -3243,11 +3243,17 @@ bool ParserBase<Traits>::IsNextLetKeyword() {
|
||||
case Token::LBRACK:
|
||||
case Token::IDENTIFIER:
|
||||
case Token::STATIC:
|
||||
case Token::LET: // Yes, you can do let let = ... in sloppy mode
|
||||
case Token::LET: // `let let;` is disallowed by static semantics, but the
|
||||
// token must be first interpreted as a keyword in order
|
||||
// for those semantics to apply. This ensures that ASI is
|
||||
// not honored when a LineTerminator separates the
|
||||
// tokens.
|
||||
case Token::YIELD:
|
||||
case Token::AWAIT:
|
||||
case Token::ASYNC:
|
||||
return true;
|
||||
case Token::FUTURE_STRICT_RESERVED_WORD:
|
||||
return is_sloppy(language_mode());
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -2015,25 +2015,28 @@ TEST(NoErrorsEvalAndArgumentsStrict) {
|
||||
RunParserSyncTest(context_data, statement_data, kSuccess);
|
||||
}
|
||||
|
||||
#define FUTURE_STRICT_RESERVED_WORDS_NO_LET(V) \
|
||||
V(implements) \
|
||||
V(interface) \
|
||||
V(package) \
|
||||
V(private) \
|
||||
V(protected) \
|
||||
V(public) \
|
||||
V(static) \
|
||||
V(yield)
|
||||
|
||||
#define FUTURE_STRICT_RESERVED_WORDS(V) \
|
||||
V(implements) \
|
||||
V(interface) \
|
||||
V(let) \
|
||||
V(package) \
|
||||
V(private) \
|
||||
V(protected) \
|
||||
V(public) \
|
||||
V(static) \
|
||||
V(yield)
|
||||
FUTURE_STRICT_RESERVED_WORDS_NO_LET(V)
|
||||
|
||||
#define LIMITED_FUTURE_STRICT_RESERVED_WORDS_NO_LET(V) \
|
||||
V(implements) \
|
||||
V(static) \
|
||||
V(yield)
|
||||
|
||||
#define LIMITED_FUTURE_STRICT_RESERVED_WORDS(V) \
|
||||
V(implements) \
|
||||
V(let) \
|
||||
V(static) \
|
||||
V(yield)
|
||||
|
||||
LIMITED_FUTURE_STRICT_RESERVED_WORDS_NO_LET(V)
|
||||
|
||||
#define FUTURE_STRICT_RESERVED_STATEMENTS(NAME) \
|
||||
"var " #NAME ";", \
|
||||
@ -2049,26 +2052,53 @@ TEST(NoErrorsEvalAndArgumentsStrict) {
|
||||
"++" #NAME ";", \
|
||||
#NAME " ++;",
|
||||
|
||||
// clang-format off
|
||||
#define FUTURE_STRICT_RESERVED_LEX_BINDINGS(NAME) \
|
||||
"let " #NAME ";", \
|
||||
"for (let " #NAME "; false; ) {}", \
|
||||
"for (let " #NAME " in {}) {}", \
|
||||
"for (let " #NAME " of []) {}", \
|
||||
"const " #NAME " = null;", \
|
||||
"for (const " #NAME " = null; false; ) {}", \
|
||||
"for (const " #NAME " in {}) {}", \
|
||||
"for (const " #NAME " of []) {}",
|
||||
// clang-format on
|
||||
|
||||
TEST(ErrorsFutureStrictReservedWords) {
|
||||
// Tests that both preparsing and parsing produce the right kind of errors for
|
||||
// using future strict reserved words as identifiers. Without the strict mode,
|
||||
// it's ok to use future strict reserved words as identifiers. With the strict
|
||||
// mode, it isn't.
|
||||
const char* context_data[][2] = {
|
||||
const char* strict_contexts[][2] = {
|
||||
{"function test_func() {\"use strict\"; ", "}"},
|
||||
{"() => { \"use strict\"; ", "}"},
|
||||
{NULL, NULL}};
|
||||
|
||||
// clang-format off
|
||||
const char* statement_data[] {
|
||||
LIMITED_FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_STATEMENTS)
|
||||
LIMITED_FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_LEX_BINDINGS)
|
||||
NULL
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RunParserSyncTest(context_data, statement_data, kError);
|
||||
RunParserSyncTest(strict_contexts, statement_data, kError);
|
||||
|
||||
// From ES2015, 13.3.1.1 Static Semantics: Early Errors:
|
||||
//
|
||||
// > LexicalDeclaration : LetOrConst BindingList ;
|
||||
// >
|
||||
// > - It is a Syntax Error if the BoundNames of BindingList contains "let".
|
||||
const char* non_strict_contexts[][2] = {{"", ""},
|
||||
{"function test_func() {", "}"},
|
||||
{"() => {", "}"},
|
||||
{NULL, NULL}};
|
||||
const char* invalid_statements[] = {FUTURE_STRICT_RESERVED_LEX_BINDINGS("let")
|
||||
NULL};
|
||||
|
||||
RunParserSyncTest(non_strict_contexts, invalid_statements, kError);
|
||||
}
|
||||
|
||||
|
||||
#undef LIMITED_FUTURE_STRICT_RESERVED_WORDS
|
||||
|
||||
|
||||
@ -2080,10 +2110,13 @@ TEST(NoErrorsFutureStrictReservedWords) {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
const char* statement_data[] = {
|
||||
FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_STATEMENTS)
|
||||
FUTURE_STRICT_RESERVED_WORDS_NO_LET(FUTURE_STRICT_RESERVED_LEX_BINDINGS)
|
||||
NULL
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RunParserSyncTest(context_data, statement_data, kSuccess);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user