diff --git a/src/messages.js b/src/messages.js index cbc25fc831..4f786b590f 100644 --- a/src/messages.js +++ b/src/messages.js @@ -127,7 +127,6 @@ var kMessages = { illegal_break: ["Illegal break statement"], illegal_continue: ["Illegal continue statement"], illegal_return: ["Illegal return statement"], - illegal_let: ["Illegal let declaration outside extended mode"], error_loading_debugger: ["Error loading debugger"], no_input_to_regexp: ["No input to ", "%0"], invalid_json: ["String '", "%0", "' is not valid JSON"], diff --git a/src/parser.cc b/src/parser.cc index f37eae5d37..6886fcca2a 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1091,13 +1091,18 @@ Statement* Parser::ParseModuleElement(ZoneList* labels, switch (peek()) { case Token::FUNCTION: return ParseFunctionDeclaration(NULL, ok); - case Token::LET: - case Token::CONST: - return ParseVariableStatement(kModuleElement, NULL, ok); case Token::IMPORT: return ParseImportDeclaration(ok); case Token::EXPORT: return ParseExportDeclaration(ok); + case Token::CONST: + return ParseVariableStatement(kModuleElement, NULL, ok); + case Token::LET: + ASSERT(allow_harmony_scoping()); + if (strict_mode() == STRICT) { + return ParseVariableStatement(kModuleElement, NULL, ok); + } + // Fall through. default: { Statement* stmt = ParseStatement(labels, CHECK_OK); // Handle 'module' as a context-sensitive keyword. @@ -1396,6 +1401,8 @@ Statement* Parser::ParseExportDeclaration(bool* ok) { // // TODO(ES6): implement structuring ExportSpecifiers + ASSERT(strict_mode() == STRICT); + Expect(Token::EXPORT, CHECK_OK); Statement* result = NULL; @@ -1478,9 +1485,14 @@ Statement* Parser::ParseBlockElement(ZoneList* labels, switch (peek()) { case Token::FUNCTION: return ParseFunctionDeclaration(NULL, ok); - case Token::LET: case Token::CONST: return ParseVariableStatement(kModuleElement, NULL, ok); + case Token::LET: + ASSERT(allow_harmony_scoping()); + if (strict_mode() == STRICT) { + return ParseVariableStatement(kModuleElement, NULL, ok); + } + // Fall through. default: return ParseStatement(labels, ok); } @@ -1516,11 +1528,6 @@ Statement* Parser::ParseStatement(ZoneList* labels, case Token::LBRACE: return ParseBlock(labels, ok); - case Token::CONST: // fall through - case Token::LET: - case Token::VAR: - return ParseVariableStatement(kStatement, NULL, ok); - case Token::SEMICOLON: Next(); return factory()->NewEmptyStatement(RelocInfo::kNoPosition); @@ -1592,6 +1599,16 @@ Statement* Parser::ParseStatement(ZoneList* labels, case Token::DEBUGGER: return ParseDebuggerStatement(ok); + case Token::VAR: + case Token::CONST: + return ParseVariableStatement(kStatement, NULL, ok); + + case Token::LET: + ASSERT(allow_harmony_scoping()); + if (strict_mode() == STRICT) { + return ParseVariableStatement(kStatement, NULL, ok); + } + // Fall through. default: return ParseExpressionOrLabelledStatement(labels, ok); } @@ -1995,20 +2012,7 @@ Block* Parser::ParseVariableDeclarations( } is_const = true; needs_init = true; - } else if (peek() == Token::LET) { - // ES6 Draft Rev4 section 12.2.1: - // - // LetDeclaration : let LetBindingList ; - // - // * It is a Syntax Error if the code that matches this production is not - // contained in extended code. - // - // TODO(rossberg): make 'let' a legal identifier in sloppy mode. - if (!allow_harmony_scoping() || strict_mode() == SLOPPY) { - ReportMessage("illegal_let"); - *ok = false; - return NULL; - } + } else if (peek() == Token::LET && strict_mode() == STRICT) { Consume(Token::LET); if (var_context == kStatement) { // Let declarations are only allowed in source element positions. @@ -3047,7 +3051,7 @@ Statement* Parser::ParseForStatement(ZoneList* labels, } else { init = variable_statement; } - } else if (peek() == Token::LET) { + } else if (peek() == Token::LET && strict_mode() == STRICT) { const AstRawString* name = NULL; VariableDeclarationProperties decl_props = kHasNoInitializers; Block* variable_statement = @@ -3478,8 +3482,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( fvar_init_op = Token::INIT_CONST; } VariableMode fvar_mode = - allow_harmony_scoping() && strict_mode() == STRICT ? CONST - : CONST_LEGACY; + allow_harmony_scoping() && strict_mode() == STRICT + ? CONST : CONST_LEGACY; ASSERT(function_name != NULL); fvar = new(zone()) Variable(scope_, function_name, fvar_mode, true /* is valid LHS */, diff --git a/src/preparser.cc b/src/preparser.cc index df9e48ef76..8c8746d5e0 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -61,6 +61,8 @@ PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) { } else if (scanner->current_token() == Token::FUTURE_STRICT_RESERVED_WORD) { return PreParserIdentifier::FutureStrictReserved(); + } else if (scanner->current_token() == Token::LET) { + return PreParserIdentifier::Let(); } else if (scanner->current_token() == Token::YIELD) { return PreParserIdentifier::Yield(); } @@ -167,9 +169,14 @@ PreParser::Statement PreParser::ParseSourceElement(bool* ok) { switch (peek()) { case Token::FUNCTION: return ParseFunctionDeclaration(ok); - case Token::LET: case Token::CONST: return ParseVariableStatement(kSourceElement, ok); + case Token::LET: + ASSERT(allow_harmony_scoping()); + if (strict_mode() == STRICT) { + return ParseVariableStatement(kSourceElement, ok); + } + // Fall through. default: return ParseStatement(ok); } @@ -237,11 +244,6 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { case Token::LBRACE: return ParseBlock(ok); - case Token::CONST: - case Token::LET: - case Token::VAR: - return ParseVariableStatement(kStatement, ok); - case Token::SEMICOLON: Next(); return Statement::Default(); @@ -297,6 +299,16 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { case Token::DEBUGGER: return ParseDebuggerStatement(ok); + case Token::VAR: + case Token::CONST: + return ParseVariableStatement(kStatement, ok); + + case Token::LET: + ASSERT(allow_harmony_scoping()); + if (strict_mode() == STRICT) { + return ParseVariableStatement(kStatement, ok); + } + // Fall through. default: return ParseExpressionOrLabelledStatement(ok); } @@ -415,23 +427,9 @@ PreParser::Statement PreParser::ParseVariableDeclarations( return Statement::Default(); } } - } else if (peek() == Token::LET) { - // ES6 Draft Rev4 section 12.2.1: - // - // LetDeclaration : let LetBindingList ; - // - // * It is a Syntax Error if the code that matches this production is not - // contained in extended code. - // - // TODO(rossberg): make 'let' a legal identifier in sloppy mode. - if (!allow_harmony_scoping() || strict_mode() == SLOPPY) { - ReportMessageAt(scanner()->peek_location(), "illegal_let"); - *ok = false; - return Statement::Default(); - } + } else if (peek() == Token::LET && strict_mode() == STRICT) { Consume(Token::LET); - if (var_context != kSourceElement && - var_context != kForStatement) { + if (var_context != kSourceElement && var_context != kForStatement) { ReportMessageAt(scanner()->peek_location(), "unprotected_let"); *ok = false; return Statement::Default(); @@ -669,7 +667,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { Expect(Token::LPAREN, CHECK_OK); if (peek() != Token::SEMICOLON) { if (peek() == Token::VAR || peek() == Token::CONST || - peek() == Token::LET) { + (peek() == Token::LET && strict_mode() == STRICT)) { bool is_let = peek() == Token::LET; int decl_count; VariableDeclarationProperties decl_props = kHasNoInitializers; diff --git a/src/preparser.h b/src/preparser.h index 489c05fea4..156fd64a2e 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -308,6 +308,7 @@ class ParserBase : public Traits { return next == Token::IDENTIFIER || next == Token::FUTURE_RESERVED_WORD || next == Token::FUTURE_STRICT_RESERVED_WORD || + next == Token::LET || next == Token::YIELD; } @@ -565,12 +566,16 @@ class PreParserIdentifier { static PreParserIdentifier FutureStrictReserved() { return PreParserIdentifier(kFutureStrictReservedIdentifier); } + static PreParserIdentifier Let() { + return PreParserIdentifier(kLetIdentifier); + } static PreParserIdentifier Yield() { return PreParserIdentifier(kYieldIdentifier); } bool IsEval() const { return type_ == kEvalIdentifier; } bool IsArguments() const { return type_ == kArgumentsIdentifier; } bool IsEvalOrArguments() const { return type_ >= kEvalIdentifier; } + bool IsLet() const { return type_ == kLetIdentifier; } bool IsYield() const { return type_ == kYieldIdentifier; } bool IsFutureReserved() const { return type_ == kFutureReservedIdentifier; } bool IsFutureStrictReserved() const { @@ -591,6 +596,7 @@ class PreParserIdentifier { kUnknownIdentifier, kFutureReservedIdentifier, kFutureStrictReservedIdentifier, + kLetIdentifier, kYieldIdentifier, kEvalIdentifier, kArgumentsIdentifier @@ -1504,6 +1510,7 @@ void ParserBase::ReportUnexpectedToken(Token::Value token) { return ReportMessageAt(source_location, "unexpected_token_identifier"); case Token::FUTURE_RESERVED_WORD: return ReportMessageAt(source_location, "unexpected_reserved"); + case Token::LET: case Token::YIELD: case Token::FUTURE_STRICT_RESERVED_WORD: return ReportMessageAt(source_location, strict_mode() == SLOPPY @@ -1531,6 +1538,7 @@ typename ParserBase::IdentifierT ParserBase::ParseIdentifier( return name; } else if (strict_mode() == SLOPPY && (next == Token::FUTURE_STRICT_RESERVED_WORD || + (next == Token::LET) || (next == Token::YIELD && !is_generator()))) { return this->GetSymbol(scanner()); } else { @@ -1549,6 +1557,7 @@ typename ParserBase::IdentifierT ParserBase< if (next == Token::IDENTIFIER) { *is_strict_reserved = false; } else if (next == Token::FUTURE_STRICT_RESERVED_WORD || + next == Token::LET || (next == Token::YIELD && !this->is_generator())) { *is_strict_reserved = true; } else { @@ -1565,6 +1574,7 @@ typename ParserBase::IdentifierT ParserBase::ParseIdentifierName(bool* ok) { Token::Value next = Next(); if (next != Token::IDENTIFIER && next != Token::FUTURE_RESERVED_WORD && + next != Token::LET && next != Token::YIELD && next != Token::FUTURE_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) { this->ReportUnexpectedToken(next); *ok = false; @@ -1660,6 +1670,7 @@ ParserBase::ParsePrimaryExpression(bool* ok) { break; case Token::IDENTIFIER: + case Token::LET: case Token::YIELD: case Token::FUTURE_STRICT_RESERVED_WORD: { // Using eval or arguments in this context is OK even in strict mode. @@ -1806,6 +1817,8 @@ typename ParserBase::ExpressionT ParserBase::ParseObjectLiteral( switch (next) { case Token::FUTURE_RESERVED_WORD: case Token::FUTURE_STRICT_RESERVED_WORD: + case Token::LET: + case Token::YIELD: case Token::IDENTIFIER: { bool is_getter = false; bool is_setter = false; @@ -1821,6 +1834,8 @@ typename ParserBase::ExpressionT ParserBase::ParseObjectLiteral( if (next != i::Token::IDENTIFIER && next != i::Token::FUTURE_RESERVED_WORD && next != i::Token::FUTURE_STRICT_RESERVED_WORD && + next != i::Token::LET && + next != i::Token::YIELD && next != i::Token::NUMBER && next != i::Token::STRING && !Token::IsKeyword(next)) { diff --git a/test/mjsunit/harmony/block-early-errors.js b/test/mjsunit/harmony/block-early-errors.js index 791f001af0..8ed5ea84ec 100644 --- a/test/mjsunit/harmony/block-early-errors.js +++ b/test/mjsunit/harmony/block-early-errors.js @@ -30,7 +30,6 @@ function CheckException(e) { var string = e.toString(); assertInstanceof(e, SyntaxError); - assertTrue(string.indexOf("Illegal let") >= 0); } function Check(str) { @@ -49,7 +48,7 @@ function Check(str) { } // Check for early syntax errors when using let -// declarations outside of extended mode. +// declarations outside of strict mode. Check("let x;"); Check("let x = 1;"); Check("let x, y;"); diff --git a/test/mjsunit/harmony/iteration-syntax.js b/test/mjsunit/harmony/iteration-syntax.js index 3bda78ed4e..015523dae6 100644 --- a/test/mjsunit/harmony/iteration-syntax.js +++ b/test/mjsunit/harmony/iteration-syntax.js @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --harmony-iteration --harmony-scoping +// Flags: --harmony-iteration --harmony-scoping --use-strict // Test for-of syntax.