Commit a94c91ca authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[parser] Fix escaped contextual keyword handling

Escaped contextual keywords are simply valid identifiers if they do not occur
in the context where they are a keyword. Escape sequences of the form \uNNNN
or \u{NNNNNN} must be consumed as part of the identifier.

If such escaped contextual keywords do occur in a context where they are a
keyword, they are a syntax error. In that case we manually check locally
whether they are escaped.

Bug: v8:6543, v8:6541

Change-Id: I7e1557963883e722310b9078d7d7636ec94aa603
Reviewed-on: https://chromium-review.googlesource.com/c/1473293Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59628}
parent 5d1d0795
...@@ -49,14 +49,14 @@ struct PerfectKeywordHashTableEntry { ...@@ -49,14 +49,14 @@ struct PerfectKeywordHashTableEntry {
Token::Value value; Token::Value value;
}; };
enum { enum {
TOTAL_KEYWORDS = 47, TOTAL_KEYWORDS = 49,
MIN_WORD_LENGTH = 2, MIN_WORD_LENGTH = 2,
MAX_WORD_LENGTH = 10, MAX_WORD_LENGTH = 10,
MIN_HASH_VALUE = 2, MIN_HASH_VALUE = 2,
MAX_HASH_VALUE = 51 MAX_HASH_VALUE = 55
}; };
/* maximum key range = 50, duplicates = 0 */ /* maximum key range = 54, duplicates = 0 */
class PerfectKeywordHash { class PerfectKeywordHash {
private: private:
...@@ -70,22 +70,22 @@ inline unsigned int PerfectKeywordHash::Hash(const char* str, int len) { ...@@ -70,22 +70,22 @@ inline unsigned int PerfectKeywordHash::Hash(const char* str, int len) {
DCHECK_LT(str[1], 128); DCHECK_LT(str[1], 128);
DCHECK_LT(str[0], 128); DCHECK_LT(str[0], 128);
static const unsigned char asso_values[128] = { static const unsigned char asso_values[128] = {
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
52, 8, 2, 6, 0, 0, 9, 52, 21, 0, 52, 52, 36, 40, 0, 3, 56, 8, 0, 6, 0, 0, 9, 9, 9, 0, 56, 56, 34, 41, 0, 3,
6, 52, 17, 13, 16, 16, 38, 25, 6, 26, 52, 52, 52, 52, 52, 52}; 6, 56, 19, 10, 13, 16, 39, 26, 37, 36, 56, 56, 56, 56, 56, 56};
return len + asso_values[static_cast<unsigned char>(str[1])] + return len + asso_values[static_cast<unsigned char>(str[1])] +
asso_values[static_cast<unsigned char>(str[0])]; asso_values[static_cast<unsigned char>(str[0])];
} }
static const unsigned char kPerfectKeywordLengthTable[64] = { static const unsigned char kPerfectKeywordLengthTable[64] = {
0, 0, 2, 3, 4, 2, 6, 7, 8, 9, 10, 2, 6, 7, 5, 3, 7, 8, 4, 5, 4, 7, 0, 0, 2, 3, 4, 2, 6, 7, 8, 9, 10, 2, 3, 3, 5, 3, 7, 8, 4, 5, 4, 7,
5, 6, 5, 0, 5, 0, 6, 4, 7, 5, 9, 8, 5, 6, 3, 4, 5, 3, 4, 4, 5, 0, 5, 5, 5, 6, 4, 5, 6, 6, 4, 5, 7, 8, 9, 3, 4, 3, 4, 5, 5, 5, 6, 6,
6, 4, 6, 5, 6, 3, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 7, 5, 4, 6, 0, 0, 3, 10, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0};
static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] = static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] =
{{"", Token::IDENTIFIER}, {{"", Token::IDENTIFIER},
...@@ -100,8 +100,8 @@ static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] = ...@@ -100,8 +100,8 @@ static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] =
{"interface", Token::FUTURE_STRICT_RESERVED_WORD}, {"interface", Token::FUTURE_STRICT_RESERVED_WORD},
{"instanceof", Token::INSTANCEOF}, {"instanceof", Token::INSTANCEOF},
{"if", Token::IF}, {"if", Token::IF},
{"export", Token::EXPORT}, {"get", Token::GET},
{"extends", Token::EXTENDS}, {"set", Token::SET},
{"const", Token::CONST}, {"const", Token::CONST},
{"for", Token::FOR}, {"for", Token::FOR},
{"finally", Token::FINALLY}, {"finally", Token::FINALLY},
...@@ -111,39 +111,39 @@ static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] = ...@@ -111,39 +111,39 @@ static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] =
{"null", Token::NULL_LITERAL}, {"null", Token::NULL_LITERAL},
{"package", Token::FUTURE_STRICT_RESERVED_WORD}, {"package", Token::FUTURE_STRICT_RESERVED_WORD},
{"false", Token::FALSE_LITERAL}, {"false", Token::FALSE_LITERAL},
{"return", Token::RETURN},
{"break", Token::BREAK},
{"", Token::IDENTIFIER},
{"async", Token::ASYNC}, {"async", Token::ASYNC},
{"", Token::IDENTIFIER}, {"break", Token::BREAK},
{"return", Token::RETURN},
{"this", Token::THIS},
{"throw", Token::THROW},
{"public", Token::FUTURE_STRICT_RESERVED_WORD}, {"public", Token::FUTURE_STRICT_RESERVED_WORD},
{"static", Token::STATIC},
{"with", Token::WITH}, {"with", Token::WITH},
{"super", Token::SUPER},
{"private", Token::FUTURE_STRICT_RESERVED_WORD}, {"private", Token::FUTURE_STRICT_RESERVED_WORD},
{"yield", Token::YIELD},
{"protected", Token::FUTURE_STRICT_RESERVED_WORD},
{"function", Token::FUNCTION}, {"function", Token::FUNCTION},
{"super", Token::SUPER}, {"protected", Token::FUTURE_STRICT_RESERVED_WORD},
{"static", Token::STATIC},
{"try", Token::TRY}, {"try", Token::TRY},
{"true", Token::TRUE_LITERAL}, {"true", Token::TRUE_LITERAL},
{"await", Token::AWAIT},
{"let", Token::LET}, {"let", Token::LET},
{"else", Token::ELSE}, {"else", Token::ELSE},
{"this", Token::THIS}, {"await", Token::AWAIT},
{"throw", Token::THROW}, {"while", Token::WHILE},
{"", Token::IDENTIFIER}, {"yield", Token::YIELD},
{"switch", Token::SWITCH}, {"switch", Token::SWITCH},
{"export", Token::EXPORT},
{"extends", Token::EXTENDS},
{"class", Token::CLASS},
{"void", Token::VOID}, {"void", Token::VOID},
{"import", Token::IMPORT}, {"import", Token::IMPORT},
{"class", Token::CLASS}, {"", Token::IDENTIFIER},
{"typeof", Token::TYPEOF}, {"", Token::IDENTIFIER},
{"var", Token::VAR}, {"var", Token::VAR},
{"implements", Token::FUTURE_STRICT_RESERVED_WORD}, {"implements", Token::FUTURE_STRICT_RESERVED_WORD},
{"while", Token::WHILE},
{"", Token::IDENTIFIER},
{"", Token::IDENTIFIER}, {"", Token::IDENTIFIER},
{"", Token::IDENTIFIER}, {"", Token::IDENTIFIER},
{"", Token::IDENTIFIER}, {"", Token::IDENTIFIER},
{"typeof", Token::TYPEOF},
{"", Token::IDENTIFIER}, {"", Token::IDENTIFIER},
{"", Token::IDENTIFIER}, {"", Token::IDENTIFIER},
{"", Token::IDENTIFIER}, {"", Token::IDENTIFIER},
......
...@@ -35,6 +35,7 @@ false, Token::FALSE_LITERAL ...@@ -35,6 +35,7 @@ false, Token::FALSE_LITERAL
finally, Token::FINALLY finally, Token::FINALLY
for, Token::FOR for, Token::FOR
function, Token::FUNCTION function, Token::FUNCTION
get, Token::GET
if, Token::IF if, Token::IF
implements, Token::FUTURE_STRICT_RESERVED_WORD implements, Token::FUTURE_STRICT_RESERVED_WORD
import, Token::IMPORT import, Token::IMPORT
...@@ -49,6 +50,7 @@ private, Token::FUTURE_STRICT_RESERVED_WORD ...@@ -49,6 +50,7 @@ private, Token::FUTURE_STRICT_RESERVED_WORD
protected, Token::FUTURE_STRICT_RESERVED_WORD protected, Token::FUTURE_STRICT_RESERVED_WORD
public, Token::FUTURE_STRICT_RESERVED_WORD public, Token::FUTURE_STRICT_RESERVED_WORD
return, Token::RETURN return, Token::RETURN
set, Token::SET
static, Token::STATIC static, Token::STATIC
super, Token::SUPER super, Token::SUPER
switch, Token::SWITCH switch, Token::SWITCH
......
...@@ -797,6 +797,7 @@ class ParserBase { ...@@ -797,6 +797,7 @@ class ParserBase {
bool PeekContextualKeyword(const AstRawString* name) { bool PeekContextualKeyword(const AstRawString* name) {
return peek() == Token::IDENTIFIER && return peek() == Token::IDENTIFIER &&
!scanner()->next_literal_contains_escapes() &&
scanner()->NextSymbol(ast_value_factory()) == name; scanner()->NextSymbol(ast_value_factory()) == name;
} }
...@@ -808,14 +809,21 @@ class ParserBase { ...@@ -808,14 +809,21 @@ class ParserBase {
return false; return false;
} }
void ExpectMetaProperty(const AstRawString* property_name, void ExpectContextualKeyword(const AstRawString* name,
const char* full_name, int pos); const char* fullname = nullptr, int pos = -1) {
void ExpectContextualKeyword(const AstRawString* name) {
Expect(Token::IDENTIFIER); Expect(Token::IDENTIFIER);
if (V8_UNLIKELY(scanner()->CurrentSymbol(ast_value_factory()) != name)) { if (V8_UNLIKELY(scanner()->CurrentSymbol(ast_value_factory()) != name)) {
ReportUnexpectedToken(scanner()->current_token()); ReportUnexpectedToken(scanner()->current_token());
} }
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
const char* full = fullname == nullptr
? reinterpret_cast<const char*>(name->raw_data())
: fullname;
int start = pos == -1 ? position() : pos;
impl()->ReportMessageAt(Scanner::Location(start, end_position()),
MessageTemplate::kInvalidEscapedMetaProperty,
full);
}
} }
bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode) { bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode) {
...@@ -1472,7 +1480,6 @@ template <typename Impl> ...@@ -1472,7 +1480,6 @@ template <typename Impl>
typename ParserBase<Impl>::IdentifierT typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseAndClassifyIdentifier(Token::Value next) { ParserBase<Impl>::ParseAndClassifyIdentifier(Token::Value next) {
DCHECK_EQ(scanner()->current_token(), next); DCHECK_EQ(scanner()->current_token(), next);
STATIC_ASSERT(Token::IDENTIFIER + 1 == Token::ASYNC);
if (V8_LIKELY(IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) { if (V8_LIKELY(IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) {
IdentifierT name = impl()->GetSymbol(); IdentifierT name = impl()->GetSymbol();
if (V8_UNLIKELY(impl()->IsArguments(name) && if (V8_UNLIKELY(impl()->IsArguments(name) &&
...@@ -1648,7 +1655,8 @@ ParserBase<Impl>::ParsePrimaryExpression() { ...@@ -1648,7 +1655,8 @@ ParserBase<Impl>::ParsePrimaryExpression() {
FunctionKind kind = FunctionKind::kArrowFunction; FunctionKind kind = FunctionKind::kArrowFunction;
if (V8_UNLIKELY(token == Token::ASYNC && if (V8_UNLIKELY(token == Token::ASYNC &&
!scanner()->HasLineTerminatorBeforeNext())) { !scanner()->HasLineTerminatorBeforeNext() &&
!scanner()->literal_contains_escapes())) {
// async function ... // async function ...
if (peek() == Token::FUNCTION) return ParseAsyncFunctionLiteral(); if (peek() == Token::FUNCTION) return ParseAsyncFunctionLiteral();
...@@ -1929,6 +1937,9 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseProperty( ...@@ -1929,6 +1937,9 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseProperty(
impl()->PushLiteralName(prop_info->name); impl()->PushLiteralName(prop_info->name);
return factory()->NewStringLiteral(prop_info->name, position()); return factory()->NewStringLiteral(prop_info->name, position());
} }
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
}
prop_info->function_flags = ParseFunctionFlag::kIsAsync; prop_info->function_flags = ParseFunctionFlag::kIsAsync;
prop_info->kind = ParsePropertyKind::kMethod; prop_info->kind = ParsePropertyKind::kMethod;
} }
...@@ -1939,21 +1950,21 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseProperty( ...@@ -1939,21 +1950,21 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseProperty(
} }
if (prop_info->kind == ParsePropertyKind::kNotSet && if (prop_info->kind == ParsePropertyKind::kNotSet &&
Check(Token::IDENTIFIER)) { IsInRange(peek(), Token::GET, Token::SET)) {
IdentifierT symbol = impl()->GetSymbol(); Token::Value token = Next();
if (!prop_info->ParsePropertyKindFromToken(peek())) { if (prop_info->ParsePropertyKindFromToken(peek())) {
if (impl()->IdentifierEquals(symbol, ast_value_factory()->get_string())) { prop_info->name = impl()->GetSymbol();
prop_info->kind = ParsePropertyKind::kAccessorGetter;
} else if (impl()->IdentifierEquals(symbol,
ast_value_factory()->set_string())) {
prop_info->kind = ParsePropertyKind::kAccessorSetter;
}
}
if (!IsAccessor(prop_info->kind)) {
prop_info->name = symbol;
impl()->PushLiteralName(prop_info->name); impl()->PushLiteralName(prop_info->name);
return factory()->NewStringLiteral(prop_info->name, position()); return factory()->NewStringLiteral(prop_info->name, position());
} }
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
}
if (token == Token::GET) {
prop_info->kind = ParsePropertyKind::kAccessorGetter;
} else if (token == Token::SET) {
prop_info->kind = ParsePropertyKind::kAccessorSetter;
}
} }
int pos = peek_position(); int pos = peek_position();
...@@ -2682,6 +2693,9 @@ ParserBase<Impl>::ParseYieldExpression() { ...@@ -2682,6 +2693,9 @@ ParserBase<Impl>::ParseYieldExpression() {
expression_scope()->RecordParameterInitializerError( expression_scope()->RecordParameterInitializerError(
scanner()->peek_location(), MessageTemplate::kYieldInParameter); scanner()->peek_location(), MessageTemplate::kYieldInParameter);
Consume(Token::YIELD); Consume(Token::YIELD);
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
}
CheckStackOverflow(); CheckStackOverflow();
...@@ -2906,6 +2920,9 @@ ParserBase<Impl>::ParseAwaitExpression() { ...@@ -2906,6 +2920,9 @@ ParserBase<Impl>::ParseAwaitExpression() {
MessageTemplate::kAwaitExpressionFormalParameter); MessageTemplate::kAwaitExpressionFormalParameter);
int await_pos = peek_position(); int await_pos = peek_position();
Consume(Token::AWAIT); Consume(Token::AWAIT);
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
}
CheckStackOverflow(); CheckStackOverflow();
...@@ -2987,7 +3004,8 @@ ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result) { ...@@ -2987,7 +3004,8 @@ ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result) {
if (V8_UNLIKELY(peek() == Token::LPAREN && impl()->IsIdentifier(result) && if (V8_UNLIKELY(peek() == Token::LPAREN && impl()->IsIdentifier(result) &&
scanner()->current_token() == Token::ASYNC && scanner()->current_token() == Token::ASYNC &&
!scanner()->HasLineTerminatorBeforeNext())) { !scanner()->HasLineTerminatorBeforeNext() &&
!scanner()->literal_contains_escapes())) {
DCHECK(impl()->IsAsync(impl()->AsIdentifier(result))); DCHECK(impl()->IsAsync(impl()->AsIdentifier(result)));
int pos = position(); int pos = position();
...@@ -3249,8 +3267,9 @@ ParserBase<Impl>::ParseImportExpressions() { ...@@ -3249,8 +3267,9 @@ ParserBase<Impl>::ParseImportExpressions() {
Consume(Token::IMPORT); Consume(Token::IMPORT);
int pos = position(); int pos = position();
if (allow_harmony_import_meta() && peek() == Token::PERIOD) { if (allow_harmony_import_meta() && Check(Token::PERIOD)) {
ExpectMetaProperty(ast_value_factory()->meta_string(), "import.meta", pos); ExpectContextualKeyword(ast_value_factory()->meta_string(), "import.meta",
pos);
if (!parsing_module_) { if (!parsing_module_) {
impl()->ReportMessageAt(scanner()->location(), impl()->ReportMessageAt(scanner()->location(),
MessageTemplate::kImportMetaOutsideModule); MessageTemplate::kImportMetaOutsideModule);
...@@ -3303,23 +3322,13 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression( ...@@ -3303,23 +3322,13 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression(
return impl()->FailureExpression(); return impl()->FailureExpression();
} }
template <typename Impl>
void ParserBase<Impl>::ExpectMetaProperty(const AstRawString* property_name,
const char* full_name, int pos) {
Consume(Token::PERIOD);
ExpectContextualKeyword(property_name);
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportMessageAt(Scanner::Location(pos, end_position()),
MessageTemplate::kInvalidEscapedMetaProperty,
full_name);
}
}
template <typename Impl> template <typename Impl>
typename ParserBase<Impl>::ExpressionT typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseNewTargetExpression() { ParserBase<Impl>::ParseNewTargetExpression() {
int pos = position(); int pos = position();
ExpectMetaProperty(ast_value_factory()->target_string(), "new.target", pos); Consume(Token::PERIOD);
ExpectContextualKeyword(ast_value_factory()->target_string(), "new.target",
pos);
if (!GetReceiverScope()->is_function_scope()) { if (!GetReceiverScope()->is_function_scope()) {
impl()->ReportMessageAt(scanner()->location(), impl()->ReportMessageAt(scanner()->location(),
...@@ -3801,6 +3810,9 @@ ParserBase<Impl>::ParseAsyncFunctionDeclaration( ...@@ -3801,6 +3810,9 @@ ParserBase<Impl>::ParseAsyncFunctionDeclaration(
// async [no LineTerminator here] function BindingIdentifier[Await] // async [no LineTerminator here] function BindingIdentifier[Await]
// ( FormalParameters[Await] ) { AsyncFunctionBody } // ( FormalParameters[Await] ) { AsyncFunctionBody }
DCHECK_EQ(scanner()->current_token(), Token::ASYNC); DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
}
int pos = position(); int pos = position();
DCHECK(!scanner()->HasLineTerminatorBeforeNext()); DCHECK(!scanner()->HasLineTerminatorBeforeNext());
Consume(Token::FUNCTION); Consume(Token::FUNCTION);
...@@ -3978,6 +3990,8 @@ bool ParserBase<Impl>::IsNextLetKeyword() { ...@@ -3978,6 +3990,8 @@ bool ParserBase<Impl>::IsNextLetKeyword() {
// tokens. // tokens.
case Token::YIELD: case Token::YIELD:
case Token::AWAIT: case Token::AWAIT:
case Token::GET:
case Token::SET:
case Token::ASYNC: case Token::ASYNC:
return true; return true;
case Token::FUTURE_STRICT_RESERVED_WORD: case Token::FUTURE_STRICT_RESERVED_WORD:
...@@ -4258,6 +4272,9 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral() { ...@@ -4258,6 +4272,9 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral() {
// async [no LineTerminator here] function BindingIdentifier[Await] // async [no LineTerminator here] function BindingIdentifier[Await]
// ( FormalParameters[Await] ) { AsyncFunctionBody } // ( FormalParameters[Await] ) { AsyncFunctionBody }
DCHECK_EQ(scanner()->current_token(), Token::ASYNC); DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
}
int pos = position(); int pos = position();
Consume(Token::FUNCTION); Consume(Token::FUNCTION);
IdentifierT name = impl()->NullIdentifier(); IdentifierT name = impl()->NullIdentifier();
......
...@@ -42,6 +42,8 @@ namespace internal { ...@@ -42,6 +42,8 @@ namespace internal {
KEYWORD("finally", Token::FINALLY) \ KEYWORD("finally", Token::FINALLY) \
KEYWORD("for", Token::FOR) \ KEYWORD("for", Token::FOR) \
KEYWORD("function", Token::FUNCTION) \ KEYWORD("function", Token::FUNCTION) \
KEYWORD_GROUP('g') \
KEYWORD("get", Token::GET) \
KEYWORD_GROUP('i') \ KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \ KEYWORD("if", Token::IF) \
KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \
...@@ -62,6 +64,7 @@ namespace internal { ...@@ -62,6 +64,7 @@ namespace internal {
KEYWORD_GROUP('r') \ KEYWORD_GROUP('r') \
KEYWORD("return", Token::RETURN) \ KEYWORD("return", Token::RETURN) \
KEYWORD_GROUP('s') \ KEYWORD_GROUP('s') \
KEYWORD("set", Token::SET) \
KEYWORD("static", Token::STATIC) \ KEYWORD("static", Token::STATIC) \
KEYWORD("super", Token::SUPER) \ KEYWORD("super", Token::SUPER) \
KEYWORD("switch", Token::SWITCH) \ KEYWORD("switch", Token::SWITCH) \
......
...@@ -1007,16 +1007,17 @@ Token::Value Scanner::ScanIdentifierOrKeywordInnerSlow(bool escaped, ...@@ -1007,16 +1007,17 @@ Token::Value Scanner::ScanIdentifierOrKeywordInnerSlow(bool escaped,
Vector<const uint8_t> chars = next().literal_chars.one_byte_literal(); Vector<const uint8_t> chars = next().literal_chars.one_byte_literal();
Token::Value token = Token::Value token =
KeywordOrIdentifierToken(chars.start(), chars.length()); KeywordOrIdentifierToken(chars.start(), chars.length());
/* TODO(adamk): YIELD should be handled specially. */ if (IsInRange(token, Token::IDENTIFIER, Token::YIELD)) return token;
if (token == Token::FUTURE_STRICT_RESERVED_WORD) { if (token == Token::FUTURE_STRICT_RESERVED_WORD) {
if (escaped) return Token::ESCAPED_STRICT_RESERVED_WORD; if (escaped) return Token::ESCAPED_STRICT_RESERVED_WORD;
return token; return token;
} }
if (token == Token::IDENTIFIER) return token;
if (!escaped) return token; if (!escaped) return token;
if (token == Token::LET || token == Token::STATIC) { STATIC_ASSERT(Token::LET + 1 == Token::STATIC);
if (IsInRange(token, Token::LET, Token::STATIC)) {
return Token::ESCAPED_STRICT_RESERVED_WORD; return Token::ESCAPED_STRICT_RESERVED_WORD;
} }
return Token::ESCAPED_KEYWORD; return Token::ESCAPED_KEYWORD;
......
...@@ -316,6 +316,10 @@ class Scanner { ...@@ -316,6 +316,10 @@ class Scanner {
return LiteralContainsEscapes(current()); return LiteralContainsEscapes(current());
} }
bool next_literal_contains_escapes() const {
return LiteralContainsEscapes(next());
}
const AstRawString* CurrentSymbol(AstValueFactory* ast_value_factory) const; const AstRawString* CurrentSymbol(AstValueFactory* ast_value_factory) const;
const AstRawString* NextSymbol(AstValueFactory* ast_value_factory) const; const AstRawString* NextSymbol(AstValueFactory* ast_value_factory) const;
...@@ -517,7 +521,6 @@ class Scanner { ...@@ -517,7 +521,6 @@ class Scanner {
bool CanAccessLiteral() const { bool CanAccessLiteral() const {
return token == Token::PRIVATE_NAME || token == Token::ILLEGAL || return token == Token::PRIVATE_NAME || token == Token::ILLEGAL ||
token == Token::UNINITIALIZED || token == Token::REGEXP_LITERAL || token == Token::UNINITIALIZED || token == Token::REGEXP_LITERAL ||
token == Token::ESCAPED_KEYWORD ||
IsInRange(token, Token::NUMBER, Token::STRING) || IsInRange(token, Token::NUMBER, Token::STRING) ||
(Token::IsAnyIdentifier(token) && !Token::IsKeyword(token)) || (Token::IsAnyIdentifier(token) && !Token::IsKeyword(token)) ||
IsInRange(token, Token::TEMPLATE_SPAN, Token::TEMPLATE_TAIL); IsInRange(token, Token::TEMPLATE_SPAN, Token::TEMPLATE_TAIL);
......
...@@ -34,8 +34,7 @@ const int8_t Token::precedence_[2][NUM_TOKENS] = {{TOKEN_LIST(T1, T1)}, ...@@ -34,8 +34,7 @@ const int8_t Token::precedence_[2][NUM_TOKENS] = {{TOKEN_LIST(T1, T1)},
#undef T2 #undef T2
#undef T1 #undef T1
#define KT(a, b, c) \ #define KT(a, b, c) IsPropertyNameBits::encode(Token::IsAnyIdentifier(a)),
IsPropertyNameBits::encode(Token::IsAnyIdentifier(a) || a == ESCAPED_KEYWORD),
#define KK(a, b, c) \ #define KK(a, b, c) \
IsKeywordBits::encode(true) | IsPropertyNameBits::encode(true), IsKeywordBits::encode(true) | IsPropertyNameBits::encode(true),
const uint8_t Token::token_flags[] = {TOKEN_LIST(KT, KK)}; const uint8_t Token::token_flags[] = {TOKEN_LIST(KT, KK)};
......
...@@ -171,6 +171,8 @@ namespace internal { ...@@ -171,6 +171,8 @@ namespace internal {
/* BEGIN AnyIdentifier */ \ /* BEGIN AnyIdentifier */ \
/* Identifiers (not keywords or future reserved words). */ \ /* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, nullptr, 0) \ T(IDENTIFIER, nullptr, 0) \
K(GET, "get", 0) \
K(SET, "set", 0) \
K(ASYNC, "async", 0) \ K(ASYNC, "async", 0) \
/* `await` is a reserved word in module code only */ \ /* `await` is a reserved word in module code only */ \
K(AWAIT, "await", 0) \ K(AWAIT, "await", 0) \
......
...@@ -92,6 +92,8 @@ TEST(AutoSemicolonToken) { ...@@ -92,6 +92,8 @@ TEST(AutoSemicolonToken) {
bool TokenIsAnyIdentifier(Token::Value token) { bool TokenIsAnyIdentifier(Token::Value token) {
switch (token) { switch (token) {
case Token::IDENTIFIER: case Token::IDENTIFIER:
case Token::GET:
case Token::SET:
case Token::ASYNC: case Token::ASYNC:
case Token::AWAIT: case Token::AWAIT:
case Token::YIELD: case Token::YIELD:
...@@ -116,6 +118,8 @@ bool TokenIsCallable(Token::Value token) { ...@@ -116,6 +118,8 @@ bool TokenIsCallable(Token::Value token) {
switch (token) { switch (token) {
case Token::SUPER: case Token::SUPER:
case Token::IDENTIFIER: case Token::IDENTIFIER:
case Token::GET:
case Token::SET:
case Token::ASYNC: case Token::ASYNC:
case Token::AWAIT: case Token::AWAIT:
case Token::YIELD: case Token::YIELD:
...@@ -140,6 +144,8 @@ bool TokenIsValidIdentifier(Token::Value token, LanguageMode language_mode, ...@@ -140,6 +144,8 @@ bool TokenIsValidIdentifier(Token::Value token, LanguageMode language_mode,
bool is_generator, bool disallow_await) { bool is_generator, bool disallow_await) {
switch (token) { switch (token) {
case Token::IDENTIFIER: case Token::IDENTIFIER:
case Token::GET:
case Token::SET:
case Token::ASYNC: case Token::ASYNC:
return true; return true;
case Token::YIELD: case Token::YIELD:
...@@ -840,19 +846,9 @@ TEST(StreamScanner) { ...@@ -840,19 +846,9 @@ TEST(StreamScanner) {
std::unique_ptr<i::Utf16CharacterStream> stream1( std::unique_ptr<i::Utf16CharacterStream> stream1(
i::ScannerStream::ForTesting(str1)); i::ScannerStream::ForTesting(str1));
i::Token::Value expectations1[] = { i::Token::Value expectations1[] = {
i::Token::LBRACE, i::Token::LBRACE, i::Token::IDENTIFIER, i::Token::GET, i::Token::FOR,
i::Token::IDENTIFIER, i::Token::COLON, i::Token::MUL, i::Token::DIV, i::Token::LT,
i::Token::IDENTIFIER, i::Token::SUB, i::Token::IDENTIFIER, i::Token::EOS, i::Token::ILLEGAL};
i::Token::FOR,
i::Token::COLON,
i::Token::MUL,
i::Token::DIV,
i::Token::LT,
i::Token::SUB,
i::Token::IDENTIFIER,
i::Token::EOS,
i::Token::ILLEGAL
};
TestStreamScanner(stream1.get(), expectations1, 0, 0); TestStreamScanner(stream1.get(), expectations1, 0, 0);
const char* str2 = "case default const {THIS\nPART\nSKIPPED} do"; const char* str2 = "case default const {THIS\nPART\nSKIPPED} do";
...@@ -9379,11 +9375,10 @@ TEST(EscapedKeywords) { ...@@ -9379,11 +9375,10 @@ TEST(EscapedKeywords) {
"class C { st\\u0061tic *bar() {} }", "class C { st\\u0061tic *bar() {} }",
"class C { st\\u0061tic get bar() {} }", "class C { st\\u0061tic get bar() {} }",
"class C { st\\u0061tic set bar() {} }", "class C { st\\u0061tic set bar() {} }",
"(async ()=>{\\u0061wait 100})()",
// TODO(adamk): These should not be errors in sloppy mode. "({\\u0067et get(){}})",
"(y\\u0069eld);", "({\\u0073et set(){}})",
"var y\\u0069eld = 1;", "(async ()=>{var \\u0061wait = 100})()",
"var { y\\u0069eld } = {};",
nullptr nullptr
}; };
// clang-format on // clang-format on
...@@ -9397,6 +9392,9 @@ TEST(EscapedKeywords) { ...@@ -9397,6 +9392,9 @@ TEST(EscapedKeywords) {
"var l\\u0065t = 1;", "var l\\u0065t = 1;",
"l\\u0065t = 1;", "l\\u0065t = 1;",
"(l\\u0065t === 1);", "(l\\u0065t === 1);",
"(y\\u0069eld);",
"var y\\u0069eld = 1;",
"var { y\\u0069eld } = {};",
nullptr nullptr
}; };
// clang-format on // clang-format on
......
...@@ -591,24 +591,6 @@ ...@@ -591,24 +591,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=6538 # https://bugs.chromium.org/p/v8/issues/detail?id=6538
# https://bugs.chromium.org/p/v8/issues/detail?id=6541
'language/export/escaped-as-export-specifier': [FAIL],
'language/export/escaped-from': [FAIL],
'language/expressions/object/method-definition/escaped-get': [FAIL],
'language/expressions/object/method-definition/escaped-set': [FAIL],
'language/import/escaped-as-import-specifier': [FAIL],
'language/import/escaped-as-namespace-import': [FAIL],
'language/import/escaped-from': [FAIL],
'language/statements/for-await-of/escaped-of': [FAIL],
'language/statements/for-of/escaped-of': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=6543
'language/statements/labeled/value-await-non-module-escaped': [FAIL],
'language/statements/labeled/value-yield-non-strict-escaped': [FAIL],
'language/expressions/async-arrow-function/escaped-async-line-terminator': [FAIL],
'language/expressions/class/class-name-ident-await-escaped': [FAIL],
'language/statements/class/class-name-ident-await-escaped': [FAIL],
############################ INVALID TESTS ############################# ############################ INVALID TESTS #############################
# Test makes unjustified assumptions about the number of calls to SortCompare. # Test makes unjustified assumptions about the number of calls to SortCompare.
......
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