Commit d0c65f93 authored by mike's avatar mike Committed by Commit bot

[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}
parent 8352ad50
......@@ -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,25 +2052,52 @@ 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);
}
......
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