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

Let LookupRecursive bind to NonLocals properly.

This gets rid of the BindingsKind flag. It replaces the factory argument with a bool that indicates whether free variables should be resolved as well.

BUG=

Review-Url: https://codereview.chromium.org/2262393004
Cr-Commit-Position: refs/heads/master@{#38844}
parent 6122465c
...@@ -272,10 +272,14 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone, ...@@ -272,10 +272,14 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
} }
current_scope = with_scope; current_scope = with_scope;
} else if (context->IsScriptContext()) { } else if (context->IsScriptContext()) {
// If we reach a script context, it's the outermost context with scope
// info. The next context will be the native context. Install the scope
// info of this script context onto the existing script scope to avoid
// nesting script scopes.
Handle<ScopeInfo> scope_info(context->scope_info(), isolate); Handle<ScopeInfo> scope_info(context->scope_info(), isolate);
DCHECK_EQ(scope_info->scope_type(), SCRIPT_SCOPE); script_scope->SetScriptScopeInfo(scope_info);
current_scope = new (zone) DCHECK(context->previous()->IsNativeContext());
DeclarationScope(zone, current_scope, SCRIPT_SCOPE, scope_info); break;
} else if (context->IsFunctionContext()) { } else if (context->IsFunctionContext()) {
Handle<ScopeInfo> scope_info(context->closure()->shared()->scope_info(), Handle<ScopeInfo> scope_info(context->closure()->shared()->scope_info(),
isolate); isolate);
...@@ -313,9 +317,10 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone, ...@@ -313,9 +317,10 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
context = context->previous(); context = context->previous();
} }
if (innermost_scope == nullptr) return script_scope;
script_scope->AddInnerScope(current_scope); script_scope->AddInnerScope(current_scope);
script_scope->PropagateScopeInfo(); script_scope->PropagateScopeInfo();
return (innermost_scope == NULL) ? script_scope : innermost_scope; return innermost_scope;
} }
void Scope::DeserializeScopeInfo(Isolate* isolate, void Scope::DeserializeScopeInfo(Isolate* isolate,
...@@ -422,11 +427,7 @@ void Scope::Analyze(ParseInfo* info) { ...@@ -422,11 +427,7 @@ void Scope::Analyze(ParseInfo* info) {
scope->outer_scope()->scope_type() == SCRIPT_SCOPE || scope->outer_scope()->scope_type() == SCRIPT_SCOPE ||
scope->outer_scope()->already_resolved_); scope->outer_scope()->already_resolved_);
// Allocate the variables. scope->AllocateVariables(info);
{
AstNodeFactory ast_node_factory(info->ast_value_factory());
scope->AllocateVariables(info, &ast_node_factory);
}
#ifdef DEBUG #ifdef DEBUG
if (info->script_is_native() ? FLAG_print_builtin_scopes if (info->script_is_native() ? FLAG_print_builtin_scopes
...@@ -844,13 +845,12 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals, ...@@ -844,13 +845,12 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
} }
} }
void DeclarationScope::AllocateVariables(ParseInfo* info, void DeclarationScope::AllocateVariables(ParseInfo* info) {
AstNodeFactory* factory) {
// 1) Propagate scope information. // 1) Propagate scope information.
PropagateScopeInfo(); PropagateScopeInfo();
// 2) Resolve variables. // 2) Resolve variables.
ResolveVariablesRecursively(info, factory); ResolveVariablesRecursively(info);
// 3) Allocate variables. // 3) Allocate variables.
AllocateVariablesRecursively(); AllocateVariablesRecursively();
...@@ -1229,13 +1229,9 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) { ...@@ -1229,13 +1229,9 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
return var; return var;
} }
Variable* Scope::LookupRecursive(VariableProxy* proxy, Variable* Scope::LookupRecursive(VariableProxy* proxy, bool declare_free,
BindingKind* binding_kind,
AstNodeFactory* factory,
Scope* outer_scope_end) { Scope* outer_scope_end) {
DCHECK_NE(outer_scope_end, this); DCHECK_NE(outer_scope_end, this);
DCHECK_NOT_NULL(binding_kind);
DCHECK_EQ(UNBOUND, *binding_kind);
// Short-cut: whenever we find a debug-evaluate scope, just look everything up // Short-cut: whenever we find a debug-evaluate scope, just look everything up
// dynamically. Debug-evaluate doesn't properly create scope info for the // dynamically. Debug-evaluate doesn't properly create scope info for the
// lookups it does. It may not have a valid 'this' declaration, and anything // lookups it does. It may not have a valid 'this' declaration, and anything
...@@ -1244,8 +1240,8 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, ...@@ -1244,8 +1240,8 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
// TODO(yangguo): Remove once debug-evaluate creates proper ScopeInfo for the // TODO(yangguo): Remove once debug-evaluate creates proper ScopeInfo for the
// scopes in which it's evaluating. // scopes in which it's evaluating.
if (is_debug_evaluate_scope_) { if (is_debug_evaluate_scope_) {
*binding_kind = DYNAMIC_LOOKUP; if (!declare_free) return nullptr;
return nullptr; return NonLocal(proxy->raw_name(), DYNAMIC);
} }
// Try to find the variable in this scope. // Try to find the variable in this scope.
...@@ -1254,31 +1250,40 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, ...@@ -1254,31 +1250,40 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
// We found a variable and we are done. (Even if there is an 'eval' in this // We found a variable and we are done. (Even if there is an 'eval' in this
// scope which introduces the same variable again, the resulting variable // scope which introduces the same variable again, the resulting variable
// remains the same.) // remains the same.)
if (var != nullptr) { if (var != nullptr) return var;
*binding_kind = BOUND;
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.
if (is_function_scope()) { if (is_function_scope()) {
var = AsDeclarationScope()->LookupFunctionVar(proxy->raw_name()); var = AsDeclarationScope()->LookupFunctionVar(proxy->raw_name());
if (var != nullptr) { if (var != nullptr) {
*binding_kind = calls_sloppy_eval() ? BOUND_EVAL_SHADOWED : BOUND; if (calls_sloppy_eval()) return NonLocal(proxy->raw_name(), DYNAMIC);
return var; return var;
} }
} }
if (outer_scope_ != outer_scope_end) { if (outer_scope_ == outer_scope_end) {
var = outer_scope_->LookupRecursive(proxy, binding_kind, factory, if (!declare_free) return nullptr;
outer_scope_end); DCHECK(is_script_scope());
if (*binding_kind == BOUND && is_function_scope()) { // No binding has been found. Declare a variable on the global object.
return AsDeclarationScope()->DeclareDynamicGlobal(proxy->raw_name(),
Variable::NORMAL);
}
DCHECK(!is_script_scope());
var = outer_scope_->LookupRecursive(proxy, declare_free, outer_scope_end);
// The variable could not be resolved statically.
if (var == nullptr) return var;
if (is_function_scope() && !var->is_dynamic()) {
var->ForceContextAllocation(); var->ForceContextAllocation();
} }
// "this" can't be shadowed by "eval"-introduced bindings or by "with" // "this" can't be shadowed by "eval"-introduced bindings or by "with"
// scopes. // scopes.
// TODO(wingo): There are other variables in this category; add them. // TODO(wingo): There are other variables in this category; add them.
if (var != nullptr && var->is_this()) return var; if (var->is_this()) return var;
if (is_with_scope()) { if (is_with_scope()) {
// The current scope is a with scope, so the variable binding can not be // The current scope is a with scope, so the variable binding can not be
...@@ -1287,21 +1292,16 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, ...@@ -1287,21 +1292,16 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
// scope, the associated variable has to be marked as potentially being // scope, the associated variable has to be marked as potentially being
// accessed from inside of an inner with scope (the property may not be in // accessed from inside of an inner with scope (the property may not be in
// the 'with' object). // the 'with' object).
if (var != nullptr && var->IsUnallocated()) { if (!var->is_dynamic() && var->IsUnallocated()) {
DCHECK(!already_resolved_); DCHECK(!already_resolved_);
var->set_is_used(); var->set_is_used();
var->ForceContextAllocation(); var->ForceContextAllocation();
if (proxy->is_assigned()) var->set_maybe_assigned(); if (proxy->is_assigned()) var->set_maybe_assigned();
} }
*binding_kind = DYNAMIC_LOOKUP; return NonLocal(proxy->raw_name(), DYNAMIC);
return nullptr;
}
} else {
DCHECK(!is_with_scope());
DCHECK(is_function_scope() || is_script_scope() || is_eval_scope());
} }
if (calls_sloppy_eval() && is_declaration_scope() && !is_script_scope()) { if (calls_sloppy_eval() && is_declaration_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 the // scope makes a sloppy 'eval' call, so the found variable may not be the
// correct one (the 'eval' may introduce a binding with the same name). In // correct one (the 'eval' may introduce a binding with the same name). In
...@@ -1309,18 +1309,21 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, ...@@ -1309,18 +1309,21 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
// scopes that can host var bindings (declaration scopes) need be considered // scopes that can host var bindings (declaration scopes) need be considered
// here (this excludes block and catch scopes), and variable lookups at // here (this excludes block and catch scopes), and variable lookups at
// script scope are always dynamic. // script scope are always dynamic.
if (*binding_kind == BOUND) { if (var->IsGlobalObjectProperty()) {
*binding_kind = BOUND_EVAL_SHADOWED; return NonLocal(proxy->raw_name(), DYNAMIC_GLOBAL);
} else if (*binding_kind == UNBOUND) {
*binding_kind = UNBOUND_EVAL_SHADOWED;
} }
if (var->is_dynamic()) return var;
Variable* invalidated = var;
var = NonLocal(proxy->raw_name(), DYNAMIC_LOCAL);
var->set_local_if_not_shadowed(invalidated);
} }
return var; return var;
} }
void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy, void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy) {
AstNodeFactory* factory) {
DCHECK(info->script_scope()->is_script_scope()); DCHECK(info->script_scope()->is_script_scope());
// If the proxy is already resolved there's nothing to do // If the proxy is already resolved there's nothing to do
...@@ -1328,21 +1331,19 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy, ...@@ -1328,21 +1331,19 @@ 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 = UNBOUND; Variable* var = LookupRecursive(proxy, true, nullptr);
Variable* var = LookupRecursive(proxy, &binding_kind, factory);
ResolveTo(info, binding_kind, proxy, var); ResolveTo(info, proxy, var);
} }
void Scope::ResolveTo(ParseInfo* info, BindingKind binding_kind, void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) {
VariableProxy* proxy, Variable* var) {
#ifdef DEBUG #ifdef DEBUG
if (info->script_is_native()) { if (info->script_is_native()) {
// To avoid polluting the global object in native scripts // To avoid polluting the global object in native scripts
// - Variables must not be allocated to the global scope. // - Variables must not be allocated to the global scope.
CHECK_NOT_NULL(outer_scope()); CHECK_NOT_NULL(outer_scope());
// - Variables must be bound locally or unallocated. // - Variables must be bound locally or unallocated.
if (BOUND != binding_kind) { if (var->IsGlobalObjectProperty()) {
// The following variable name may be minified. If so, disable // The following variable name may be minified. If so, disable
// minification in js2c.py for better output. // minification in js2c.py for better output.
Handle<String> name = proxy->raw_name()->string(); Handle<String> name = proxy->raw_name()->string();
...@@ -1357,62 +1358,23 @@ void Scope::ResolveTo(ParseInfo* info, BindingKind binding_kind, ...@@ -1357,62 +1358,23 @@ void Scope::ResolveTo(ParseInfo* info, BindingKind binding_kind,
} }
#endif #endif
switch (binding_kind) { DCHECK_NOT_NULL(var);
case BOUND:
break;
case BOUND_EVAL_SHADOWED:
// We either found a variable binding that might be shadowed by eval or
// gave up on it (e.g. by encountering a local with the same in the outer
// scope which was not promoted to a context, this can happen if we use
// debugger to evaluate arbitrary expressions at a break point).
if (var->IsGlobalObjectProperty()) {
var = NonLocal(proxy->raw_name(), DYNAMIC_GLOBAL);
} else if (var->is_dynamic()) {
var = NonLocal(proxy->raw_name(), DYNAMIC);
} else {
Variable* invalidated = var;
var = NonLocal(proxy->raw_name(), DYNAMIC_LOCAL);
var->set_local_if_not_shadowed(invalidated);
}
break;
case UNBOUND:
// No binding has been found. Declare a variable on the global object.
var = info->script_scope()->DeclareDynamicGlobal(proxy->raw_name(),
Variable::NORMAL);
break;
case UNBOUND_EVAL_SHADOWED:
// No binding has been found. But some scope makes a sloppy 'eval' call.
var = NonLocal(proxy->raw_name(), DYNAMIC_GLOBAL);
break;
case DYNAMIC_LOOKUP:
// The variable could not be resolved statically.
var = NonLocal(proxy->raw_name(), DYNAMIC);
break;
}
DCHECK(var != NULL);
if (proxy->is_assigned()) var->set_maybe_assigned(); if (proxy->is_assigned()) var->set_maybe_assigned();
proxy->BindTo(var); proxy->BindTo(var);
} }
void Scope::ResolveVariablesRecursively(ParseInfo* info, void Scope::ResolveVariablesRecursively(ParseInfo* info) {
AstNodeFactory* factory) {
DCHECK(info->script_scope()->is_script_scope()); DCHECK(info->script_scope()->is_script_scope());
// Resolve unresolved variables for this scope. // Resolve unresolved variables for this scope.
for (VariableProxy* proxy = unresolved_; proxy != nullptr; for (VariableProxy* proxy = unresolved_; proxy != nullptr;
proxy = proxy->next_unresolved()) { proxy = proxy->next_unresolved()) {
ResolveVariable(info, proxy, factory); ResolveVariable(info, proxy);
} }
// Resolve unresolved variables for inner scopes. // Resolve unresolved variables for inner scopes.
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) { for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->ResolveVariablesRecursively(info, factory); scope->ResolveVariablesRecursively(info);
} }
} }
...@@ -1423,19 +1385,13 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope, ...@@ -1423,19 +1385,13 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
proxy = next) { proxy = next) {
next = proxy->next_unresolved(); next = proxy->next_unresolved();
if (proxy->is_resolved()) continue; if (proxy->is_resolved()) continue;
// Note that we pass nullptr as AstNodeFactory: this phase should not create Variable* var =
// any new AstNodes, since none of the Scopes involved are backed up by LookupRecursive(proxy, false, max_outer_scope->outer_scope());
// ScopeInfo.
BindingKind binding_kind = UNBOUND;
Variable* var = LookupRecursive(proxy, &binding_kind, nullptr,
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;
} else if (info != nullptr) { } else if (info != nullptr) {
DCHECK_NE(UNBOUND, binding_kind); ResolveTo(info, proxy, var);
DCHECK_NE(UNBOUND_EVAL_SHADOWED, binding_kind);
ResolveTo(info, binding_kind, proxy, var);
} }
} }
......
...@@ -530,63 +530,18 @@ class Scope: public ZoneObject { ...@@ -530,63 +530,18 @@ class Scope: public ZoneObject {
Variable* NonLocal(const AstRawString* name, VariableMode mode); Variable* NonLocal(const AstRawString* name, VariableMode mode);
// Variable resolution. // Variable resolution.
// Possible results of a recursive variable lookup telling if and how a
// variable is bound. These are returned in the output parameter *binding_kind
// of the LookupRecursive function.
enum BindingKind {
// The variable reference could be statically resolved to a variable binding
// which is returned. There is no 'with' statement between the reference and
// the binding and no scope between the reference scope (inclusive) and
// binding scope (exclusive) makes a sloppy 'eval' call.
BOUND,
// The variable reference could be statically resolved to a variable binding
// which is returned. There is no 'with' statement between the reference and
// the binding, but some scope between the reference scope (inclusive) and
// binding scope (exclusive) makes a sloppy 'eval' call, that might
// possibly introduce variable bindings shadowing the found one. Thus the
// found variable binding is just a guess.
BOUND_EVAL_SHADOWED,
// The variable reference could not be statically resolved to any binding
// and thus should be considered referencing a global variable. NULL is
// returned. The variable reference is not inside any 'with' statement and
// no scope between the reference scope (inclusive) and script scope
// (exclusive) makes a sloppy 'eval' call.
UNBOUND,
// The variable reference could not be statically resolved to any binding
// NULL is returned. The variable reference is not inside any 'with'
// statement, but some scope between the reference scope (inclusive) and
// script scope (exclusive) makes a sloppy 'eval' call, that might
// possibly introduce a variable binding. Thus the reference should be
// considered referencing a global variable unless it is shadowed by an
// 'eval' introduced binding.
UNBOUND_EVAL_SHADOWED,
// The variable could not be statically resolved and needs to be looked up
// dynamically. NULL is returned. There are two possible reasons:
// * A 'with' statement has been encountered and there is no variable
// binding for the name between the variable reference and the 'with'.
// The variable potentially references a property of the 'with' object.
// * The code is being executed as part of a call to 'eval' and the calling
// context chain contains either a variable binding for the name or it
// contains a 'with' context.
DYNAMIC_LOOKUP
};
// Lookup a variable reference given by name recursively starting with this // Lookup a variable reference given by name recursively starting with this
// scope, and stopping when reaching the outer_scope_end scope. 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, // {declare_free} indicates whether nullptr should be returned for free
AstNodeFactory* factory, // variables when falling off outer_scope_end, or whether they should be
Scope* outer_scope_end = nullptr); // declared automatically as non-locals.
void ResolveTo(ParseInfo* info, BindingKind binding_kind, Variable* LookupRecursive(VariableProxy* proxy, bool declare_free,
VariableProxy* proxy, Variable* var); Scope* outer_scope_end);
void ResolveVariable(ParseInfo* info, VariableProxy* proxy, void ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var);
AstNodeFactory* factory); void ResolveVariable(ParseInfo* info, VariableProxy* proxy);
void ResolveVariablesRecursively(ParseInfo* info, AstNodeFactory* factory); void ResolveVariablesRecursively(ParseInfo* info);
// Finds free variables of this scope. This mutates the unresolved variables // Finds free variables of this scope. This mutates the unresolved variables
// list along the way, so full resolution cannot be done afterwards. // list along the way, so full resolution cannot be done afterwards.
...@@ -681,6 +636,12 @@ class DeclarationScope : public Scope { ...@@ -681,6 +636,12 @@ class DeclarationScope : public Scope {
IsClassConstructor(function_kind()))); IsClassConstructor(function_kind())));
} }
void SetScriptScopeInfo(Handle<ScopeInfo> scope_info) {
DCHECK(is_script_scope());
DCHECK(scope_info_.is_null());
scope_info_ = scope_info;
}
bool asm_module() const { return asm_module_; } bool asm_module() const { return asm_module_; }
void set_asm_module() { asm_module_ = true; } void set_asm_module() { asm_module_ = true; }
bool asm_function() const { return asm_function_; } bool asm_function() const { return asm_function_; }
...@@ -832,7 +793,7 @@ class DeclarationScope : public Scope { ...@@ -832,7 +793,7 @@ class DeclarationScope : public Scope {
// In the case of code compiled and run using 'eval', the context // In the case of code compiled and run using 'eval', the context
// parameter is the context in which eval was called. In all other // parameter is the context in which eval was called. In all other
// cases the context parameter is an empty handle. // cases the context parameter is an empty handle.
void AllocateVariables(ParseInfo* info, AstNodeFactory* factory); void AllocateVariables(ParseInfo* info);
// To be called during parsing. Do just enough scope analysis that we can // To be called during parsing. Do just enough scope analysis that we can
// discard the Scope for lazily compiled functions. In particular, this // discard the Scope for lazily compiled functions. In particular, this
......
...@@ -115,8 +115,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, ...@@ -115,8 +115,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
CollectNonLocals(info.get(), scope); CollectNonLocals(info.get(), scope);
} }
if (!ignore_nested_scopes) { if (!ignore_nested_scopes) {
AstNodeFactory ast_node_factory(info.get()->ast_value_factory()); scope->AllocateVariables(info.get());
scope->AllocateVariables(info.get(), &ast_node_factory);
RetrieveScopeChain(scope); RetrieveScopeChain(scope);
} }
} else if (!ignore_nested_scopes) { } else if (!ignore_nested_scopes) {
......
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