Implement ES2015 labelled function declaration restrictions

ES#sec-islabelledfunction specifies that labelled function declarations
may not occur as the body of a control flow construct such as an if
statement. This patch implements those restrictions, which also
eliminates a previous case resulting in a DCHECK failure which is now
a SyntaxError.

BUG=chromium:595309
R=adamk
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#35049}
This commit is contained in:
littledan 2016-03-23 18:57:53 -07:00 committed by Commit bot
parent b224651b73
commit 7f108b655b
7 changed files with 73 additions and 31 deletions

View File

@ -398,6 +398,9 @@ class CallSite {
T(JsonParseUnexpectedTokenNumber, "Unexpected number in JSON at position %") \ T(JsonParseUnexpectedTokenNumber, "Unexpected number in JSON at position %") \
T(JsonParseUnexpectedTokenString, "Unexpected string in JSON at position %") \ T(JsonParseUnexpectedTokenString, "Unexpected string in JSON at position %") \
T(LabelRedeclaration, "Label '%' has already been declared") \ T(LabelRedeclaration, "Label '%' has already been declared") \
T(LabelledFunctionDeclaration, \
"Labelled function declaration not allowed as the body of a control flow " \
"structure") \
T(MalformedArrowFunParamList, "Malformed arrow function parameter list") \ T(MalformedArrowFunParamList, "Malformed arrow function parameter list") \
T(MalformedRegExp, "Invalid regular expression: /%/: %") \ T(MalformedRegExp, "Invalid regular expression: /%/: %") \
T(MalformedRegExpFlags, "Invalid regular expression flags") \ T(MalformedRegExpFlags, "Invalid regular expression flags") \

View File

@ -24,6 +24,10 @@ enum FunctionNameValidity {
kFunctionNameValidityUnknown kFunctionNameValidityUnknown
}; };
enum AllowLabelledFunctionStatement {
kAllowLabelledFunctionStatement,
kDisallowLabelledFunctionStatement,
};
struct FormalParametersBase { struct FormalParametersBase {
explicit FormalParametersBase(Scope* scope) : scope(scope) {} explicit FormalParametersBase(Scope* scope) : scope(scope) {}

View File

@ -1271,7 +1271,7 @@ Statement* Parser::ParseStatementListItem(bool* ok) {
default: default:
break; break;
} }
return ParseStatement(NULL, ok); return ParseStatement(NULL, kAllowLabelledFunctionStatement, ok);
} }
@ -1721,8 +1721,8 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
return result; return result;
} }
Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels, Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
AllowLabelledFunctionStatement allow_function,
bool* ok) { bool* ok) {
// Statement :: // Statement ::
// EmptyStatement // EmptyStatement
@ -1732,12 +1732,12 @@ Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
Next(); Next();
return factory()->NewEmptyStatement(RelocInfo::kNoPosition); return factory()->NewEmptyStatement(RelocInfo::kNoPosition);
} }
return ParseSubStatement(labels, ok); return ParseSubStatement(labels, allow_function, ok);
} }
Statement* Parser::ParseSubStatement(
Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels, ZoneList<const AstRawString*>* labels,
bool* ok) { AllowLabelledFunctionStatement allow_function, bool* ok) {
// Statement :: // Statement ::
// Block // Block
// VariableStatement // VariableStatement
@ -1826,7 +1826,7 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
return ParseVariableStatement(kStatement, NULL, ok); return ParseVariableStatement(kStatement, NULL, ok);
default: default:
return ParseExpressionOrLabelledStatement(labels, ok); return ParseExpressionOrLabelledStatement(labels, allow_function, ok);
} }
} }
@ -2415,9 +2415,9 @@ static bool ContainsLabel(ZoneList<const AstRawString*>* labels,
return false; return false;
} }
Statement* Parser::ParseExpressionOrLabelledStatement( Statement* Parser::ParseExpressionOrLabelledStatement(
ZoneList<const AstRawString*>* labels, bool* ok) { ZoneList<const AstRawString*>* labels,
AllowLabelledFunctionStatement allow_function, bool* ok) {
// ExpressionStatement | LabelledStatement :: // ExpressionStatement | LabelledStatement ::
// Expression ';' // Expression ';'
// Identifier ':' Statement // Identifier ':' Statement
@ -2470,9 +2470,13 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
Expect(Token::COLON, CHECK_OK); Expect(Token::COLON, CHECK_OK);
// ES#sec-labelled-function-declarations Labelled Function Declarations // ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) { if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
return ParseFunctionDeclaration(labels, ok); if (allow_function == kAllowLabelledFunctionStatement) {
return ParseFunctionDeclaration(labels, ok);
} else {
return ParseScopedStatement(labels, true, ok);
}
} }
return ParseStatement(labels, ok); return ParseStatement(labels, kDisallowLabelledFunctionStatement, ok);
} }
// If we have an extension, we allow a native function declaration. // If we have an extension, we allow a native function declaration.
@ -3460,7 +3464,7 @@ Statement* Parser::ParseScopedStatement(ZoneList<const AstRawString*>* labels,
bool legacy, bool* ok) { bool legacy, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION || if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
(legacy && allow_harmony_restrictive_declarations())) { (legacy && allow_harmony_restrictive_declarations())) {
return ParseSubStatement(labels, ok); return ParseSubStatement(labels, kDisallowLabelledFunctionStatement, ok);
} else { } else {
if (legacy) { if (legacy) {
++use_counts_[v8::Isolate::kLegacyFunctionDeclaration]; ++use_counts_[v8::Isolate::kLegacyFunctionDeclaration];

View File

@ -757,8 +757,12 @@ class Parser : public ParserBase<ParserTraits> {
ZoneList<const AstRawString*>* local_names, ZoneList<const AstRawString*>* local_names,
Scanner::Location* reserved_loc, bool* ok); Scanner::Location* reserved_loc, bool* ok);
ZoneList<ImportDeclaration*>* ParseNamedImports(int pos, bool* ok); ZoneList<ImportDeclaration*>* ParseNamedImports(int pos, bool* ok);
Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok); Statement* ParseStatement(ZoneList<const AstRawString*>* labels,
Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels, bool* ok); AllowLabelledFunctionStatement allow_function,
bool* ok);
Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels,
AllowLabelledFunctionStatement allow_function,
bool* ok);
Statement* ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels, Statement* ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels,
bool* ok); bool* ok);
Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names, Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
@ -900,7 +904,8 @@ class Parser : public ParserBase<ParserTraits> {
ZoneList<const AstRawString*>* names, ZoneList<const AstRawString*>* names,
bool* ok); bool* ok);
Statement* ParseExpressionOrLabelledStatement( Statement* ParseExpressionOrLabelledStatement(
ZoneList<const AstRawString*>* labels, bool* ok); ZoneList<const AstRawString*>* labels,
AllowLabelledFunctionStatement allow_function, bool* ok);
IfStatement* ParseIfStatement(ZoneList<const AstRawString*>* labels, IfStatement* ParseIfStatement(ZoneList<const AstRawString*>* labels,
bool* ok); bool* ok);
Statement* ParseContinueStatement(bool* ok); Statement* ParseContinueStatement(bool* ok);

