[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}
This commit is contained in:
parent
cf9957eeb6
commit
5bf360ef57
@ -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:
|
||||
|
@ -1532,17 +1532,19 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
|
||||
|
||||
void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
i::EnumSet<ParserFlag> flags,
|
||||
ParserSyncTestResult result) {
|
||||
ParserSyncTestResult result,
|
||||
bool is_module = false) {
|
||||
i::Isolate* isolate = CcTest::i_isolate();
|
||||
i::Factory* factory = isolate->factory();
|
||||
|
||||
uintptr_t stack_limit = isolate->stack_guard()->real_climit();
|
||||
int preparser_materialized_literals = -1;
|
||||
int parser_materialized_literals = -2;
|
||||
bool test_preparser = !is_module;
|
||||
|
||||
// Preparse the data.
|
||||
i::CompleteParserRecorder log;
|
||||
{
|
||||
if (test_preparser) {
|
||||
i::Scanner scanner(isolate->unicode_cache());
|
||||
i::GenericStringUtf16CharacterStream stream(source, 0, source->length());
|
||||
i::Zone zone;
|
||||
@ -1556,7 +1558,6 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
&preparser_materialized_literals);
|
||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||
}
|
||||
|
||||
bool preparse_error = log.HasError();
|
||||
|
||||
// Parse the data
|
||||
@ -1567,7 +1568,11 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
i::ParseInfo info(&zone, script);
|
||||
i::Parser parser(&info);
|
||||
SetParserFlags(&parser, flags);
|
||||
info.set_global();
|
||||
if (is_module) {
|
||||
info.set_module();
|
||||
} else {
|
||||
info.set_global();
|
||||
}
|
||||
parser.Parse(&info);
|
||||
function = info.literal();
|
||||
if (function) {
|
||||
@ -1596,7 +1601,7 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
CHECK(false);
|
||||
}
|
||||
|
||||
if (!preparse_error) {
|
||||
if (test_preparser && !preparse_error) {
|
||||
v8::base::OS::Print(
|
||||
"Parser failed on:\n"
|
||||
"\t%s\n"
|
||||
@ -1607,21 +1612,22 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
CHECK(false);
|
||||
}
|
||||
// Check that preparser and parser produce the same error.
|
||||
i::Handle<i::String> preparser_message =
|
||||
FormatMessage(log.ErrorMessageData());
|
||||
if (!i::String::Equals(message_string, preparser_message)) {
|
||||
v8::base::OS::Print(
|
||||
"Expected parser and preparser to produce the same error on:\n"
|
||||
"\t%s\n"
|
||||
"However, found the following error messages\n"
|
||||
"\tparser: %s\n"
|
||||
"\tpreparser: %s\n",
|
||||
source->ToCString().get(),
|
||||
message_string->ToCString().get(),
|
||||
preparser_message->ToCString().get());
|
||||
CHECK(false);
|
||||
if (test_preparser) {
|
||||
i::Handle<i::String> preparser_message =
|
||||
FormatMessage(log.ErrorMessageData());
|
||||
if (!i::String::Equals(message_string, preparser_message)) {
|
||||
v8::base::OS::Print(
|
||||
"Expected parser and preparser to produce the same error on:\n"
|
||||
"\t%s\n"
|
||||
"However, found the following error messages\n"
|
||||
"\tparser: %s\n"
|
||||
"\tpreparser: %s\n",
|
||||
source->ToCString().get(), message_string->ToCString().get(),
|
||||
preparser_message->ToCString().get());
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
} else if (preparse_error) {
|
||||
} else if (test_preparser && preparse_error) {
|
||||
v8::base::OS::Print(
|
||||
"Preparser failed on:\n"
|
||||
"\t%s\n"
|
||||
@ -1638,7 +1644,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
"However, parser and preparser succeeded",
|
||||
source->ToCString().get());
|
||||
CHECK(false);
|
||||
} else if (preparser_materialized_literals != parser_materialized_literals) {
|
||||
} else if (test_preparser &&
|
||||
preparser_materialized_literals != parser_materialized_literals) {
|
||||
v8::base::OS::Print(
|
||||
"Preparser materialized literals (%d) differ from Parser materialized "
|
||||
"literals (%d) on:\n"
|
||||
@ -1651,14 +1658,14 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
||||
}
|
||||
|
||||
|
||||
void TestParserSync(const char* source,
|
||||
const ParserFlag* varying_flags,
|
||||
void TestParserSync(const char* source, const ParserFlag* varying_flags,
|
||||
size_t varying_flags_length,
|
||||
ParserSyncTestResult result = kSuccessOrError,
|
||||
const ParserFlag* always_true_flags = NULL,
|
||||
size_t always_true_flags_length = 0,
|
||||
const ParserFlag* always_false_flags = NULL,
|
||||
size_t always_false_flags_length = 0) {
|
||||
size_t always_false_flags_length = 0,
|
||||
bool is_module = false) {
|
||||
i::Handle<i::String> str =
|
||||
CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(source);
|
||||
for (int bits = 0; bits < (1 << varying_flags_length); bits++) {
|
||||
@ -1675,7 +1682,7 @@ void TestParserSync(const char* source,
|
||||
++flag_index) {
|
||||
flags.Remove(always_false_flags[flag_index]);
|
||||
}
|
||||
TestParserSyncWithFlags(str, flags, result);
|
||||
TestParserSyncWithFlags(str, flags, result, is_module);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1819,12 +1826,11 @@ TEST(StrictOctal) {
|
||||
void RunParserSyncTest(const char* context_data[][2],
|
||||
const char* statement_data[],
|
||||
ParserSyncTestResult result,
|
||||
const ParserFlag* flags = NULL,
|
||||
int flags_len = 0,
|
||||
const ParserFlag* flags = NULL, int flags_len = 0,
|
||||
const ParserFlag* always_true_flags = NULL,
|
||||
int always_true_len = 0,
|
||||
const ParserFlag* always_false_flags = NULL,
|
||||
int always_false_len = 0) {
|
||||
int always_false_len = 0, bool is_module = false) {
|
||||
v8::HandleScope handles(CcTest::isolate());
|
||||
v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
|
||||
v8::Context::Scope context_scope(context);
|
||||
@ -1877,20 +1883,32 @@ void RunParserSyncTest(const char* context_data[][2],
|
||||
statement_data[j],
|
||||
context_data[i][1]);
|
||||
CHECK(length == kProgramSize);
|
||||
TestParserSync(program.start(),
|
||||
flags,
|
||||
flags_len,
|
||||
result,
|
||||
always_true_flags,
|
||||
always_true_len,
|
||||
always_false_flags,
|
||||
always_false_len);
|
||||
TestParserSync(program.start(), flags, flags_len, result,
|
||||
always_true_flags, always_true_len, always_false_flags,
|
||||
always_false_len, is_module);
|
||||
}
|
||||
}
|
||||
delete[] generated_flags;
|
||||
}
|
||||
|
||||
|
||||
void RunModuleParserSyncTest(const char* context_data[][2],
|
||||
const char* statement_data[],
|
||||
ParserSyncTestResult result,
|
||||
const ParserFlag* flags = NULL, int flags_len = 0,
|
||||
const ParserFlag* always_true_flags = NULL,
|
||||
int always_true_len = 0,
|
||||
const ParserFlag* always_false_flags = NULL,
|
||||
int always_false_len = 0) {
|
||||
bool flag = i::FLAG_harmony_modules;
|
||||
i::FLAG_harmony_modules = true;
|
||||
RunParserSyncTest(context_data, statement_data, result, flags, flags_len,
|
||||
always_true_flags, always_true_len, always_false_flags,
|
||||
always_false_len, true);
|
||||
i::FLAG_harmony_modules = flag;
|
||||
}
|
||||
|
||||
|
||||
TEST(ErrorsEvalAndArguments) {
|
||||
// Tests that both preparsing and parsing produce the right kind of errors for
|
||||
// using "eval" and "arguments" as identifiers. Without the strict mode, it's
|
||||
@ -7305,3 +7323,151 @@ TEST(LetSloppyOnly) {
|
||||
RunParserSyncTest(context_data, fail_data, kError, NULL, 0, fail_flags,
|
||||
arraysize(fail_flags));
|
||||
}
|
||||
|
||||
|
||||
TEST(EscapedKeywords) {
|
||||
// clang-format off
|
||||
const char* sloppy_context_data[][2] = {
|
||||
{"", ""},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
const char* strict_context_data[][2] = {
|
||||
{"'use strict';", ""},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
const char* fail_data[] = {
|
||||
"for (var i = 0; i < 100; ++i) { br\\u0065ak; }",
|
||||
"cl\\u0061ss Foo {}",
|
||||
"var x = cl\\u0061ss {}",
|
||||
"\\u0063onst foo = 1;",
|
||||
"while (i < 10) { if (i++ & 1) c\\u006fntinue; this.x++; }",
|
||||
"d\\u0065bugger;",
|
||||
"d\\u0065lete this.a;",
|
||||
"\\u0063o { } while(0)",
|
||||
"if (d\\u006f { true }) {}",
|
||||
"if (false) { this.a = 1; } \\u0065lse { this.b = 1; }",
|
||||
"e\\u0078port var foo;",
|
||||
"try { } catch (e) {} f\\u0069nally { }",
|
||||
"f\\u006fr (var i = 0; i < 10; ++i);",
|
||||
"f\\u0075nction fn() {}",
|
||||
"var f = f\\u0075nction() {}",
|
||||
"\\u0069f (true) { }",
|
||||
"\\u0069mport blah from './foo.js';",
|
||||
"n\\u0065w function f() {}",
|
||||
"(function() { r\\u0065turn; })()",
|
||||
"class C extends function() {} { constructor() { sup\\u0065r() } }",
|
||||
"class C extends function() {} { constructor() { sup\\u0065r.a = 1 } }",
|
||||
"sw\\u0069tch (this.a) {}",
|
||||
"var x = th\\u0069s;",
|
||||
"th\\u0069s.a = 1;",
|
||||
"thr\\u006fw 'boo';",
|
||||
"t\\u0072y { true } catch (e) {}",
|
||||
"var x = typ\\u0065of 'blah'",
|
||||
"v\\u0061r a = true",
|
||||
"var v\\u0061r = true",
|
||||
"(function() { return v\\u006fid 0; })()",
|
||||
"wh\\u0069le (true) { }",
|
||||
"w\\u0069th (this.scope) { }",
|
||||
"(function*() { y\\u0069eld 1; })()",
|
||||
|
||||
"var \\u0065num = 1;",
|
||||
"var { \\u0065num } = {}",
|
||||
"(\\u0065num = 1);",
|
||||
|
||||
// Null / Boolean literals
|
||||
"(x === n\\u0075ll);",
|
||||
"var x = n\\u0075ll;",
|
||||
"var n\\u0075ll = 1;",
|
||||
"var { n\\u0075ll } = { 1 };",
|
||||
"n\\u0075ll = 1;",
|
||||
"(x === tr\\u0075e);",
|
||||
"var x = tr\\u0075e;",
|
||||
"var tr\\u0075e = 1;",
|
||||
"var { tr\\u0075e } = {};",
|
||||
"tr\\u0075e = 1;",
|
||||
"(x === f\\u0061lse);",
|
||||
"var x = f\\u0061lse;",
|
||||
"var f\\u0061lse = 1;",
|
||||
"var { f\\u0061lse } = {};",
|
||||
"f\\u0061lse = 1;",
|
||||
|
||||
// TODO(caitp): consistent error messages for labeled statements and
|
||||
// expressions
|
||||
"switch (this.a) { c\\u0061se 6: break; }",
|
||||
"try { } c\\u0061tch (e) {}",
|
||||
"switch (this.a) { d\\u0065fault: break; }",
|
||||
"class C \\u0065xtends function B() {} {}",
|
||||
"for (var a i\\u006e this) {}",
|
||||
"if ('foo' \\u0069n this) {}",
|
||||
"if (this \\u0069nstanceof Array) {}",
|
||||
"(n\\u0065w function f() {})",
|
||||
"(typ\\u0065of 123)",
|
||||
"(v\\u006fid 0)",
|
||||
"do { ; } wh\\u0069le (true) { }",
|
||||
"(function*() { return (n++, y\\u0069eld 1); })()",
|
||||
"class C { st\\u0061tic bar() {} }",
|
||||
|
||||
"(y\\u0069eld);",
|
||||
"var y\\u0069eld = 1;",
|
||||
"var { y\\u0069eld } = {};",
|
||||
NULL
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static const ParserFlag always_flags[] = {kAllowHarmonySloppy,
|
||||
kAllowHarmonyDestructuring};
|
||||
RunParserSyncTest(sloppy_context_data, fail_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
RunParserSyncTest(strict_context_data, fail_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
RunModuleParserSyncTest(sloppy_context_data, fail_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
|
||||
// clang-format off
|
||||
const char* let_data[] = {
|
||||
"var l\\u0065t = 1;",
|
||||
"l\\u0065t = 1;",
|
||||
"(l\\u0065t === 1);",
|
||||
NULL
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RunParserSyncTest(sloppy_context_data, let_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
RunParserSyncTest(strict_context_data, let_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
|
||||
static const ParserFlag sloppy_let_flags[] = {
|
||||
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kAllowHarmonyDestructuring};
|
||||
RunParserSyncTest(sloppy_context_data, let_data, kError, NULL, 0,
|
||||
sloppy_let_flags, arraysize(sloppy_let_flags));
|
||||
|
||||
// Non-errors in sloppy mode
|
||||
const char* valid_data[] = {"(\\u0069mplements = 1);",
|
||||
"var impl\\u0065ments = 1;",
|
||||
"var { impl\\u0065ments } = {};",
|
||||
"(\\u0069nterface = 1);",
|
||||
"var int\\u0065rface = 1;",
|
||||
"var { int\\u0065rface } = {};",
|
||||
"(p\\u0061ckage = 1);",
|
||||
"var packa\\u0067e = 1;",
|
||||
"var { packa\\u0067e } = {};",
|
||||
"(p\\u0072ivate = 1);",
|
||||
"var p\\u0072ivate;",
|
||||
"var { p\\u0072ivate } = {};",
|
||||
"(prot\\u0065cted);",
|
||||
"var prot\\u0065cted = 1;",
|
||||
"var { prot\\u0065cted } = {};",
|
||||
"(publ\\u0069c);",
|
||||
"var publ\\u0069c = 1;",
|
||||
"var { publ\\u0069c } = {};",
|
||||
NULL};
|
||||
RunParserSyncTest(sloppy_context_data, valid_data, kSuccess, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
RunParserSyncTest(strict_context_data, valid_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
RunModuleParserSyncTest(strict_context_data, valid_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
}
|
||||
|
@ -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],
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user