Put Scopes into temporary Zone

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/2193793002
Cr-Commit-Position: refs/heads/master@{#38232}
This commit is contained in:
marja 2016-08-02 01:06:04 -07:00 committed by Commit bot
parent a758144329
commit eaebdd858b
8 changed files with 340 additions and 212 deletions

View File

@ -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);

View File

@ -1690,6 +1690,7 @@ class VariableProxy final : public Expression {
VariableProxy(Zone* zone, const AstRawString* name,
Variable::Kind variable_kind, int start_position,
int end_position);
VariableProxy(Zone* zone, const VariableProxy* copy_from);
static int parent_num_ids() { return Expression::num_ids(); }
int local_id(int n) const { return base_id() + parent_num_ids() + n; }
@ -3043,27 +3044,22 @@ class AstVisitor BASE_EMBEDDED {
class AstNodeFactory final BASE_EMBEDDED {
public:
explicit AstNodeFactory(AstValueFactory* ast_value_factory)
: local_zone_(nullptr),
parser_zone_(nullptr),
ast_value_factory_(ast_value_factory) {
: zone_(nullptr), ast_value_factory_(ast_value_factory) {
if (ast_value_factory != nullptr) {
local_zone_ = ast_value_factory->zone();
parser_zone_ = ast_value_factory->zone();
zone_ = ast_value_factory->zone();
}
}
AstValueFactory* ast_value_factory() const { return ast_value_factory_; }
void set_ast_value_factory(AstValueFactory* ast_value_factory) {
ast_value_factory_ = ast_value_factory;
local_zone_ = ast_value_factory->zone();
parser_zone_ = ast_value_factory->zone();
zone_ = ast_value_factory->zone();
}
VariableDeclaration* NewVariableDeclaration(VariableProxy* proxy,
VariableMode mode, Scope* scope,
int pos) {
return new (parser_zone_)
VariableDeclaration(parser_zone_, proxy, mode, scope, pos);
return new (zone_) VariableDeclaration(zone_, proxy, mode, scope, pos);
}
FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy,
@ -3071,19 +3067,18 @@ class AstNodeFactory final BASE_EMBEDDED {
FunctionLiteral* fun,
Scope* scope,
int pos) {
return new (parser_zone_)
FunctionDeclaration(parser_zone_, proxy, mode, fun, scope, pos);
return new (zone_) FunctionDeclaration(zone_, proxy, mode, fun, scope, pos);
}
Block* NewBlock(ZoneList<const AstRawString*>* labels, int capacity,
bool ignore_completion_value, int pos) {
return new (local_zone_)
Block(local_zone_, labels, capacity, ignore_completion_value, pos);
return new (zone_)
Block(zone_, labels, capacity, ignore_completion_value, pos);
}
#define STATEMENT_WITH_LABELS(NodeType) \
NodeType* New##NodeType(ZoneList<const AstRawString*>* labels, int pos) { \
return new (local_zone_) NodeType(local_zone_, labels, pos); \
return new (zone_) NodeType(zone_, labels, pos); \
}
STATEMENT_WITH_LABELS(DoWhileStatement)
STATEMENT_WITH_LABELS(WhileStatement)
@ -3096,10 +3091,10 @@ class AstNodeFactory final BASE_EMBEDDED {
int pos) {
switch (visit_mode) {
case ForEachStatement::ENUMERATE: {
return new (local_zone_) ForInStatement(local_zone_, labels, pos);
return new (zone_) ForInStatement(zone_, labels, pos);
}
case ForEachStatement::ITERATE: {
return new (local_zone_) ForOfStatement(local_zone_, labels, pos);
return new (zone_) ForOfStatement(zone_, labels, pos);
}
}
UNREACHABLE();
@ -3107,42 +3102,41 @@ class AstNodeFactory final BASE_EMBEDDED {
}
ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) {
return new (local_zone_) ExpressionStatement(local_zone_, expression, pos);
return new (zone_) ExpressionStatement(zone_, expression, pos);
}
ContinueStatement* NewContinueStatement(IterationStatement* target, int pos) {
return new (local_zone_) ContinueStatement(local_zone_, target, pos);
return new (zone_) ContinueStatement(zone_, target, pos);
}
BreakStatement* NewBreakStatement(BreakableStatement* target, int pos) {
return new (local_zone_) BreakStatement(local_zone_, target, pos);
return new (zone_) BreakStatement(zone_, target, pos);
}
ReturnStatement* NewReturnStatement(Expression* expression, int pos) {
return new (local_zone_) ReturnStatement(local_zone_, expression, pos);
return new (zone_) ReturnStatement(zone_, expression, pos);
}
WithStatement* NewWithStatement(Scope* scope,
Expression* expression,
Statement* statement,
int pos) {
return new (local_zone_)
WithStatement(local_zone_, scope, expression, statement, pos);
return new (zone_) WithStatement(zone_, scope, expression, statement, pos);
}
IfStatement* NewIfStatement(Expression* condition,
Statement* then_statement,
Statement* else_statement,
int pos) {
return new (local_zone_) IfStatement(local_zone_, condition, then_statement,
else_statement, pos);
return new (zone_)
IfStatement(zone_, condition, then_statement, else_statement, pos);
}
TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope,
Variable* variable,
Block* catch_block, int pos) {
return new (local_zone_)
TryCatchStatement(local_zone_, try_block, scope, variable, catch_block,
return new (zone_)
TryCatchStatement(zone_, try_block, scope, variable, catch_block,
HandlerTable::CAUGHT, pos);
}
@ -3151,8 +3145,8 @@ class AstNodeFactory final BASE_EMBEDDED {
Variable* variable,
Block* catch_block,
int pos) {
return new (local_zone_)
TryCatchStatement(local_zone_, try_block, scope, variable, catch_block,
return new (zone_)
TryCatchStatement(zone_, try_block, scope, variable, catch_block,
HandlerTable::UNCAUGHT, pos);
}
@ -3161,88 +3155,81 @@ class AstNodeFactory final BASE_EMBEDDED {
Variable* variable,
Block* catch_block,
int pos) {
return new (local_zone_)
TryCatchStatement(local_zone_, try_block, scope, variable, catch_block,
return new (zone_)
TryCatchStatement(zone_, try_block, scope, variable, catch_block,
HandlerTable::PROMISE, pos);
}
TryFinallyStatement* NewTryFinallyStatement(Block* try_block,
Block* finally_block, int pos) {
return new (local_zone_)
TryFinallyStatement(local_zone_, try_block, finally_block, pos);
return new (zone_)
TryFinallyStatement(zone_, try_block, finally_block, pos);
}
DebuggerStatement* NewDebuggerStatement(int pos) {
return new (local_zone_) DebuggerStatement(local_zone_, pos);
return new (zone_) DebuggerStatement(zone_, pos);
}
EmptyStatement* NewEmptyStatement(int pos) {
return new (local_zone_) EmptyStatement(local_zone_, pos);
return new (zone_) EmptyStatement(zone_, pos);
}
SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement(
Statement* statement, Scope* scope) {
return new (parser_zone_)
SloppyBlockFunctionStatement(parser_zone_, statement, scope);
return new (zone_) SloppyBlockFunctionStatement(zone_, statement, scope);
}
CaseClause* NewCaseClause(
Expression* label, ZoneList<Statement*>* statements, int pos) {
return new (local_zone_) CaseClause(local_zone_, label, statements, pos);
return new (zone_) CaseClause(zone_, label, statements, pos);
}
Literal* NewStringLiteral(const AstRawString* string, int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewString(string), pos);
return new (zone_)
Literal(zone_, ast_value_factory_->NewString(string), pos);
}
// A JavaScript symbol (ECMA-262 edition 6).
Literal* NewSymbolLiteral(const char* name, int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewSymbol(name), pos);
return new (zone_) Literal(zone_, ast_value_factory_->NewSymbol(name), pos);
}
Literal* NewNumberLiteral(double number, int pos, bool with_dot = false) {
return new (local_zone_) Literal(
local_zone_, ast_value_factory_->NewNumber(number, with_dot), pos);
return new (zone_)
Literal(zone_, ast_value_factory_->NewNumber(number, with_dot), pos);
}
Literal* NewSmiLiteral(int number, int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewSmi(number), pos);
return new (zone_) Literal(zone_, ast_value_factory_->NewSmi(number), pos);
}
Literal* NewBooleanLiteral(bool b, int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewBoolean(b), pos);
return new (zone_) Literal(zone_, ast_value_factory_->NewBoolean(b), pos);
}
Literal* NewNullLiteral(int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewNull(), pos);
return new (zone_) Literal(zone_, ast_value_factory_->NewNull(), pos);
}
Literal* NewUndefinedLiteral(int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewUndefined(), pos);
return new (zone_) Literal(zone_, ast_value_factory_->NewUndefined(), pos);
}
Literal* NewTheHoleLiteral(int pos) {
return new (local_zone_)
Literal(local_zone_, ast_value_factory_->NewTheHole(), pos);
return new (zone_) Literal(zone_, ast_value_factory_->NewTheHole(), pos);
}
ObjectLiteral* NewObjectLiteral(
ZoneList<ObjectLiteral::Property*>* properties, int literal_index,
uint32_t boilerplate_properties, int pos) {
return new (local_zone_) ObjectLiteral(
local_zone_, properties, literal_index, boilerplate_properties, pos);
return new (zone_) ObjectLiteral(zone_, properties, literal_index,
boilerplate_properties, pos);
}
ObjectLiteral::Property* NewObjectLiteralProperty(
Expression* key, Expression* value, ObjectLiteralProperty::Kind kind,
bool is_static, bool is_computed_name) {
return new (local_zone_)
return new (zone_)
ObjectLiteral::Property(key, value, kind, is_static, is_computed_name);
}
@ -3250,35 +3237,32 @@ class AstNodeFactory final BASE_EMBEDDED {
Expression* value,
bool is_static,
bool is_computed_name) {
return new (local_zone_) ObjectLiteral::Property(
ast_value_factory_, key, value, is_static, is_computed_name);
return new (zone_) ObjectLiteral::Property(ast_value_factory_, key, value,
is_static, is_computed_name);
}
RegExpLiteral* NewRegExpLiteral(const AstRawString* pattern, int flags,
int literal_index, int pos) {
return new (local_zone_)
RegExpLiteral(local_zone_, pattern, flags, literal_index, pos);
return new (zone_) RegExpLiteral(zone_, pattern, flags, literal_index, pos);
}
ArrayLiteral* NewArrayLiteral(ZoneList<Expression*>* values,
int literal_index,
int pos) {
return new (local_zone_)
ArrayLiteral(local_zone_, values, -1, literal_index, pos);
return new (zone_) ArrayLiteral(zone_, values, -1, literal_index, pos);
}
ArrayLiteral* NewArrayLiteral(ZoneList<Expression*>* values,
int first_spread_index, int literal_index,
int pos) {
return new (local_zone_) ArrayLiteral(
local_zone_, values, first_spread_index, literal_index, pos);
return new (zone_)
ArrayLiteral(zone_, values, first_spread_index, literal_index, pos);
}
VariableProxy* NewVariableProxy(Variable* var,
int start_position = kNoSourcePosition,
int end_position = kNoSourcePosition) {
return new (parser_zone_)
VariableProxy(parser_zone_, var, start_position, end_position);
return new (zone_) VariableProxy(zone_, var, start_position, end_position);
}
VariableProxy* NewVariableProxy(const AstRawString* name,
@ -3286,87 +3270,89 @@ class AstNodeFactory final BASE_EMBEDDED {
int start_position = kNoSourcePosition,
int end_position = kNoSourcePosition) {
DCHECK_NOT_NULL(name);
return new (parser_zone_) VariableProxy(parser_zone_, name, variable_kind,
start_position, end_position);
return new (zone_)
VariableProxy(zone_, name, variable_kind, start_position, end_position);
}
// Recreates the VariableProxy in this Zone.
VariableProxy* CopyVariableProxy(VariableProxy* proxy) {
return new (zone_) VariableProxy(zone_, proxy);
}
Property* NewProperty(Expression* obj, Expression* key, int pos) {
return new (local_zone_) Property(local_zone_, obj, key, pos);
return new (zone_) Property(zone_, obj, key, pos);
}
Call* NewCall(Expression* expression,
ZoneList<Expression*>* arguments,
int pos) {
return new (local_zone_) Call(local_zone_, expression, arguments, pos);
return new (zone_) Call(zone_, expression, arguments, pos);
}
CallNew* NewCallNew(Expression* expression,
ZoneList<Expression*>* arguments,
int pos) {
return new (local_zone_) CallNew(local_zone_, expression, arguments, pos);
return new (zone_) CallNew(zone_, expression, arguments, pos);
}
CallRuntime* NewCallRuntime(Runtime::FunctionId id,
ZoneList<Expression*>* arguments, int pos) {
return new (local_zone_)
CallRuntime(local_zone_, Runtime::FunctionForId(id), arguments, pos);
return new (zone_)
CallRuntime(zone_, Runtime::FunctionForId(id), arguments, pos);
}
CallRuntime* NewCallRuntime(const Runtime::Function* function,
ZoneList<Expression*>* arguments, int pos) {
return new (local_zone_) CallRuntime(local_zone_, function, arguments, pos);
return new (zone_) CallRuntime(zone_, function, arguments, pos);
}
CallRuntime* NewCallRuntime(int context_index,
ZoneList<Expression*>* arguments, int pos) {
return new (local_zone_)
CallRuntime(local_zone_, context_index, arguments, pos);
return new (zone_) CallRuntime(zone_, context_index, arguments, pos);
}
UnaryOperation* NewUnaryOperation(Token::Value op,
Expression* expression,
int pos) {
return new (local_zone_) UnaryOperation(local_zone_, op, expression, pos);
return new (zone_) UnaryOperation(zone_, op, expression, pos);
}
BinaryOperation* NewBinaryOperation(Token::Value op,
Expression* left,
Expression* right,
int pos) {
return new (local_zone_) BinaryOperation(local_zone_, op, left, right, pos);
return new (zone_) BinaryOperation(zone_, op, left, right, pos);
}
CountOperation* NewCountOperation(Token::Value op,
bool is_prefix,
Expression* expr,
int pos) {
return new (local_zone_)
CountOperation(local_zone_, op, is_prefix, expr, pos);
return new (zone_) CountOperation(zone_, op, is_prefix, expr, pos);
}
CompareOperation* NewCompareOperation(Token::Value op,
Expression* left,
Expression* right,
int pos) {
return new (local_zone_)
CompareOperation(local_zone_, op, left, right, pos);
return new (zone_) CompareOperation(zone_, op, left, right, pos);
}
Spread* NewSpread(Expression* expression, int pos, int expr_pos) {
return new (local_zone_) Spread(local_zone_, expression, pos, expr_pos);
return new (zone_) Spread(zone_, expression, pos, expr_pos);
}
Conditional* NewConditional(Expression* condition,
Expression* then_expression,
Expression* else_expression,
int position) {
return new (local_zone_) Conditional(
local_zone_, condition, then_expression, else_expression, position);
return new (zone_) Conditional(zone_, condition, then_expression,
else_expression, position);
}
RewritableExpression* NewRewritableExpression(Expression* expression) {
DCHECK_NOT_NULL(expression);
return new (local_zone_) RewritableExpression(local_zone_, expression);
return new (zone_) RewritableExpression(zone_, expression);
}
Assignment* NewAssignment(Token::Value op,
@ -3374,8 +3360,7 @@ class AstNodeFactory final BASE_EMBEDDED {
Expression* value,
int pos) {
DCHECK(Token::IsAssignmentOp(op));
Assignment* assign =
new (local_zone_) Assignment(local_zone_, op, target, value, pos);
Assignment* assign = new (zone_) Assignment(zone_, op, target, value, pos);
if (assign->is_compound()) {
DCHECK(Token::IsAssignmentOp(op));
assign->binary_operation_ =
@ -3387,12 +3372,12 @@ class AstNodeFactory final BASE_EMBEDDED {
Yield* NewYield(Expression* generator_object, Expression* expression, int pos,
Yield::OnException on_exception) {
if (!expression) expression = NewUndefinedLiteral(pos);
return new (local_zone_)
Yield(local_zone_, generator_object, expression, pos, on_exception);
return new (zone_)
Yield(zone_, generator_object, expression, pos, on_exception);
}
Throw* NewThrow(Expression* exception, int pos) {
return new (local_zone_) Throw(local_zone_, exception, pos);
return new (zone_) Throw(zone_, exception, pos);
}
FunctionLiteral* NewFunctionLiteral(
@ -3403,8 +3388,8 @@ class AstNodeFactory final BASE_EMBEDDED {
FunctionLiteral::FunctionType function_type,
FunctionLiteral::EagerCompileHint eager_compile_hint, FunctionKind kind,
int position) {
return new (local_zone_) FunctionLiteral(
local_zone_, name, ast_value_factory_, scope, body,
return new (zone_) FunctionLiteral(
zone_, name, ast_value_factory_, scope, body,
materialized_literal_count, expected_property_count, parameter_count,
function_type, has_duplicate_parameters, eager_compile_hint, kind,
position, true);
@ -3416,9 +3401,9 @@ class AstNodeFactory final BASE_EMBEDDED {
FunctionLiteral* NewScriptOrEvalFunctionLiteral(
Scope* scope, ZoneList<Statement*>* body, int materialized_literal_count,
int expected_property_count) {
return new (local_zone_) FunctionLiteral(
local_zone_, ast_value_factory_->empty_string(), ast_value_factory_,
scope, body, materialized_literal_count, expected_property_count, 0,
return new (zone_) FunctionLiteral(
zone_, ast_value_factory_->empty_string(), ast_value_factory_, scope,
body, materialized_literal_count, expected_property_count, 0,
FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kShouldLazyCompile, FunctionKind::kNormalFunction, 0,
@ -3429,59 +3414,58 @@ class AstNodeFactory final BASE_EMBEDDED {
FunctionLiteral* constructor,
ZoneList<ObjectLiteral::Property*>* properties,
int start_position, int end_position) {
return new (local_zone_)
ClassLiteral(local_zone_, proxy, extends, constructor, properties,
start_position, end_position);
return new (zone_) ClassLiteral(zone_, proxy, extends, constructor,
properties, start_position, end_position);
}
NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name,
v8::Extension* extension,
int pos) {
return new (local_zone_)
NativeFunctionLiteral(local_zone_, name, extension, pos);
return new (zone_) NativeFunctionLiteral(zone_, name, extension, pos);
}
DoExpression* NewDoExpression(Block* block, Variable* result_var, int pos) {
VariableProxy* result = NewVariableProxy(result_var, pos);
return new (local_zone_) DoExpression(local_zone_, block, result, pos);
return new (zone_) DoExpression(zone_, block, result, pos);
}
ThisFunction* NewThisFunction(int pos) {
return new (local_zone_) ThisFunction(local_zone_, pos);
return new (zone_) ThisFunction(zone_, pos);
}
SuperPropertyReference* NewSuperPropertyReference(VariableProxy* this_var,
Expression* home_object,
int pos) {
return new (local_zone_)
SuperPropertyReference(local_zone_, this_var, home_object, pos);
return new (zone_)
SuperPropertyReference(zone_, this_var, home_object, pos);
}
SuperCallReference* NewSuperCallReference(VariableProxy* this_var,
VariableProxy* new_target_var,
VariableProxy* this_function_var,
int pos) {
return new (local_zone_) SuperCallReference(
local_zone_, this_var, new_target_var, this_function_var, pos);
return new (zone_) SuperCallReference(zone_, this_var, new_target_var,
this_function_var, pos);
}
EmptyParentheses* NewEmptyParentheses(int pos) {
return new (local_zone_) EmptyParentheses(local_zone_, pos);
return new (zone_) EmptyParentheses(zone_, pos);
}
Zone* zone() const { return local_zone_; }
Zone* zone() const { return zone_; }
void set_zone(Zone* zone) { zone_ = zone; }
// Handles use of temporary zones when parsing inner function bodies.
class BodyScope {
public:
BodyScope(AstNodeFactory* factory, Zone* temp_zone, bool use_temp_zone)
: factory_(factory), prev_zone_(factory->local_zone_) {
: factory_(factory), prev_zone_(factory->zone_) {
if (use_temp_zone) {
factory->local_zone_ = temp_zone;
factory->zone_ = temp_zone;
}
}
~BodyScope() { factory_->local_zone_ = prev_zone_; }
~BodyScope() { factory_->zone_ = prev_zone_; }
private:
AstNodeFactory* factory_;
@ -3493,10 +3477,7 @@ class AstNodeFactory final BASE_EMBEDDED {
// which we can guarantee is not going to be compiled or have its AST
// inspected.
// See ParseFunctionLiteral in parser.cc for preconditions.
Zone* local_zone_;
// ZoneObjects which need to persist until scope analysis must be allocated in
// the parser-level zone.
Zone* parser_zone_;
Zone* zone_;
AstValueFactory* ast_value_factory_;
};

View File

@ -279,6 +279,7 @@ bool Scope::Analyze(ParseInfo* info) {
scope->Print();
}
scope->CheckScopePositions();
scope->CheckZones();
#endif
info->set_scope(scope);
@ -502,9 +503,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;
@ -889,6 +892,41 @@ 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 collect 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.
VariableProxy* still_unresolved = nullptr;
CollectUnresolvableLocals(&still_unresolved, this);
// Re-create the VariableProxies in the right Zone and insert them into
// migrate_to.
for (VariableProxy* proxy = still_unresolved; proxy != nullptr;
proxy = proxy->next_unresolved()) {
// Recreate the VariableProxy.
DCHECK(!proxy->is_resolved());
VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy);
migrate_to->AddUnresolved(copy);
}
// 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_;
outer_scope_->RemoveInnerScope(this);
DCHECK_EQ(outer_scope_, migrate_to->outer_scope_);
DCHECK_EQ(outer_scope_->zone(), migrate_to->zone());
}
#ifdef DEBUG
static const char* Header(ScopeType scope_type, FunctionKind function_kind,
@ -1097,6 +1135,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
@ -1119,10 +1163,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
@ -1149,13 +1193,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.
@ -1288,6 +1333,26 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info,
return true;
}
void Scope::CollectUnresolvableLocals(VariableProxy** still_unresolved,
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) {
proxy->set_next_unresolved(*still_unresolved);
*still_unresolved = proxy;
}
}
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->CollectUnresolvableLocals(still_unresolved, max_outer_scope);
}
}
void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) {
if (outer_scope_calls_sloppy_eval) {

View File

@ -201,6 +201,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_);
@ -609,6 +610,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.
@ -617,6 +625,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
// ---------------------------------------------------------------------------
@ -777,16 +788,23 @@ 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; collects those
// which cannot be resolved.
void CollectUnresolvableLocals(VariableProxy** still_unresolved,
Scope* max_outer_scope);
// Scope analysis.
void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval);
bool HasTrivialContext() const;

View File

@ -472,7 +472,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;
}
@ -1245,6 +1249,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>
@ -1638,8 +1644,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;

View File

@ -176,18 +176,26 @@ class DiscardableZoneScope {
: ast_node_factory_scope_(parser->factory(), temp_zone, use_temp_zone),
fni_(parser->ast_value_factory_, temp_zone),
parser_(parser),
prev_fni_(parser->fni_) {
prev_fni_(parser->fni_),
prev_zone_(parser->zone_) {
if (use_temp_zone) {
parser_->fni_ = &fni_;
parser_->zone_ = temp_zone;
}
}
~DiscardableZoneScope() { parser_->fni_ = prev_fni_; }
~DiscardableZoneScope() {
parser_->fni_ = prev_fni_;
parser_->zone_ = prev_zone_;
}
private:
AstNodeFactory::BodyScope ast_node_factory_scope_;
FuncNameInferrer fni_;
Parser* parser_;
FuncNameInferrer* prev_fni_;
Zone* prev_zone_;
DISALLOW_COPY_AND_ASSIGN(DiscardableZoneScope);
};
void Parser::SetCachedData(ParseInfo* info) {
@ -4284,29 +4292,113 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
function_name = ast_value_factory()->empty_string();
}
Scope* scope = NewFunctionScope(kind);
SetLanguageMode(scope, language_mode);
ZoneList<Statement*>* body = NULL;
FunctionLiteral::EagerCompileHint eager_compile_hint =
function_state_->next_function_is_parenthesized()
? FunctionLiteral::kShouldEagerCompile
: FunctionLiteral::kShouldLazyCompile;
// Determine if the function can be parsed lazily. Lazy parsing is
// different from lazy compilation; we need to parse more eagerly than we
// compile.
// We can only parse lazily if we also compile lazily. The heuristics for lazy
// compilation are:
// - It must not have been prohibited by the caller to Parse (some callers
// need a full AST).
// - The outer scope must allow lazy compilation of inner functions.
// - The function mustn't be a function expression with an open parenthesis
// before; we consider that a hint that the function will be called
// immediately, and it would be a waste of time to make it lazily
// compiled.
// These are all things we can know at this point, without looking at the
// function itself.
// In addition, we need to distinguish between these cases:
// (function foo() {
// bar = function() { return 1; }
// })();
// and
// (function foo() {
// var a = 1;
// bar = function() { return a; }
// })();
// Now foo will be parsed eagerly and compiled eagerly (optimization: assume
// parenthesis before the function means that it will be called
// immediately). The inner function *must* be parsed eagerly to resolve the
// possible reference to the variable in foo's scope. However, it's possible
// that it will be compiled lazily.
// To make this additional case work, both Parser and PreParser implement a
// logic where only top-level functions will be parsed lazily.
bool is_lazily_parsed = mode() == PARSE_LAZILY &&
this->scope()->AllowsLazyParsing() &&
!function_state_->next_function_is_parenthesized();
// Determine whether the function body can be discarded after parsing.
// The preconditions are:
// - Lazy compilation has to be enabled.
// - Neither V8 natives nor native function declarations can be allowed,
// since parsing one would retroactively force the function to be
// eagerly compiled.
// - The invoker of this parser can't depend on the AST being eagerly
// built (either because the function is about to be compiled, or
// because the AST is going to be inspected for some reason).
// - Because of the above, we can't be attempting to parse a
// FunctionExpression; even without enclosing parentheses it might be
// immediately invoked.
// - The function literal shouldn't be hinted to eagerly compile.
// - For asm.js functions the body needs to be available when module
// validation is active, because we examine the entire module at once.
bool use_temp_zone =
!is_lazily_parsed && FLAG_lazy && !allow_natives() &&
extension_ == NULL && allow_lazy() &&
function_type == FunctionLiteral::kDeclaration &&
eager_compile_hint != FunctionLiteral::kShouldEagerCompile &&
!(FLAG_validate_asm && scope()->asm_module());
Scope* main_scope = nullptr;
if (use_temp_zone) {
// This Scope lives in the main Zone; we'll migrate data into it later.
main_scope = NewFunctionScope(kind);
}
ZoneList<Statement*>* body = nullptr;
int arity = -1;
int materialized_literal_count = -1;
int expected_property_count = -1;
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
bool should_be_used_once_hint = false;
bool has_duplicate_parameters;
FunctionLiteral::EagerCompileHint eager_compile_hint;
// Parse function.
{
// Temporary zones can nest. When we migrate free variables (see below), we
// need to recreate them in the previous Zone.
AstNodeFactory previous_zone_ast_node_factory(ast_value_factory());
previous_zone_ast_node_factory.set_zone(zone());
// Open a new zone scope, which sets our AstNodeFactory to allocate in the
// new temporary zone if the preconditions are satisfied, and ensures that
// the previous zone is always restored after parsing the body. To be able
// to do scope analysis correctly after full parsing, we migrate needed
// information from scope into main_scope when the function has been parsed.
Zone temp_zone(zone()->allocator());
DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone);
Scope* scope = NewFunctionScope(kind);
SetLanguageMode(scope, language_mode);
if (!use_temp_zone) {
main_scope = scope;
} else {
DCHECK(main_scope->zone() != scope->zone());
}
FunctionState function_state(&function_state_, &scope_state_, scope, kind);
#ifdef DEBUG
this->scope()->SetScopeName(function_name);
#endif
ExpressionClassifier formals_classifier(this, &duplicate_finder);
eager_compile_hint = function_state_->this_function_is_parenthesized()
? FunctionLiteral::kShouldEagerCompile
: FunctionLiteral::kShouldLazyCompile;
if (is_generator) {
// For generators, allocating variables in contexts is currently a win
// because it minimizes the work needed to suspend and resume an
@ -4339,43 +4431,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// which says whether we need to create an arguments adaptor frame).
if (formals.has_rest) arity--;
// Determine if the function can be parsed lazily. Lazy parsing is different
// from lazy compilation; we need to parse more eagerly than we compile.
// We can only parse lazily if we also compile lazily. The heuristics for
// lazy compilation are:
// - It must not have been prohibited by the caller to Parse (some callers
// need a full AST).
// - The outer scope must allow lazy compilation of inner functions.
// - The function mustn't be a function expression with an open parenthesis
// before; we consider that a hint that the function will be called
// immediately, and it would be a waste of time to make it lazily
// compiled.
// These are all things we can know at this point, without looking at the
// function itself.
// In addition, we need to distinguish between these cases:
// (function foo() {
// bar = function() { return 1; }
// })();
// and
// (function foo() {
// var a = 1;
// bar = function() { return a; }
// })();
// Now foo will be parsed eagerly and compiled eagerly (optimization: assume
// parenthesis before the function means that it will be called
// immediately). The inner function *must* be parsed eagerly to resolve the
// possible reference to the variable in foo's scope. However, it's possible
// that it will be compiled lazily.
// To make this additional case work, both Parser and PreParser implement a
// logic where only top-level functions will be parsed lazily.
bool is_lazily_parsed = mode() == PARSE_LAZILY &&
this->scope()->AllowsLazyParsing() &&
!function_state_->this_function_is_parenthesized();
// Eager or lazy parse?
// If is_lazily_parsed, we'll parse lazy. If we can set a bookmark, we'll
// pass it to SkipLazyFunctionBody, which may use it to abort lazy
@ -4404,45 +4459,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
}
}
if (!is_lazily_parsed) {
// Determine whether the function body can be discarded after parsing.
// The preconditions are:
// - Lazy compilation has to be enabled.
// - Neither V8 natives nor native function declarations can be allowed,
// since parsing one would retroactively force the function to be
// eagerly compiled.
// - The invoker of this parser can't depend on the AST being eagerly
// built (either because the function is about to be compiled, or
// because the AST is going to be inspected for some reason).
// - Because of the above, we can't be attempting to parse a
// FunctionExpression; even without enclosing parentheses it might be
// immediately invoked.
// - The function literal shouldn't be hinted to eagerly compile.
// - For asm.js functions the body needs to be available when module
// validation is active, because we examine the entire module at once.
bool use_temp_zone =
FLAG_lazy && !allow_natives() && extension_ == NULL && allow_lazy() &&
function_type == FunctionLiteral::kDeclaration &&
eager_compile_hint != FunctionLiteral::kShouldEagerCompile &&
!(FLAG_validate_asm && scope->asm_function());
// Open a new zone scope, which sets our AstNodeFactory to allocate in the
// new temporary zone if the preconditions are satisfied, and ensures that
// the previous zone is always restored after parsing the body.
// For the purpose of scope analysis, some ZoneObjects allocated by the
// factory must persist after the function body is thrown away and
// temp_zone is deallocated. These objects are instead allocated in a
// parser-persistent zone (see parser_zone_ in AstNodeFactory).
{
Zone temp_zone(zone()->allocator());
DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone);
body = ParseEagerFunctionBody(function_name, pos, formals, kind,
function_type, CHECK_OK);
}
body = ParseEagerFunctionBody(function_name, pos, formals, kind,
function_type, CHECK_OK);
materialized_literal_count = function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
if (use_temp_zone) {
// If the preconditions are correct the function body should never be
// accessed, but do this anyway for better behaviour if they're wrong.
body = NULL;
body = nullptr;
}
}
@ -4472,14 +4497,20 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
}
has_duplicate_parameters =
!formals_classifier.is_valid_formal_parameter_list_without_duplicates();
}
if (use_temp_zone) {
DCHECK(main_scope != scope);
scope->AnalyzePartially(main_scope, &previous_zone_ast_node_factory);
}
} // DiscardableZoneScope goes out of scope.
FunctionLiteral::ParameterFlag duplicate_parameters =
has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters
: FunctionLiteral::kNoDuplicateParameters;
// Note that the FunctionLiteral needs to be created in the main Zone again.
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
function_name, scope, body, materialized_literal_count,
function_name, main_scope, body, materialized_literal_count,
expected_property_count, arity, duplicate_parameters, function_type,
eager_compile_hint, kind, pos);
function_literal->set_function_token_position(function_token_pos);

View File

@ -724,7 +724,6 @@ class Parser : public ParserBase<ParserTraits> {
private:
friend class ParserTraits;
friend class DiscardableZoneScope;
// Runtime encoding of different completion modes.
enum CompletionKind {

View File

@ -35,3 +35,19 @@ function foo() {
}
assertEquals("hello world", foo());
// Also test that it works from more deeply nested inner functions:
var v = (function foo2() {
var a, b;
var bar = function() {
var baz = function() {
a = b = "bye world";
}
baz();
}
bar();
return a;
})();
assertEquals("bye world", v);