Index: src/parser.cc |
diff --git a/src/parser.cc b/src/parser.cc |
index 4e5ba1389a030cd3f32a1130b6218d3f2056b4cc..7cb0209a17a43687f0df46001d470b693c42a744 100644 |
--- a/src/parser.cc |
+++ b/src/parser.cc |
@@ -411,6 +411,15 @@ Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) { |
return result; |
} |
+ |
+Scope* Parser::DeclarationScope() { |
Kevin Millikin (Chromium)
2011/06/30 09:28:45
As an alternative, we could keep a pair of scopes.
|
+ Scope* scope = top_scope_; |
+ while (scope->is_catch_scope()) { |
+ scope = scope->outer_scope(); |
+ } |
+ return scope; |
+} |
+ |
// ---------------------------------------------------------------------------- |
// Target is a support class to facilitate manipulation of the |
// Parser's target_stack_ (the stack of potential 'break' and |
@@ -1301,13 +1310,14 @@ 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. |
- if (top_scope_->is_function_scope() || |
- top_scope_->is_strict_mode_eval_scope()) { |
+ Scope* declaration_scope = DeclarationScope(); |
+ if (declaration_scope->is_function_scope() || |
+ declaration_scope->is_strict_mode_eval_scope()) { |
// Declare the variable in the function scope. |
- var = top_scope_->LocalLookup(name); |
+ var = declaration_scope->LocalLookup(name); |
if (var == NULL) { |
// Declare the name. |
- var = top_scope_->DeclareLocal(name, mode); |
+ var = declaration_scope->DeclareLocal(name, mode); |
} else { |
// The name was declared before; check for conflicting |
// re-declarations. If the previous declaration was a const or the |
@@ -1323,7 +1333,7 @@ VariableProxy* Parser::Declare(Handle<String> name, |
Expression* expression = |
NewThrowTypeError(isolate()->factory()->redeclaration_symbol(), |
type_string, name); |
- top_scope_->SetIllegalRedeclaration(expression); |
+ declaration_scope->SetIllegalRedeclaration(expression); |
} |
} |
} |
@@ -1344,14 +1354,18 @@ VariableProxy* Parser::Declare(Handle<String> name, |
// semantic issue as long as we keep the source order, but it may be |
// a performance issue since it may lead to repeated |
// Runtime::DeclareContextSlot() calls. |
- VariableProxy* proxy = top_scope_->NewUnresolved(name, inside_with()); |
- top_scope_->AddDeclaration(new(zone()) Declaration(proxy, mode, fun)); |
+ VariableProxy* proxy = declaration_scope->NewUnresolved(name, false); |
+ declaration_scope->AddDeclaration(new(zone()) Declaration(proxy, mode, fun)); |
// For global const variables we bind the proxy to a variable. |
- if (mode == Variable::CONST && top_scope_->is_global_scope()) { |
+ if (mode == Variable::CONST && declaration_scope->is_global_scope()) { |
ASSERT(resolve); // should be set by all callers |
Variable::Kind kind = Variable::NORMAL; |
- var = new(zone()) Variable(top_scope_, name, Variable::CONST, true, kind); |
+ var = new(zone()) Variable(declaration_scope, |
+ name, |
+ Variable::CONST, |
+ true, |
+ kind); |
} |
// If requested and we have a local variable, bind the proxy to the variable |
@@ -1407,7 +1421,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { |
// isn't lazily compiled. The extension structures are only |
// accessible while parsing the first time not when reparsing |
// because of lazy compilation. |
- top_scope_->ForceEagerCompilation(); |
+ DeclarationScope()->ForceEagerCompilation(); |
// Compute the function template for the native function. |
v8::Handle<v8::FunctionTemplate> fun_template = |
@@ -1485,8 +1499,8 @@ Block* Parser::ParseVariableStatement(bool* ok) { |
// VariableStatement :: |
// VariableDeclarations ';' |
- Expression* dummy; // to satisfy the ParseVariableDeclarations() signature |
- Block* result = ParseVariableDeclarations(true, &dummy, CHECK_OK); |
+ Handle<String> ignore; |
+ Block* result = ParseVariableDeclarations(true, &ignore, CHECK_OK); |
ExpectSemicolon(CHECK_OK); |
return result; |
} |
@@ -1504,18 +1518,19 @@ bool Parser::IsEvalOrArguments(Handle<String> string) { |
// to initialize it properly. This mechanism is used for the parsing |
// of 'for-in' loops. |
Block* Parser::ParseVariableDeclarations(bool accept_IN, |
- Expression** var, |
+ Handle<String>* name, |
Kevin Millikin (Chromium)
2011/06/30 09:28:45
In most cases (all but 'for (var ... in ...)' the
|
bool* ok) { |
// VariableDeclarations :: |
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] |
Variable::Mode mode = Variable::VAR; |
bool is_const = false; |
+ Scope* declaration_scope = DeclarationScope(); |
if (peek() == Token::VAR) { |
Consume(Token::VAR); |
} else if (peek() == Token::CONST) { |
Consume(Token::CONST); |
- if (top_scope_->is_strict_mode()) { |
+ if (declaration_scope->is_strict_mode()) { |
ReportMessage("strict_const", Vector<const char*>::empty()); |
*ok = false; |
return NULL; |
@@ -1540,18 +1555,17 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
// |
// Create new block with one expected declaration. |
Block* block = new(zone()) Block(NULL, 1, true); |
- VariableProxy* last_var = NULL; // the last variable declared |
int nvars = 0; // the number of variables declared |
do { |
if (fni_ != NULL) fni_->Enter(); |
// Parse variable name. |
if (nvars > 0) Consume(Token::COMMA); |
- Handle<String> name = ParseIdentifier(CHECK_OK); |
- if (fni_ != NULL) fni_->PushVariableName(name); |
+ *name = ParseIdentifier(CHECK_OK); |
+ if (fni_ != NULL) fni_->PushVariableName(*name); |
// Strict mode variables may not be named eval or arguments |
- if (top_scope_->is_strict_mode() && IsEvalOrArguments(name)) { |
+ if (declaration_scope->is_strict_mode() && IsEvalOrArguments(*name)) { |
ReportMessage("strict_var_name", Vector<const char*>::empty()); |
*ok = false; |
return NULL; |
@@ -1569,11 +1583,10 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
// 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). |
- last_var = Declare(name, mode, NULL, |
- is_const /* always bound for CONST! */, |
- CHECK_OK); |
+ Declare(*name, mode, NULL, is_const /* always bound for CONST! */, |
+ CHECK_OK); |
nvars++; |
- if (top_scope_->num_var_or_const() > kMaxNumFunctionLocals) { |
+ if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { |
ReportMessageAt(scanner().location(), "too_many_variables", |
Vector<const char*>::empty()); |
*ok = false; |
@@ -1589,10 +1602,10 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
// |
// var v; v = x; |
// |
- // In particular, we need to re-lookup 'v' as it may be a |
- // different 'v' than the 'v' in the declaration (if we are inside |
- // a 'with' statement that makes a object property with name 'v' |
- // visible). |
+ // In particular, we need to re-lookup 'v' (in top_scope_, not |
+ // declaration_scope) as it may be a different 'v' than the 'v' in the |
+ // declaration (e.g., if we are inside a 'with' statement or 'catch' |
+ // block). |
// |
// However, note that const declarations are different! A const |
// declaration of the form: |
@@ -1607,6 +1620,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
// one - there is no re-lookup (see the last parameter of the |
// Declare() call above). |
+ Scope* initialization_scope = is_const ? declaration_scope : top_scope_; |
Kevin Millikin (Chromium)
2011/06/30 09:28:45
It's subtle that the declaration and initializatio
|
Expression* value = NULL; |
int position = -1; |
if (peek() == Token::ASSIGN) { |
@@ -1647,11 +1661,11 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
// browsers where the global object (window) has lots of |
// properties defined in prototype objects. |
- if (top_scope_->is_global_scope()) { |
+ if (initialization_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(new(zone()) Literal(name)); |
+ arguments->Add(new(zone()) Literal(*name)); |
CallRuntime* initialize; |
if (is_const) { |
@@ -1670,8 +1684,10 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
} else { |
// Add strict mode. |
// We may want to pass singleton to avoid Literal allocations. |
- arguments->Add(NewNumberLiteral( |
- top_scope_->is_strict_mode() ? kStrictMode : kNonStrictMode)); |
+ StrictModeFlag flag = initialization_scope->is_strict_mode() |
+ ? kStrictMode |
+ : kNonStrictMode; |
+ arguments->Add(NewNumberLiteral(flag)); |
// Be careful not to assign a value to the global variable if |
// we're in a with. The initialization value should not |
@@ -1708,8 +1724,11 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
// the top context for variables). Sigh... |
if (value != NULL) { |
Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR); |
+ bool in_with = is_const ? false : inside_with(); |
+ VariableProxy* proxy = |
+ initialization_scope->NewUnresolved(*name, in_with); |
Assignment* assignment = |
- new(zone()) Assignment(op, last_var, value, position); |
+ new(zone()) Assignment(op, proxy, value, position); |
if (block) { |
block->AddStatement(new(zone()) ExpressionStatement(assignment)); |
} |
@@ -1718,12 +1737,6 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
if (fni_ != NULL) fni_->Leave(); |
} while (peek() == Token::COMMA); |
- if (!is_const && nvars == 1) { |
- // We have a single, non-const variable. |
- ASSERT(last_var != NULL); |
- *var = last_var; |
- } |
- |
return block; |
} |
@@ -1895,7 +1908,9 @@ Statement* Parser::ParseReturnStatement(bool* ok) { |
// function. See ECMA-262, section 12.9, page 67. |
// |
// To be consistent with KJS we report the syntax error at runtime. |
- if (!top_scope_->is_function_scope()) { |
+ Scope* declaration_scope = DeclarationScope(); |
+ if (declaration_scope->is_global_scope() || |
+ declaration_scope->is_eval_scope()) { |
Handle<String> type = isolate()->factory()->illegal_return_symbol(); |
Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null()); |
return new(zone()) ExpressionStatement(throw_error); |
@@ -1922,7 +1937,7 @@ Block* Parser::WithHelper(Expression* obj, ZoneStringList* labels, bool* ok) { |
Statement* stat; |
{ Target target(&this->target_stack_, &collector); |
with_nesting_level_++; |
- top_scope_->RecordWithStatement(); |
+ DeclarationScope()->RecordWithStatement(); |
Kevin Millikin (Chromium)
2011/06/30 09:28:45
This is wrong, let's talk about what to do here.
Mads Ager (chromium)
2011/06/30 12:08:32
But it is safe, right?
|
stat = ParseStatement(labels, CHECK_OK); |
with_nesting_level_--; |
} |
@@ -2082,6 +2097,8 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { |
// block. Since we don't know yet if there will be a finally block, we |
// always collect the targets. |
TargetCollector catch_collector; |
+ Scope* catch_scope = NULL; |
+ Variable* catch_variable = NULL; |
Block* catch_block = NULL; |
Handle<String> name; |
if (tok == Token::CATCH) { |
@@ -2108,10 +2125,16 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { |
TargetCollector inner_collector; |
{ Target target(&this->target_stack_, &catch_collector); |
{ Target target(&this->target_stack_, &inner_collector); |
- ++with_nesting_level_; |
- top_scope_->RecordWithStatement(); |
+ catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with()); |
+ if (top_scope_->is_strict_mode()) { |
+ catch_scope->EnableStrictMode(); |
+ } |
+ catch_variable = catch_scope->DeclareLocal(name, Variable::VAR); |
+ |
+ Scope* saved_scope = top_scope_; |
+ top_scope_ = catch_scope; |
inner_body = ParseBlock(NULL, CHECK_OK); |
- --with_nesting_level_; |
+ top_scope_ = saved_scope; |
} |
} |
@@ -2145,19 +2168,28 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { |
// 'try { try B0 catch B1 } finally B2' |
if (catch_block != NULL && finally_block != NULL) { |
+ // If we have both, create an inner try/catch. |
+ ASSERT(catch_scope != NULL && catch_variable != NULL); |
TryCatchStatement* statement = |
- new(zone()) TryCatchStatement(try_block, name, catch_block); |
+ new(zone()) TryCatchStatement(try_block, |
+ catch_scope, |
+ catch_variable, |
+ catch_block); |
statement->set_escaping_targets(try_collector.targets()); |
try_block = new(zone()) Block(NULL, 1, false); |
try_block->AddStatement(statement); |
- catch_block = NULL; |
+ catch_block = NULL; // Clear to indicate it's been handled. |
} |
TryStatement* result = NULL; |
if (catch_block != NULL) { |
ASSERT(finally_block == NULL); |
+ ASSERT(catch_scope != NULL && catch_variable != NULL); |
result = |
- new(zone()) TryCatchStatement(try_block, name, catch_block); |
+ new(zone()) TryCatchStatement(try_block, |
+ catch_scope, |
+ catch_variable, |
+ catch_block); |
} else { |
ASSERT(finally_block != NULL); |
result = new(zone()) TryFinallyStatement(try_block, finally_block); |
@@ -2230,9 +2262,11 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { |
Expect(Token::LPAREN, CHECK_OK); |
if (peek() != Token::SEMICOLON) { |
if (peek() == Token::VAR || peek() == Token::CONST) { |
- Expression* each = NULL; |
+ Handle<String> name; |
Block* variable_statement = |
- ParseVariableDeclarations(false, &each, CHECK_OK); |
+ ParseVariableDeclarations(false, &name, CHECK_OK); |
+ VariableProxy* each = top_scope_->NewUnresolved(name, inside_with()); |
+ |
if (peek() == Token::IN && each != NULL) { |
ForInStatement* loop = new(zone()) ForInStatement(labels); |
Target target(&this->target_stack_, loop); |
@@ -2901,8 +2935,7 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { |
switch (peek()) { |
case Token::THIS: { |
Consume(Token::THIS); |
- VariableProxy* recv = top_scope_->receiver(); |
- result = recv; |
+ result = new(zone()) VariableProxy(top_scope_->receiver()); |
break; |
} |
@@ -3762,7 +3795,7 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) { |
if (extension_ != NULL) { |
// The extension structures are only accessible while parsing the |
// very first time not when reparsing because of lazy compilation. |
- top_scope_->ForceEagerCompilation(); |
+ DeclarationScope()->ForceEagerCompilation(); |
} |
const Runtime::Function* function = Runtime::FunctionForSymbol(name); |