Chromium Code Reviews| 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))); |