Commit bf3081c8 authored by marja's avatar marja Committed by Commit bot

Put Scopes into temporary Zone (second try)

When parsing a eagerly-parsed-but-lazily-compiled function, we
used to put some of its AST nodes into a discardable Zone. This
CL puts the function Scope, its inner Scopes and the related AST
nodes (Declarations, VariableProxys) into the temporary Zone
too. This reduces peak memory usage and enables future work to
keep the temporary Zone around for later compilation.

BUG=

Review-Url: https://codereview.chromium.org/2210243002
Cr-Commit-Position: refs/heads/master@{#38348}
parent 9977a2ca
...@@ -204,6 +204,18 @@ VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, ...@@ -204,6 +204,18 @@ VariableProxy::VariableProxy(Zone* zone, const AstRawString* name,
raw_name_(name), raw_name_(name),
next_unresolved_(nullptr) {} next_unresolved_(nullptr) {}
VariableProxy::VariableProxy(Zone* zone, const VariableProxy* copy_from)
: Expression(zone, copy_from->position(), kVariableProxy),
bit_field_(copy_from->bit_field_),
end_position_(copy_from->end_position_),
next_unresolved_(nullptr) {
if (copy_from->is_resolved()) {
var_ = copy_from->var_;
} else {
raw_name_ = copy_from->raw_name_;
}
}
void VariableProxy::BindTo(Variable* var) { void VariableProxy::BindTo(Variable* var) {
DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name()); DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name());
set_var(var); set_var(var);
......
This diff is collapsed.
...@@ -357,6 +357,7 @@ bool Scope::Analyze(ParseInfo* info) { ...@@ -357,6 +357,7 @@ bool Scope::Analyze(ParseInfo* info) {
scope->Print(); scope->Print();
} }
scope->CheckScopePositions(); scope->CheckScopePositions();
scope->CheckZones();
#endif #endif
info->set_scope(scope); info->set_scope(scope);
...@@ -580,9 +581,11 @@ Variable* Scope::LookupFunctionVar(const AstRawString* name, ...@@ -580,9 +581,11 @@ Variable* Scope::LookupFunctionVar(const AstRawString* name,
if (index < 0) return NULL; if (index < 0) return NULL;
Variable* var = new (zone()) Variable* var = new (zone())
Variable(this, name, mode, Variable::NORMAL, kCreatedInitialized); Variable(this, name, mode, Variable::NORMAL, kCreatedInitialized);
DCHECK_NOT_NULL(factory);
VariableProxy* proxy = factory->NewVariableProxy(var); VariableProxy* proxy = factory->NewVariableProxy(var);
VariableDeclaration* declaration = VariableDeclaration* declaration =
factory->NewVariableDeclaration(proxy, mode, this, kNoSourcePosition); factory->NewVariableDeclaration(proxy, mode, this, kNoSourcePosition);
DCHECK_EQ(factory->zone(), zone());
DeclareFunctionVar(declaration); DeclareFunctionVar(declaration);
var->AllocateTo(VariableLocation::CONTEXT, index); var->AllocateTo(VariableLocation::CONTEXT, index);
return var; return var;
...@@ -967,6 +970,35 @@ Handle<StringSet> Scope::CollectNonLocals(Handle<StringSet> non_locals) { ...@@ -967,6 +970,35 @@ Handle<StringSet> Scope::CollectNonLocals(Handle<StringSet> non_locals) {
return non_locals; return non_locals;
} }
void Scope::AnalyzePartially(Scope* migrate_to,
AstNodeFactory* ast_node_factory) {
// Gather info from inner scopes.
PropagateScopeInfo(false);
// 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.
MigrateUnresolvableLocals(migrate_to, ast_node_factory, this);
// Push scope data up to migrate_to. Note that migrate_to and this Scope
// describe the same Scope, just in different Zones.
PropagateUsageFlagsToScope(migrate_to);
if (inner_scope_calls_eval_) {
migrate_to->inner_scope_calls_eval_ = true;
}
DCHECK(!force_eager_compilation_);
migrate_to->set_start_position(start_position_);
migrate_to->set_end_position(end_position_);
migrate_to->language_mode_ = language_mode_;
migrate_to->arity_ = arity_;
migrate_to->force_context_allocation_ = force_context_allocation_;
outer_scope_->RemoveInnerScope(this);
DCHECK_EQ(outer_scope_, migrate_to->outer_scope_);
DCHECK_EQ(outer_scope_->zone(), migrate_to->zone());
DCHECK_EQ(NeedsHomeObject(), migrate_to->NeedsHomeObject());
DCHECK_EQ(asm_function_, migrate_to->asm_function_);
DCHECK_EQ(arguments() != nullptr, migrate_to->arguments() != nullptr);
}
#ifdef DEBUG #ifdef DEBUG
static const char* Header(ScopeType scope_type, FunctionKind function_kind, static const char* Header(ScopeType scope_type, FunctionKind function_kind,
...@@ -1175,6 +1207,12 @@ void Scope::CheckScopePositions() { ...@@ -1175,6 +1207,12 @@ void Scope::CheckScopePositions() {
scope->CheckScopePositions(); scope->CheckScopePositions();
} }
} }
void Scope::CheckZones() {
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
CHECK_EQ(scope->zone(), zone());
}
}
#endif // DEBUG #endif // DEBUG
...@@ -1197,10 +1235,10 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) { ...@@ -1197,10 +1235,10 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
return var; return var;
} }
Variable* Scope::LookupRecursive(VariableProxy* proxy, Variable* Scope::LookupRecursive(VariableProxy* proxy,
BindingKind* binding_kind, BindingKind* binding_kind,
AstNodeFactory* factory) { AstNodeFactory* factory,
Scope* max_outer_scope) {
DCHECK(binding_kind != NULL); DCHECK(binding_kind != NULL);
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
...@@ -1227,13 +1265,14 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, ...@@ -1227,13 +1265,14 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
var = LookupFunctionVar(proxy->raw_name(), factory); var = LookupFunctionVar(proxy->raw_name(), factory);
if (var != NULL) { if (var != NULL) {
*binding_kind = BOUND; *binding_kind = BOUND;
} else if (outer_scope_ != NULL) { } else if (outer_scope_ != nullptr && this != max_outer_scope) {
var = outer_scope_->LookupRecursive(proxy, binding_kind, factory); var = outer_scope_->LookupRecursive(proxy, binding_kind, factory,
max_outer_scope);
if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) { if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) {
var->ForceContextAllocation(); var->ForceContextAllocation();
} }
} else { } else {
DCHECK(is_script_scope()); DCHECK(is_script_scope() || this == max_outer_scope);
} }
// "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes. // "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes.
...@@ -1366,6 +1405,31 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info, ...@@ -1366,6 +1405,31 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info,
return true; return true;
} }
void Scope::MigrateUnresolvableLocals(Scope* migrate_to,
AstNodeFactory* ast_node_factory,
Scope* max_outer_scope) {
BindingKind binding_kind;
for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr;
proxy = next) {
next = proxy->next_unresolved();
// 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
// ScopeInfo.
if (LookupRecursive(proxy, &binding_kind, nullptr, max_outer_scope) ==
nullptr) {
// Re-create the VariableProxies in the right Zone and insert them into
// migrate_to.
DCHECK(!proxy->is_resolved());
VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy);
migrate_to->AddUnresolved(copy);
}
}
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->MigrateUnresolvableLocals(migrate_to, ast_node_factory,
max_outer_scope);
}
}
void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) { void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) {
if (outer_scope_calls_sloppy_eval) { if (outer_scope_calls_sloppy_eval) {
......
...@@ -204,6 +204,7 @@ class Scope: public ZoneObject { ...@@ -204,6 +204,7 @@ class Scope: public ZoneObject {
// the same name because they may be removed selectively via // the same name because they may be removed selectively via
// RemoveUnresolved(). // RemoveUnresolved().
DCHECK(!already_resolved()); DCHECK(!already_resolved());
DCHECK_EQ(factory->zone(), zone());
VariableProxy* proxy = VariableProxy* proxy =
factory->NewVariableProxy(name, kind, start_position, end_position); factory->NewVariableProxy(name, kind, start_position, end_position);
proxy->set_next_unresolved(unresolved_); proxy->set_next_unresolved(unresolved_);
...@@ -612,6 +613,13 @@ class Scope: public ZoneObject { ...@@ -612,6 +613,13 @@ class Scope: public ZoneObject {
return &sloppy_block_function_map_; return &sloppy_block_function_map_;
} }
// To be called during parsing. Do just enough scope analysis that we can
// discard the Scope for lazily compiled functions. In particular, this
// records variables which cannot be resolved inside the Scope (we don't yet
// know what they will resolve to since the outer Scopes are incomplete) and
// migrates them into migrate_to.
void AnalyzePartially(Scope* migrate_to, AstNodeFactory* ast_node_factory);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Debugging. // Debugging.
...@@ -620,6 +628,9 @@ class Scope: public ZoneObject { ...@@ -620,6 +628,9 @@ class Scope: public ZoneObject {
// Check that the scope has positions assigned. // Check that the scope has positions assigned.
void CheckScopePositions(); void CheckScopePositions();
// Check that all Scopes in the scope tree use the same Zone.
void CheckZones();
#endif #endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -780,16 +791,24 @@ class Scope: public ZoneObject { ...@@ -780,16 +791,24 @@ 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. If the code is executed because of a call to 'eval', the context // scope, but only until max_outer_scope (if not nullptr). If the code is
// parameter should be set to the calling context of 'eval'. // executed because of a call to 'eval', the context parameter should be set
// 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);
MUST_USE_RESULT MUST_USE_RESULT
bool ResolveVariable(ParseInfo* info, VariableProxy* proxy, bool ResolveVariable(ParseInfo* info, VariableProxy* proxy,
AstNodeFactory* factory); AstNodeFactory* factory);
MUST_USE_RESULT MUST_USE_RESULT
bool ResolveVariablesRecursively(ParseInfo* info, AstNodeFactory* factory); bool ResolveVariablesRecursively(ParseInfo* info, AstNodeFactory* factory);
// Tries to resolve local variables inside max_outer_scope; migrates those
// which cannot be resolved into migrate_to.
void MigrateUnresolvableLocals(Scope* migrate_to,
AstNodeFactory* ast_node_factory,
Scope* max_outer_scope);
// Scope analysis. // Scope analysis.
void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval); void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval);
bool HasTrivialContext() const; bool HasTrivialContext() const;
......
...@@ -13418,6 +13418,8 @@ void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared, ...@@ -13418,6 +13418,8 @@ void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared,
void SharedFunctionInfo::InitFromFunctionLiteral( void SharedFunctionInfo::InitFromFunctionLiteral(
Handle<SharedFunctionInfo> shared_info, FunctionLiteral* lit) { Handle<SharedFunctionInfo> shared_info, FunctionLiteral* lit) {
// When adding fields here, make sure Scope::AnalyzePartially is updated
// accordingly.
shared_info->set_length(lit->scope()->default_function_length()); shared_info->set_length(lit->scope()->default_function_length());
shared_info->set_internal_formal_parameter_count(lit->parameter_count()); shared_info->set_internal_formal_parameter_count(lit->parameter_count());
shared_info->set_function_token_position(lit->function_token_position()); shared_info->set_function_token_position(lit->function_token_position());
......
...@@ -464,7 +464,11 @@ class ParserBase : public Traits { ...@@ -464,7 +464,11 @@ class ParserBase : public Traits {
return &non_patterns_to_rewrite_; return &non_patterns_to_rewrite_;
} }
void next_function_is_parenthesized(bool parenthesized) { bool next_function_is_parenthesized() const {
return next_function_is_parenthesized_;
}
void set_next_function_is_parenthesized(bool parenthesized) {
next_function_is_parenthesized_ = parenthesized; next_function_is_parenthesized_ = parenthesized;
} }
...@@ -1237,6 +1241,8 @@ class ParserBase : public Traits { ...@@ -1237,6 +1241,8 @@ class ParserBase : public Traits {
bool allow_harmony_async_await_; bool allow_harmony_async_await_;
bool allow_harmony_restrictive_generators_; bool allow_harmony_restrictive_generators_;
bool allow_harmony_trailing_commas_; bool allow_harmony_trailing_commas_;
friend class DiscardableZoneScope;
}; };
template <class Traits> template <class Traits>
...@@ -1630,8 +1636,8 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, ...@@ -1630,8 +1636,8 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
} }
// Heuristically try to detect immediately called functions before // Heuristically try to detect immediately called functions before
// seeing the call parentheses. // seeing the call parentheses.
function_state_->next_function_is_parenthesized(peek() == function_state_->set_next_function_is_parenthesized(peek() ==
Token::FUNCTION); Token::FUNCTION);
ExpressionT expr = this->ParseExpression(true, classifier, CHECK_OK); ExpressionT expr = this->ParseExpression(true, classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
return expr; return expr;
......
This diff is collapsed.
...@@ -755,7 +755,6 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -755,7 +755,6 @@ class Parser : public ParserBase<ParserTraits> {
private: private:
friend class ParserTraits; friend class ParserTraits;
friend class DiscardableZoneScope;
// Runtime encoding of different completion modes. // Runtime encoding of different completion modes.
enum CompletionKind { enum CompletionKind {
......
// 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.
// Flags: --min-preparse-length=0
// The test functions in this file will be eagerly compiled. The functions
// inside will be eagerly parsed but lazily compiled.
(function TestLengths() {
function inner(p1, p2, p3) { }
assertEquals(3, inner.length);
})();
(function TestAccessingContextVariables() {
var in_context = 8;
function inner() { return in_context; }
assertEquals(8, inner());
})();
(function TestAccessingContextVariablesFromDeeper() {
var in_context = 8;
function inner() {
function inner_inner() {
function inner_inner_inner() {
return in_context;
}
return inner_inner_inner;
}
return inner_inner;
}
assertEquals(8, inner()()());
})();
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