View File

@ -194,7 +194,7 @@ PreParser::Statement PreParser::ParseStatementListItem(bool* ok) {
default: default:
break; break;
} }
return ParseStatement(ok); return ParseStatement(kAllowLabelledFunctionStatement, ok);
} }
@ -263,8 +263,8 @@ void PreParser::ParseStatementList(int end_token, bool* ok,
#define DUMMY ) // to make indentation work #define DUMMY ) // to make indentation work
#undef DUMMY #undef DUMMY
PreParser::Statement PreParser::ParseStatement(
PreParser::Statement PreParser::ParseStatement(bool* ok) { AllowLabelledFunctionStatement allow_function, bool* ok) {
// Statement :: // Statement ::
// EmptyStatement // EmptyStatement
// ... // ...
@ -273,19 +273,20 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
Next(); Next();
return Statement::Default(); return Statement::Default();
} }
return ParseSubStatement(ok); return ParseSubStatement(allow_function, ok);
} }
PreParser::Statement PreParser::ParseScopedStatement(bool legacy, bool* ok) { PreParser::Statement PreParser::ParseScopedStatement(bool legacy, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION || if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
(legacy && allow_harmony_restrictive_declarations())) { (legacy && allow_harmony_restrictive_declarations())) {
return ParseSubStatement(ok); return ParseSubStatement(kDisallowLabelledFunctionStatement, ok);
} else { } else {
return ParseFunctionDeclaration(CHECK_OK); return ParseFunctionDeclaration(CHECK_OK);
} }
} }
PreParser::Statement PreParser::ParseSubStatement(bool* ok) { PreParser::Statement PreParser::ParseSubStatement(
AllowLabelledFunctionStatement allow_function, bool* ok) {
// Statement :: // Statement ::
// Block // Block
// VariableStatement // VariableStatement
@ -372,7 +373,7 @@ PreParser::Statement PreParser::ParseSubStatement(bool* ok) {
return ParseVariableStatement(kStatement, ok); return ParseVariableStatement(kStatement, ok);
default: default:
return ParseExpressionOrLabelledStatement(ok); return ParseExpressionOrLabelledStatement(allow_function, ok);
} }
} }
@ -555,8 +556,8 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
return Statement::Default(); return Statement::Default();
} }
PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(
PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { AllowLabelledFunctionStatement allow_function, bool* ok) {
// ExpressionStatement | LabelledStatement :: // ExpressionStatement | LabelledStatement ::
// Expression ';' // Expression ';'
// Identifier ':' Statement // Identifier ':' Statement
@ -591,9 +592,14 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
Consume(Token::COLON); Consume(Token::COLON);
// ES#sec-labelled-function-declarations Labelled Function Declarations // ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) { if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
return ParseFunctionDeclaration(ok); if (allow_function == kAllowLabelledFunctionStatement) {
return ParseFunctionDeclaration(ok);
} else {
return ParseScopedStatement(true, ok);
}
} }
Statement statement = ParseStatement(ok); Statement statement =
ParseStatement(kDisallowLabelledFunctionStatement, ok);
return statement.IsJumpStatement() ? Statement::Default() : statement; return statement.IsJumpStatement() ? Statement::Default() : statement;
// Preparsing is disabled for extensions (because the extension details // Preparsing is disabled for extensions (because the extension details
// aren't passed to lazily compiled functions), so we don't // aren't passed to lazily compiled functions), so we don't

