[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:
mike 2016-05-17 14:12:16 -07:00 committed by Commit bot
parent 8352ad50e6
commit d0c65f93bf
2 changed files with 55 additions and 16 deletions

View File

@ -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;
}

View File

@ -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);
}