Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Unified Diff: src/parser.cc

Issue 7992005: Block scoped const variables. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Rebased. Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/parser.h ('k') | src/preparser.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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)));
« no previous file with comments | « src/parser.h ('k') | src/preparser.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698