Commit 5bf360ef authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[es6] early error when Identifier is an escaped reserved word

Per http://tc39.github.io/ecma262/#sec-identifiers-static-semantics-early-errors (13.2.2),
make it a SyntaxError if an Identifier has the same StringValue as a ReservedWord.

BUG=v8:2222, v8:1972
LOG=N
R=adamk@chromium.org, rossberg@chromium.org, wingo@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#32052}
parent cf9957ee
......@@ -312,6 +312,7 @@ class CallSite {
T(IllegalLanguageModeDirective, \
"Illegal '%' directive in function with non-simple parameter list") \
T(IllegalReturn, "Illegal return statement") \
T(InvalidEscapedReservedWord, "Keyword must not contain escaped characters") \
T(InvalidLhsInAssignment, "Invalid left-hand side in assignment") \
T(InvalidLhsInFor, "Invalid left-hand side in for-loop") \
T(InvalidLhsInPostfixOp, \
......
......@@ -684,6 +684,7 @@ 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(
......@@ -2016,6 +2017,11 @@ void ParserBase<Traits>::GetUnexpectedTokenMessage(
*message = MessageTemplate::kUnexpectedTemplateString;
*arg = nullptr;
break;
case Token::ESCAPED_STRICT_RESERVED_WORD:
case Token::ESCAPED_KEYWORD:
*message = MessageTemplate::kInvalidEscapedReservedWord;
*arg = nullptr;
break;
default:
const char* name = Token::String(token);
DCHECK(name != NULL);
......@@ -2115,10 +2121,17 @@ ParserBase<Traits>::ParseAndClassifyIdentifier(ExpressionClassifier* classifier,
return name;
} else if (is_sloppy(language_mode()) &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
next == Token::ESCAPED_STRICT_RESERVED_WORD ||
next == Token::LET || next == Token::STATIC ||
(next == Token::YIELD && !is_generator()))) {
classifier->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kUnexpectedStrictReserved);
if (next == Token::ESCAPED_STRICT_RESERVED_WORD &&
is_strict(language_mode())) {
ReportUnexpectedToken(next);
*ok = false;
return Traits::EmptyIdentifier();
}
if (next == Token::LET) {
classifier->RecordLetPatternError(scanner()->location(),
MessageTemplate::kLetInLexicalBinding);
......@@ -2161,7 +2174,9 @@ ParserBase<Traits>::ParseIdentifierName(bool* ok) {
Token::Value next = Next();
if (next != Token::IDENTIFIER && next != Token::FUTURE_RESERVED_WORD &&
next != Token::LET && next != Token::STATIC && next != Token::YIELD &&
next != Token::FUTURE_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) {
next != Token::FUTURE_STRICT_RESERVED_WORD &&
next != Token::ESCAPED_KEYWORD &&
next != Token::ESCAPED_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) {
this->ReportUnexpectedToken(next);
*ok = false;
return Traits::EmptyIdentifier();
......@@ -2278,6 +2293,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
case Token::LET:
case Token::STATIC:
case Token::YIELD:
case Token::ESCAPED_STRICT_RESERVED_WORD:
case Token::FUTURE_STRICT_RESERVED_WORD: {
// Using eval or arguments in this context is OK even in strict mode.
IdentifierT name = ParseAndClassifyIdentifier(classifier, CHECK_OK);
......@@ -2542,7 +2558,8 @@ 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, ExpressionClassifier* classifier, bool* ok) {
bool* is_computed_name, bool* is_identifier, bool* is_escaped_keyword,
ExpressionClassifier* classifier, bool* ok) {
Token::Value token = peek();
int pos = peek_position();
......@@ -2583,11 +2600,17 @@ 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;
}
......@@ -2616,14 +2639,21 @@ 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, classifier,
&name, &is_get, &is_set, &name_is_static, is_computed_name,
&is_identifier, &is_escaped_keyword, 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);
......@@ -2641,8 +2671,7 @@ ParserBase<Traits>::ParsePropertyDefinition(
*is_computed_name);
}
if (Token::IsIdentifier(name_token, language_mode(),
this->is_generator()) &&
if ((is_identifier || is_escaped_keyword) &&
(peek() == Token::COMMA || peek() == Token::RBRACE ||
peek() == Token::ASSIGN)) {
// PropertyDefinition
......@@ -2651,6 +2680,14 @@ 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());
......@@ -2683,6 +2720,11 @@ ParserBase<Traits>::ParsePropertyDefinition(
}
}
if (in_class && escaped_static && !is_static) {
ReportUnexpectedTokenAt(scanner()->location(), name_token);
*ok = false;
return this->EmptyObjectLiteralProperty();
}
if (is_generator || peek() == Token::LPAREN) {
// MethodDefinition
......@@ -2732,8 +2774,8 @@ ParserBase<Traits>::ParsePropertyDefinition(
name_token = peek();
name_expression = ParsePropertyName(
&name, &dont_care, &dont_care, &dont_care, is_computed_name, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
&name, &dont_care, &dont_care, &dont_care, is_computed_name, &dont_care,
&dont_care, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (!*is_computed_name) {
checker->CheckProperty(name_token, kAccessorProperty, is_static,
......
......@@ -1177,7 +1177,7 @@ uc32 Scanner::ScanUnicodeEscape() {
static Token::Value KeywordOrIdentifierToken(const uint8_t* input,
int input_length) {
int input_length, bool escaped) {
DCHECK(input_length >= 1);
const int kMinLength = 2;
const int kMaxLength = 10;
......@@ -1189,26 +1189,30 @@ static Token::Value KeywordOrIdentifierToken(const uint8_t* input,
#define KEYWORD_GROUP_CASE(ch) \
break; \
case ch:
#define KEYWORD(keyword, token) \
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
const int keyword_length = sizeof(keyword) - 1; \
STATIC_ASSERT(keyword_length >= kMinLength); \
STATIC_ASSERT(keyword_length <= kMaxLength); \
if (input_length == keyword_length && \
input[1] == keyword[1] && \
(keyword_length <= 2 || input[2] == keyword[2]) && \
(keyword_length <= 3 || input[3] == keyword[3]) && \
(keyword_length <= 4 || input[4] == keyword[4]) && \
(keyword_length <= 5 || input[5] == keyword[5]) && \
(keyword_length <= 6 || input[6] == keyword[6]) && \
(keyword_length <= 7 || input[7] == keyword[7]) && \
(keyword_length <= 8 || input[8] == keyword[8]) && \
(keyword_length <= 9 || input[9] == keyword[9])) { \
return token; \
} \
}
#define KEYWORD(keyword, token) \
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
const int keyword_length = sizeof(keyword) - 1; \
STATIC_ASSERT(keyword_length >= kMinLength); \
STATIC_ASSERT(keyword_length <= kMaxLength); \
if (input_length == keyword_length && input[1] == keyword[1] && \
(keyword_length <= 2 || input[2] == keyword[2]) && \
(keyword_length <= 3 || input[3] == keyword[3]) && \
(keyword_length <= 4 || input[4] == keyword[4]) && \
(keyword_length <= 5 || input[5] == keyword[5]) && \
(keyword_length <= 6 || input[6] == keyword[6]) && \
(keyword_length <= 7 || input[7] == keyword[7]) && \
(keyword_length <= 8 || input[8] == keyword[8]) && \
(keyword_length <= 9 || input[9] == keyword[9])) { \
if (escaped) { \
return token == Token::FUTURE_STRICT_RESERVED_WORD \
? Token::ESCAPED_STRICT_RESERVED_WORD \
: Token::ESCAPED_KEYWORD; \
} \
return token; \
} \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
}
return Token::IDENTIFIER;
......@@ -1224,7 +1228,7 @@ bool Scanner::IdentifierIsFutureStrictReserved(
return true;
}
return Token::FUTURE_STRICT_RESERVED_WORD ==
KeywordOrIdentifierToken(string->raw_data(), string->length());
KeywordOrIdentifierToken(string->raw_data(), string->length(), false);
}
......@@ -1257,7 +1261,7 @@ Token::Value Scanner::ScanIdentifierOrKeyword() {
// Only a-z+: could be a keyword or identifier.
literal.Complete();
Vector<const uint8_t> chars = next_.literal_chars->one_byte_literal();
return KeywordOrIdentifierToken(chars.start(), chars.length());
return KeywordOrIdentifierToken(chars.start(), chars.length(), false);
}
HandleLeadSurrogate();
......@@ -1284,7 +1288,7 @@ Token::Value Scanner::ScanIdentifierOrKeyword() {
return Token::ILLEGAL;
}
AddLiteralChar(c);
return ScanIdentifierSuffix(&literal);
return ScanIdentifierSuffix(&literal, true);
} else {
uc32 first_char = c0_;
Advance();
......@@ -1300,24 +1304,26 @@ Token::Value Scanner::ScanIdentifierOrKeyword() {
continue;
}
// Fallthrough if no longer able to complete keyword.
return ScanIdentifierSuffix(&literal);
return ScanIdentifierSuffix(&literal, false);
}
literal.Complete();
if (next_.literal_chars->is_one_byte()) {
Vector<const uint8_t> chars = next_.literal_chars->one_byte_literal();
return KeywordOrIdentifierToken(chars.start(), chars.length());
return KeywordOrIdentifierToken(chars.start(), chars.length(), false);
}
return Token::IDENTIFIER;
}
Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal) {
Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal,
bool escaped) {
// Scan the rest of the identifier characters.
while (c0_ >= 0 && unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
escaped = true;
// Only allow legal identifier part characters.
if (c < 0 ||
c == '\\' ||
......@@ -1332,6 +1338,10 @@ Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal) {
}
literal->Complete();
if (escaped && next_.literal_chars->is_one_byte()) {
Vector<const uint8_t> chars = next_.literal_chars->one_byte_literal();
return KeywordOrIdentifierToken(chars.start(), chars.length(), true);
}
return Token::IDENTIFIER;
}
......
......@@ -373,13 +373,10 @@ class Scanner {
Location peek_location() const { return next_.location; }
bool literal_contains_escapes() const {
Location location = current_.location;
int source_length = (location.end_pos - location.beg_pos);
if (current_.token == Token::STRING) {
// Subtract delimiters.
source_length -= 2;
}
return current_.literal_chars->length() != source_length;
return LiteralContainsEscapes(current_);
}
bool next_literal_contains_escapes() const {
return LiteralContainsEscapes(next_);
}
bool is_literal_contextual_keyword(Vector<const char> keyword) {
DCHECK_NOT_NULL(current_.literal_chars);
......@@ -665,7 +662,7 @@ class Scanner {
void ScanDecimalDigits();
Token::Value ScanNumber(bool seen_period);
Token::Value ScanIdentifierOrKeyword();
Token::Value ScanIdentifierSuffix(LiteralScope* literal);
Token::Value ScanIdentifierSuffix(LiteralScope* literal, bool escaped);
Token::Value ScanString();
......@@ -689,6 +686,16 @@ class Scanner {
return static_cast<int>(source_->pos()) - kCharacterLookaheadBufferSize;
}
static bool LiteralContainsEscapes(const TokenDesc& token) {
Location location = token.location;
int source_length = (location.end_pos - location.beg_pos);
if (token.token == Token::STRING) {
// Subtract delimiters.
source_length -= 2;
}
return token.literal_chars->length() != source_length;
}
UnicodeCache* unicode_cache_;
// Buffers collecting literal strings, numbers, etc.
......
......@@ -160,6 +160,8 @@ namespace internal {
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
T(ESCAPED_KEYWORD, NULL, 0) \
T(ESCAPED_STRICT_RESERVED_WORD, NULL, 0) \
\
/* Scanner-internal use only. */ \
T(WHITESPACE, NULL, 0) \
......@@ -197,6 +199,7 @@ class Token {
switch (tok) {
case IDENTIFIER:
return true;
case ESCAPED_STRICT_RESERVED_WORD:
case FUTURE_STRICT_RESERVED_WORD:
case LET:
case STATIC:
......
This diff is collapsed.
......@@ -90,6 +90,9 @@
# ES2015 ToLength semantics
'ecma_3/RegExp/15.10.6.2-2': [FAIL],
# Escaped keywords are early errors in ES6
'ecma_3/Unicode/uc-003': [FAIL],
# RegExp.multiline is not part of any ECMAScript specification, and is
# slated for deprecation in Mozilla
# (https://bugzilla.mozilla.org/show_bug.cgi?id=1220457)
......
......@@ -374,78 +374,6 @@
# https://code.google.com/p/v8/issues/detail?id=4361
'intl402/Collator/10.1.1_a': [FAIL],
# https://code.google.com/p/v8/issues/detail?id=1972
'language/identifiers/val-break-via-escape-hex': [FAIL],
'language/identifiers/val-break-via-escape-hex4': [FAIL],
'language/identifiers/val-case-via-escape-hex': [FAIL],
'language/identifiers/val-case-via-escape-hex4': [FAIL],
'language/identifiers/val-catch-via-escape-hex': [FAIL],
'language/identifiers/val-catch-via-escape-hex4': [FAIL],
'language/identifiers/val-class-via-escape-hex': [FAIL],
'language/identifiers/val-class-via-escape-hex4': [FAIL],
'language/identifiers/val-const-via-escape-hex': [FAIL],
'language/identifiers/val-const-via-escape-hex4': [FAIL],
'language/identifiers/val-continue-via-escape-hex': [FAIL],
'language/identifiers/val-continue-via-escape-hex4': [FAIL],
'language/identifiers/val-debugger-via-escape-hex': [FAIL],
'language/identifiers/val-debugger-via-escape-hex4': [FAIL],
'language/identifiers/val-default-via-escape-hex': [FAIL],
'language/identifiers/val-default-via-escape-hex4': [FAIL],
'language/identifiers/val-delete-via-escape-hex': [FAIL],
'language/identifiers/val-delete-via-escape-hex4': [FAIL],
'language/identifiers/val-do-via-escape-hex': [FAIL],
'language/identifiers/val-do-via-escape-hex4': [FAIL],
'language/identifiers/val-else-via-escape-hex': [FAIL],
'language/identifiers/val-else-via-escape-hex4': [FAIL],
'language/identifiers/val-enum-via-escape-hex': [FAIL],
'language/identifiers/val-enum-via-escape-hex4': [FAIL],
'language/identifiers/val-export-via-escape-hex': [FAIL],
'language/identifiers/val-export-via-escape-hex4': [FAIL],
'language/identifiers/val-extends-via-escape-hex': [FAIL],
'language/identifiers/val-extends-via-escape-hex4': [FAIL],
'language/identifiers/val-false-via-escape-hex': [FAIL],
'language/identifiers/val-false-via-escape-hex4': [FAIL],
'language/identifiers/val-finally-via-escape-hex': [FAIL],
'language/identifiers/val-finally-via-escape-hex4': [FAIL],
'language/identifiers/val-for-via-escape-hex': [FAIL],
'language/identifiers/val-for-via-escape-hex4': [FAIL],
'language/identifiers/val-function-via-escape-hex': [FAIL],
'language/identifiers/val-function-via-escape-hex4': [FAIL],
'language/identifiers/val-if-via-escape-hex': [FAIL],
'language/identifiers/val-if-via-escape-hex4': [FAIL],
'language/identifiers/val-import-via-escape-hex': [FAIL],
'language/identifiers/val-import-via-escape-hex4': [FAIL],
'language/identifiers/val-in-via-escape-hex': [FAIL],
'language/identifiers/val-in-via-escape-hex4': [FAIL],
'language/identifiers/val-instanceof-via-escape-hex': [FAIL],
'language/identifiers/val-instanceof-via-escape-hex4': [FAIL],
'language/identifiers/val-new-via-escape-hex': [FAIL],
'language/identifiers/val-new-via-escape-hex4': [FAIL],
'language/identifiers/val-null-via-escape-hex': [FAIL],
'language/identifiers/val-null-via-escape-hex4': [FAIL],
'language/identifiers/val-return-via-escape-hex': [FAIL],
'language/identifiers/val-return-via-escape-hex4': [FAIL],
'language/identifiers/val-super-via-escape-hex': [FAIL],
'language/identifiers/val-super-via-escape-hex4': [FAIL],
'language/identifiers/val-switch-via-escape-hex': [FAIL],
'language/identifiers/val-switch-via-escape-hex4': [FAIL],
'language/identifiers/val-throw-via-escape-hex': [FAIL],
'language/identifiers/val-throw-via-escape-hex4': [FAIL],
'language/identifiers/val-true-via-escape-hex': [FAIL],
'language/identifiers/val-true-via-escape-hex4': [FAIL],
'language/identifiers/val-try-via-escape-hex': [FAIL],
'language/identifiers/val-try-via-escape-hex4': [FAIL],
'language/identifiers/val-typeof-via-escape-hex': [FAIL],
'language/identifiers/val-typeof-via-escape-hex4': [FAIL],
'language/identifiers/val-var-via-escape-hex': [FAIL],
'language/identifiers/val-var-via-escape-hex4': [FAIL],
'language/identifiers/val-void-via-escape-hex': [FAIL],
'language/identifiers/val-void-via-escape-hex4': [FAIL],
'language/identifiers/val-while-via-escape-hex': [FAIL],
'language/identifiers/val-while-via-escape-hex4': [FAIL],
'language/identifiers/val-with-via-escape-hex': [FAIL],
'language/identifiers/val-with-via-escape-hex4': [FAIL],
# https://code.google.com/p/v8/issues/detail?id=4362
'built-ins/String/prototype/repeat/empty-string-returns-empty': [PASS, FAIL],
......
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