View File

@ -1019,8 +1019,10 @@ class PreParser : public ParserBase<PreParserTraits> {
Statement ParseStatementListItem(bool* ok); Statement ParseStatementListItem(bool* ok);
void ParseStatementList(int end_token, bool* ok, void ParseStatementList(int end_token, bool* ok,
Scanner::BookmarkScope* bookmark = nullptr); Scanner::BookmarkScope* bookmark = nullptr);
Statement ParseStatement(bool* ok); Statement ParseStatement(AllowLabelledFunctionStatement allow_function,
Statement ParseSubStatement(bool* ok); bool* ok);
Statement ParseSubStatement(AllowLabelledFunctionStatement allow_function,
bool* ok);
Statement ParseScopedStatement(bool legacy, bool* ok); Statement ParseScopedStatement(bool legacy, bool* ok);
Statement ParseFunctionDeclaration(bool* ok); Statement ParseFunctionDeclaration(bool* ok);
Statement ParseClassDeclaration(bool* ok); Statement ParseClassDeclaration(bool* ok);
@ -1033,7 +1035,8 @@ class PreParser : public ParserBase<PreParserTraits> {
Scanner::Location* first_initializer_loc, Scanner::Location* first_initializer_loc,
Scanner::Location* bindings_loc, Scanner::Location* bindings_loc,
bool* ok); bool* ok);
Statement ParseExpressionOrLabelledStatement(bool* ok); Statement ParseExpressionOrLabelledStatement(
AllowLabelledFunctionStatement allow_function, bool* ok);
Statement ParseIfStatement(bool* ok); Statement ParseIfStatement(bool* ok);
Statement ParseContinueStatement(bool* ok); Statement ParseContinueStatement(bool* ok);
Statement ParseBreakStatement(bool* ok); Statement ParseBreakStatement(bool* ok);

