Index: src/parser.cc |
diff --git a/src/parser.cc b/src/parser.cc |
index c3ce44f4a1790ceaa67bc01b3eabe27c8d2d58b5..9164a4a9f515514af6c4a20ad23e45b2050e38b0 100644 |
--- a/src/parser.cc |
+++ b/src/parser.cc |
@@ -1138,14 +1138,14 @@ Statement* Parser::ParseSourceElement(ZoneStringList* labels, |
// In harmony mode we allow additionally the following productions |
// SourceElement: |
// LetDeclaration |
+ // ConstDeclaration |
if (peek() == Token::FUNCTION) { |
return ParseFunctionDeclaration(ok); |
- } else if (peek() == Token::LET) { |
+ } else if (peek() == Token::LET || peek() == Token::CONST) { |
return ParseVariableStatement(kSourceElement, ok); |
- } else { |
- return ParseStatement(labels, ok); |
} |
+ return ParseStatement(labels, ok); |
} |
@@ -1363,6 +1363,10 @@ VariableProxy* Parser::Declare(Handle<String> name, |
// If we are inside a function, a declaration of a var/const variable is a |
// truly local variable, and the scope of the variable is always the function |
// scope. |
+ // Let/const variables in harmony mode are always added to the immediately |
+ // enclosing scope. |
+ Scope* declaration_scope = mode == LET || mode == CONST_HARMONY |
+ ? top_scope_ : top_scope_->DeclarationScope(); |
// If a function scope exists, then we can statically declare this |
// variable and also set its mode. In any case, a Declaration node |
@@ -1372,9 +1376,8 @@ VariableProxy* Parser::Declare(Handle<String> name, |
// to the calling function context. |
// Similarly, strict mode eval scope does not leak variable declarations to |
// the caller's scope so we declare all locals, too. |
- |
- Scope* declaration_scope = mode == LET ? top_scope_ |
- : top_scope_->DeclarationScope(); |
+ // Also for block scoped let/const bindings the variable can be |
+ // statically declared. |
if (declaration_scope->is_function_scope() || |
declaration_scope->is_strict_mode_eval_scope() || |
declaration_scope->is_block_scope()) { |
@@ -1399,6 +1402,7 @@ VariableProxy* Parser::Declare(Handle<String> name, |
// We only have vars, consts and lets in declarations. |
ASSERT(var->mode() == VAR || |
var->mode() == CONST || |
+ var->mode() == CONST_HARMONY || |
var->mode() == LET); |
if (harmony_scoping_) { |
// In harmony mode we treat re-declarations as early errors. See |
@@ -1410,8 +1414,8 @@ VariableProxy* Parser::Declare(Handle<String> name, |
*ok = false; |
return NULL; |
} |
- const char* type = (var->mode() == VAR) ? "var" : |
- (var->mode() == CONST) ? "const" : "let"; |
+ const char* type = (var->mode() == VAR) |
+ ? "var" : var->is_const_mode() ? "const" : "let"; |
Handle<String> type_string = |
isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED); |
Expression* expression = |
@@ -1444,7 +1448,8 @@ VariableProxy* Parser::Declare(Handle<String> name, |
new(zone()) Declaration(proxy, mode, fun, top_scope_)); |
// For global const variables we bind the proxy to a variable. |
- if (mode == CONST && declaration_scope->is_global_scope()) { |
+ if ((mode == CONST || mode == CONST_HARMONY) && |
+ declaration_scope->is_global_scope()) { |
ASSERT(resolve); // should be set by all callers |
Variable::Kind kind = Variable::NORMAL; |
var = new(zone()) Variable(declaration_scope, name, CONST, true, kind); |
@@ -1654,8 +1659,18 @@ Block* Parser::ParseVariableDeclarations( |
Handle<String>* out, |
bool* ok) { |
// VariableDeclarations :: |
- // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] |
- |
+ // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[','] |
+ // |
+ // The ES6 Draft Rev3 specifies the following grammar for const declarations |
+ // |
+ // ConstDeclaration :: |
+ // const ConstBinding (',' ConstBinding)* ';' |
+ // ConstBinding :: |
+ // Identifier '=' AssignmentExpression |
+ // |
+ // TODO(ES6): |
+ // ConstBinding :: |
+ // BindingPattern '=' AssignmentExpression |
VariableMode mode = VAR; |
// True if the binding needs initialization. 'let' and 'const' declared |
// bindings are created uninitialized by their declaration nodes and |
@@ -1668,19 +1683,32 @@ Block* Parser::ParseVariableDeclarations( |
Consume(Token::VAR); |
} else if (peek() == Token::CONST) { |
Consume(Token::CONST); |
- if (top_scope_->is_strict_mode()) { |
+ if (harmony_scoping_) { |
+ if (var_context != kSourceElement && |
+ var_context != kForStatement) { |
+ // In harmony mode 'const' declarations are only allowed in source |
+ // element positions. |
+ ReportMessage("unprotected_const", Vector<const char*>::empty()); |
+ *ok = false; |
+ return NULL; |
+ } |
+ mode = CONST_HARMONY; |
+ init_op = Token::INIT_CONST_HARMONY; |
+ } else if (top_scope_->is_strict_mode()) { |
ReportMessage("strict_const", Vector<const char*>::empty()); |
*ok = false; |
return NULL; |
+ } else { |
+ mode = CONST; |
+ init_op = Token::INIT_CONST; |
} |
- mode = CONST; |
is_const = true; |
needs_init = true; |
- init_op = Token::INIT_CONST; |
} else if (peek() == Token::LET) { |
Consume(Token::LET); |
if (var_context != kSourceElement && |
var_context != kForStatement) { |
+ // Let declarations are only allowed in source element positions. |
ASSERT(var_context == kStatement); |
ReportMessage("unprotected_let", Vector<const char*>::empty()); |
*ok = false; |
@@ -1693,7 +1721,7 @@ Block* Parser::ParseVariableDeclarations( |
UNREACHABLE(); // by current callers |
} |
- Scope* declaration_scope = (mode == LET) |
+ Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY) |
? top_scope_ : top_scope_->DeclarationScope(); |
// The scope of a var/const declared variable anywhere inside a function |
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can |
@@ -1738,8 +1766,10 @@ Block* Parser::ParseVariableDeclarations( |
// If we have a const declaration, in an inner scope, the proxy is always |
// bound to the declared variable (independent of possibly surrounding with |
// statements). |
- Declare(name, mode, NULL, is_const /* always bound for CONST! */, |
- CHECK_OK); |
+ // 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. |
+ Declare(name, mode, NULL, mode != VAR, CHECK_OK); |
nvars++; |
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { |
ReportMessageAt(scanner().location(), "too_many_variables", |
@@ -1778,7 +1808,8 @@ Block* Parser::ParseVariableDeclarations( |
Scope* initialization_scope = is_const ? declaration_scope : top_scope_; |
Expression* value = NULL; |
int position = -1; |
- if (peek() == Token::ASSIGN) { |
+ // Harmony consts have non-optional initializers. |
+ if (peek() == Token::ASSIGN || mode == CONST_HARMONY) { |
Expect(Token::ASSIGN, CHECK_OK); |
position = scanner().location().beg_pos; |
value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK); |
@@ -1817,7 +1848,6 @@ Block* Parser::ParseVariableDeclarations( |
// declaration statement has been executed. This is important in |
// browsers where the global object (window) has lots of |
// properties defined in prototype objects. |
- |
if (initialization_scope->is_global_scope()) { |
// Compute the arguments for the runtime call. |
ZoneList<Expression*>* arguments = new(zone()) ZoneList<Expression*>(3); |
@@ -1881,9 +1911,9 @@ Block* Parser::ParseVariableDeclarations( |
// dynamically looked-up variables and constants (the start context |
// for constant lookups is always the function context, while it is |
// the top context for var declared variables). Sigh... |
- // For 'let' declared variables the initialization is in the same scope |
- // as the declaration. Thus dynamic lookups are unnecessary even if the |
- // block scope is inside a with. |
+ // For 'let' and 'const' declared variables in harmony mode the |
+ // initialization is in the same scope as the declaration. Thus dynamic |
+ // lookups are unnecessary even if the block scope is inside a with. |
if (value != NULL) { |
VariableProxy* proxy = initialization_scope->NewUnresolved(name); |
Assignment* assignment = |
@@ -3938,12 +3968,21 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, |
// future we can change the AST to only refer to VariableProxies |
// instead of Variables and Proxis as is the case now. |
if (type == FunctionLiteral::NAMED_EXPRESSION) { |
- Variable* fvar = top_scope_->DeclareFunctionVar(function_name); |
+ VariableMode fvar_mode; |
+ Token::Value fvar_init_op; |
+ if (harmony_scoping_) { |
+ fvar_mode = CONST_HARMONY; |
+ fvar_init_op = Token::INIT_CONST_HARMONY; |
+ } else { |
+ fvar_mode = CONST; |
+ fvar_init_op = Token::INIT_CONST; |
+ } |
+ Variable* fvar = top_scope_->DeclareFunctionVar(function_name, fvar_mode); |
VariableProxy* fproxy = top_scope_->NewUnresolved(function_name); |
fproxy->BindTo(fvar); |
body->Add(new(zone()) ExpressionStatement( |
new(zone()) Assignment(isolate(), |
- Token::INIT_CONST, |
+ fvar_init_op, |
fproxy, |
new(zone()) ThisFunction(isolate()), |
RelocInfo::kNoPosition))); |