harmony-scoping: Allow 'const' iteration variables in strict mode.
R=rossberg@chromium.org BUG=v8:2506 LOG=N Review URL: https://codereview.chromium.org/671913002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24834 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
778c500df7
commit
b54f7d3c46
@ -2156,6 +2156,7 @@ Block* Parser::ParseVariableDeclarations(
|
||||
Block* block = factory()->NewBlock(NULL, 1, true, pos);
|
||||
int nvars = 0; // the number of variables declared
|
||||
const AstRawString* name = NULL;
|
||||
bool is_for_iteration_variable;
|
||||
do {
|
||||
if (fni_ != NULL) fni_->Enter();
|
||||
|
||||
@ -2179,6 +2180,13 @@ Block* Parser::ParseVariableDeclarations(
|
||||
// For let/const declarations in harmony mode, we can also immediately
|
||||
// pre-resolve the proxy because it resides in the same scope as the
|
||||
// declaration.
|
||||
is_for_iteration_variable =
|
||||
var_context == kForStatement &&
|
||||
(peek() == Token::IN || PeekContextualKeyword(CStrVector("of")));
|
||||
if (is_for_iteration_variable && mode == CONST) {
|
||||
needs_init = false;
|
||||
}
|
||||
|
||||
Interface* interface =
|
||||
is_const ? Interface::NewConst() : Interface::NewValue();
|
||||
VariableProxy* proxy = NewUnresolved(name, mode, interface);
|
||||
@ -2224,7 +2232,8 @@ Block* Parser::ParseVariableDeclarations(
|
||||
Expression* value = NULL;
|
||||
int pos = -1;
|
||||
// Harmony consts have non-optional initializers.
|
||||
if (peek() == Token::ASSIGN || mode == CONST) {
|
||||
if (peek() == Token::ASSIGN ||
|
||||
(mode == CONST && !is_for_iteration_variable)) {
|
||||
Expect(Token::ASSIGN, CHECK_OK);
|
||||
pos = position();
|
||||
value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
|
||||
@ -2357,7 +2366,7 @@ Block* Parser::ParseVariableDeclarations(
|
||||
|
||||
// If there was a single non-const declaration, return it in the output
|
||||
// parameter for possible use by for/in.
|
||||
if (nvars == 1 && !is_const) {
|
||||
if (nvars == 1 && (!is_const || is_for_iteration_variable)) {
|
||||
*out = name;
|
||||
}
|
||||
|
||||
@ -3094,7 +3103,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
||||
Expect(Token::LPAREN, CHECK_OK);
|
||||
for_scope->set_start_position(scanner()->location().beg_pos);
|
||||
if (peek() != Token::SEMICOLON) {
|
||||
if (peek() == Token::VAR || peek() == Token::CONST) {
|
||||
if (peek() == Token::VAR ||
|
||||
(peek() == Token::CONST && strict_mode() == SLOPPY)) {
|
||||
bool is_const = peek() == Token::CONST;
|
||||
const AstRawString* name = NULL;
|
||||
VariableDeclarationProperties decl_props = kHasNoInitializers;
|
||||
@ -3131,8 +3141,10 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
||||
} else {
|
||||
init = variable_statement;
|
||||
}
|
||||
} else if (peek() == Token::LET && strict_mode() == STRICT) {
|
||||
} else if ((peek() == Token::LET || peek() == Token::CONST) &&
|
||||
strict_mode() == STRICT) {
|
||||
DCHECK(allow_harmony_scoping());
|
||||
bool is_const = peek() == Token::CONST;
|
||||
const AstRawString* name = NULL;
|
||||
VariableDeclarationProperties decl_props = kHasNoInitializers;
|
||||
Block* variable_statement =
|
||||
@ -3145,13 +3157,13 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
||||
if (accept_IN && CheckInOrOf(accept_OF, &mode)) {
|
||||
// Rewrite a for-in statement of the form
|
||||
//
|
||||
// for (let x in e) b
|
||||
// for (let/const x in e) b
|
||||
//
|
||||
// into
|
||||
//
|
||||
// <let x' be a temporary variable>
|
||||
// for (x' in e) {
|
||||
// let x;
|
||||
// let/const x;
|
||||
// x = x';
|
||||
// b;
|
||||
// }
|
||||
@ -3171,13 +3183,13 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
||||
scope_ = for_scope;
|
||||
Expect(Token::RPAREN, CHECK_OK);
|
||||
|
||||
VariableProxy* each =
|
||||
scope_->NewUnresolved(factory(), name, Interface::NewValue());
|
||||
VariableProxy* each = scope_->NewUnresolved(factory(), name);
|
||||
Statement* body = ParseStatement(NULL, CHECK_OK);
|
||||
Block* body_block =
|
||||
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
||||
Token::Value init_op = is_const ? Token::INIT_CONST : Token::ASSIGN;
|
||||
Assignment* assignment = factory()->NewAssignment(
|
||||
Token::ASSIGN, each, temp_proxy, RelocInfo::kNoPosition);
|
||||
init_op, each, temp_proxy, RelocInfo::kNoPosition);
|
||||
Statement* assignment_statement = factory()->NewExpressionStatement(
|
||||
assignment, RelocInfo::kNoPosition);
|
||||
body_block->AddStatement(variable_statement, zone());
|
||||
|
@ -409,6 +409,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
|
||||
// ConstBinding ::
|
||||
// BindingPattern '=' AssignmentExpression
|
||||
bool require_initializer = false;
|
||||
bool is_strict_const = false;
|
||||
if (peek() == Token::VAR) {
|
||||
Consume(Token::VAR);
|
||||
} else if (peek() == Token::CONST) {
|
||||
@ -430,7 +431,8 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
|
||||
*ok = false;
|
||||
return Statement::Default();
|
||||
}
|
||||
require_initializer = true;
|
||||
is_strict_const = true;
|
||||
require_initializer = var_context != kForStatement;
|
||||
} else {
|
||||
Scanner::Location location = scanner()->peek_location();
|
||||
ReportMessageAt(location, "strict_const");
|
||||
@ -460,7 +462,9 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
|
||||
if (nvars > 0) Consume(Token::COMMA);
|
||||
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
||||
nvars++;
|
||||
if (peek() == Token::ASSIGN || require_initializer) {
|
||||
if (peek() == Token::ASSIGN || require_initializer ||
|
||||
// require initializers for multiple consts.
|
||||
(is_strict_const && peek() == Token::COMMA)) {
|
||||
Expect(Token::ASSIGN, CHECK_OK);
|
||||
ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
|
||||
if (decl_props != NULL) *decl_props = kHasInitializers;
|
||||
@ -678,13 +682,14 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
|
||||
if (peek() != Token::SEMICOLON) {
|
||||
if (peek() == Token::VAR || peek() == Token::CONST ||
|
||||
(peek() == Token::LET && strict_mode() == STRICT)) {
|
||||
bool is_let = peek() == Token::LET;
|
||||
bool is_lexical = peek() == Token::LET ||
|
||||
(peek() == Token::CONST && strict_mode() == STRICT);
|
||||
int decl_count;
|
||||
VariableDeclarationProperties decl_props = kHasNoInitializers;
|
||||
ParseVariableDeclarations(
|
||||
kForStatement, &decl_props, &decl_count, CHECK_OK);
|
||||
bool has_initializers = decl_props == kHasInitializers;
|
||||
bool accept_IN = decl_count == 1 && !(is_let && has_initializers);
|
||||
bool accept_IN = decl_count == 1 && !(is_lexical && has_initializers);
|
||||
bool accept_OF = !has_initializers;
|
||||
if (accept_IN && CheckInOrOf(accept_OF)) {
|
||||
ParseExpression(true, CHECK_OK);
|
||||
|
@ -344,14 +344,18 @@ class ParserBase : public Traits {
|
||||
}
|
||||
|
||||
bool CheckContextualKeyword(Vector<const char> keyword) {
|
||||
if (peek() == Token::IDENTIFIER &&
|
||||
scanner()->is_next_contextual_keyword(keyword)) {
|
||||
if (PeekContextualKeyword(keyword)) {
|
||||
Consume(Token::IDENTIFIER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeekContextualKeyword(Vector<const char> keyword) {
|
||||
return peek() == Token::IDENTIFIER &&
|
||||
scanner()->is_next_contextual_keyword(keyword);
|
||||
}
|
||||
|
||||
void ExpectContextualKeyword(Vector<const char> keyword, bool* ok) {
|
||||
Expect(Token::IDENTIFIER, ok);
|
||||
if (!*ok) return;
|
||||
|
@ -4205,3 +4205,42 @@ TEST(ObjectLiteralPropertyShorthandYieldInGeneratorError) {
|
||||
RunParserSyncTest(context_data, name_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
}
|
||||
|
||||
|
||||
TEST(ConstParsingInForIn) {
|
||||
const char* context_data[][2] = {{"'use strict';", ""},
|
||||
{"function foo(){ 'use strict';", "}"},
|
||||
{NULL, NULL}};
|
||||
|
||||
const char* data[] = {
|
||||
"for(const x = 1; ; ) {}",
|
||||
"for(const x = 1, y = 2;;){}",
|
||||
"for(const x in [1,2,3]) {}",
|
||||
"for(const x of [1,2,3]) {}",
|
||||
NULL};
|
||||
static const ParserFlag always_flags[] = {kAllowHarmonyScoping};
|
||||
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
|
||||
arraysize(always_flags));
|
||||
}
|
||||
|
||||
|
||||
TEST(ConstParsingInForInError) {
|
||||
const char* context_data[][2] = {{"'use strict';", ""},
|
||||
{"function foo(){ 'use strict';", "}"},
|
||||
{NULL, NULL}};
|
||||
|
||||
const char* data[] = {
|
||||
"for(const x,y = 1; ; ) {}",
|
||||
"for(const x = 4 in [1,2,3]) {}",
|
||||
"for(const x = 4, y in [1,2,3]) {}",
|
||||
"for(const x = 4 of [1,2,3]) {}",
|
||||
"for(const x = 4, y of [1,2,3]) {}",
|
||||
"for(const x = 1, y = 2 in []) {}",
|
||||
"for(const x,y in []) {}",
|
||||
"for(const x = 1, y = 2 of []) {}",
|
||||
"for(const x,y of []) {}",
|
||||
NULL};
|
||||
static const ParserFlag always_flags[] = {kAllowHarmonyScoping};
|
||||
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
|
||||
arraysize(always_flags));
|
||||
}
|
||||
|
78
test/mjsunit/regress/regress-2506.js
Normal file
78
test/mjsunit/regress/regress-2506.js
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2014 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-scoping
|
||||
|
||||
'use strict';
|
||||
|
||||
// Top-level code
|
||||
let s = 0;
|
||||
let f = [undefined, undefined, undefined]
|
||||
for (const x of [1,2,3]) {
|
||||
s += x;
|
||||
f[x-1] = function() { return x; }
|
||||
}
|
||||
assertEquals(6, s);
|
||||
assertEquals(1, f[0]());
|
||||
assertEquals(2, f[1]());
|
||||
assertEquals(3, f[2]());
|
||||
|
||||
let x = 1;
|
||||
s = 0;
|
||||
for (const x of [x, x+1, x+2]) {
|
||||
s += x;
|
||||
}
|
||||
assertEquals(6, s);
|
||||
|
||||
s = 0;
|
||||
var q = 1;
|
||||
for (const q of [q, q+1, q+2]) {
|
||||
s += q;
|
||||
}
|
||||
assertEquals(6, s);
|
||||
|
||||
let z = 1;
|
||||
s = 0;
|
||||
for (const x = 1; z < 2; z++) {
|
||||
s += x + z;
|
||||
}
|
||||
assertEquals(2, s);
|
||||
|
||||
|
||||
s = "";
|
||||
for (const x in [1,2,3]) {
|
||||
s += x;
|
||||
}
|
||||
assertEquals("012", s);
|
||||
|
||||
assertThrows(function() { for(const x in [1,2,3]) { x++ } }, SyntaxError);
|
||||
|
||||
// Function scope
|
||||
(function() {
|
||||
let s = 0;
|
||||
for (const x of [1,2,3]) {
|
||||
s += x;
|
||||
}
|
||||
assertEquals(6, s);
|
||||
|
||||
let x = 1;
|
||||
s = 0;
|
||||
for (const x of [x, x+1, x+2]) {
|
||||
s += x;
|
||||
}
|
||||
assertEquals(6, s);
|
||||
|
||||
s = 0;
|
||||
var q = 1;
|
||||
for (const q of [q, q+1, q+2]) {
|
||||
s += q;
|
||||
}
|
||||
assertEquals(6, s);
|
||||
|
||||
s = "";
|
||||
for (const x in [1,2,3]) {
|
||||
s += x;
|
||||
}
|
||||
assertEquals("012", s);
|
||||
}());
|
Loading…
Reference in New Issue
Block a user