Make let usable as an identifier in ES6 sloppy mode.

All of our mjsunit suite now runs through with --harmony-scoping enabled, up to expected failures (tests checking syntax errors for const/function in strict mode).

R=marja@chromium.org, ulan@chromium.org
BUG=v8:2198
LOG=Y

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22323 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2014-07-10 14:06:37 +00:00
parent a087f30500
commit e274edc8b8
6 changed files with 68 additions and 53 deletions

View File

@ -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"],

View File

@ -1091,13 +1091,18 @@ Statement* Parser::ParseModuleElement(ZoneList<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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 */,

View File

@ -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;

View File

@ -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<Traits>::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<Traits>::IdentifierT ParserBase<Traits>::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<Traits>::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<Traits>::IdentifierT
ParserBase<Traits>::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<Traits>::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<Traits>::ExpressionT ParserBase<Traits>::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<Traits>::ExpressionT ParserBase<Traits>::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)) {

View File

@ -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;");

View File

@ -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.