| Index: src/scopes.cc
|
| diff --git a/src/scopes.cc b/src/scopes.cc
|
| index be991a1a17bc78acc8ae85aae36b4c9e45987979..d0b3c41a852c542a5285d53f94b06ed0b969757f 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 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,14 @@ 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);
|
| + if (kind == Variable::CLASS) {
|
| + p->value = new (zone())
|
| + ClassVariable(scope, name, mode, kind, initialization_flag,
|
| + maybe_assigned_flag, declaration_group_start);
|
| + } else {
|
| + p->value = new (zone()) Variable(
|
| + scope, name, mode, kind, initialization_flag, maybe_assigned_flag);
|
| + }
|
| }
|
| return reinterpret_cast<Variable*>(p->value);
|
| }
|
| @@ -76,7 +83,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),
|
| + 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 +105,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),
|
| + class_declaration_group_start_(-1) {
|
| SetDefaults(scope_type, NULL, scope_info);
|
| if (!scope_info.is_null()) {
|
| num_heap_slots_ = scope_info_->ContextLength();
|
| @@ -122,7 +131,8 @@ Scope::Scope(Zone* zone, Scope* inner_scope,
|
| module_descriptor_(NULL),
|
| already_resolved_(true),
|
| ast_value_factory_(value_factory),
|
| - zone_(zone) {
|
| + zone_(zone),
|
| + class_declaration_group_start_(-1) {
|
| SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null());
|
| AddInnerScope(inner_scope);
|
| ++num_var_or_const_;
|
| @@ -410,6 +420,7 @@ Variable* Scope::LookupLocal(const AstRawString* name) {
|
| maybe_assigned_flag = kMaybeAssigned;
|
| }
|
|
|
| + // TODO(marja, rossberg): Declare variables of the right Kind.
|
| Variable* var = variables_.Declare(this, name, mode, Variable::NORMAL,
|
| init_flag, maybe_assigned_flag);
|
| var->AllocateTo(location, index);
|
| @@ -471,7 +482,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 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
|
| @@ -479,7 +491,7 @@ 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, declaration_group_start);
|
| }
|
|
|
|
|
| @@ -1132,6 +1144,11 @@ bool Scope::CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var) {
|
| // we can only do this in the case where we have seen the declaration. And we
|
| // always allow referencing functions (for now).
|
|
|
| + // This might happen during lazy compilation; we don't keep track of
|
| + // initializer positions for variables stored in ScopeInfo, so we cannot check
|
| + // bindings against them. TODO(marja, rossberg): remove this hack.
|
| + if (var->initializer_position() == RelocInfo::kNoPosition) return true;
|
| +
|
| // Allow referencing the class name from methods of that class, even though
|
| // the initializer position for class names is only after the body.
|
| Scope* scope = this;
|
| @@ -1141,27 +1158,57 @@ 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.
|
| + if (var->is_class() &&
|
| + var->AsClassVariable()->declaration_group_start() >= 0) {
|
| + for (scope = this; scope && scope != var->scope();
|
| + scope = scope->outer_scope()) {
|
| + ClassVariable* class_var = scope->ClassVariableForMethod();
|
| + if (class_var) {
|
| + // 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.
|
| + class_var = class_var->corresponding_outer_class_variable();
|
| + if (class_var &&
|
| + class_var->declaration_group_start() ==
|
| + var->AsClassVariable()->declaration_group_start()) {
|
| + return true;
|
| + }
|
|
|
| - // 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.
|
| +
|
| + // The cycle detection can work roughly like this: 1) detect init-time
|
| + // references here (they are free variables which are inside the class
|
| + // scope but not inside a method scope - no parser changes needed to
|
| + // detect them) 2) if we encounter an init-time reference here, allow
|
| + // it, but record it for a later dependency cycle check 3) also record
|
| + // non-init-time references here 4) after scope analysis is done,
|
| + // analyse the dependency cycles: an illegal cycle is one starting with
|
| + // an init-time reference and leading back to the starting point with
|
| + // either non-init-time and init-time references.
|
| + }
|
| + }
|
| }
|
|
|
| // If both the use and the declaration are inside an eval scope (possibly
|
| @@ -1185,7 +1232,11 @@ bool Scope::CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var) {
|
| }
|
|
|
|
|
| -Variable* Scope::ClassVariableForMethod() const {
|
| +ClassVariable* 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_) &&
|
| @@ -1198,7 +1249,9 @@ Variable* Scope::ClassVariableForMethod() const {
|
| DCHECK(outer_scope_->variables_.occupancy() <= 1);
|
| if (outer_scope_->variables_.occupancy() == 0) return nullptr;
|
| VariableMap::Entry* p = outer_scope_->variables_.Start();
|
| - return reinterpret_cast<Variable*>(p->value);
|
| + Variable* var = reinterpret_cast<Variable*>(p->value);
|
| + if (!var->is_class()) return nullptr;
|
| + return var->AsClassVariable();
|
| }
|
|
|
|
|
|
|