Commit 0e7f095c authored by littledan's avatar littledan Committed by Commit bot

Restrict FunctionDeclarations in Statement position

ES2015 generally bans FunctionDeclarations in positions which expect a Statement,
as opposed to a StatementListItem, such as a FunctionDeclaration which constitutes
the body of a for loop. However, Annex B 3.2 and 3.4 make exceptions for labeled
function declarations and function declarations as the body of an if statement in
sloppy mode, in the latter case specifying that the semantics are as if the
function declaration occurred in a block. Chrome has historically permitted
further extensions, for the body of any flow control construct.

This patch addresses both the syntactic and semantic mismatches between V8 and
the spec. For the semantic mismatch, function declarations as the body of if
statements change from unconditionally hoisting in certain cases to acquiring
the sloppy mode function in block semantics (based on Annex B 3.3). For the
extra syntax permitted, this patch adds a flag,
--harmony-restrictive-declarations, which excludes disallowed function declaration
cases. A new UseCounter, LegacyFunctionDeclaration, is added to count how often
function declarations occur as the body of other constructs in sloppy mode. With
this patch, the code generally follows the form of the specification with respect
to parsing FunctionDeclarations, rather than allowing them in arbitrary Statement
positions, and makes it more clear where our extensions occur.

BUG=v8:4647
R=adamk
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#34470}
parent 045fa997
...@@ -5485,6 +5485,7 @@ class V8_EXPORT Isolate { ...@@ -5485,6 +5485,7 @@ class V8_EXPORT Isolate {
kArrayPrototypeConstructorModified = 26, kArrayPrototypeConstructorModified = 26,
kArrayInstanceProtoModified = 27, kArrayInstanceProtoModified = 27,
kArrayInstanceConstructorModified = 28, kArrayInstanceConstructorModified = 28,
kLegacyFunctionDeclaration = 29,
// If you add new values here, you'll also need to update V8Initializer.cpp // If you add new values here, you'll also need to update V8Initializer.cpp
// in Chromium. // in Chromium.
......
...@@ -2303,6 +2303,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent) ...@@ -2303,6 +2303,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(promise_extra) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(promise_extra)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_instanceof) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_instanceof)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_declarations)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context, void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) { const char* name, Handle<Symbol> value) {
...@@ -2956,6 +2957,7 @@ bool Genesis::InstallExperimentalNatives() { ...@@ -2956,6 +2957,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_regexp_subclass_natives[] = {nullptr}; static const char* harmony_regexp_subclass_natives[] = {nullptr};
static const char* harmony_regexp_lookbehind_natives[] = {nullptr}; static const char* harmony_regexp_lookbehind_natives[] = {nullptr};
static const char* harmony_instanceof_natives[] = {nullptr}; static const char* harmony_instanceof_natives[] = {nullptr};
static const char* harmony_restrictive_declarations_natives[] = {nullptr};
static const char* harmony_regexp_property_natives[] = {nullptr}; static const char* harmony_regexp_property_natives[] = {nullptr};
static const char* harmony_function_name_natives[] = {nullptr}; static const char* harmony_function_name_natives[] = {nullptr};
static const char* harmony_function_sent_natives[] = {nullptr}; static const char* harmony_function_sent_natives[] = {nullptr};
......
...@@ -212,7 +212,9 @@ DEFINE_IMPLICATION(es_staging, harmony_tailcalls) ...@@ -212,7 +212,9 @@ DEFINE_IMPLICATION(es_staging, harmony_tailcalls)
V(harmony_simd, "harmony simd") \ V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \ V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_tailcalls, "harmony tail calls") \ V(harmony_tailcalls, "harmony tail calls") \
V(harmony_regexp_property, "harmony unicode regexp property classes") V(harmony_regexp_property, "harmony unicode regexp property classes") \
V(harmony_restrictive_declarations, \
"harmony limitations on sloppy mode function declarations")
// Features that are complete (but still behind --harmony/es-staging flag). // Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \ #define HARMONY_STAGED(V) \
......
...@@ -423,6 +423,9 @@ class CallSite { ...@@ -423,6 +423,9 @@ class CallSite {
T(ParenthesisInArgString, "Function arg string contains parenthesis") \ T(ParenthesisInArgString, "Function arg string contains parenthesis") \
T(RuntimeWrongNumArgs, "Runtime function given wrong number of arguments") \ T(RuntimeWrongNumArgs, "Runtime function given wrong number of arguments") \
T(SingleFunctionLiteral, "Single function literal required") \ T(SingleFunctionLiteral, "Single function literal required") \
T(SloppyFunction, \
"In non-strict mode code, functions can only be declared at top level, " \
"inside a block, or as the body of an if statement.") \
T(SloppyLexical, \ T(SloppyLexical, \
"Block-scoped declarations (let, const, function, class) not yet " \ "Block-scoped declarations (let, const, function, class) not yet " \
"supported outside strict mode") \ "supported outside strict mode") \
......
...@@ -114,6 +114,7 @@ class ParserBase : public Traits { ...@@ -114,6 +114,7 @@ class ParserBase : public Traits {
allow_harmony_default_parameters_(false), allow_harmony_default_parameters_(false),
allow_harmony_destructuring_bind_(false), allow_harmony_destructuring_bind_(false),
allow_harmony_destructuring_assignment_(false), allow_harmony_destructuring_assignment_(false),
allow_harmony_restrictive_declarations_(false),
allow_strong_mode_(false), allow_strong_mode_(false),
allow_legacy_const_(true), allow_legacy_const_(true),
allow_harmony_do_expressions_(false), allow_harmony_do_expressions_(false),
...@@ -132,6 +133,7 @@ class ParserBase : public Traits { ...@@ -132,6 +133,7 @@ class ParserBase : public Traits {
ALLOW_ACCESSORS(harmony_default_parameters); ALLOW_ACCESSORS(harmony_default_parameters);
ALLOW_ACCESSORS(harmony_destructuring_bind); ALLOW_ACCESSORS(harmony_destructuring_bind);
ALLOW_ACCESSORS(harmony_destructuring_assignment); ALLOW_ACCESSORS(harmony_destructuring_assignment);
ALLOW_ACCESSORS(harmony_restrictive_declarations);
ALLOW_ACCESSORS(strong_mode); ALLOW_ACCESSORS(strong_mode);
ALLOW_ACCESSORS(legacy_const); ALLOW_ACCESSORS(legacy_const);
ALLOW_ACCESSORS(harmony_do_expressions); ALLOW_ACCESSORS(harmony_do_expressions);
...@@ -951,6 +953,7 @@ class ParserBase : public Traits { ...@@ -951,6 +953,7 @@ class ParserBase : public Traits {
bool allow_harmony_default_parameters_; bool allow_harmony_default_parameters_;
bool allow_harmony_destructuring_bind_; bool allow_harmony_destructuring_bind_;
bool allow_harmony_destructuring_assignment_; bool allow_harmony_destructuring_assignment_;
bool allow_harmony_restrictive_declarations_;
bool allow_strong_mode_; bool allow_strong_mode_;
bool allow_legacy_const_; bool allow_legacy_const_;
bool allow_harmony_do_expressions_; bool allow_harmony_do_expressions_;
......
...@@ -774,6 +774,8 @@ Parser::Parser(ParseInfo* info) ...@@ -774,6 +774,8 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions); set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
set_allow_harmony_function_name(FLAG_harmony_function_name); set_allow_harmony_function_name(FLAG_harmony_function_name);
set_allow_harmony_function_sent(FLAG_harmony_function_sent); set_allow_harmony_function_sent(FLAG_harmony_function_sent);
set_allow_harmony_restrictive_declarations(
FLAG_harmony_restrictive_declarations);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) { ++feature) {
use_counts_[feature] = 0; use_counts_[feature] = 0;
...@@ -1839,26 +1841,18 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels, ...@@ -1839,26 +1841,18 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
case Token::SWITCH: case Token::SWITCH:
return ParseSwitchStatement(labels, ok); return ParseSwitchStatement(labels, ok);
case Token::FUNCTION: { case Token::FUNCTION:
// FunctionDeclaration is only allowed in the context of SourceElements // FunctionDeclaration only allowed as a StatementListItem, not in
// (Ecma 262 5th Edition, clause 14): // an arbitrary Statement position. Exceptions such as
// SourceElement: // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// Statement // are handled by calling ParseScopedStatement rather than
// FunctionDeclaration // ParseSubStatement directly.
// Common language extension is to allow function declaration in place
// of any statement. This language extension is disabled in strict mode.
//
// In Harmony mode, this case also handles the extension:
// Statement:
// GeneratorDeclaration
if (is_strict(language_mode())) {
ReportMessageAt(scanner()->peek_location(), ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kStrictFunction); is_strict(language_mode())
? MessageTemplate::kStrictFunction
: MessageTemplate::kSloppyFunction);
*ok = false; *ok = false;
return NULL; return nullptr;
}
return ParseFunctionDeclaration(NULL, ok);
}
case Token::DEBUGGER: case Token::DEBUGGER:
return ParseDebuggerStatement(ok); return ParseDebuggerStatement(ok);
...@@ -2581,6 +2575,10 @@ Statement* Parser::ParseExpressionOrLabelledStatement( ...@@ -2581,6 +2575,10 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
// during the scope processing. // during the scope processing.
scope_->RemoveUnresolved(var); scope_->RemoveUnresolved(var);
Expect(Token::COLON, CHECK_OK); Expect(Token::COLON, CHECK_OK);
// ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
return ParseFunctionDeclaration(labels, ok);
}
return ParseStatement(labels, ok); return ParseStatement(labels, ok);
} }
...@@ -2621,11 +2619,11 @@ IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels, ...@@ -2621,11 +2619,11 @@ IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels,
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
Expression* condition = ParseExpression(true, CHECK_OK); Expression* condition = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
Statement* then_statement = ParseSubStatement(labels, CHECK_OK); Statement* then_statement = ParseScopedStatement(labels, false, CHECK_OK);
Statement* else_statement = NULL; Statement* else_statement = NULL;
if (peek() == Token::ELSE) { if (peek() == Token::ELSE) {
Next(); Next();
else_statement = ParseSubStatement(labels, CHECK_OK); else_statement = ParseScopedStatement(labels, false, CHECK_OK);
} else { } else {
else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition); else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
} }
...@@ -2824,25 +2822,10 @@ Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels, ...@@ -2824,25 +2822,10 @@ Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels,
scope_->DeclarationScope()->RecordWithStatement(); scope_->DeclarationScope()->RecordWithStatement();
Scope* with_scope = NewScope(scope_, WITH_SCOPE); Scope* with_scope = NewScope(scope_, WITH_SCOPE);
Block* body; Statement* body;
{ BlockState block_state(&scope_, with_scope); { BlockState block_state(&scope_, with_scope);
with_scope->set_start_position(scanner()->peek_location().beg_pos); with_scope->set_start_position(scanner()->peek_location().beg_pos);
body = ParseScopedStatement(labels, true, CHECK_OK);
// The body of the with statement must be enclosed in an additional
// lexical scope in case the body is a FunctionDeclaration.
body = factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition);
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
block_scope->set_start_position(scanner()->location().beg_pos);
{
BlockState block_state(&scope_, block_scope);
Target target(&this->target_stack_, body);
Statement* stmt = ParseSubStatement(labels, CHECK_OK);
body->statements()->Add(stmt, zone());
block_scope->set_end_position(scanner()->location().end_pos);
block_scope = block_scope->FinalizeBlockScope();
body->set_scope(block_scope);
}
with_scope->set_end_position(scanner()->location().end_pos); with_scope->set_end_position(scanner()->location().end_pos);
} }
return factory()->NewWithStatement(with_scope, expr, body, pos); return factory()->NewWithStatement(with_scope, expr, body, pos);
...@@ -3183,7 +3166,7 @@ DoWhileStatement* Parser::ParseDoWhileStatement( ...@@ -3183,7 +3166,7 @@ DoWhileStatement* Parser::ParseDoWhileStatement(
Target target(&this->target_stack_, loop); Target target(&this->target_stack_, loop);
Expect(Token::DO, CHECK_OK); Expect(Token::DO, CHECK_OK);
Statement* body = ParseSubStatement(NULL, CHECK_OK); Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
Expect(Token::WHILE, CHECK_OK); Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
...@@ -3213,7 +3196,7 @@ WhileStatement* Parser::ParseWhileStatement( ...@@ -3213,7 +3196,7 @@ WhileStatement* Parser::ParseWhileStatement(
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
Expression* cond = ParseExpression(true, CHECK_OK); Expression* cond = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseSubStatement(NULL, CHECK_OK); Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
if (loop != NULL) loop->Initialize(cond, body); if (loop != NULL) loop->Initialize(cond, body);
return loop; return loop;
...@@ -3595,6 +3578,28 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( ...@@ -3595,6 +3578,28 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
return outer_block; return outer_block;
} }
Statement* Parser::ParseScopedStatement(ZoneList<const AstRawString*>* labels,
bool legacy, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
(legacy && allow_harmony_restrictive_declarations())) {
return ParseSubStatement(labels, ok);
} else {
if (legacy) {
++use_counts_[v8::Isolate::kLegacyFunctionDeclaration];
}
// Make a block around the statement for a lexical binding
// is introduced by a FunctionDeclaration.
Scope* body_scope = NewScope(scope_, BLOCK_SCOPE);
BlockState block_state(&scope_, body_scope);
Block* block = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
Statement* body = ParseFunctionDeclaration(NULL, CHECK_OK);
block->statements()->Add(body, zone());
body_scope->set_end_position(scanner()->location().end_pos);
body_scope = body_scope->FinalizeBlockScope();
block->set_scope(body_scope);
return block;
}
}
Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
bool* ok) { bool* ok) {
...@@ -3708,7 +3713,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3708,7 +3713,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
{ {
BlockState block_state(&scope_, body_scope); BlockState block_state(&scope_, body_scope);
Statement* body = ParseSubStatement(NULL, CHECK_OK); Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
auto each_initialization_block = auto each_initialization_block =
factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition); factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition);
...@@ -3825,25 +3830,11 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3825,25 +3830,11 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
// Make a block around the statement in case a lexical binding // For legacy compat reasons, give for loops similar treatment to
// is introduced, e.g. by a FunctionDeclaration. // if statements in allowing a function declaration for a body
// This block must not use for_scope as its scope because if a Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
// lexical binding is introduced which overlaps with the for-in/of, InitializeForEachStatement(loop, expression, enumerable, body,
// expressions in head of the loop should actually have variables
// resolved in the outer scope.
Scope* body_scope = NewScope(for_scope, BLOCK_SCOPE);
{
BlockState block_state(&scope_, body_scope);
Block* block =
factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
block->statements()->Add(body, zone());
InitializeForEachStatement(loop, expression, enumerable, block,
is_destructuring); is_destructuring);
body_scope->set_end_position(scanner()->location().end_pos);
body_scope = body_scope->FinalizeBlockScope();
block->set_scope(body_scope);
}
Statement* final_loop = loop->IsForOfStatement() Statement* final_loop = loop->IsForOfStatement()
? FinalizeForOfStatement( ? FinalizeForOfStatement(
...@@ -3900,7 +3891,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, ...@@ -3900,7 +3891,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
body = ParseSubStatement(NULL, CHECK_OK); body = ParseScopedStatement(NULL, true, CHECK_OK);
} }
Statement* result = NULL; Statement* result = NULL;
......
...@@ -919,6 +919,14 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -919,6 +919,14 @@ class Parser : public ParserBase<ParserTraits> {
class CollectExpressionsInTailPositionToListScope; class CollectExpressionsInTailPositionToListScope;
TryStatement* ParseTryStatement(bool* ok); TryStatement* ParseTryStatement(bool* ok);
DebuggerStatement* ParseDebuggerStatement(bool* ok); DebuggerStatement* ParseDebuggerStatement(bool* ok);
// Parse a SubStatement in strict mode, or with an extra block scope in
// sloppy mode to handle
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// The legacy parameter indicates whether function declarations are
// banned by the ES2015 specification in this location, and they are being
// permitted here to match previous V8 behavior.
Statement* ParseScopedStatement(ZoneList<const AstRawString*>* labels,
bool legacy, bool* ok);
// !%_IsJSReceiver(result = iterator.next()) && // !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result) // %ThrowIteratorResultNotAnObject(result)
......
...@@ -323,6 +323,14 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { ...@@ -323,6 +323,14 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
return ParseSubStatement(ok); return ParseSubStatement(ok);
} }
PreParser::Statement PreParser::ParseScopedStatement(bool legacy, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
(legacy && allow_harmony_restrictive_declarations())) {
return ParseSubStatement(ok);
} else {
return ParseFunctionDeclaration(CHECK_OK);
}
}
PreParser::Statement PreParser::ParseSubStatement(bool* ok) { PreParser::Statement PreParser::ParseSubStatement(bool* ok) {
// Statement :: // Statement ::
...@@ -397,20 +405,18 @@ PreParser::Statement PreParser::ParseSubStatement(bool* ok) { ...@@ -397,20 +405,18 @@ PreParser::Statement PreParser::ParseSubStatement(bool* ok) {
case Token::TRY: case Token::TRY:
return ParseTryStatement(ok); return ParseTryStatement(ok);
case Token::FUNCTION: { case Token::FUNCTION:
Scanner::Location start_location = scanner()->peek_location(); // FunctionDeclaration only allowed as a StatementListItem, not in
Statement statement = ParseFunctionDeclaration(CHECK_OK); // an arbitrary Statement position. Exceptions such as
Scanner::Location end_location = scanner()->location(); // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
if (is_strict(language_mode())) { // are handled by calling ParseScopedStatement rather than
PreParserTraits::ReportMessageAt(start_location.beg_pos, // ParseSubStatement directly.
end_location.end_pos, ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kStrictFunction); is_strict(language_mode())
? MessageTemplate::kStrictFunction
: MessageTemplate::kSloppyFunction);
*ok = false; *ok = false;
return Statement::Default(); return Statement::Default();
} else {
return statement;
}
}
case Token::DEBUGGER: case Token::DEBUGGER:
return ParseDebuggerStatement(ok); return ParseDebuggerStatement(ok);
...@@ -698,6 +704,10 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { ...@@ -698,6 +704,10 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
DCHECK(is_sloppy(language_mode()) || DCHECK(is_sloppy(language_mode()) ||
!IsFutureStrictReserved(expr.AsIdentifier())); !IsFutureStrictReserved(expr.AsIdentifier()));
Consume(Token::COLON); Consume(Token::COLON);
// ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
return ParseFunctionDeclaration(ok);
}
Statement statement = ParseStatement(ok); Statement statement = ParseStatement(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
...@@ -726,10 +736,10 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) { ...@@ -726,10 +736,10 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
Statement stat = ParseSubStatement(CHECK_OK); Statement stat = ParseScopedStatement(false, CHECK_OK);
if (peek() == Token::ELSE) { if (peek() == Token::ELSE) {
Next(); Next();
Statement else_stat = ParseSubStatement(CHECK_OK); Statement else_stat = ParseScopedStatement(false, CHECK_OK);
stat = (stat.IsJumpStatement() && else_stat.IsJumpStatement()) ? stat = (stat.IsJumpStatement() && else_stat.IsJumpStatement()) ?
Statement::Jump() : Statement::Default(); Statement::Jump() : Statement::Default();
} else { } else {
...@@ -825,7 +835,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) { ...@@ -825,7 +835,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
Scope* with_scope = NewScope(scope_, WITH_SCOPE); Scope* with_scope = NewScope(scope_, WITH_SCOPE);
BlockState block_state(&scope_, with_scope); BlockState block_state(&scope_, with_scope);
ParseSubStatement(CHECK_OK); ParseScopedStatement(true, CHECK_OK);
return Statement::Default(); return Statement::Default();
} }
...@@ -875,7 +885,7 @@ PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) { ...@@ -875,7 +885,7 @@ PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
// 'do' Statement 'while' '(' Expression ')' ';' // 'do' Statement 'while' '(' Expression ')' ';'
Expect(Token::DO, CHECK_OK); Expect(Token::DO, CHECK_OK);
ParseSubStatement(CHECK_OK); ParseScopedStatement(true, CHECK_OK);
Expect(Token::WHILE, CHECK_OK); Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
...@@ -893,7 +903,7 @@ PreParser::Statement PreParser::ParseWhileStatement(bool* ok) { ...@@ -893,7 +903,7 @@ PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(ok); ParseScopedStatement(true, ok);
return Statement::Default(); return Statement::Default();
} }
...@@ -945,7 +955,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { ...@@ -945,7 +955,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK); ParseScopedStatement(true, CHECK_OK);
return Statement::Default(); return Statement::Default();
} }
} else { } else {
...@@ -983,7 +993,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { ...@@ -983,7 +993,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK); ParseScopedStatement(true, CHECK_OK);
return Statement::Default(); return Statement::Default();
} }
} }
...@@ -1009,7 +1019,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { ...@@ -1009,7 +1019,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(ok); ParseScopedStatement(true, ok);
return Statement::Default(); return Statement::Default();
} }
......
...@@ -1030,6 +1030,7 @@ class PreParser : public ParserBase<PreParserTraits> { ...@@ -1030,6 +1030,7 @@ class PreParser : public ParserBase<PreParserTraits> {
Scanner::BookmarkScope* bookmark = nullptr); Scanner::BookmarkScope* bookmark = nullptr);
Statement ParseStatement(bool* ok); Statement ParseStatement(bool* ok);
Statement ParseSubStatement(bool* ok); Statement ParseSubStatement(bool* ok);
Statement ParseScopedStatement(bool legacy, bool* ok);
Statement ParseFunctionDeclaration(bool* ok); Statement ParseFunctionDeclaration(bool* ok);
Statement ParseClassDeclaration(bool* ok); Statement ParseClassDeclaration(bool* ok);
Statement ParseBlock(bool* ok); Statement ParseBlock(bool* ok);
......
...@@ -1511,7 +1511,8 @@ enum ParserFlag { ...@@ -1511,7 +1511,8 @@ enum ParserFlag {
kAllowHarmonyNewTarget, kAllowHarmonyNewTarget,
kAllowStrongMode, kAllowStrongMode,
kNoLegacyConst, kNoLegacyConst,
kAllowHarmonyFunctionSent kAllowHarmonyFunctionSent,
kAllowHarmonyRestrictiveDeclarations,
}; };
enum ParserSyncTestResult { enum ParserSyncTestResult {
...@@ -1537,6 +1538,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser, ...@@ -1537,6 +1538,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
parser->set_allow_legacy_const(!flags.Contains(kNoLegacyConst)); parser->set_allow_legacy_const(!flags.Contains(kNoLegacyConst));
parser->set_allow_harmony_function_sent( parser->set_allow_harmony_function_sent(
flags.Contains(kAllowHarmonyFunctionSent)); flags.Contains(kAllowHarmonyFunctionSent));
parser->set_allow_harmony_restrictive_declarations(
flags.Contains(kAllowHarmonyRestrictiveDeclarations));
} }
...@@ -8013,3 +8016,72 @@ TEST(NewTargetErrors) { ...@@ -8013,3 +8016,72 @@ TEST(NewTargetErrors) {
// clang-format on // clang-format on
RunParserSyncTest(context_data, error_data, kError); RunParserSyncTest(context_data, error_data, kError);
} }
TEST(FunctionDeclarationError) {
// clang-format off
const char* strict_context[][2] = {
{ "'use strict';", "" },
{ "'use strict'; { ", "}" },
{"(function() { 'use strict';", "})()"},
{"(function() { 'use strict'; {", "} })()"},
{ NULL, NULL }
};
const char* sloppy_context[][2] = {
{ "", "" },
{ "{", "}" },
{"(function() {", "})()"},
{"(function() { {", "} })()"},
{ NULL, NULL }
};
const char* error_data[] = {
"try function foo() {} catch (e) {}",
NULL
};
const char* unrestricted_data[] = {
"do function foo() {} while (0);",
"for (;false;) function foo() {}",
"for (var i = 0; i < 1; i++) function f() { };",
"for (var x in {a: 1}) function f() { };",
"for (var x in {}) function f() { };",
"for (var x in {}) function foo() {}",
"for (x in {a: 1}) function f() { };",
"for (x in {}) function f() { };",
"var x; for (x in {}) function foo() {}",
"with ({}) function f() { };",
NULL
};
const char* sloppy_data[] = {
"if (true) function foo() {}",
"if (false) {} else function f() { };",
"label: function f() { }",
"label: if (true) function f() { }",
"label: if (true) {} else function f() { }",
"if (true) label: function f() {}",
"if (true) {} else label: function f() {}",
NULL
};
// clang-format on
static const ParserFlag restrictive_flags[] = {
kAllowHarmonyRestrictiveDeclarations};
RunParserSyncTest(strict_context, error_data, kError);
RunParserSyncTest(strict_context, error_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(strict_context, unrestricted_data, kError);
RunParserSyncTest(strict_context, unrestricted_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(strict_context, sloppy_data, kError);
RunParserSyncTest(strict_context, sloppy_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(sloppy_context, error_data, kError);
RunParserSyncTest(sloppy_context, error_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(sloppy_context, unrestricted_data, kSuccess);
RunParserSyncTest(sloppy_context, unrestricted_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(sloppy_context, sloppy_data, kSuccess);
RunParserSyncTest(sloppy_context, sloppy_data, kSuccess, restrictive_flags,
arraysize(restrictive_flags));
}
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-harmony-restrictive-declarations
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// Annex B 3.4 FunctionDeclarations in IfStatement Statement Clauses
// In sloppy mode, function declarations in if statements act like
// they have a block around them. Prohibited in strict mode.
(function() {
assertEquals(undefined, f);
if (false) function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (true) function f() { };
assertEquals('function', typeof f);
})();
(function() {
assertEquals(undefined, f);
if (true) {} else function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (false) {} else function f() { };
assertEquals('function', typeof f);
})();
// For legacy reasons, we also support these types of semantics as
// the body of a for or with statement.
(function() {
for (;false;) function f() { };
assertEquals(undefined, f);
})();
(function() {
for (var x in {}) function f() { };
assertEquals(undefined, f);
})();
(function() {
var x;
for (x in {}) function f() { };
assertEquals(undefined, f);
})();
(function() {
for (var i = 0; i < 1; i++) function f() { };
assertEquals('function', typeof f);
})();
(function() {
for (var x in {a: 1}) function f() { };
assertEquals('function', typeof f);
})();
(function() {
var x;
for (x in {a: 1}) function f() { };
assertEquals('function', typeof f);
})();
(function() {
with ({}) function f() { };
assertEquals('function', typeof f);
})();
(function() {
do function f() {} while (0);
assertEquals('function', typeof f);
})();
// Labeled function declarations undergo the same hoisting/FiB semantics as if
// they were unalbeled.
(function() {
function bar() {
return f;
x: function f() {}
}
assertEquals('function', typeof bar());
})();
(function() {
function bar() {
return f;
{
x: function f() {}
}
}
assertEquals(undefined, bar());
})();
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-restrictive-declarations
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// Annex B 3.4 FunctionDeclarations in IfStatement Statement Clauses
// In sloppy mode, function declarations in if statements act like
// they have a block around them. Prohibited in strict mode.
(function() {
if (false) function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (true) function f() { };
assertEquals('function', typeof f);
})();
(function() {
assertEquals(undefined, f);
if (true) {} else function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (false) {} else function f() { };
assertEquals('function', typeof f);
})();
// Labeled function declarations undergo the same hoisting/FiB semantics as if
// they were unalbeled.
(function() {
function bar() {
return f;
x: function f() {}
}
assertEquals('function', typeof bar());
})();
(function() {
function bar() {
return f;
{
x: function f() {}
}
}
assertEquals(undefined, bar());
})();
...@@ -885,6 +885,9 @@ ...@@ -885,6 +885,9 @@
'regress/regress-crbug-42414': [SKIP], 'regress/regress-crbug-42414': [SKIP],
'regress/regress-1853': [SKIP], 'regress/regress-1853': [SKIP],
'regress/regress-crbug-424142': [SKIP], 'regress/regress-crbug-424142': [SKIP],
# with statements no longer always have a block as their body
'regress/regress-95485': [SKIP],
}], # ignition == True }], # ignition == True
['ignition == True and arch == arm64', { ['ignition == True and arch == arm64', {
......
...@@ -672,9 +672,6 @@ ...@@ -672,9 +672,6 @@
# We do not correctly handle assignments within "with" # We do not correctly handle assignments within "with"
'ecma_3/Statements/12.10-01': [FAIL], 'ecma_3/Statements/12.10-01': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=4647
'ecma_3/FunExpr/fe-001': [FAIL_OK],
##################### MOZILLA EXTENSION TESTS ##################### ##################### MOZILLA EXTENSION TESTS #####################
'ecma/extensions/15.1.2.1-1': [FAIL_OK], 'ecma/extensions/15.1.2.1-1': [FAIL_OK],
......
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