View File

@ -7217,10 +7217,13 @@ TEST(FunctionDeclarationError) {
{"(function() { {", "} })()"}, {"(function() { {", "} })()"},
{ NULL, NULL } { NULL, NULL }
}; };
// Invalid in all contexts
const char* error_data[] = { const char* error_data[] = {
"try function foo() {} catch (e) {}", "try function foo() {} catch (e) {}",
NULL NULL
}; };
// Valid in sloppy mode only, and only when the
// --harmony-restrictive-declarations flag is off
const char* unrestricted_data[] = { const char* unrestricted_data[] = {
"do function foo() {} while (0);", "do function foo() {} while (0);",
"for (;false;) function foo() {}", "for (;false;) function foo() {}",
@ -7232,16 +7235,28 @@ TEST(FunctionDeclarationError) {
"for (x in {}) function f() { };", "for (x in {}) function f() { };",
"var x; for (x in {}) function foo() {}", "var x; for (x in {}) function foo() {}",
"with ({}) function f() { };", "with ({}) function f() { };",
"do label: function foo() {} while (0);",
"for (;false;) label: function foo() {}",
"for (var i = 0; i < 1; i++) label: function f() { };",
"for (var x in {a: 1}) label: function f() { };",
"for (var x in {}) label: function f() { };",
"for (var x in {}) label: function foo() {}",
"for (x in {a: 1}) label: function f() { };",
"for (x in {}) label: function f() { };",
"var x; for (x in {}) label: function foo() {}",
"with ({}) label: function f() { };",
"if (true) label: function f() {}",
"if (true) {} else label: function f() {}",
NULL NULL
}; };
// Valid only in sloppy mode, with or without
// --harmony-restrictive-declarations
const char* sloppy_data[] = { const char* sloppy_data[] = {
"if (true) function foo() {}", "if (true) function foo() {}",
"if (false) {} else function f() { };", "if (false) {} else function f() { };",
"label: function f() { }", "label: function f() { }",
"label: if (true) function f() { }", "label: if (true) function f() { }",
"label: if (true) {} else function f() { }", "label: if (true) {} else function f() { }",
"if (true) label: function f() {}",
"if (true) {} else label: function f() {}",
NULL NULL
}; };
// clang-format on // clang-format on
@ -7249,6 +7264,7 @@ TEST(FunctionDeclarationError) {
static const ParserFlag restrictive_flags[] = { static const ParserFlag restrictive_flags[] = {
kAllowHarmonyRestrictiveDeclarations}; kAllowHarmonyRestrictiveDeclarations};
// Nothing parses in strict mode without a SyntaxError
RunParserSyncTest(strict_context, error_data, kError); RunParserSyncTest(strict_context, error_data, kError);
RunParserSyncTest(strict_context, error_data, kError, NULL, 0, RunParserSyncTest(strict_context, error_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags)); restrictive_flags, arraysize(restrictive_flags));
@ -7259,6 +7275,7 @@ TEST(FunctionDeclarationError) {
RunParserSyncTest(strict_context, sloppy_data, kError, NULL, 0, RunParserSyncTest(strict_context, sloppy_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags)); restrictive_flags, arraysize(restrictive_flags));
// In sloppy mode, some things are successful, depending on the flag
RunParserSyncTest(sloppy_context, error_data, kError); RunParserSyncTest(sloppy_context, error_data, kError);
RunParserSyncTest(sloppy_context, error_data, kError, NULL, 0, RunParserSyncTest(sloppy_context, error_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags)); restrictive_flags, arraysize(restrictive_flags));