Commit 11a10861 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[parser] Better separate scope_info-backed lookup from other lookup

Change-Id: Id81b028629d552e2f3ebbab8bc3ab1f0e9cff3fb
Reviewed-on: https://chromium-review.googlesource.com/c/1337572Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57537}
parent fed1364a
...@@ -555,7 +555,9 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) { ...@@ -555,7 +555,9 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
// example, that does not prevent hoisting of the function in // example, that does not prevent hoisting of the function in
// `{ let e; try {} catch (e) { function e(){} } }` // `{ let e; try {} catch (e) { function e(){} } }`
do { do {
var = query_scope->LookupLocal(name); var = query_scope->scope_info_.is_null()
? query_scope->LookupLocal(name)
: query_scope->LookupInScopeInfo(name);
if (var != nullptr && IsLexical(var)) { if (var != nullptr && IsLexical(var)) {
should_hoist = false; should_hoist = false;
break; break;
...@@ -910,6 +912,10 @@ void Scope::ReplaceOuterScope(Scope* outer) { ...@@ -910,6 +912,10 @@ void Scope::ReplaceOuterScope(Scope* outer) {
} }
Variable* Scope::LookupInScopeInfo(const AstRawString* name) { Variable* Scope::LookupInScopeInfo(const AstRawString* name) {
DCHECK(!scope_info_.is_null());
Variable* cached = variables_.Lookup(name);
if (cached) return cached;
Handle<String> name_handle = name->string(); Handle<String> name_handle = name->string();
// The Scope is backed up by ScopeInfo. This means it cannot operate in a // The Scope is backed up by ScopeInfo. This means it cannot operate in a
// heap-independent mode, and all strings must be internalized immediately. So // heap-independent mode, and all strings must be internalized immediately. So
...@@ -959,14 +965,6 @@ Variable* Scope::LookupInScopeInfo(const AstRawString* name) { ...@@ -959,14 +965,6 @@ Variable* Scope::LookupInScopeInfo(const AstRawString* name) {
return var; return var;
} }
Variable* Scope::Lookup(const AstRawString* name) {
for (Scope* scope = this; scope != nullptr; scope = scope->outer_scope()) {
Variable* var = scope->LookupLocal(name);
if (var != nullptr) return var;
}
return nullptr;
}
Variable* DeclarationScope::DeclareParameter(const AstRawString* name, Variable* DeclarationScope::DeclareParameter(const AstRawString* name,
VariableMode mode, VariableMode mode,
bool is_optional, bool is_rest, bool is_optional, bool is_rest,
...@@ -1399,7 +1397,8 @@ void Scope::CollectNonLocals(DeclarationScope* max_outer_scope, ...@@ -1399,7 +1397,8 @@ void Scope::CollectNonLocals(DeclarationScope* max_outer_scope,
for (VariableProxy* proxy = unresolved_list_.first(); proxy != nullptr; for (VariableProxy* proxy = unresolved_list_.first(); proxy != nullptr;
proxy = proxy->next_unresolved()) { proxy = proxy->next_unresolved()) {
DCHECK(!proxy->is_resolved()); DCHECK(!proxy->is_resolved());
Variable* var = Lookup(proxy, lookup, max_outer_scope->outer_scope()); Variable* var =
Lookup<kParsedScope>(proxy, lookup, max_outer_scope->outer_scope());
if (var == nullptr) { if (var == nullptr) {
*non_locals = StringSet::Add(isolate, *non_locals, proxy->name()); *non_locals = StringSet::Add(isolate, *non_locals, proxy->name());
} else { } else {
...@@ -1428,7 +1427,8 @@ void Scope::AnalyzePartially( ...@@ -1428,7 +1427,8 @@ void Scope::AnalyzePartially(
for (VariableProxy* proxy = unresolved_list_.first(); proxy != nullptr; for (VariableProxy* proxy = unresolved_list_.first(); proxy != nullptr;
proxy = proxy->next_unresolved()) { proxy = proxy->next_unresolved()) {
DCHECK(!proxy->is_resolved()); DCHECK(!proxy->is_resolved());
Variable* var = Lookup(proxy, this, max_outer_scope->outer_scope()); Variable* var =
Lookup<kParsedScope>(proxy, this, max_outer_scope->outer_scope());
if (var == nullptr) { if (var == nullptr) {
// Don't copy unresolved references to the script scope, unless it's a // Don't copy unresolved references to the script scope, unless it's a
// reference to a private name or method. In that case keep it so we // reference to a private name or method. In that case keep it so we
...@@ -1797,10 +1797,11 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) { ...@@ -1797,10 +1797,11 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
} }
// static // static
template <Scope::ScopeLookupMode mode>
Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope, Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
Scope* outer_scope_end, bool force_context_allocation) { Scope* outer_scope_end, bool force_context_allocation) {
while (true) { do {
DCHECK_NE(outer_scope_end, scope); DCHECK_IMPLIES(mode == kParsedScope, !scope->is_debug_evaluate_scope_);
// Short-cut: whenever we find a debug-evaluate scope, just look everything // Short-cut: whenever we find a debug-evaluate scope, just look everything
// up dynamically. Debug-evaluate doesn't properly create scope info for the // up 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
...@@ -1808,18 +1809,22 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope, ...@@ -1808,18 +1809,22 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
// stack-allocated variables. // stack-allocated variables.
// TODO(yangguo): Remove once debug-evaluate creates proper ScopeInfo for // TODO(yangguo): Remove once debug-evaluate creates proper ScopeInfo for
// the scopes in which it's evaluating. // the scopes in which it's evaluating.
if (V8_UNLIKELY(scope->is_debug_evaluate_scope_)) { if (mode == kDeserializedScope &&
V8_UNLIKELY(scope->is_debug_evaluate_scope_)) {
return scope->NonLocal(proxy->raw_name(), VariableMode::kDynamic); return scope->NonLocal(proxy->raw_name(), VariableMode::kDynamic);
} }
// Try to find the variable in this scope. // Try to find the variable in this scope.
Variable* var = scope->LookupLocal(proxy->raw_name()); Variable* var = mode == kParsedScope
? scope->LookupLocal(proxy->raw_name())
: scope->LookupInScopeInfo(proxy->raw_name());
// 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) {
if (force_context_allocation && !var->is_dynamic()) { if (mode == kParsedScope && force_context_allocation &&
!var->is_dynamic()) {
var->ForceContextAllocation(); var->ForceContextAllocation();
} }
return var; return var;
...@@ -1840,7 +1845,11 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope, ...@@ -1840,7 +1845,11 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
force_context_allocation |= scope->is_function_scope(); force_context_allocation |= scope->is_function_scope();
scope = scope->outer_scope_; scope = scope->outer_scope_;
} // TODO(verwaest): Separate through AnalyzePartially.
if (mode == kParsedScope && !scope->scope_info_.is_null()) {
return Lookup<kDeserializedScope>(proxy, scope, outer_scope_end);
}
} while (mode == kParsedScope || !scope->is_script_scope());
// We may just be trying to find all free variables. In that case, don't // We may just be trying to find all free variables. In that case, don't
// declare them in the outer scope. // declare them in the outer scope.
...@@ -1853,6 +1862,13 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope, ...@@ -1853,6 +1862,13 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
NORMAL_VARIABLE); NORMAL_VARIABLE);
} }
template Variable* Scope::Lookup<Scope::kParsedScope>(
VariableProxy* proxy, Scope* scope, Scope* outer_scope_end,
bool force_context_allocation);
template Variable* Scope::Lookup<Scope::kDeserializedScope>(
VariableProxy* proxy, Scope* scope, Scope* outer_scope_end,
bool force_context_allocation);
namespace { namespace {
bool CanBeShadowed(Scope* scope, Variable* var) { bool CanBeShadowed(Scope* scope, Variable* var) {
if (var == nullptr) return false; if (var == nullptr) return false;
...@@ -1875,8 +1891,13 @@ Variable* Scope::LookupWith(VariableProxy* proxy, Scope* scope, ...@@ -1875,8 +1891,13 @@ Variable* Scope::LookupWith(VariableProxy* proxy, Scope* scope,
bool force_context_allocation) { bool force_context_allocation) {
DCHECK(scope->is_with_scope()); DCHECK(scope->is_with_scope());
Variable* var = Lookup(proxy, scope->outer_scope_, outer_scope_end, Variable* var =
force_context_allocation); scope->outer_scope_->scope_info_.is_null()
? Lookup<kParsedScope>(proxy, scope->outer_scope_, outer_scope_end,
force_context_allocation)
: Lookup<kDeserializedScope>(proxy, scope->outer_scope_,
outer_scope_end);
if (!CanBeShadowed(scope, var)) return var; if (!CanBeShadowed(scope, var)) return var;
// 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
...@@ -1900,8 +1921,12 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope, ...@@ -1900,8 +1921,12 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope,
DCHECK(scope->is_declaration_scope() && DCHECK(scope->is_declaration_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval()); scope->AsDeclarationScope()->calls_sloppy_eval());
Variable* var = Lookup(proxy, scope->outer_scope_, outer_scope_end, Variable* var =
force_context_allocation); scope->outer_scope_->scope_info_.is_null()
? Lookup<kParsedScope>(proxy, scope->outer_scope_, outer_scope_end,
force_context_allocation)
: Lookup<kDeserializedScope>(proxy, scope->outer_scope_,
outer_scope_end);
if (!CanBeShadowed(scope, var)) return var; if (!CanBeShadowed(scope, var)) return var;
// 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
...@@ -1927,7 +1952,7 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope, ...@@ -1927,7 +1952,7 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope,
bool Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy) { bool Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy) {
DCHECK(info->script_scope()->is_script_scope()); DCHECK(info->script_scope()->is_script_scope());
DCHECK(!proxy->is_resolved()); DCHECK(!proxy->is_resolved());
Variable* var = Lookup(proxy, this, nullptr); Variable* var = Lookup<kParsedScope>(proxy, this, nullptr);
if (var == nullptr) { if (var == nullptr) {
DCHECK(proxy->is_private_name()); DCHECK(proxy->is_private_name());
info->pending_error_handler()->ReportMessageAt( info->pending_error_handler()->ReportMessageAt(
...@@ -2041,7 +2066,7 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info) { ...@@ -2041,7 +2066,7 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info) {
if (is_declaration_scope() && AsDeclarationScope()->was_lazily_parsed()) { if (is_declaration_scope() && AsDeclarationScope()->was_lazily_parsed()) {
DCHECK_EQ(variables_.occupancy(), 0); DCHECK_EQ(variables_.occupancy(), 0);
for (VariableProxy* proxy : unresolved_list_) { for (VariableProxy* proxy : unresolved_list_) {
Variable* var = Lookup(proxy, outer_scope(), nullptr); Variable* var = Lookup<kParsedScope>(proxy, outer_scope(), nullptr);
if (var == nullptr) { if (var == nullptr) {
info->pending_error_handler()->ReportMessageAt( info->pending_error_handler()->ReportMessageAt(
proxy->position(), proxy->position() + 1, proxy->position(), proxy->position() + 1,
......
...@@ -184,17 +184,12 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -184,17 +184,12 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Lookup a variable in this scope. Returns the variable or nullptr if not // Lookup a variable in this scope. Returns the variable or nullptr if not
// found. // found.
Variable* LookupLocal(const AstRawString* name) { Variable* LookupLocal(const AstRawString* name) {
Variable* result = variables_.Lookup(name); DCHECK(scope_info_.is_null());
if (result != nullptr || scope_info_.is_null()) return result; return variables_.Lookup(name);
return LookupInScopeInfo(name);
} }
Variable* LookupInScopeInfo(const AstRawString* name); Variable* LookupInScopeInfo(const AstRawString* name);
// Lookup a variable in this scope or outer scopes.
// Returns the variable or nullptr if not found.
Variable* Lookup(const AstRawString* name);
// Declare a local variable in this scope. If the variable has been // Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned. // declared before, the previously declared variable is returned.
Variable* DeclareLocal(const AstRawString* name, VariableMode mode, Variable* DeclareLocal(const AstRawString* name, VariableMode mode,
...@@ -486,6 +481,16 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -486,6 +481,16 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
return false; return false;
} }
Variable* LookupForTesting(const AstRawString* name) {
for (Scope* scope = this; scope != nullptr; scope = scope->outer_scope()) {
Variable* var = scope->scope_info_.is_null()
? scope->LookupLocal(name)
: scope->LookupInScopeInfo(name);
if (var != nullptr) return var;
}
return nullptr;
}
static void* const kDummyPreParserVariable; static void* const kDummyPreParserVariable;
static void* const kDummyPreParserLexicalVariable; static void* const kDummyPreParserLexicalVariable;
...@@ -594,11 +599,17 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -594,11 +599,17 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// These variables are looked up dynamically at runtime. // These variables are looked up dynamically at runtime.
Variable* NonLocal(const AstRawString* name, VariableMode mode); Variable* NonLocal(const AstRawString* name, VariableMode mode);
enum ScopeLookupMode {
kParsedScope,
kDeserializedScope,
};
// Variable resolution. // Variable resolution.
// Lookup a variable reference given by name starting with this scope, and // Lookup a variable reference given by name starting with this scope, and
// stopping when reaching the outer_scope_end scope. If the code is executed // 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 to the // because of a call to 'eval', the context parameter should be set to the
// calling context of 'eval'. // calling context of 'eval'.
template <ScopeLookupMode mode>
static Variable* Lookup(VariableProxy* proxy, Scope* scope, static Variable* Lookup(VariableProxy* proxy, Scope* scope,
Scope* outer_scope_end, Scope* outer_scope_end,
bool force_context_allocation = false); bool force_context_allocation = false);
...@@ -696,6 +707,13 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -696,6 +707,13 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
bool was_lazily_parsed() const { return was_lazily_parsed_; } bool was_lazily_parsed() const { return was_lazily_parsed_; }
Variable* LookupInModule(const AstRawString* name) {
DCHECK(is_module_scope());
Variable* var = LookupInScopeInfo(name);
DCHECK_NOT_NULL(var);
return var;
}
#ifdef DEBUG #ifdef DEBUG
void set_is_being_lazily_parsed(bool is_being_lazily_parsed) { void set_is_being_lazily_parsed(bool is_being_lazily_parsed) {
is_being_lazily_parsed_ = is_being_lazily_parsed; is_being_lazily_parsed_ = is_being_lazily_parsed;
......
...@@ -1299,8 +1299,7 @@ void BytecodeGenerator::VisitModuleNamespaceImports() { ...@@ -1299,8 +1299,7 @@ void BytecodeGenerator::VisitModuleNamespaceImports() {
->LoadLiteral(Smi::FromInt(entry->module_request)) ->LoadLiteral(Smi::FromInt(entry->module_request))
.StoreAccumulatorInRegister(module_request) .StoreAccumulatorInRegister(module_request)
.CallRuntime(Runtime::kGetModuleNamespace, module_request); .CallRuntime(Runtime::kGetModuleNamespace, module_request);
Variable* var = closure_scope()->LookupLocal(entry->local_name); Variable* var = closure_scope()->LookupInModule(entry->local_name);
DCHECK_NOT_NULL(var);
BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided); BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided);
} }
} }
......
...@@ -1059,8 +1059,8 @@ TEST(ScopeUsesArgumentsSuperThis) { ...@@ -1059,8 +1059,8 @@ TEST(ScopeUsesArgumentsSuperThis) {
if ((source_data[i].expected & THIS) != 0) { if ((source_data[i].expected & THIS) != 0) {
// Currently the is_used() flag is conservative; all variables in a // Currently the is_used() flag is conservative; all variables in a
// script scope are marked as used. // script scope are marked as used.
CHECK( CHECK(scope->LookupForTesting(info.ast_value_factory()->this_string())
scope->Lookup(info.ast_value_factory()->this_string())->is_used()); ->is_used());
} }
if (is_sloppy(scope->language_mode())) { if (is_sloppy(scope->language_mode())) {
CHECK_EQ((source_data[i].expected & EVAL) != 0, CHECK_EQ((source_data[i].expected & EVAL) != 0,
...@@ -3204,7 +3204,7 @@ TEST(SerializationOfMaybeAssignmentFlag) { ...@@ -3204,7 +3204,7 @@ TEST(SerializationOfMaybeAssignmentFlag) {
CHECK_NOT_NULL(name); CHECK_NOT_NULL(name);
// Get result from h's function context (that is f's context) // Get result from h's function context (that is f's context)
i::Variable* var = s->Lookup(name); i::Variable* var = s->LookupForTesting(name);
CHECK_NOT_NULL(var); CHECK_NOT_NULL(var);
// Maybe assigned should survive deserialization // Maybe assigned should survive deserialization
...@@ -3252,7 +3252,7 @@ TEST(IfArgumentsArrayAccessedThenParametersMaybeAssigned) { ...@@ -3252,7 +3252,7 @@ TEST(IfArgumentsArrayAccessedThenParametersMaybeAssigned) {
CHECK(s != script_scope); CHECK(s != script_scope);
// Get result from f's function context (that is g's outer context) // Get result from f's function context (that is g's outer context)
i::Variable* var_x = s->Lookup(name_x); i::Variable* var_x = s->LookupForTesting(name_x);
CHECK_NOT_NULL(var_x); CHECK_NOT_NULL(var_x);
CHECK_EQ(var_x->maybe_assigned(), i::kMaybeAssigned); CHECK_EQ(var_x->maybe_assigned(), i::kMaybeAssigned);
} }
...@@ -3424,7 +3424,7 @@ TEST(InnerAssignment) { ...@@ -3424,7 +3424,7 @@ TEST(InnerAssignment) {
DCHECK(scope->is_function_scope()); DCHECK(scope->is_function_scope());
const i::AstRawString* var_name = const i::AstRawString* var_name =
info->ast_value_factory()->GetOneByteString("x"); info->ast_value_factory()->GetOneByteString("x");
i::Variable* var = scope->Lookup(var_name); i::Variable* var = scope->LookupForTesting(var_name);
bool expected = outers[i].assigned || inners[j].assigned; bool expected = outers[i].assigned || inners[j].assigned;
CHECK_NOT_NULL(var); CHECK_NOT_NULL(var);
CHECK(var->is_used() || !expected); CHECK(var->is_used() || !expected);
...@@ -3521,7 +3521,7 @@ TEST(MaybeAssignedParameters) { ...@@ -3521,7 +3521,7 @@ TEST(MaybeAssignedParameters) {
CHECK(scope->is_function_scope()); CHECK(scope->is_function_scope());
const i::AstRawString* var_name = const i::AstRawString* var_name =
info->ast_value_factory()->GetOneByteString("arg"); info->ast_value_factory()->GetOneByteString("arg");
i::Variable* var = scope->Lookup(var_name); i::Variable* var = scope->LookupForTesting(var_name);
CHECK(var->is_used() || !assigned); CHECK(var->is_used() || !assigned);
bool is_maybe_assigned = var->maybe_assigned() == i::kMaybeAssigned; bool is_maybe_assigned = var->maybe_assigned() == i::kMaybeAssigned;
CHECK_EQ(is_maybe_assigned, assigned); CHECK_EQ(is_maybe_assigned, assigned);
...@@ -3565,7 +3565,7 @@ static void TestMaybeAssigned(Input input, const char* variable, bool module, ...@@ -3565,7 +3565,7 @@ static void TestMaybeAssigned(Input input, const char* variable, bool module,
scope = i::ScopeTestHelper::FindScope(scope, input.location); scope = i::ScopeTestHelper::FindScope(scope, input.location);
const i::AstRawString* var_name = const i::AstRawString* var_name =
info->ast_value_factory()->GetOneByteString(variable); info->ast_value_factory()->GetOneByteString(variable);
var = scope->Lookup(var_name); var = scope->LookupForTesting(var_name);
} }
CHECK_NOT_NULL(var); CHECK_NOT_NULL(var);
...@@ -10025,7 +10025,7 @@ TEST(NoPessimisticContextAllocation) { ...@@ -10025,7 +10025,7 @@ TEST(NoPessimisticContextAllocation) {
DCHECK(scope->is_function_scope()); DCHECK(scope->is_function_scope());
const i::AstRawString* var_name = const i::AstRawString* var_name =
info.ast_value_factory()->GetOneByteString("my_var"); info.ast_value_factory()->GetOneByteString("my_var");
i::Variable* var = scope->Lookup(var_name); i::Variable* var = scope->LookupForTesting(var_name);
CHECK_EQ(inners[i].ctxt_allocate, CHECK_EQ(inners[i].ctxt_allocate,
i::ScopeTestHelper::MustAllocateInContext(var)); i::ScopeTestHelper::MustAllocateInContext(var));
} }
......
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