Commit 13616615 authored by arv's avatar arv Committed by Commit bot

Lexical declarations should not be allowed in Statement

For example let and class should only be allowed inside function/block/script.

We have to continue to support const in statements in sloppy mode for backwards compatibility.

BUG=3831
LOG=Y
R=dslomov@chromium.org, adamk

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

Cr-Commit-Position: refs/heads/master@{#26337}
parent b004b1d8
...@@ -165,8 +165,6 @@ var kMessages = { ...@@ -165,8 +165,6 @@ var kMessages = {
strict_caller: ["Illegal access to a strict mode caller function."], strict_caller: ["Illegal access to a strict mode caller function."],
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"], malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."], generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."],
unprotected_let: ["Illegal let declaration in unprotected statement context."],
unprotected_const: ["Illegal const declaration in unprotected statement context."],
cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"], cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
redef_external_array_element: ["Cannot redefine a property of an object with external array elements"], redef_external_array_element: ["Cannot redefine a property of an object with external array elements"],
harmony_const_assign: ["Assignment to constant variable."], harmony_const_assign: ["Assignment to constant variable."],
......
...@@ -1618,22 +1618,21 @@ Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels, ...@@ -1618,22 +1618,21 @@ Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
return ParseFunctionDeclaration(NULL, ok); return ParseFunctionDeclaration(NULL, ok);
} }
case Token::CLASS:
return ParseClassDeclaration(NULL, ok);
case Token::DEBUGGER: case Token::DEBUGGER:
return ParseDebuggerStatement(ok); return ParseDebuggerStatement(ok);
case Token::VAR: case Token::VAR:
case Token::CONST:
return ParseVariableStatement(kStatement, NULL, ok); return ParseVariableStatement(kStatement, NULL, ok);
case Token::LET: case Token::CONST:
DCHECK(allow_harmony_scoping()); // In ES6 CONST is not allowed as a Statement, only as a
if (strict_mode() == STRICT) { // LexicalDeclaration, however we continue to allow it in sloppy mode for
// backwards compatibility.
if (strict_mode() == SLOPPY) {
return ParseVariableStatement(kStatement, NULL, ok); return ParseVariableStatement(kStatement, NULL, ok);
} }
// Fall through.
// Fall through.
default: default:
return ParseExpressionOrLabelledStatement(labels, ok); return ParseExpressionOrLabelledStatement(labels, ok);
} }
...@@ -2039,16 +2038,6 @@ Block* Parser::ParseVariableDeclarations( ...@@ -2039,16 +2038,6 @@ Block* Parser::ParseVariableDeclarations(
if (peek() == Token::VAR) { if (peek() == Token::VAR) {
Consume(Token::VAR); Consume(Token::VAR);
} else if (peek() == Token::CONST) { } else if (peek() == Token::CONST) {
// TODO(ES6): The ES6 Draft Rev4 section 12.2.2 reads:
//
// ConstDeclaration : const ConstBinding (',' ConstBinding)* ';'
//
// * It is a Syntax Error if the code that matches this production is not
// contained in extended code.
//
// However disallowing const in sloppy mode will break compatibility with
// existing pages. Therefore we keep allowing const with the old
// non-harmony semantics in sloppy mode.
Consume(Token::CONST); Consume(Token::CONST);
switch (strict_mode()) { switch (strict_mode()) {
case SLOPPY: case SLOPPY:
...@@ -2056,33 +2045,22 @@ Block* Parser::ParseVariableDeclarations( ...@@ -2056,33 +2045,22 @@ Block* Parser::ParseVariableDeclarations(
init_op = Token::INIT_CONST_LEGACY; init_op = Token::INIT_CONST_LEGACY;
break; break;
case STRICT: case STRICT:
if (allow_harmony_scoping()) { DCHECK(var_context != kStatement);
if (var_context == kStatement) { // In ES5 const is not allowed in strict mode.
// In strict mode 'const' declarations are only allowed in source if (!allow_harmony_scoping()) {
// element positions.
ReportMessage("unprotected_const");
*ok = false;
return NULL;
}
mode = CONST;
init_op = Token::INIT_CONST;
} else {
ReportMessage("strict_const"); ReportMessage("strict_const");
*ok = false; *ok = false;
return NULL; return NULL;
} }
mode = CONST;
init_op = Token::INIT_CONST;
} }
is_const = true; is_const = true;
needs_init = true; needs_init = true;
} else if (peek() == Token::LET && strict_mode() == STRICT) { } else if (peek() == Token::LET && strict_mode() == STRICT) {
DCHECK(allow_harmony_scoping()); DCHECK(allow_harmony_scoping());
Consume(Token::LET); Consume(Token::LET);
if (var_context == kStatement) { DCHECK(var_context != kStatement);
// Let declarations are only allowed in source element positions.
ReportMessage("unprotected_let");
*ok = false;
return NULL;
}
mode = LET; mode = LET;
needs_init = true; needs_init = true;
init_op = Token::INIT_LET; init_op = Token::INIT_LET;
...@@ -2345,6 +2323,26 @@ Statement* Parser::ParseExpressionOrLabelledStatement( ...@@ -2345,6 +2323,26 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
// ExpressionStatement | LabelledStatement :: // ExpressionStatement | LabelledStatement ::
// Expression ';' // Expression ';'
// Identifier ':' Statement // Identifier ':' Statement
//
// ExpressionStatement[Yield] :
// [lookahead ∉ {{, function, class, let [}] Expression[In, ?Yield] ;
switch (peek()) {
case Token::FUNCTION:
case Token::LBRACE:
UNREACHABLE(); // Always handled by the callers.
case Token::CLASS:
ReportUnexpectedToken(Next());
*ok = false;
return nullptr;
// TODO(arv): Handle `let [`
// https://code.google.com/p/v8/issues/detail?id=3847
default:
break;
}
int pos = peek_position(); int pos = peek_position();
bool starts_with_idenfifier = peek_any_identifier(); bool starts_with_idenfifier = peek_any_identifier();
Expression* expr = ParseExpression(true, CHECK_OK); Expression* expr = ParseExpression(true, CHECK_OK);
......
...@@ -162,16 +162,22 @@ PreParserExpression PreParserTraits::ParseClassLiteral( ...@@ -162,16 +162,22 @@ PreParserExpression PreParserTraits::ParseClassLiteral(
PreParser::Statement PreParser::ParseSourceElement(bool* ok) { PreParser::Statement PreParser::ParseSourceElement(bool* ok) {
// (Ecma 262 5th Edition, clause 14): // ECMA 262 6th Edition
// SourceElement: // StatementListItem[Yield, Return] :
// Statement // Statement[?Yield, ?Return]
// FunctionDeclaration // Declaration[?Yield]
// //
// In harmony mode we allow additionally the following productions // Declaration[Yield] :
// SourceElement: // HoistableDeclaration[?Yield]
// LetDeclaration // ClassDeclaration[?Yield]
// ConstDeclaration // LexicalDeclaration[In, ?Yield]
// GeneratorDeclaration //
// HoistableDeclaration[Yield, Default] :
// FunctionDeclaration[?Yield, ?Default]
// GeneratorDeclaration[?Yield, ?Default]
//
// LexicalDeclaration[In, Yield] :
// LetOrConst BindingList[?In, ?Yield] ;
switch (peek()) { switch (peek()) {
case Token::FUNCTION: case Token::FUNCTION:
...@@ -305,22 +311,21 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { ...@@ -305,22 +311,21 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
} }
} }
case Token::CLASS:
return ParseClassDeclaration(CHECK_OK);
case Token::DEBUGGER: case Token::DEBUGGER:
return ParseDebuggerStatement(ok); return ParseDebuggerStatement(ok);
case Token::VAR: case Token::VAR:
case Token::CONST:
return ParseVariableStatement(kStatement, ok); return ParseVariableStatement(kStatement, ok);
case Token::LET: case Token::CONST:
DCHECK(allow_harmony_scoping()); // In ES6 CONST is not allowed as a Statement, only as a
if (strict_mode() == STRICT) { // LexicalDeclaration, however we continue to allow it in sloppy mode for
// backwards compatibility.
if (strict_mode() == SLOPPY) {
return ParseVariableStatement(kStatement, ok); return ParseVariableStatement(kStatement, ok);
} }
// Fall through.
// Fall through.
default: default:
return ParseExpressionOrLabelledStatement(ok); return ParseExpressionOrLabelledStatement(ok);
} }
...@@ -441,28 +446,19 @@ PreParser::Statement PreParser::ParseVariableDeclarations( ...@@ -441,28 +446,19 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
// non-harmony semantics in sloppy mode. // non-harmony semantics in sloppy mode.
Consume(Token::CONST); Consume(Token::CONST);
if (strict_mode() == STRICT) { if (strict_mode() == STRICT) {
if (allow_harmony_scoping()) { DCHECK(var_context != kStatement);
if (var_context != kSourceElement && var_context != kForStatement) { if (!allow_harmony_scoping()) {
ReportMessageAt(scanner()->peek_location(), "unprotected_const");
*ok = false;
return Statement::Default();
}
is_strict_const = true;
require_initializer = var_context != kForStatement;
} else {
Scanner::Location location = scanner()->peek_location(); Scanner::Location location = scanner()->peek_location();
ReportMessageAt(location, "strict_const"); ReportMessageAt(location, "strict_const");
*ok = false; *ok = false;
return Statement::Default(); return Statement::Default();
} }
is_strict_const = true;
require_initializer = var_context != kForStatement;
} }
} else if (peek() == Token::LET && strict_mode() == STRICT) { } else if (peek() == Token::LET && strict_mode() == STRICT) {
Consume(Token::LET); Consume(Token::LET);
if (var_context != kSourceElement && var_context != kForStatement) { DCHECK(var_context != kStatement);
ReportMessageAt(scanner()->peek_location(), "unprotected_let");
*ok = false;
return Statement::Default();
}
} else { } else {
*ok = false; *ok = false;
return Statement::Default(); return Statement::Default();
...@@ -497,6 +493,22 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { ...@@ -497,6 +493,22 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
// Expression ';' // Expression ';'
// Identifier ':' Statement // Identifier ':' Statement
switch (peek()) {
case Token::FUNCTION:
case Token::LBRACE:
UNREACHABLE(); // Always handled by the callers.
case Token::CLASS:
ReportUnexpectedToken(Next());
*ok = false;
return Statement::Default();
// TODO(arv): Handle `let [`
// https://code.google.com/p/v8/issues/detail?id=3847
default:
break;
}
bool starts_with_identifier = peek_any_identifier(); bool starts_with_identifier = peek_any_identifier();
Expression expr = ParseExpression(true, CHECK_OK); Expression expr = ParseExpression(true, CHECK_OK);
// Even if the expression starts with an identifier, it is not necessarily an // Even if the expression starts with an identifier, it is not necessarily an
......
...@@ -3788,9 +3788,9 @@ TEST(ClassExpressionNoErrors) { ...@@ -3788,9 +3788,9 @@ TEST(ClassExpressionNoErrors) {
TEST(ClassDeclarationNoErrors) { TEST(ClassDeclarationNoErrors) {
const char* context_data[][2] = {{"", ""}, const char* context_data[][2] = {{"'use strict'; ", ""},
{"{", "}"}, {"'use strict'; {", "}"},
{"if (true) {", "}"}, {"'use strict'; if (true) {", "}"},
{NULL, NULL}}; {NULL, NULL}};
const char* statement_data[] = { const char* statement_data[] = {
"class name {}", "class name {}",
...@@ -3801,7 +3801,7 @@ TEST(ClassDeclarationNoErrors) { ...@@ -3801,7 +3801,7 @@ TEST(ClassDeclarationNoErrors) {
NULL}; NULL};
static const ParserFlag always_flags[] = { static const ParserFlag always_flags[] = {
kAllowHarmonyClasses, kAllowHarmonySloppy}; kAllowHarmonyClasses, kAllowHarmonyScoping};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0, RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags)); always_flags, arraysize(always_flags));
} }
...@@ -4831,3 +4831,26 @@ TEST(DuplicateProtoNoError) { ...@@ -4831,3 +4831,26 @@ TEST(DuplicateProtoNoError) {
RunParserSyncTest(context_data, error_data, kSuccess, NULL, 0, RunParserSyncTest(context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags)); always_flags, arraysize(always_flags));
} }
TEST(DeclarationsError) {
const char* context_data[][2] = {{"'use strict'; if (true)", ""},
{"'use strict'; if (false) {} else", ""},
{"'use strict'; while (false)", ""},
{"'use strict'; for (;;)", ""},
{"'use strict'; for (x in y)", ""},
{"'use strict'; do ", " while (false)"},
{NULL, NULL}};
const char* statement_data[] = {
"let x = 1;",
"const x = 1;",
"class C {}",
NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonyClasses, kAllowHarmonyScoping
};
RunParserSyncTest(context_data, statement_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
}
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