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}
This commit is contained in:
parent
b004b1d821
commit
13616615fd
@ -165,8 +165,6 @@ var kMessages = {
|
||||
strict_caller: ["Illegal access to a strict mode caller function."],
|
||||
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
|
||||
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"],
|
||||
redef_external_array_element: ["Cannot redefine a property of an object with external array elements"],
|
||||
harmony_const_assign: ["Assignment to constant variable."],
|
||||
|
@ -1618,22 +1618,21 @@ Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
|
||||
return ParseFunctionDeclaration(NULL, ok);
|
||||
}
|
||||
|
||||
case Token::CLASS:
|
||||
return ParseClassDeclaration(NULL, ok);
|
||||
|
||||
case Token::DEBUGGER:
|
||||
return ParseDebuggerStatement(ok);
|
||||
|
||||
case Token::VAR:
|
||||
case Token::CONST:
|
||||
return ParseVariableStatement(kStatement, NULL, ok);
|
||||
|
||||
case Token::LET:
|
||||
DCHECK(allow_harmony_scoping());
|
||||
if (strict_mode() == STRICT) {
|
||||
case Token::CONST:
|
||||
// In ES6 CONST is not allowed as a Statement, only as a
|
||||
// LexicalDeclaration, however we continue to allow it in sloppy mode for
|
||||
// backwards compatibility.
|
||||
if (strict_mode() == SLOPPY) {
|
||||
return ParseVariableStatement(kStatement, NULL, ok);
|
||||
}
|
||||
// Fall through.
|
||||
|
||||
// Fall through.
|
||||
default:
|
||||
return ParseExpressionOrLabelledStatement(labels, ok);
|
||||
}
|
||||
@ -2039,16 +2038,6 @@ Block* Parser::ParseVariableDeclarations(
|
||||
if (peek() == Token::VAR) {
|
||||
Consume(Token::VAR);
|
||||
} 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);
|
||||
switch (strict_mode()) {
|
||||
case SLOPPY:
|
||||
@ -2056,33 +2045,22 @@ Block* Parser::ParseVariableDeclarations(
|
||||
init_op = Token::INIT_CONST_LEGACY;
|
||||
break;
|
||||
case STRICT:
|
||||
if (allow_harmony_scoping()) {
|
||||
if (var_context == kStatement) {
|
||||
// In strict mode 'const' declarations are only allowed in source
|
||||
// element positions.
|
||||
ReportMessage("unprotected_const");
|
||||
*ok = false;
|
||||
return NULL;
|
||||
}
|
||||
mode = CONST;
|
||||
init_op = Token::INIT_CONST;
|
||||
} else {
|
||||
DCHECK(var_context != kStatement);
|
||||
// In ES5 const is not allowed in strict mode.
|
||||
if (!allow_harmony_scoping()) {
|
||||
ReportMessage("strict_const");
|
||||
*ok = false;
|
||||
return NULL;
|
||||
}
|
||||
mode = CONST;
|
||||
init_op = Token::INIT_CONST;
|
||||
}
|
||||
is_const = true;
|
||||
needs_init = true;
|
||||
} else if (peek() == Token::LET && strict_mode() == STRICT) {
|
||||
DCHECK(allow_harmony_scoping());
|
||||
Consume(Token::LET);
|
||||
if (var_context == kStatement) {
|
||||
// Let declarations are only allowed in source element positions.
|
||||
ReportMessage("unprotected_let");
|
||||
*ok = false;
|
||||
return NULL;
|
||||
}
|
||||
DCHECK(var_context != kStatement);
|
||||
mode = LET;
|
||||
needs_init = true;
|
||||
init_op = Token::INIT_LET;
|
||||
@ -2345,6 +2323,26 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
|
||||
// ExpressionStatement | LabelledStatement ::
|
||||
// Expression ';'
|
||||
// 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();
|
||||
bool starts_with_idenfifier = peek_any_identifier();
|
||||
Expression* expr = ParseExpression(true, CHECK_OK);
|
||||
|
@ -162,16 +162,22 @@ PreParserExpression PreParserTraits::ParseClassLiteral(
|
||||
|
||||
|
||||
PreParser::Statement PreParser::ParseSourceElement(bool* ok) {
|
||||
// (Ecma 262 5th Edition, clause 14):
|
||||
// SourceElement:
|
||||
// Statement
|
||||
// FunctionDeclaration
|
||||
// ECMA 262 6th Edition
|
||||
// StatementListItem[Yield, Return] :
|
||||
// Statement[?Yield, ?Return]
|
||||
// Declaration[?Yield]
|
||||
//
|
||||
// In harmony mode we allow additionally the following productions
|
||||
// SourceElement:
|
||||
// LetDeclaration
|
||||
// ConstDeclaration
|
||||
// GeneratorDeclaration
|
||||
// Declaration[Yield] :
|
||||
// HoistableDeclaration[?Yield]
|
||||
// ClassDeclaration[?Yield]
|
||||
// LexicalDeclaration[In, ?Yield]
|
||||
//
|
||||
// HoistableDeclaration[Yield, Default] :
|
||||
// FunctionDeclaration[?Yield, ?Default]
|
||||
// GeneratorDeclaration[?Yield, ?Default]
|
||||
//
|
||||
// LexicalDeclaration[In, Yield] :
|
||||
// LetOrConst BindingList[?In, ?Yield] ;
|
||||
|
||||
switch (peek()) {
|
||||
case Token::FUNCTION:
|
||||
@ -305,22 +311,21 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
|
||||
}
|
||||
}
|
||||
|
||||
case Token::CLASS:
|
||||
return ParseClassDeclaration(CHECK_OK);
|
||||
|
||||
case Token::DEBUGGER:
|
||||
return ParseDebuggerStatement(ok);
|
||||
|
||||
case Token::VAR:
|
||||
case Token::CONST:
|
||||
return ParseVariableStatement(kStatement, ok);
|
||||
|
||||
case Token::LET:
|
||||
DCHECK(allow_harmony_scoping());
|
||||
if (strict_mode() == STRICT) {
|
||||
case Token::CONST:
|
||||
// In ES6 CONST is not allowed as a Statement, only as a
|
||||
// LexicalDeclaration, however we continue to allow it in sloppy mode for
|
||||
// backwards compatibility.
|
||||
if (strict_mode() == SLOPPY) {
|
||||
return ParseVariableStatement(kStatement, ok);
|
||||
}
|
||||
// Fall through.
|
||||
|
||||
// Fall through.
|
||||
default:
|
||||
return ParseExpressionOrLabelledStatement(ok);
|
||||
}
|
||||
@ -441,28 +446,19 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
|
||||
// non-harmony semantics in sloppy mode.
|
||||
Consume(Token::CONST);
|
||||
if (strict_mode() == STRICT) {
|
||||
if (allow_harmony_scoping()) {
|
||||
if (var_context != kSourceElement && var_context != kForStatement) {
|
||||
ReportMessageAt(scanner()->peek_location(), "unprotected_const");
|
||||
*ok = false;
|
||||
return Statement::Default();
|
||||
}
|
||||
is_strict_const = true;
|
||||
require_initializer = var_context != kForStatement;
|
||||
} else {
|
||||
DCHECK(var_context != kStatement);
|
||||
if (!allow_harmony_scoping()) {
|
||||
Scanner::Location location = scanner()->peek_location();
|
||||
ReportMessageAt(location, "strict_const");
|
||||
*ok = false;
|
||||
return Statement::Default();
|
||||
}
|
||||
is_strict_const = true;
|
||||
require_initializer = var_context != kForStatement;
|
||||
}
|
||||
} else if (peek() == Token::LET && strict_mode() == STRICT) {
|
||||
Consume(Token::LET);
|
||||
if (var_context != kSourceElement && var_context != kForStatement) {
|
||||
ReportMessageAt(scanner()->peek_location(), "unprotected_let");
|
||||
*ok = false;
|
||||
return Statement::Default();
|
||||
}
|
||||
DCHECK(var_context != kStatement);
|
||||
} else {
|
||||
*ok = false;
|
||||
return Statement::Default();
|
||||
@ -497,6 +493,22 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
|
||||
// Expression ';'
|
||||
// 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();
|
||||
Expression expr = ParseExpression(true, CHECK_OK);
|
||||
// Even if the expression starts with an identifier, it is not necessarily an
|
||||
|
@ -3788,9 +3788,9 @@ TEST(ClassExpressionNoErrors) {
|
||||
|
||||
|
||||
TEST(ClassDeclarationNoErrors) {
|
||||
const char* context_data[][2] = {{"", ""},
|
||||
{"{", "}"},
|
||||
{"if (true) {", "}"},
|
||||
const char* context_data[][2] = {{"'use strict'; ", ""},
|
||||
{"'use strict'; {", "}"},
|
||||
{"'use strict'; if (true) {", "}"},
|
||||
{NULL, NULL}};
|
||||
const char* statement_data[] = {
|
||||
"class name {}",
|
||||
@ -3801,7 +3801,7 @@ TEST(ClassDeclarationNoErrors) {
|
||||
NULL};
|
||||
|
||||
static const ParserFlag always_flags[] = {
|
||||
kAllowHarmonyClasses, kAllowHarmonySloppy};
|
||||
kAllowHarmonyClasses, kAllowHarmonyScoping};
|
||||
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
}
|
||||
@ -4831,3 +4831,26 @@ TEST(DuplicateProtoNoError) {
|
||||
RunParserSyncTest(context_data, error_data, kSuccess, NULL, 0,
|
||||
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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user