Chromium Code Reviews| Index: src/scopes.cc |
| diff --git a/src/scopes.cc b/src/scopes.cc |
| index 364f2f1cf7ce88ca08e207172c1cba1613dbb3f7..59d2aee30b62a095f7ff70a2d3594ef981c60af1 100644 |
| --- a/src/scopes.cc |
| +++ b/src/scopes.cc |
| @@ -32,7 +32,8 @@ VariableMap::~VariableMap() {} |
| Variable* VariableMap::Declare(Scope* scope, const AstRawString* name, |
| VariableMode mode, Variable::Kind kind, |
| InitializationFlag initialization_flag, |
| - MaybeAssignedFlag maybe_assigned_flag) { |
| + MaybeAssignedFlag maybe_assigned_flag, |
| + int consecutive_declaration_group_start) { |
| // AstRawStrings are unambiguous, i.e., the same string is always represented |
| // by the same AstRawString*. |
| // FIXME(marja): fix the type of Lookup. |
| @@ -42,8 +43,9 @@ Variable* VariableMap::Declare(Scope* scope, const AstRawString* name, |
| if (p->value == NULL) { |
| // The variable has not been declared yet -> insert it. |
| DCHECK(p->key == name); |
| - p->value = new (zone()) Variable(scope, name, mode, kind, |
| - initialization_flag, maybe_assigned_flag); |
| + p->value = new (zone()) |
| + Variable(scope, name, mode, kind, initialization_flag, |
| + maybe_assigned_flag, consecutive_declaration_group_start); |
| } |
| return reinterpret_cast<Variable*>(p->value); |
| } |
| @@ -76,7 +78,8 @@ Scope::Scope(Zone* zone, Scope* outer_scope, ScopeType scope_type, |
| scope_type == MODULE_SCOPE ? ModuleDescriptor::New(zone) : NULL), |
| already_resolved_(false), |
| ast_value_factory_(ast_value_factory), |
| - zone_(zone) { |
| + zone_(zone), |
| + consecutive_class_declaration_group_start_(-1) { |
| SetDefaults(scope_type, outer_scope, Handle<ScopeInfo>::null(), |
| function_kind); |
| // The outermost scope must be a script scope. |
| @@ -97,7 +100,8 @@ Scope::Scope(Zone* zone, Scope* inner_scope, ScopeType scope_type, |
| module_descriptor_(NULL), |
| already_resolved_(true), |
| ast_value_factory_(value_factory), |
| - zone_(zone) { |
| + zone_(zone), |
| + consecutive_class_declaration_group_start_(-1) { |
| SetDefaults(scope_type, NULL, scope_info); |
| if (!scope_info.is_null()) { |
| num_heap_slots_ = scope_info_->ContextLength(); |
| @@ -122,7 +126,8 @@ Scope::Scope(Zone* zone, Scope* inner_scope, |
| module_descriptor_(NULL), |
| already_resolved_(true), |
| ast_value_factory_(value_factory), |
| - zone_(zone) { |
| + zone_(zone), |
| + consecutive_class_declaration_group_start_(-1) { |
| SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null()); |
| AddInnerScope(inner_scope); |
| ++num_var_or_const_; |
| @@ -469,7 +474,8 @@ Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode, |
| Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode, |
| InitializationFlag init_flag, Variable::Kind kind, |
| - MaybeAssignedFlag maybe_assigned_flag) { |
| + MaybeAssignedFlag maybe_assigned_flag, |
| + int consecutive_declaration_group_start) { |
| DCHECK(!already_resolved()); |
| // This function handles VAR, LET, and CONST modes. DYNAMIC variables are |
| // introduces during variable allocation, INTERNAL variables are allocated |
| @@ -477,7 +483,8 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode, |
| DCHECK(IsDeclaredVariableMode(mode)); |
| ++num_var_or_const_; |
| return variables_.Declare(this, name, mode, kind, init_flag, |
| - maybe_assigned_flag); |
| + maybe_assigned_flag, |
| + consecutive_declaration_group_start); |
| } |
| @@ -1139,27 +1146,47 @@ bool Scope::CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var) { |
| } |
| // Allow references from methods to classes declared later, if we detect no |
| - // problematic dependency cycles. |
| - |
| - if (ClassVariableForMethod() && var->is_class()) { |
| - // A method is referring to some other class, possibly declared |
| - // later. Referring to a class declared earlier is always OK and covered by |
| - // the code outside this if. Here we only need to allow special cases for |
| - // referring to a class which is declared later. |
| - |
| - // Referring to a class C declared later is OK under the following |
| - // circumstances: |
| - |
| - // 1. The class declarations are in a consecutive group with no other |
| - // declarations or statements in between, and |
| - |
| - // 2. There is no dependency cycle where the first edge is an initialization |
| - // time dependency (computed property name or extends clause) from C to |
| - // something that depends on this class directly or transitively. |
| + // problematic dependency cycles. Note that we can be inside multiple methods |
| + // at the same time, and it's enough if we find one where the reference is |
| + // allowed. |
| + scope = this; |
| + while (scope) { |
|
rossberg
2015/04/21 13:30:02
A couple of observations:
- I shouldn't need to lo
marja
2015/04/23 09:52:37
I rewrote the code so that it's closer to what you
|
| + Variable* class_var = scope->ClassVariableForMethod(); |
| + if (class_var && var->is_class()) { |
| + // A method is referring to some other class, possibly declared |
| + // later. Referring to a class declared earlier is always OK and covered |
| + // by the code outside this if. Here we only need to allow special cases |
| + // for referring to a class which is declared later. |
| + |
| + // Referring to a class C declared later is OK under the following |
| + // circumstances: |
| + |
| + // 1. The class declarations are in a consecutive group with no other |
| + // declarations or statements in between, and |
| + |
| + // 2. There is no dependency cycle where the first edge is an |
| + // initialization time dependency (computed property name or extends |
| + // clause) from C to something that depends on this class directly or |
| + // transitively. |
| + |
| + // This is needed because a class ("class Name { }") creates two bindings |
| + // (one in the outer scope, and one in the class scope). The method is a |
| + // function scope inside the inner scope (class scope). The consecutive |
| + // class declarations are in the outer scope. |
| + if (class_var->corresponding_outer_class_variable()) { |
| + class_var = class_var->corresponding_outer_class_variable(); |
| + } |
| - // TODO(marja,rossberg): implement these checks. Here we undershoot the |
| - // target and allow referring to any class. |
| - return true; |
| + // TODO(marja,rossberg): implement the dependency cycle detection. Here we |
| + // undershoot the target and allow referring to any class in the same |
| + // consectuive declaration group. |
| + if (class_var->consecutive_declaration_group_start() == |
| + var->consecutive_declaration_group_start() && |
| + class_var->consecutive_declaration_group_start() >= 0) { |
| + return true; |
| + } |
| + } |
| + scope = scope->outer_scope(); |
| } |
| // If both the use and the declaration are inside an eval scope (possibly |
| @@ -1184,6 +1211,10 @@ bool Scope::CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var) { |
| Variable* Scope::ClassVariableForMethod() const { |
| + // TODO(marja, rossberg): This fails to find a class variable in the following |
| + // cases: |
| + // let A = class { ... } |
| + // It needs to be investigated whether this causes any practical problems. |
| if (!is_function_scope()) return nullptr; |
| if (IsInObjectLiteral(function_kind_)) return nullptr; |
| if (!IsConciseMethod(function_kind_) && !IsConstructor(function_kind_) && |