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,
raw_name_(name),
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) {
DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name());
set_var(var);
......
This diff is collapsed.
......@@ -357,6 +357,7 @@ bool Scope::Analyze(ParseInfo* info) {
scope->Print();
}
scope->CheckScopePositions();
scope->CheckZones();
#endif
info->set_scope(scope);
......@@ -580,9 +581,11 @@ Variable* Scope::LookupFunctionVar(const AstRawString* name,
if (index < 0) return NULL;
Variable* var = new (zone())
Variable(this, name, mode, Variable::NORMAL, kCreatedInitialized);
DCHECK_NOT_NULL(factory);
VariableProxy* proxy = factory->NewVariableProxy(var);
VariableDeclaration* declaration =
factory->NewVariableDeclaration(proxy, mode, this, kNoSourcePosition);
DCHECK_EQ(factory->zone(), zone());
DeclareFunctionVar(declaration);
var->AllocateTo(VariableLocation::CONTEXT, index);
return var;
......@@ -967,6 +970,35 @@ Handle<StringSet> Scope::CollectNonLocals(Handle<StringSet> 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
static const char* Header(ScopeType scope_type, FunctionKind function_kind,
......@@ -1175,6 +1207,12 @@ void Scope::CheckScopePositions() {
scope->CheckScopePositions();
}
}
void Scope::CheckZones() {
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
CHECK_EQ(scope->zone(), zone());
}
}
#endif // DEBUG
......@@ -1197,10 +1235,10 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) {
return var;
}
Variable* Scope::LookupRecursive(VariableProxy* proxy,
BindingKind* binding_kind,
AstNodeFactory* factory) {
AstNodeFactory* factory,
Scope* max_outer_scope) {
DCHECK(binding_kind != NULL);
if (already_resolved() && is_with_scope()) {
// Short-cut: if the scope is deserialized from a scope info, variable
......@@ -1227,13 +1265,14 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
var = LookupFunctionVar(proxy->raw_name(), factory);
if (var != NULL) {
*binding_kind = BOUND;
} else if (outer_scope_ != NULL) {
var = outer_scope_->LookupRecursive(proxy, binding_kind, factory);
} else if (outer_scope_ != nullptr && this != max_outer_scope) {
var = outer_scope_->LookupRecursive(proxy, binding_kind, factory,
max_outer_scope);
if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) {
var->ForceContextAllocation();
}
} 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.
......@@ -1366,6 +1405,31 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info,
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 ) {
if (outer_scope_calls_sloppy_eval) {
......
......@@ -204,6 +204,7 @@ class Scope: public ZoneObject {
// the same name because they may be removed selectively via
// RemoveUnresolved().
DCHECK(!already_resolved());
DCHECK_EQ(factory->zone(), zone());
VariableProxy* proxy =
factory->NewVariableProxy(name, kind, start_position, end_position);
proxy->set_next_unresolved(unresolved_);
......@@ -612,6 +613,13 @@ class Scope: public ZoneObject {
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.
......@@ -620,6 +628,9 @@ class Scope: public ZoneObject {
// Check that the scope has positions assigned.
void CheckScopePositions();
// Check that all Scopes in the scope tree use the same Zone.
void CheckZones();
#endif
// ---------------------------------------------------------------------------
......@@ -780,16 +791,24 @@ class Scope: public ZoneObject {
};
// 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
// parameter should be set to the calling context of 'eval'.
// scope, but only until max_outer_scope (if not nullptr). If the code is
// 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,
AstNodeFactory* factory);
AstNodeFactory* factory,
Scope* max_outer_scope = nullptr);
MUST_USE_RESULT
bool ResolveVariable(ParseInfo* info, VariableProxy* proxy,
AstNodeFactory* factory);
MUST_USE_RESULT
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.
void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval);
bool HasTrivialContext() const;
......
......@@ -13418,6 +13418,8 @@ void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared,
void SharedFunctionInfo::InitFromFunctionLiteral(
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_internal_formal_parameter_count(lit->parameter_count());
shared_info->set_function_token_position(lit->function_token_position());
......
......@@ -464,7 +464,11 @@ class ParserBase : public Traits {
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;
}
......@@ -1237,6 +1241,8 @@ class ParserBase : public Traits {
bool allow_harmony_async_await_;
bool allow_harmony_restrictive_generators_;
bool allow_harmony_trailing_commas_;
friend class DiscardableZoneScope;
};
template <class Traits>
......@@ -1630,8 +1636,8 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
}
// Heuristically try to detect immediately called functions before
// seeing the call parentheses.
function_state_->next_function_is_parenthesized(peek() ==
Token::FUNCTION);
function_state_->set_next_function_is_parenthesized(peek() ==
Token::FUNCTION);
ExpressionT expr = this->ParseExpression(true, classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
return expr;
......
This diff is collapsed.
......@@ -755,7 +755,6 @@ class Parser : public ParserBase<ParserTraits> {
private:
friend class ParserTraits;
friend class DiscardableZoneScope;
// Runtime encoding of different completion modes.
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