Commit e77a78cd authored by verwaest's avatar verwaest Committed by Commit bot

Cleanup scope resolution

BUG=v8:5209

Review-Url: https://codereview.chromium.org/2230323004
Cr-Commit-Position: refs/heads/master@{#38580}
parent b2b40134
...@@ -1261,78 +1261,85 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) { ...@@ -1261,78 +1261,85 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
Variable* Scope::LookupRecursive(VariableProxy* proxy, Variable* Scope::LookupRecursive(VariableProxy* proxy,
BindingKind* binding_kind, BindingKind* binding_kind,
AstNodeFactory* factory, AstNodeFactory* factory,
Scope* max_outer_scope) { Scope* outer_scope_end) {
DCHECK(binding_kind != NULL); DCHECK_NE(outer_scope_end, this);
DCHECK_NOT_NULL(binding_kind);
DCHECK_EQ(UNBOUND, *binding_kind);
if (already_resolved() && is_with_scope()) { if (already_resolved() && is_with_scope()) {
// Short-cut: if the scope is deserialized from a scope info, variable // Short-cut: if the scope is deserialized from a scope info, variable
// allocation is already fixed. We can simply return with dynamic lookup. // allocation is already fixed. We can simply return with dynamic lookup.
*binding_kind = DYNAMIC_LOOKUP; *binding_kind = DYNAMIC_LOOKUP;
return NULL; return nullptr;
} }
// Try to find the variable in this scope. // Try to find the variable in this scope.
Variable* var = LookupLocal(proxy->raw_name()); Variable* var = LookupLocal(proxy->raw_name());
// We found a variable and we are done. (Even if there is an 'eval' in // We found a variable and we are done. (Even if there is an 'eval' in this
// this scope which introduces the same variable again, the resulting // scope which introduces the same variable again, the resulting variable
// variable remains the same.) // remains the same.)
if (var != NULL) { if (var != nullptr) {
*binding_kind = BOUND; *binding_kind = BOUND;
return var; return var;
} }
// We did not find a variable locally. Check against the function variable, if // We did not find a variable locally. Check against the function variable, if
// any. // any.
*binding_kind = UNBOUND; if (is_function_scope()) {
var = is_function_scope() var = AsDeclarationScope()->LookupFunctionVar(proxy->raw_name());
? AsDeclarationScope()->LookupFunctionVar(proxy->raw_name()) if (var != nullptr) {
: nullptr; *binding_kind = calls_sloppy_eval() ? BOUND_EVAL_SHADOWED : BOUND;
if (var != NULL) { return var;
*binding_kind = BOUND; }
} else if (outer_scope_ != nullptr && this != max_outer_scope) { }
if (outer_scope_ != outer_scope_end) {
var = outer_scope_->LookupRecursive(proxy, binding_kind, factory, var = outer_scope_->LookupRecursive(proxy, binding_kind, factory,
max_outer_scope); outer_scope_end);
if (*binding_kind == BOUND && is_function_scope()) { if (*binding_kind == BOUND && is_function_scope()) {
var->ForceContextAllocation(); var->ForceContextAllocation();
} }
} else { // "this" can't be shadowed by "eval"-introduced bindings or by "with"
DCHECK(is_script_scope() || this == max_outer_scope); // scopes.
} // TODO(wingo): There are other variables in this category; add them.
if (var != nullptr && var->is_this()) return var;
// "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes.
// TODO(wingo): There are other variables in this category; add them. if (is_with_scope()) {
bool name_can_be_shadowed = var == nullptr || !var->is_this(); DCHECK(!already_resolved());
// The current scope is a with scope, so the variable binding can not be
if (is_with_scope() && name_can_be_shadowed) { // statically resolved. However, note that it was necessary to do a lookup
DCHECK(!already_resolved()); // in the outer scope anyway, because if a binding exists in an outer
// The current scope is a with scope, so the variable binding can not be // scope, the associated variable has to be marked as potentially being
// statically resolved. However, note that it was necessary to do a lookup // accessed from inside of an inner with scope (the property may not be in
// in the outer scope anyway, because if a binding exists in an outer scope, // the 'with' object).
// the associated variable has to be marked as potentially being accessed if (var != nullptr) {
// from inside of an inner with scope (the property may not be in the 'with' var->set_is_used();
// object). var->ForceContextAllocation();
if (var != NULL) { if (proxy->is_assigned()) var->set_maybe_assigned();
var->set_is_used(); }
var->ForceContextAllocation(); *binding_kind = DYNAMIC_LOOKUP;
if (proxy->is_assigned()) var->set_maybe_assigned(); return nullptr;
} }
*binding_kind = DYNAMIC_LOOKUP; } else {
return NULL; DCHECK(is_function_scope() || is_script_scope() || is_eval_scope());
} else if (calls_sloppy_eval() && is_declaration_scope() && DCHECK(!is_with_scope());
!is_script_scope() && name_can_be_shadowed) { }
if (calls_sloppy_eval() && is_declaration_scope() && !is_script_scope()) {
// A variable binding may have been found in an outer scope, but the current // A variable binding may have been found in an outer scope, but the current
// scope makes a sloppy 'eval' call, so the found variable may not be // scope makes a sloppy 'eval' call, so the found variable may not be the
// the correct one (the 'eval' may introduce a binding with the same name). // correct one (the 'eval' may introduce a binding with the same name). In
// In that case, change the lookup result to reflect this situation. // that case, change the lookup result to reflect this situation. Only
// Only scopes that can host var bindings (declaration scopes) need be // scopes that can host var bindings (declaration scopes) need be considered
// considered here (this excludes block and catch scopes), and variable // here (this excludes block and catch scopes), and variable lookups at
// lookups at script scope are always dynamic. // script scope are always dynamic.
if (*binding_kind == BOUND) { if (*binding_kind == BOUND) {
*binding_kind = BOUND_EVAL_SHADOWED; *binding_kind = BOUND_EVAL_SHADOWED;
} else if (*binding_kind == UNBOUND) { } else if (*binding_kind == UNBOUND) {
*binding_kind = UNBOUND_EVAL_SHADOWED; *binding_kind = UNBOUND_EVAL_SHADOWED;
} }
} }
return var; return var;
} }
...@@ -1345,7 +1352,7 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy, ...@@ -1345,7 +1352,7 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy,
if (proxy->is_resolved()) return; if (proxy->is_resolved()) return;
// Otherwise, try to resolve the variable. // Otherwise, try to resolve the variable.
BindingKind binding_kind; BindingKind binding_kind = UNBOUND;
Variable* var = LookupRecursive(proxy, &binding_kind, factory); Variable* var = LookupRecursive(proxy, &binding_kind, factory);
ResolveTo(info, binding_kind, proxy, var); ResolveTo(info, binding_kind, proxy, var);
...@@ -1435,7 +1442,6 @@ void Scope::ResolveVariablesRecursively(ParseInfo* info, ...@@ -1435,7 +1442,6 @@ void Scope::ResolveVariablesRecursively(ParseInfo* info,
VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope, VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
ParseInfo* info, ParseInfo* info,
VariableProxy* stack) { VariableProxy* stack) {
BindingKind binding_kind = BOUND;
for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr; for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr;
proxy = next) { proxy = next) {
next = proxy->next_unresolved(); next = proxy->next_unresolved();
...@@ -1443,9 +1449,9 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope, ...@@ -1443,9 +1449,9 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
// Note that we pass nullptr as AstNodeFactory: this phase should not create // Note that we pass nullptr as AstNodeFactory: this phase should not create
// any new AstNodes, since none of the Scopes involved are backed up by // any new AstNodes, since none of the Scopes involved are backed up by
// ScopeInfo. // ScopeInfo.
Variable* var = BindingKind binding_kind = UNBOUND;
LookupRecursive(proxy, &binding_kind, nullptr, max_outer_scope); Variable* var = LookupRecursive(proxy, &binding_kind, nullptr,
// Anything that was bound max_outer_scope->outer_scope());
if (var == nullptr) { if (var == nullptr) {
proxy->set_next_unresolved(stack); proxy->set_next_unresolved(stack);
stack = proxy; stack = proxy;
......
...@@ -571,12 +571,12 @@ class Scope: public ZoneObject { ...@@ -571,12 +571,12 @@ class Scope: public ZoneObject {
}; };
// Lookup a variable reference given by name recursively starting with this // Lookup a variable reference given by name recursively starting with this
// scope, but only until max_outer_scope (if not nullptr). If the code is // scope, and stopping when reaching the outer_scope_end scope. If the code is
// executed because of a call to 'eval', the context parameter should be set // executed because of a call to 'eval', the context parameter should be set
// to the calling context of 'eval'. // to the calling context of 'eval'.
Variable* LookupRecursive(VariableProxy* proxy, BindingKind* binding_kind, Variable* LookupRecursive(VariableProxy* proxy, BindingKind* binding_kind,
AstNodeFactory* factory, AstNodeFactory* factory,
Scope* max_outer_scope = nullptr); Scope* outer_scope_end = nullptr);
void ResolveTo(ParseInfo* info, BindingKind binding_kind, void ResolveTo(ParseInfo* info, BindingKind binding_kind,
VariableProxy* proxy, Variable* var); VariableProxy* proxy, Variable* var);
void ResolveVariable(ParseInfo* info, VariableProxy* proxy, void ResolveVariable(ParseInfo* info, VariableProxy* proxy,
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
assertEquals(200, (function f() { eval("var f = 100"); f = 200; return f })());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment