summaryrefslogtreecommitdiff
path: root/deps/v8/src/ast/scopes.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/ast/scopes.cc')
-rw-r--r--deps/v8/src/ast/scopes.cc511
1 files changed, 309 insertions, 202 deletions
diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc
index c1679a40b8..b61bcdab55 100644
--- a/deps/v8/src/ast/scopes.cc
+++ b/deps/v8/src/ast/scopes.cc
@@ -9,12 +9,27 @@
#include "src/accessors.h"
#include "src/ast/ast.h"
#include "src/bootstrapper.h"
+#include "src/counters.h"
#include "src/messages.h"
+#include "src/objects-inl.h"
+#include "src/objects/module-info.h"
#include "src/parsing/parse-info.h"
namespace v8 {
namespace internal {
+namespace {
+void* kDummyPreParserVariable = reinterpret_cast<void*>(0x1);
+void* kDummyPreParserLexicalVariable = reinterpret_cast<void*>(0x2);
+
+bool IsLexical(Variable* variable) {
+ if (variable == kDummyPreParserLexicalVariable) return true;
+ if (variable == kDummyPreParserVariable) return false;
+ return IsLexicalVariableMode(variable->mode());
+}
+
+} // namespace
+
// ----------------------------------------------------------------------------
// Implementation of LocalsMap
//
@@ -49,6 +64,19 @@ Variable* VariableMap::Declare(Zone* zone, Scope* scope,
return reinterpret_cast<Variable*>(p->value);
}
+void VariableMap::DeclareName(Zone* zone, const AstRawString* name,
+ VariableMode mode) {
+ Entry* p =
+ ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(),
+ ZoneAllocationPolicy(zone));
+ if (p->value == nullptr) {
+ // The variable has not been declared yet -> insert it.
+ DCHECK_EQ(name, p->key);
+ p->value =
+ mode == VAR ? kDummyPreParserVariable : kDummyPreParserLexicalVariable;
+ }
+}
+
void VariableMap::Remove(Variable* var) {
const AstRawString* name = var->raw_name();
ZoneHashMap::Remove(const_cast<AstRawString*>(name), name->hash());
@@ -74,21 +102,27 @@ Variable* VariableMap::Lookup(const AstRawString* name) {
return NULL;
}
+void SloppyBlockFunctionMap::Delegate::set_statement(Statement* statement) {
+ if (statement_ != nullptr) {
+ statement_->set_statement(statement);
+ }
+}
+
SloppyBlockFunctionMap::SloppyBlockFunctionMap(Zone* zone)
: ZoneHashMap(8, ZoneAllocationPolicy(zone)) {}
-void SloppyBlockFunctionMap::Declare(Zone* zone, const AstRawString* name,
- SloppyBlockFunctionStatement* stmt) {
+void SloppyBlockFunctionMap::Declare(
+ Zone* zone, const AstRawString* name,
+ SloppyBlockFunctionMap::Delegate* delegate) {
// AstRawStrings are unambiguous, i.e., the same string is always represented
// by the same AstRawString*.
Entry* p =
ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(),
ZoneAllocationPolicy(zone));
- stmt->set_next(static_cast<SloppyBlockFunctionStatement*>(p->value));
- p->value = stmt;
+ delegate->set_next(static_cast<SloppyBlockFunctionMap::Delegate*>(p->value));
+ p->value = delegate;
}
-
// ----------------------------------------------------------------------------
// Implementation of Scope
@@ -243,8 +277,7 @@ Scope::Scope(Zone* zone, const AstRawString* catch_variable_name,
// Cache the catch variable, even though it's also available via the
// scope_info, as the parser expects that a catch scope always has the catch
// variable as first and only variable.
- Variable* variable = Declare(zone, this, catch_variable_name, VAR,
- NORMAL_VARIABLE, kCreatedInitialized);
+ Variable* variable = Declare(zone, catch_variable_name, VAR);
AllocateHeapSlot(variable);
}
@@ -263,7 +296,14 @@ void DeclarationScope::SetDefaults() {
arguments_ = nullptr;
this_function_ = nullptr;
should_eager_compile_ = false;
- is_lazily_parsed_ = false;
+ was_lazily_parsed_ = false;
+#ifdef DEBUG
+ DeclarationScope* outer_declaration_scope =
+ outer_scope_ ? outer_scope_->GetDeclarationScope() : nullptr;
+ is_being_lazily_parsed_ =
+ outer_declaration_scope ? outer_declaration_scope->is_being_lazily_parsed_
+ : false;
+#endif
}
void Scope::SetDefaults() {
@@ -305,7 +345,7 @@ bool DeclarationScope::ShouldEagerCompile() const {
}
void DeclarationScope::set_should_eager_compile() {
- should_eager_compile_ = !is_lazily_parsed_;
+ should_eager_compile_ = !was_lazily_parsed_;
}
void DeclarationScope::set_asm_module() {
@@ -354,17 +394,16 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
}
DCHECK(!scope_info->HasOuterScopeInfo());
break;
- } else if (scope_info->scope_type() == FUNCTION_SCOPE ||
- scope_info->scope_type() == EVAL_SCOPE) {
- // TODO(neis): For an eval scope, we currently create an ordinary function
- // context. This is wrong and needs to be fixed.
- // https://bugs.chromium.org/p/v8/issues/detail?id=5295
+ } else if (scope_info->scope_type() == FUNCTION_SCOPE) {
outer_scope =
new (zone) DeclarationScope(zone, FUNCTION_SCOPE, handle(scope_info));
if (scope_info->IsAsmFunction())
outer_scope->AsDeclarationScope()->set_asm_function();
if (scope_info->IsAsmModule())
outer_scope->AsDeclarationScope()->set_asm_module();
+ } else if (scope_info->scope_type() == EVAL_SCOPE) {
+ outer_scope =
+ new (zone) DeclarationScope(zone, EVAL_SCOPE, handle(scope_info));
} else if (scope_info->scope_type() == BLOCK_SCOPE) {
if (scope_info->is_declaration_scope()) {
outer_scope =
@@ -424,11 +463,21 @@ int Scope::num_parameters() const {
return is_declaration_scope() ? AsDeclarationScope()->num_parameters() : 0;
}
+void DeclarationScope::DeclareSloppyBlockFunction(
+ const AstRawString* name, Scope* scope,
+ SloppyBlockFunctionStatement* statement) {
+ auto* delegate =
+ new (zone()) SloppyBlockFunctionMap::Delegate(scope, statement);
+ sloppy_block_function_map_.Declare(zone(), name, delegate);
+}
+
void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
DCHECK(is_sloppy(language_mode()));
DCHECK(is_function_scope() || is_eval_scope() || is_script_scope() ||
(is_block_scope() && outer_scope()->is_function_scope()));
- DCHECK(HasSimpleParameters() || is_block_scope());
+ DCHECK(HasSimpleParameters() || is_block_scope() || is_being_lazily_parsed_);
+ DCHECK_EQ(factory == nullptr, is_being_lazily_parsed_);
+
bool has_simple_parameters = HasSimpleParameters();
// For each variable which is used as a function declaration in a sloppy
// block,
@@ -460,7 +509,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
bool var_created = false;
// Write in assignments to var for each block-scoped function declaration
- auto delegates = static_cast<SloppyBlockFunctionStatement*>(p->value);
+ auto delegates = static_cast<SloppyBlockFunctionMap::Delegate*>(p->value);
DeclarationScope* decl_scope = this;
while (decl_scope->is_eval_scope()) {
@@ -468,7 +517,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
}
Scope* outer_scope = decl_scope->outer_scope();
- for (SloppyBlockFunctionStatement* delegate = delegates;
+ for (SloppyBlockFunctionMap::Delegate* delegate = delegates;
delegate != nullptr; delegate = delegate->next()) {
// Check if there's a conflict with a lexical declaration
Scope* query_scope = delegate->scope()->outer_scope();
@@ -482,7 +531,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
// `{ let e; try {} catch (e) { function e(){} } }`
do {
var = query_scope->LookupLocal(name);
- if (var != nullptr && IsLexicalVariableMode(var->mode())) {
+ if (var != nullptr && IsLexical(var)) {
should_hoist = false;
break;
}
@@ -494,30 +543,39 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
// Declare a var-style binding for the function in the outer scope
if (!var_created) {
var_created = true;
- VariableProxy* proxy = factory->NewVariableProxy(name, NORMAL_VARIABLE);
- Declaration* declaration =
- factory->NewVariableDeclaration(proxy, this, kNoSourcePosition);
- // Based on the preceding check, it doesn't matter what we pass as
- // allow_harmony_restrictive_generators and
- // sloppy_mode_block_scope_function_redefinition.
- bool ok = true;
- DeclareVariable(declaration, VAR,
- Variable::DefaultInitializationFlag(VAR), false,
- nullptr, &ok);
- CHECK(ok); // Based on the preceding check, this should not fail
+ if (factory) {
+ VariableProxy* proxy =
+ factory->NewVariableProxy(name, NORMAL_VARIABLE);
+ auto declaration =
+ factory->NewVariableDeclaration(proxy, this, kNoSourcePosition);
+ // Based on the preceding check, it doesn't matter what we pass as
+ // allow_harmony_restrictive_generators and
+ // sloppy_mode_block_scope_function_redefinition.
+ bool ok = true;
+ DeclareVariable(declaration, VAR,
+ Variable::DefaultInitializationFlag(VAR), false,
+ nullptr, &ok);
+ CHECK(ok); // Based on the preceding check, this should not fail
+ } else {
+ DeclareVariableName(name, VAR);
+ }
}
- Expression* assignment = factory->NewAssignment(
- Token::ASSIGN, NewUnresolved(factory, name),
- delegate->scope()->NewUnresolved(factory, name), kNoSourcePosition);
- Statement* statement =
- factory->NewExpressionStatement(assignment, kNoSourcePosition);
- delegate->set_statement(statement);
+ if (factory) {
+ Expression* assignment = factory->NewAssignment(
+ Token::ASSIGN, NewUnresolved(factory, name),
+ delegate->scope()->NewUnresolved(factory, name), kNoSourcePosition);
+ Statement* statement =
+ factory->NewExpressionStatement(assignment, kNoSourcePosition);
+ delegate->set_statement(statement);
+ }
}
}
}
void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
+ RuntimeCallTimerScope runtimeTimer(info->isolate(),
+ &RuntimeCallStats::CompileScopeAnalysis);
DCHECK(info->literal() != NULL);
DeclarationScope* scope = info->literal()->scope();
@@ -542,13 +600,15 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
scope->HoistSloppyBlockFunctions(&factory);
}
- // We are compiling one of three cases:
+ // We are compiling one of four cases:
// 1) top-level code,
// 2) a function/eval/module on the top-level
// 3) a function/eval in a scope that was already resolved.
+ // 4) an asm.js function
DCHECK(scope->scope_type() == SCRIPT_SCOPE ||
scope->outer_scope()->scope_type() == SCRIPT_SCOPE ||
- scope->outer_scope()->already_resolved_);
+ scope->outer_scope()->already_resolved_ ||
+ (info->asm_function_scope() && scope->is_function_scope()));
// The outer scope is never lazy.
scope->set_should_eager_compile();
@@ -577,11 +637,11 @@ void DeclarationScope::DeclareThis(AstValueFactory* ast_value_factory) {
DCHECK(is_declaration_scope());
DCHECK(has_this_declaration());
- bool subclass_constructor = IsSubclassConstructor(function_kind_);
- Variable* var = Declare(
- zone(), this, ast_value_factory->this_string(),
- subclass_constructor ? CONST : VAR, THIS_VARIABLE,
- subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
+ bool derived_constructor = IsDerivedConstructor(function_kind_);
+ Variable* var =
+ Declare(zone(), ast_value_factory->this_string(),
+ derived_constructor ? CONST : VAR, THIS_VARIABLE,
+ derived_constructor ? kNeedsInitialization : kCreatedInitialized);
receiver_ = var;
}
@@ -594,8 +654,7 @@ void DeclarationScope::DeclareArguments(AstValueFactory* ast_value_factory) {
// Declare 'arguments' variable which exists in all non arrow functions.
// Note that it might never be accessed, in which case it won't be
// allocated during variable allocation.
- arguments_ = Declare(zone(), this, ast_value_factory->arguments_string(),
- VAR, NORMAL_VARIABLE, kCreatedInitialized);
+ arguments_ = Declare(zone(), ast_value_factory->arguments_string(), VAR);
} else if (IsLexicalVariableMode(arguments_->mode())) {
// Check if there's lexically declared variable named arguments to avoid
// redeclaration. See ES#sec-functiondeclarationinstantiation, step 20.
@@ -609,14 +668,12 @@ void DeclarationScope::DeclareDefaultFunctionVariables(
DCHECK(!is_arrow_scope());
DeclareThis(ast_value_factory);
- new_target_ = Declare(zone(), this, ast_value_factory->new_target_string(),
- CONST, NORMAL_VARIABLE, kCreatedInitialized);
+ new_target_ = Declare(zone(), ast_value_factory->new_target_string(), CONST);
if (IsConciseMethod(function_kind_) || IsClassConstructor(function_kind_) ||
IsAccessorFunction(function_kind_)) {
this_function_ =
- Declare(zone(), this, ast_value_factory->this_function_string(), CONST,
- NORMAL_VARIABLE, kCreatedInitialized);
+ Declare(zone(), ast_value_factory->this_function_string(), CONST);
}
}
@@ -637,23 +694,12 @@ Variable* DeclarationScope::DeclareFunctionVar(const AstRawString* name) {
}
bool Scope::HasBeenRemoved() const {
- // TODO(neis): Store this information somewhere instead of calculating it.
-
- if (!is_block_scope()) return false; // Shortcut.
-
- Scope* parent = outer_scope();
- if (parent == nullptr) {
- DCHECK(is_script_scope());
- return false;
- }
-
- Scope* sibling = parent->inner_scope();
- for (; sibling != nullptr; sibling = sibling->sibling()) {
- if (sibling == this) return false;
+ if (sibling() == this) {
+ DCHECK_NULL(inner_scope_);
+ DCHECK(is_block_scope());
+ return true;
}
-
- DCHECK_NULL(inner_scope_);
- return true;
+ return false;
}
Scope* Scope::GetUnremovedScope() {
@@ -667,6 +713,7 @@ Scope* Scope::GetUnremovedScope() {
Scope* Scope::FinalizeBlockScope() {
DCHECK(is_block_scope());
+ DCHECK(!HasBeenRemoved());
if (variables_.occupancy() > 0 ||
(is_declaration_scope() && calls_sloppy_eval())) {
@@ -705,7 +752,12 @@ Scope* Scope::FinalizeBlockScope() {
PropagateUsageFlagsToScope(outer_scope_);
// This block does not need a context.
num_heap_slots_ = 0;
- return NULL;
+
+ // Mark scope as removed by making it its own sibling.
+ sibling_ = this;
+ DCHECK(HasBeenRemoved());
+
+ return nullptr;
}
void DeclarationScope::AddLocal(Variable* var) {
@@ -715,13 +767,13 @@ void DeclarationScope::AddLocal(Variable* var) {
locals_.Add(var);
}
-Variable* Scope::Declare(Zone* zone, Scope* scope, const AstRawString* name,
+Variable* Scope::Declare(Zone* zone, const AstRawString* name,
VariableMode mode, VariableKind kind,
InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag) {
bool added;
Variable* var =
- variables_.Declare(zone, scope, name, mode, kind, initialization_flag,
+ variables_.Declare(zone, this, name, mode, kind, initialization_flag,
maybe_assigned_flag, &added);
if (added) locals_.Add(var);
return var;
@@ -796,6 +848,7 @@ void Scope::PropagateUsageFlagsToScope(Scope* other) {
DCHECK(!already_resolved_);
DCHECK(!other->already_resolved_);
if (calls_eval()) other->RecordEvalCall();
+ if (inner_scope_calls_eval_) other->inner_scope_calls_eval_ = true;
}
Variable* Scope::LookupInScopeInfo(const AstRawString* name) {
@@ -869,12 +922,13 @@ Variable* DeclarationScope::DeclareParameter(
DCHECK(is_function_scope() || is_module_scope());
DCHECK(!has_rest_);
DCHECK(!is_optional || !is_rest);
+ DCHECK(!is_being_lazily_parsed_);
+ DCHECK(!was_lazily_parsed_);
Variable* var;
if (mode == TEMPORARY) {
var = NewTemporary(name);
} else {
- var =
- Declare(zone(), this, name, mode, NORMAL_VARIABLE, kCreatedInitialized);
+ var = Declare(zone(), name, mode);
// TODO(wingo): Avoid O(n^2) check.
*is_duplicate = IsDeclaredParameter(name);
}
@@ -894,8 +948,9 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
// introduced during variable allocation, and TEMPORARY variables are
// allocated via NewTemporary().
DCHECK(IsDeclaredVariableMode(mode));
- return Declare(zone(), this, name, mode, kind, init_flag,
- maybe_assigned_flag);
+ DCHECK(!GetDeclarationScope()->is_being_lazily_parsed());
+ DCHECK(!GetDeclarationScope()->was_lazily_parsed());
+ return Declare(zone(), name, mode, kind, init_flag, maybe_assigned_flag);
}
Variable* Scope::DeclareVariable(
@@ -904,6 +959,8 @@ Variable* Scope::DeclareVariable(
bool* sloppy_mode_block_scope_function_redefinition, bool* ok) {
DCHECK(IsDeclaredVariableMode(mode));
DCHECK(!already_resolved_);
+ DCHECK(!GetDeclarationScope()->is_being_lazily_parsed());
+ DCHECK(!GetDeclarationScope()->was_lazily_parsed());
if (mode == VAR && !is_declaration_scope()) {
return GetDeclarationScope()->DeclareVariable(
@@ -1002,6 +1059,28 @@ Variable* Scope::DeclareVariable(
return var;
}
+void Scope::DeclareVariableName(const AstRawString* name, VariableMode mode) {
+ DCHECK(IsDeclaredVariableMode(mode));
+ DCHECK(!already_resolved_);
+ DCHECK(GetDeclarationScope()->is_being_lazily_parsed());
+
+ if (mode == VAR && !is_declaration_scope()) {
+ return GetDeclarationScope()->DeclareVariableName(name, mode);
+ }
+ DCHECK(!is_with_scope());
+ DCHECK(!is_eval_scope());
+ // Unlike DeclareVariable, DeclareVariableName allows declaring variables in
+ // catch scopes: Parser::RewriteCatchPattern bypasses DeclareVariable by
+ // calling DeclareLocal directly, and it doesn't make sense to add a similar
+ // bypass mechanism for PreParser.
+ DCHECK(is_declaration_scope() || (IsLexicalVariableMode(mode) &&
+ (is_block_scope() || is_catch_scope())));
+ DCHECK(scope_info_.is_null());
+
+ // Declare the variable in the declaration scope.
+ variables_.DeclareName(zone(), name, mode);
+}
+
VariableProxy* Scope::NewUnresolved(AstNodeFactory* factory,
const AstRawString* name,
int start_position, VariableKind kind) {
@@ -1009,7 +1088,7 @@ VariableProxy* Scope::NewUnresolved(AstNodeFactory* factory,
// the same name because they may be removed selectively via
// RemoveUnresolved().
DCHECK(!already_resolved_);
- DCHECK_EQ(!needs_migration_, factory->zone() == zone());
+ DCHECK_EQ(factory->zone(), zone());
VariableProxy* proxy = factory->NewVariableProxy(name, kind, start_position);
proxy->set_next_unresolved(unresolved_);
unresolved_ = proxy;
@@ -1026,8 +1105,7 @@ void Scope::AddUnresolved(VariableProxy* proxy) {
Variable* DeclarationScope::DeclareDynamicGlobal(const AstRawString* name,
VariableKind kind) {
DCHECK(is_script_scope());
- return variables_.Declare(zone(), this, name, DYNAMIC_GLOBAL, kind,
- kCreatedInitialized);
+ return variables_.Declare(zone(), this, name, DYNAMIC_GLOBAL, kind);
}
@@ -1050,26 +1128,6 @@ bool Scope::RemoveUnresolved(VariableProxy* var) {
return false;
}
-bool Scope::RemoveUnresolved(const AstRawString* name) {
- if (unresolved_ != nullptr && unresolved_->raw_name() == name) {
- VariableProxy* removed = unresolved_;
- unresolved_ = unresolved_->next_unresolved();
- removed->set_next_unresolved(nullptr);
- return true;
- }
- VariableProxy* current = unresolved_;
- while (current != nullptr) {
- VariableProxy* next = current->next_unresolved();
- if (next != nullptr && next->raw_name() == name) {
- current->set_next_unresolved(next->next_unresolved());
- next->set_next_unresolved(nullptr);
- return true;
- }
- current = next;
- }
- return false;
-}
-
Variable* Scope::NewTemporary(const AstRawString* name) {
DeclarationScope* scope = GetClosureScope();
Variable* var = new (zone())
@@ -1157,9 +1215,9 @@ bool Scope::AllowsLazyParsingWithoutUnresolvedVariables(
// guaranteed to be correct.
for (const Scope* s = this; s != outer; s = s->outer_scope_) {
// Eval forces context allocation on all outer scopes, so we don't need to
- // look at those scopes. Sloppy eval makes all top-level variables dynamic,
- // whereas strict-mode requires context allocation.
- if (s->is_eval_scope()) return !is_strict(s->language_mode());
+ // look at those scopes. Sloppy eval makes top-level non-lexical variables
+ // dynamic, whereas strict-mode requires context allocation.
+ if (s->is_eval_scope()) return is_sloppy(s->language_mode());
// Catch scopes force context allocation of all variables.
if (s->is_catch_scope()) continue;
// With scopes do not introduce variables that need allocation.
@@ -1276,7 +1334,7 @@ Scope* Scope::GetOuterScopeWithContext() {
Handle<StringSet> DeclarationScope::CollectNonLocals(
ParseInfo* info, Handle<StringSet> non_locals) {
- VariableProxy* free_variables = FetchFreeVariables(this, true, info);
+ VariableProxy* free_variables = FetchFreeVariables(this, info);
for (VariableProxy* proxy = free_variables; proxy != nullptr;
proxy = proxy->next_unresolved()) {
non_locals = StringSet::Add(non_locals, proxy->name());
@@ -1292,21 +1350,30 @@ void DeclarationScope::ResetAfterPreparsing(AstValueFactory* ast_value_factory,
params_.Clear();
decls_.Clear();
locals_.Clear();
- sloppy_block_function_map_.Clear();
- variables_.Clear();
- // Make sure we won't walk the scope tree from here on.
inner_scope_ = nullptr;
unresolved_ = nullptr;
- if (aborted && !IsArrowFunction(function_kind_)) {
- DeclareDefaultFunctionVariables(ast_value_factory);
+ if (aborted) {
+ // Prepare scope for use in the outer zone.
+ zone_ = ast_value_factory->zone();
+ variables_.Reset(ZoneAllocationPolicy(zone_));
+ sloppy_block_function_map_.Reset(ZoneAllocationPolicy(zone_));
+ if (!IsArrowFunction(function_kind_)) {
+ DeclareDefaultFunctionVariables(ast_value_factory);
+ }
+ } else {
+ // Make sure this scope isn't used for allocation anymore.
+ zone_ = nullptr;
+ variables_.Invalidate();
+ sloppy_block_function_map_.Invalidate();
}
#ifdef DEBUG
needs_migration_ = false;
+ is_being_lazily_parsed_ = false;
#endif
- is_lazily_parsed_ = !aborted;
+ was_lazily_parsed_ = !aborted;
}
void DeclarationScope::AnalyzePartially(AstNodeFactory* ast_node_factory) {
@@ -1317,9 +1384,8 @@ void DeclarationScope::AnalyzePartially(AstNodeFactory* ast_node_factory) {
// Try to resolve unresolved variables for this Scope and migrate those
// which cannot be resolved inside. It doesn't make sense to try to resolve
// them in the outer Scopes here, because they are incomplete.
- for (VariableProxy* proxy =
- FetchFreeVariables(this, !FLAG_lazy_inner_functions);
- proxy != nullptr; proxy = proxy->next_unresolved()) {
+ for (VariableProxy* proxy = FetchFreeVariables(this); proxy != nullptr;
+ proxy = proxy->next_unresolved()) {
DCHECK(!proxy->is_resolved());
VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy);
copy->set_next_unresolved(unresolved);
@@ -1339,8 +1405,10 @@ void DeclarationScope::AnalyzePartially(AstNodeFactory* ast_node_factory) {
}
#ifdef DEBUG
-static const char* Header(ScopeType scope_type, FunctionKind function_kind,
- bool is_declaration_scope) {
+namespace {
+
+const char* Header(ScopeType scope_type, FunctionKind function_kind,
+ bool is_declaration_scope) {
switch (scope_type) {
case EVAL_SCOPE: return "eval";
// TODO(adamk): Should we print concise method scopes specially?
@@ -1359,18 +1427,13 @@ static const char* Header(ScopeType scope_type, FunctionKind function_kind,
return NULL;
}
+void Indent(int n, const char* str) { PrintF("%*s%s", n, "", str); }
-static void Indent(int n, const char* str) {
- PrintF("%*s%s", n, "", str);
-}
-
-
-static void PrintName(const AstRawString* name) {
+void PrintName(const AstRawString* name) {
PrintF("%.*s", name->length(), name->raw_data());
}
-
-static void PrintLocation(Variable* var) {
+void PrintLocation(Variable* var) {
switch (var->location()) {
case VariableLocation::UNALLOCATED:
break;
@@ -1392,45 +1455,48 @@ static void PrintLocation(Variable* var) {
}
}
-
-static void PrintVar(int indent, Variable* var) {
- if (var->is_used() || !var->IsUnallocated()) {
- Indent(indent, VariableMode2String(var->mode()));
- PrintF(" ");
- if (var->raw_name()->IsEmpty())
- PrintF(".%p", reinterpret_cast<void*>(var));
- else
- PrintName(var->raw_name());
- PrintF("; // ");
- PrintLocation(var);
- bool comma = !var->IsUnallocated();
- if (var->has_forced_context_allocation()) {
- if (comma) PrintF(", ");
- PrintF("forced context allocation");
- comma = true;
- }
- if (var->maybe_assigned() == kNotAssigned) {
- if (comma) PrintF(", ");
- PrintF("never assigned");
- }
- PrintF("\n");
+void PrintVar(int indent, Variable* var) {
+ Indent(indent, VariableMode2String(var->mode()));
+ PrintF(" ");
+ if (var->raw_name()->IsEmpty())
+ PrintF(".%p", reinterpret_cast<void*>(var));
+ else
+ PrintName(var->raw_name());
+ PrintF("; // ");
+ PrintLocation(var);
+ bool comma = !var->IsUnallocated();
+ if (var->has_forced_context_allocation()) {
+ if (comma) PrintF(", ");
+ PrintF("forced context allocation");
+ comma = true;
+ }
+ if (var->maybe_assigned() == kNotAssigned) {
+ if (comma) PrintF(", ");
+ PrintF("never assigned");
}
+ PrintF("\n");
}
-static void PrintMap(int indent, VariableMap* map, bool locals) {
+void PrintMap(int indent, const char* label, VariableMap* map, bool locals,
+ Variable* function_var) {
+ bool printed_label = false;
for (VariableMap::Entry* p = map->Start(); p != nullptr; p = map->Next(p)) {
Variable* var = reinterpret_cast<Variable*>(p->value);
+ if (var == function_var) continue;
bool local = !IsDynamicVariableMode(var->mode());
- if (locals ? local : !local) {
- if (var == nullptr) {
- Indent(indent, "<?>\n");
- } else {
- PrintVar(indent, var);
+ if ((locals ? local : !local) &&
+ (var->is_used() || !var->IsUnallocated())) {
+ if (!printed_label) {
+ Indent(indent, label);
+ printed_label = true;
}
+ PrintVar(indent, var);
}
}
}
+} // anonymous namespace
+
void DeclarationScope::PrintParameters() {
PrintF(" (");
for (int i = 0; i < params_.length(); i++) {
@@ -1487,9 +1553,12 @@ void Scope::Print(int n) {
if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n");
if (is_declaration_scope()) {
DeclarationScope* scope = AsDeclarationScope();
- if (scope->is_lazily_parsed()) Indent(n1, "// lazily parsed\n");
+ if (scope->was_lazily_parsed()) Indent(n1, "// lazily parsed\n");
if (scope->ShouldEagerCompile()) Indent(n1, "// will be compiled\n");
}
+ if (has_forced_context_allocation()) {
+ Indent(n1, "// forces context allocation\n");
+ }
if (num_stack_slots_ > 0) {
Indent(n1, "// ");
PrintF("%d stack slots\n", num_stack_slots_);
@@ -1505,12 +1574,22 @@ void Scope::Print(int n) {
PrintVar(n1, function);
}
- if (variables_.Start() != NULL) {
- Indent(n1, "// local vars:\n");
- PrintMap(n1, &variables_, true);
+ // Print temporaries.
+ {
+ bool printed_header = false;
+ for (Variable* local : locals_) {
+ if (local->mode() != TEMPORARY) continue;
+ if (!printed_header) {
+ printed_header = true;
+ Indent(n1, "// temporary vars:\n");
+ }
+ PrintVar(n1, local);
+ }
+ }
- Indent(n1, "// dynamic vars:\n");
- PrintMap(n1, &variables_, false);
+ if (variables_.occupancy() > 0) {
+ PrintMap(n1, "// local vars:\n", &variables_, true, function);
+ PrintMap(n1, "// dynamic vars:\n", &variables_, false, function);
}
// Print inner scopes (disable by providing negative n).
@@ -1539,6 +1618,12 @@ void Scope::CheckScopePositions() {
void Scope::CheckZones() {
DCHECK(!needs_migration_);
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
+ if (scope->is_declaration_scope() &&
+ scope->AsDeclarationScope()->was_lazily_parsed()) {
+ DCHECK_NULL(scope->zone());
+ DCHECK_NULL(scope->inner_scope_);
+ continue;
+ }
CHECK_EQ(scope->zone(), zone());
scope->CheckZones();
}
@@ -1548,8 +1633,7 @@ void Scope::CheckZones() {
Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
// Declare a new non-local.
DCHECK(IsDynamicVariableMode(mode));
- Variable* var = variables_.Declare(zone(), NULL, name, mode, NORMAL_VARIABLE,
- kCreatedInitialized);
+ Variable* var = variables_.Declare(zone(), nullptr, name, mode);
// Allocate it by giving it a dynamic lookup.
var->AllocateTo(VariableLocation::LOOKUP, -1);
return var;
@@ -1590,6 +1674,13 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, Scope* outer_scope_end) {
// The variable could not be resolved statically.
if (var == nullptr) return var;
+ // TODO(marja): Separate LookupRecursive for preparsed scopes better.
+ if (var == kDummyPreParserVariable || var == kDummyPreParserLexicalVariable) {
+ DCHECK(GetDeclarationScope()->is_being_lazily_parsed());
+ DCHECK(FLAG_lazy_inner_functions);
+ return var;
+ }
+
if (is_function_scope() && !var->is_dynamic()) {
var->ForceContextAllocation();
}
@@ -1641,34 +1732,20 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy) {
DCHECK(!proxy->is_resolved());
Variable* var = LookupRecursive(proxy, nullptr);
ResolveTo(info, proxy, var);
-
- if (FLAG_lazy_inner_functions) {
- if (info != nullptr && info->is_native()) return;
- // Pessimistically force context allocation for all variables to which inner
- // scope variables could potentially resolve to.
- Scope* scope = GetClosureScope()->outer_scope_;
- while (scope != nullptr && scope->scope_info_.is_null()) {
- var = scope->LookupLocal(proxy->raw_name());
- if (var != nullptr) {
- // Since we don't lazy parse inner arrow functions, inner functions
- // cannot refer to the outer "this".
- if (!var->is_dynamic() && !var->is_this() &&
- !var->has_forced_context_allocation()) {
- var->ForceContextAllocation();
- var->set_is_used();
- // We don't know what the (potentially lazy parsed) inner function
- // does with the variable; pessimistically assume that it's assigned.
- var->set_maybe_assigned();
- }
- }
- scope = scope->outer_scope_;
- }
- }
}
namespace {
bool AccessNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) {
+ if (var->mode() == DYNAMIC_LOCAL) {
+ // Dynamically introduced variables never need a hole check (since they're
+ // VAR bindings, either from var or function declarations), but the variable
+ // they shadow might need a hole check, which we want to do if we decide
+ // that no shadowing variable was dynamically introoduced.
+ DCHECK(!var->binding_needs_init());
+ return AccessNeedsHoleCheck(var->local_if_not_shadowed(), proxy, scope);
+ }
+
if (!var->binding_needs_init()) {
return false;
}
@@ -1703,8 +1780,7 @@ bool AccessNeedsHoleCheck(Variable* var, VariableProxy* proxy, Scope* scope) {
}
if (var->is_this()) {
- DCHECK(
- IsSubclassConstructor(scope->GetDeclarationScope()->function_kind()));
+ DCHECK(IsDerivedConstructor(scope->GetDeclarationScope()->function_kind()));
// TODO(littledan): implement 'this' hole check elimination.
return true;
}
@@ -1749,37 +1825,65 @@ void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) {
void Scope::ResolveVariablesRecursively(ParseInfo* info) {
DCHECK(info->script_scope()->is_script_scope());
+ // Lazy parsed declaration scopes are already partially analyzed. If there are
+ // unresolved references remaining, they just need to be resolved in outer
+ // scopes.
+ if (is_declaration_scope() && AsDeclarationScope()->was_lazily_parsed()) {
+ DCHECK(variables_.occupancy() == 0);
+ for (VariableProxy* proxy = unresolved_; proxy != nullptr;
+ proxy = proxy->next_unresolved()) {
+ Variable* var = outer_scope()->LookupRecursive(proxy, nullptr);
+ if (!var->is_dynamic()) {
+ var->set_is_used();
+ var->ForceContextAllocation();
+ if (proxy->is_assigned()) var->set_maybe_assigned();
+ }
+ }
+ } else {
+ // Resolve unresolved variables for this scope.
+ for (VariableProxy* proxy = unresolved_; proxy != nullptr;
+ proxy = proxy->next_unresolved()) {
+ ResolveVariable(info, proxy);
+ }
- // Resolve unresolved variables for this scope.
- for (VariableProxy* proxy = unresolved_; proxy != nullptr;
- proxy = proxy->next_unresolved()) {
- ResolveVariable(info, proxy);
- }
-
- // Resolve unresolved variables for inner scopes.
- for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
- scope->ResolveVariablesRecursively(info);
+ // Resolve unresolved variables for inner scopes.
+ for (Scope* scope = inner_scope_; scope != nullptr;
+ scope = scope->sibling_) {
+ scope->ResolveVariablesRecursively(info);
+ }
}
}
VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
- bool try_to_resolve, ParseInfo* info,
+ ParseInfo* info,
VariableProxy* stack) {
+ // Lazy parsed declaration scopes are already partially analyzed. If there are
+ // unresolved references remaining, they just need to be resolved in outer
+ // scopes.
+ Scope* lookup =
+ is_declaration_scope() && AsDeclarationScope()->was_lazily_parsed()
+ ? outer_scope()
+ : this;
for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr;
proxy = next) {
next = proxy->next_unresolved();
DCHECK(!proxy->is_resolved());
- Variable* var = nullptr;
- if (try_to_resolve) {
- var = LookupRecursive(proxy, max_outer_scope->outer_scope());
- }
+ Variable* var =
+ lookup->LookupRecursive(proxy, max_outer_scope->outer_scope());
if (var == nullptr) {
proxy->set_next_unresolved(stack);
stack = proxy;
- } else if (info != nullptr) {
- ResolveTo(info, proxy, var);
- } else {
- var->set_is_used();
+ } else if (var != kDummyPreParserVariable &&
+ var != kDummyPreParserLexicalVariable) {
+ if (info != nullptr) {
+ // In this case we need to leave scopes in a way that they can be
+ // allocated. If we resolved variables from lazy parsed scopes, we need
+ // to context allocate the var.
+ ResolveTo(info, proxy, var);
+ if (!var->is_dynamic() && lookup != this) var->ForceContextAllocation();
+ } else {
+ var->set_is_used();
+ }
}
}
@@ -1787,8 +1891,7 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
unresolved_ = nullptr;
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
- stack =
- scope->FetchFreeVariables(max_outer_scope, try_to_resolve, info, stack);
+ stack = scope->FetchFreeVariables(max_outer_scope, info, stack);
}
return stack;
@@ -1823,7 +1926,10 @@ bool Scope::MustAllocateInContext(Variable* var) {
if (has_forced_context_allocation()) return true;
if (var->mode() == TEMPORARY) return false;
if (is_catch_scope()) return true;
- if (is_script_scope() && IsLexicalVariableMode(var->mode())) return true;
+ if ((is_script_scope() || is_eval_scope()) &&
+ IsLexicalVariableMode(var->mode())) {
+ return true;
+ }
return var->has_forced_context_allocation() || inner_scope_calls_eval_;
}
@@ -1880,6 +1986,7 @@ void DeclarationScope::AllocateParameterLocals() {
DCHECK_EQ(this, var->scope());
if (uses_sloppy_arguments) {
var->set_is_used();
+ var->set_maybe_assigned();
var->ForceContextAllocation();
}
AllocateParameter(var, i);
@@ -1969,7 +2076,7 @@ void Scope::AllocateVariablesRecursively() {
DCHECK(!already_resolved_);
DCHECK_EQ(0, num_stack_slots_);
// Don't allocate variables of preparsed scopes.
- if (is_declaration_scope() && AsDeclarationScope()->is_lazily_parsed()) {
+ if (is_declaration_scope() && AsDeclarationScope()->was_lazily_parsed()) {
return;
}
@@ -1994,9 +2101,9 @@ void Scope::AllocateVariablesRecursively() {
// Force allocation of a context for this scope if necessary. For a 'with'
// scope and for a function scope that makes an 'eval' call we need a context,
// even if no local variables were statically allocated in the scope.
- // Likewise for modules.
+ // Likewise for modules and function scopes representing asm.js modules.
bool must_have_context =
- is_with_scope() || is_module_scope() ||
+ is_with_scope() || is_module_scope() || IsAsmModule() ||
(is_function_scope() && calls_sloppy_eval()) ||
(is_block_scope() && is_declaration_scope() && calls_sloppy_eval());