diff --git a/src/char-predicates-inl.h b/src/char-predicates-inl.h index 7e198d5808..c05d989e12 100644 --- a/src/char-predicates-inl.h +++ b/src/char-predicates-inl.h @@ -29,12 +29,6 @@ inline bool IsLineFeed(uc32 c) { } -inline bool IsInRange(int value, int lower_limit, int higher_limit) { - DCHECK(lower_limit <= higher_limit); - return static_cast(value - lower_limit) <= - static_cast(higher_limit - lower_limit); -} - inline bool IsAsciiIdentifier(uc32 c) { return IsAlphaNumeric(c) || c == '$' || c == '_'; } diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h index 1791121d73..eb068b238b 100644 --- a/src/parsing/parser-base.h +++ b/src/parsing/parser-base.h @@ -769,14 +769,7 @@ class ParserBase { return result; } - bool is_any_identifier(Token::Value token) { - return token == Token::IDENTIFIER || token == Token::ENUM || - token == Token::AWAIT || token == Token::ASYNC || - token == Token::ESCAPED_STRICT_RESERVED_WORD || - token == Token::FUTURE_STRICT_RESERVED_WORD || token == Token::LET || - token == Token::STATIC || token == Token::YIELD; - } - bool peek_any_identifier() { return is_any_identifier(peek()); } + bool peek_any_identifier() { return Token::IsAnyIdentifier(peek()); } bool CheckContextualKeyword(Token::Value token) { if (PeekContextualKeyword(token)) { @@ -997,7 +990,7 @@ class ParserBase { } bool IsValidArrowFormalParametersStart(Token::Value token) { - return is_any_identifier(token) || token == Token::LPAREN; + return Token::IsAnyIdentifier(token) || token == Token::LPAREN; } void ValidateArrowFormalParameters(ExpressionT expr, diff --git a/src/parsing/token.cc b/src/parsing/token.cc index 258c7b5d09..4cbf244a2b 100644 --- a/src/parsing/token.cc +++ b/src/parsing/token.cc @@ -29,7 +29,6 @@ const uint8_t Token::string_length_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; const int8_t Token::precedence_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; #undef T - #define KT(a, b, c) 'T', #define KK(a, b, c) 'K', #define KC(a, b, c) 'C', diff --git a/src/parsing/token.h b/src/parsing/token.h index cdbc1d8ebb..36544ae8dd 100644 --- a/src/parsing/token.h +++ b/src/parsing/token.h @@ -7,6 +7,7 @@ #include "src/base/logging.h" #include "src/globals.h" +#include "src/utils.h" namespace v8 { namespace internal { @@ -32,6 +33,27 @@ namespace internal { #define IGNORE_TOKEN(name, string, precedence) +/* Binary operators sorted by precedence */ +#define BINARY_OP_TOKEN_LIST(T, E) \ + E(T, BIT_OR, "|", 6) \ + E(T, BIT_XOR, "^", 7) \ + E(T, BIT_AND, "&", 8) \ + E(T, SHL, "<<", 11) \ + E(T, SAR, ">>", 11) \ + E(T, SHR, ">>>", 11) \ + E(T, ADD, "+", 12) \ + E(T, SUB, "-", 12) \ + E(T, MUL, "*", 13) \ + E(T, DIV, "/", 13) \ + E(T, MOD, "%", 13) \ + E(T, EXP, "**", 14) + +#define EXPAND_BINOP_ASSIGN_TOKEN(T, name, string, precedence) \ + T(ASSIGN_##name, string "=", 2) + +#define EXPAND_BINOP_TOKEN(T, name, string, precedence) \ + T(name, string, precedence) + #define TOKEN_LIST(T, K, C) \ /* End of source indicator. */ \ T(EOS, "EOS", 0) \ @@ -57,18 +79,7 @@ namespace internal { /* contiguous and sorted in the same order! */ \ T(INIT, "=init", 2) /* AST-use only. */ \ T(ASSIGN, "=", 2) \ - T(ASSIGN_BIT_OR, "|=", 2) \ - T(ASSIGN_BIT_XOR, "^=", 2) \ - T(ASSIGN_BIT_AND, "&=", 2) \ - T(ASSIGN_SHL, "<<=", 2) \ - T(ASSIGN_SAR, ">>=", 2) \ - T(ASSIGN_SHR, ">>>=", 2) \ - T(ASSIGN_ADD, "+=", 2) \ - T(ASSIGN_SUB, "-=", 2) \ - T(ASSIGN_MUL, "*=", 2) \ - T(ASSIGN_DIV, "/=", 2) \ - T(ASSIGN_MOD, "%=", 2) \ - T(ASSIGN_EXP, "**=", 2) \ + BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_ASSIGN_TOKEN) \ \ /* Binary operators sorted by precedence. */ \ /* IsBinaryOp() relies on this block of enum values */ \ @@ -76,25 +87,14 @@ namespace internal { T(COMMA, ",", 1) \ T(OR, "||", 4) \ T(AND, "&&", 5) \ - T(BIT_OR, "|", 6) \ - T(BIT_XOR, "^", 7) \ - T(BIT_AND, "&", 8) \ - T(SHL, "<<", 11) \ - T(SAR, ">>", 11) \ - T(SHR, ">>>", 11) \ - T(ADD, "+", 12) \ - T(SUB, "-", 12) \ - T(MUL, "*", 13) \ - T(DIV, "/", 13) \ - T(MOD, "%", 13) \ - T(EXP, "**", 14) \ + BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_TOKEN) \ \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ T(EQ, "==", 9) \ - T(NE, "!=", 9) \ T(EQ_STRICT, "===", 9) \ + T(NE, "!=", 9) \ T(NE_STRICT, "!==", 9) \ T(LT, "<", 10) \ T(GT, ">", 10) \ @@ -151,28 +151,27 @@ namespace internal { \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, nullptr, 0) \ - T(PRIVATE_NAME, nullptr, 0) \ - \ - /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ - T(FUTURE_STRICT_RESERVED_WORD, nullptr, 0) \ K(ASYNC, "async", 0) \ /* `await` is a reserved word in module code only */ \ K(AWAIT, "await", 0) \ - K(CLASS, "class", 0) \ - K(CONST, "const", 0) \ K(ENUM, "enum", 0) \ - K(EXPORT, "export", 0) \ - K(EXTENDS, "extends", 0) \ - K(IMPORT, "import", 0) \ K(LET, "let", 0) \ K(STATIC, "static", 0) \ K(YIELD, "yield", 0) \ + /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ + T(FUTURE_STRICT_RESERVED_WORD, nullptr, 0) \ + T(ESCAPED_STRICT_RESERVED_WORD, nullptr, 0) \ + K(CLASS, "class", 0) \ + K(CONST, "const", 0) \ + K(EXPORT, "export", 0) \ + K(EXTENDS, "extends", 0) \ + K(IMPORT, "import", 0) \ K(SUPER, "super", 0) \ + T(PRIVATE_NAME, nullptr, 0) \ \ /* Illegal token - not able to scan. */ \ T(ILLEGAL, "ILLEGAL", 0) \ T(ESCAPED_KEYWORD, nullptr, 0) \ - T(ESCAPED_STRICT_RESERVED_WORD, nullptr, 0) \ \ /* Scanner-internal use only. */ \ T(WHITESPACE, nullptr, 0) \ @@ -243,70 +242,41 @@ class Token { } static bool IsAssignmentOp(Value tok) { - return INIT <= tok && tok <= ASSIGN_EXP; + return IsInRange(tok, INIT, ASSIGN_EXP); } - static bool IsBinaryOp(Value op) { return COMMA <= op && op <= EXP; } + static bool IsBinaryOp(Value op) { return IsInRange(op, COMMA, EXP); } - static bool IsCompareOp(Value op) { - return EQ <= op && op <= IN; - } + static bool IsCompareOp(Value op) { return IsInRange(op, EQ, IN); } static bool IsOrderedRelationalCompareOp(Value op) { - return op == LT || op == LTE || op == GT || op == GTE; + return IsInRange(op, LT, GTE); } - static bool IsEqualityOp(Value op) { - return op == EQ || op == EQ_STRICT; + static bool IsEqualityOp(Value op) { return IsInRange(op, EQ, EQ_STRICT); } + + static bool IsAnyIdentifier(Value tok) { + return IsInRange(tok, IDENTIFIER, ESCAPED_STRICT_RESERVED_WORD); } static Value BinaryOpForAssignment(Value op) { - DCHECK(IsAssignmentOp(op)); - switch (op) { - case Token::ASSIGN_BIT_OR: - return Token::BIT_OR; - case Token::ASSIGN_BIT_XOR: - return Token::BIT_XOR; - case Token::ASSIGN_BIT_AND: - return Token::BIT_AND; - case Token::ASSIGN_SHL: - return Token::SHL; - case Token::ASSIGN_SAR: - return Token::SAR; - case Token::ASSIGN_SHR: - return Token::SHR; - case Token::ASSIGN_ADD: - return Token::ADD; - case Token::ASSIGN_SUB: - return Token::SUB; - case Token::ASSIGN_MUL: - return Token::MUL; - case Token::ASSIGN_DIV: - return Token::DIV; - case Token::ASSIGN_MOD: - return Token::MOD; - case Token::ASSIGN_EXP: - return Token::EXP; - default: - UNREACHABLE(); - } + DCHECK(IsInRange(op, ASSIGN_BIT_OR, ASSIGN_EXP)); + Value result = static_cast(op - ASSIGN_BIT_OR + BIT_OR); + DCHECK(IsBinaryOp(result)); + return result; } static bool IsBitOp(Value op) { - return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; + return IsInRange(op, BIT_OR, SHR) || op == BIT_NOT; } static bool IsUnaryOp(Value op) { - return (NOT <= op && op <= VOID) || op == ADD || op == SUB; + return IsInRange(op, NOT, VOID) || IsInRange(op, ADD, SUB); } - static bool IsCountOp(Value op) { - return op == INC || op == DEC; - } + static bool IsCountOp(Value op) { return IsInRange(op, INC, DEC); } - static bool IsShiftOp(Value op) { - return (SHL <= op) && (op <= SHR); - } + static bool IsShiftOp(Value op) { return IsInRange(op, SHL, SHR); } // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or nullptr if the token doesn't diff --git a/src/utils.h b/src/utils.h index 9f4bb08614..2ef6bbf9a6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -56,6 +56,14 @@ inline bool CStringEquals(const char* s1, const char* s2) { return (s1 == s2) || (s1 != nullptr && s2 != nullptr && strcmp(s1, s2) == 0); } +// Checks if value is in range [lower_limit, higher_limit] using a single +// branch. +inline bool IsInRange(int value, int lower_limit, int higher_limit) { + DCHECK_LE(lower_limit, higher_limit); + return static_cast(value - lower_limit) <= + static_cast(higher_limit - lower_limit); +} + // X must be a power of 2. Returns the number of trailing zeros. template ::value>::type> diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 72e3711405..86e4edacbb 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -71,6 +71,212 @@ void MockUseCounterCallback(v8::Isolate* isolate, } // namespace +bool TokenIsAnyIdentifier(Token::Value tok) { + switch (tok) { + case Token::IDENTIFIER: + case Token::ASYNC: + case Token::AWAIT: + case Token::ENUM: + case Token::LET: + case Token::STATIC: + case Token::YIELD: + case Token::FUTURE_STRICT_RESERVED_WORD: + case Token::ESCAPED_STRICT_RESERVED_WORD: + return true; + default: + return false; + } +} + +TEST(AnyIdentifierToken) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsAnyIdentifier(tok), Token::IsAnyIdentifier(tok)); + } +} + +bool TokenIsAssignmentOp(Token::Value tok) { + switch (tok) { + case Token::INIT: + case Token::ASSIGN: +#define T(name, string, precedence) case Token::name: + BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_ASSIGN_TOKEN) +#undef T + return true; + default: + return false; + } +} + +TEST(AssignmentOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsAssignmentOp(tok), Token::IsAssignmentOp(tok)); + } +} + +bool TokenIsBinaryOp(Token::Value tok) { + switch (tok) { + case Token::COMMA: + case Token::OR: + case Token::AND: +#define T(name, string, precedence) case Token::name: + BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_TOKEN) +#undef T + return true; + default: + return false; + } +} + +TEST(BinaryOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsBinaryOp(tok), Token::IsBinaryOp(tok)); + } +} + +bool TokenIsCompareOp(Token::Value tok) { + switch (tok) { + case Token::EQ: + case Token::EQ_STRICT: + case Token::NE: + case Token::NE_STRICT: + case Token::LT: + case Token::GT: + case Token::LTE: + case Token::GTE: + case Token::INSTANCEOF: + case Token::IN: + return true; + default: + return false; + } +} + +TEST(CompareOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsCompareOp(tok), Token::IsCompareOp(tok)); + } +} + +bool TokenIsOrderedRelationalCompareOp(Token::Value tok) { + switch (tok) { + case Token::LT: + case Token::GT: + case Token::LTE: + case Token::GTE: + return true; + default: + return false; + } +} + +TEST(IsOrderedRelationalCompareOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsOrderedRelationalCompareOp(tok), + Token::IsOrderedRelationalCompareOp(tok)); + } +} + +bool TokenIsEqualityOp(Token::Value tok) { + switch (tok) { + case Token::EQ: + case Token::EQ_STRICT: + return true; + default: + return false; + } +} + +TEST(IsEqualityOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsEqualityOp(tok), Token::IsEqualityOp(tok)); + } +} + +bool TokenIsBitOp(Token::Value tok) { + switch (tok) { + case Token::BIT_OR: + case Token::BIT_XOR: + case Token::BIT_AND: + case Token::SHL: + case Token::SAR: + case Token::SHR: + case Token::BIT_NOT: + return true; + default: + return false; + } +} + +TEST(IsBitOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsBitOp(tok), Token::IsBitOp(tok)); + } +} + +bool TokenIsUnaryOp(Token::Value tok) { + switch (tok) { + case Token::NOT: + case Token::BIT_NOT: + case Token::DELETE: + case Token::TYPEOF: + case Token::VOID: + case Token::ADD: + case Token::SUB: + return true; + default: + return false; + } +} + +TEST(IsUnaryOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsUnaryOp(tok), Token::IsUnaryOp(tok)); + } +} + +bool TokenIsCountOp(Token::Value tok) { + switch (tok) { + case Token::INC: + case Token::DEC: + return true; + default: + return false; + } +} + +TEST(IsCountOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsCountOp(tok), Token::IsCountOp(tok)); + } +} + +bool TokenIsShiftOp(Token::Value tok) { + switch (tok) { + case Token::SHL: + case Token::SAR: + case Token::SHR: + return true; + default: + return false; + } +} + +TEST(IsShiftOp) { + for (int i = 0; i < Token::NUM_TOKENS; i++) { + Token::Value tok = static_cast(i); + CHECK_EQ(TokenIsShiftOp(tok), Token::IsShiftOp(tok)); + } +} + TEST(ScanKeywords) { struct KeywordToken { const char* keyword;