Index: src/parser.cc |
diff --git a/src/parser.cc b/src/parser.cc |
index edd2e531d439b16327afe1ca84952a0165b5a0c1..c391ca7acc7b7aba67e52f669e8f288cc348278d 100644 |
--- a/src/parser.cc |
+++ b/src/parser.cc |
@@ -1141,14 +1141,16 @@ 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) { |
return ParseVariableStatement(kSourceElement, ok); |
- } else { |
- return ParseStatement(labels, ok); |
+ } else if (peek() == Token::CONST && harmony_scoping_) { |
+ return ParseHarmonyConstDeclaration(ok); |
} |
+ return ParseStatement(labels, ok); |
} |
@@ -1366,6 +1368,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 |
@@ -1375,9 +1381,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()) { |
@@ -1402,6 +1407,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 |
@@ -1413,8 +1419,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 = |
@@ -1447,7 +1453,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); |
@@ -1645,6 +1652,114 @@ bool Parser::IsEvalOrArguments(Handle<String> string) { |
} |
+Block* Parser::ParseHarmonyConstDeclaration(bool* ok) { |
+ // ES6 Draft Rev3 |
+ // |
+ // ConstDeclaration :: |
+ // const ConstBinding (',' ConstBinding)* ';' |
+ // ConstBinding :: |
+ // Identifier '=' AssignmentExpression |
+ // |
+ // TODO(ES6): |
+ // ConstBinding :: |
+ // BindingPattern '=' AssignmentExpression |
+ |
+ Consume(Token::CONST); |
+ |
+ // A block is used to collect the initialization assignments and it |
+ // is marked as an initializer block to prevent the rewriter from |
+ // adding a '.result' assignment to such a block. |
+ // |
+ // Create new block with one expected declaration. |
+ Block* block = new(zone()) Block(isolate(), NULL, 1, true); |
+ Handle<String> name; |
+ do { |
+ if (fni_ != NULL) fni_->Enter(); |
+ |
+ // Parse variable name. |
+ name = ParseIdentifier(CHECK_OK); |
+ if (fni_ != NULL) fni_->PushVariableName(name); |
+ |
+ // Strict mode variables may not be named eval or arguments |
+ if (IsEvalOrArguments(name)) { |
+ ReportMessage("strict_var_name", Vector<const char*>::empty()); |
+ *ok = false; |
+ return NULL; |
+ } |
+ |
+ // Declare the variable. The proxy can always be pre-resolved to the |
+ // declared variable, because it resides in the same scope as the |
+ // declaration. |
+ VariableProxy* proxy = Declare(name, CONST_HARMONY, NULL, true, CHECK_OK); |
+ if (top_scope_->num_var_or_const() > kMaxNumFunctionLocals) { |
fschneider
2011/10/21 09:19:40
This function contains a lot of duplicated code. C
Steven
2011/10/24 09:37:55
Done. Was actually easier than I thought to add it
|
+ ReportMessageAt(scanner().location(), "too_many_variables", |
+ Vector<const char*>::empty()); |
+ *ok = false; |
+ return NULL; |
+ } |
+ |
+ // Parse initialiser. |
+ Expression* value = NULL; |
+ Expect(Token::ASSIGN, CHECK_OK); |
+ int position = scanner().location().beg_pos; |
+ value = ParseAssignmentExpression(false, CHECK_OK); |
+ |
+ // Don't infer if it is "a = function(){...}();"-like expression. |
+ if (fni_ != NULL && value->AsCall() == NULL && value->AsCallNew() == NULL) { |
+ fni_->Infer(); |
+ } |
+ |
+ // Global variable declarations must be compiled in a specific |
+ // way. When the script containing the global variable declaration |
+ // is entered, the global variable must be declared, so that if it |
+ // doesn't exist (not even in a prototype of the global object) it |
+ // gets created with an initial undefined value. This is handled |
+ // by the declarations part of the function representing the |
+ // top-level global code; see Runtime::DeclareGlobals. If |
+ // it already exists (in the object or in a prototype), it is |
+ // *not* touched until the variable declaration statement is |
+ // executed. |
+ // |
+ // Executing the variable declaration statement will always |
+ // guarantee to give the global object a "local" variable; a |
+ // variable defined in the global object and not in any |
+ // prototype. This way, global variable declarations can shadow |
+ // properties in the prototype chain, but only after the variable |
+ // declaration statement has been executed. This is important in |
+ // browsers where the global object (window) has lots of |
+ // properties defined in prototype objects. |
+ if (top_scope_->is_global_scope()) { |
+ // Compute the arguments for the runtime call. |
+ ZoneList<Expression*>* arguments = new(zone()) ZoneList<Expression*>(3); |
+ // We have at least 1 parameter. |
+ arguments->Add(NewLiteral(name)); |
+ arguments->Add(value); |
+ |
+ // Construct the call to Runtime_InitializeConstGlobal |
+ // and add it to the initialization statement block. |
+ CallRuntime* initialize = |
+ new(zone()) CallRuntime( |
+ isolate(), |
+ isolate()->factory()->InitializeConstGlobal_symbol(), |
+ Runtime::FunctionForId(Runtime::kInitializeConstGlobal), |
+ arguments); |
+ |
+ block->AddStatement(new(zone()) ExpressionStatement(initialize)); |
+ } else { |
+ // Add an assignment node for the initializations to the initialization |
+ // statement block. |
+ Assignment* assignment = new(zone()) Assignment( |
+ isolate(), Token::INIT_CONST_HARMONY, proxy, value, position); |
+ block->AddStatement(new(zone()) ExpressionStatement(assignment)); |
+ } |
+ if (fni_ != NULL) fni_->Leave(); |
+ } while (Check(Token::COMMA)); |
+ |
+ ExpectSemicolon(CHECK_OK); |
+ return block; |
+} |
+ |
+ |
// If the variable declaration declares exactly one non-const |
// variable, then *var is set to that variable. In all other cases, |
// *var is untouched; in particular, it is the caller's responsibility |
@@ -1670,6 +1785,15 @@ Block* Parser::ParseVariableDeclarations( |
Consume(Token::VAR); |
} else if (peek() == Token::CONST) { |
Consume(Token::CONST); |
+ if (harmony_scoping_) { |
+ ASSERT(var_context == kForStatement || |
+ var_context == kStatement); |
+ // In harmony mode 'const' declarations are only allowed in source |
+ // element positions and that is handled by ParseHarmonyConstDeclaration. |
+ ReportMessage("unprotected_const", Vector<const char*>::empty()); |
+ *ok = false; |
+ return NULL; |
+ } |
if (top_scope_->is_strict_mode()) { |
ReportMessage("strict_const", Vector<const char*>::empty()); |
*ok = false; |
@@ -1683,6 +1807,7 @@ Block* Parser::ParseVariableDeclarations( |
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; |
@@ -1740,8 +1865,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", |
@@ -1883,9 +2010,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 = |
@@ -3877,12 +4004,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))); |