Commit 33ec0b79 authored by conradw's avatar conradw Committed by Commit bot

Parsing especially large nested functions takes up more memory than necessary.

Inner functions must be eagerly parsed for scope analysis, but the full AST is
also kept around even though it's not needed.

This CL mitigates this problem by allocating some AstNodes of the inner function
to a temporary Zone which is deallocated once the scope information has been
built. The remaining nodes (such as VariableProxy) must persist until scope
analysis actually happens, and have to be allocated to a parser-persistent Zone.

BUG=417697
LOG=N

Review URL: https://codereview.chromium.org/1304923004

Cr-Commit-Position: refs/heads/master@{#30685}
parent deb5f524
This diff is collapsed.
......@@ -4211,10 +4211,44 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
}
}
if (!is_lazily_parsed) {
body = ParseEagerFunctionBody(function_name, pos, formals, kind,
function_type, CHECK_OK);
// 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.
bool can_use_temp_zone =
FLAG_lazy && !allow_natives() && extension_ == NULL && allow_lazy() &&
function_type == FunctionLiteral::DECLARATION &&
eager_compile_hint != FunctionLiteral::kShouldEagerCompile;
// Open a new BodyScope, 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;
AstNodeFactory::BodyScope(factory(), &temp_zone, can_use_temp_zone);
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 (can_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;
}
}
// Parsing the body may change the language mode in our scope.
......
......@@ -1398,6 +1398,68 @@ TEST(ScopePositions) {
}
TEST(DiscardFunctionBody) {
// Test that inner function bodies are discarded if possible.
// See comments in ParseFunctionLiteral in parser.cc.
const char* discard_sources[] = {
"(function f() { function g() { var a; } })();",
/* TODO(conradw): In future it may be possible to apply this optimisation
* to these productions.
"(function f() { 0, function g() { var a; } })();",
"(function f() { 0, { g() { var a; } } })();",
"(function f() { 0, class c { g() { var a; } } })();", */
NULL
};
i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory();
v8::HandleScope handles(CcTest::isolate());
i::FunctionLiteral* function;
for (int i = 0; discard_sources[i]; i++) {
const char* source = discard_sources[i];
i::Handle<i::String> source_code =
factory->NewStringFromUtf8(i::CStrVector(source)).ToHandleChecked();
i::Handle<i::Script> script = factory->NewScript(source_code);
i::Zone zone;
i::ParseInfo info(&zone, script);
info.set_allow_lazy_parsing();
i::Parser parser(&info);
parser.set_allow_harmony_sloppy(true);
parser.Parse(&info);
function = info.literal();
CHECK_NOT_NULL(function);
CHECK_NOT_NULL(function->body());
CHECK_EQ(1, function->body()->length());
i::FunctionLiteral* inner =
function->body()->first()->AsExpressionStatement()->expression()->
AsCall()->expression()->AsFunctionLiteral();
i::Scope* inner_scope = inner->scope();
i::FunctionLiteral* fun = nullptr;
if (inner_scope->declarations()->length() > 1) {
fun = inner_scope->declarations()->at(1)->AsFunctionDeclaration()->fun();
} else {
// TODO(conradw): This path won't be hit until the other test cases can be
// uncommented.
CHECK_NOT_NULL(inner->body());
CHECK_GE(2, inner->body()->length());
i::Expression* exp = inner->body()->at(1)->AsExpressionStatement()->
expression()->AsBinaryOperation()->right();
if (exp->IsFunctionLiteral()) {
fun = exp->AsFunctionLiteral();
} else if (exp->IsObjectLiteral()) {
fun = exp->AsObjectLiteral()->properties()->at(0)->value()->
AsFunctionLiteral();
} else {
fun = exp->AsClassLiteral()->properties()->at(0)->value()->
AsFunctionLiteral();
}
}
CHECK_NULL(fun->body());
}
}
const char* ReadString(unsigned* start) {
int length = start[0];
char* result = i::NewArray<char>(length + 1);
......
// Copyright 2015 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.
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// comments to trigger lazy compilation comments to trigger lazy compilation
// Test that IIFEs are compilable even under lazy conditions where the enclosing
// parentheses heuristic has not been triggered.
function f() {
return function(){ return 0; }();
}
function g() {
function h() {
return function(){ return 0; }();
}
return h();
}
f();
g();
0, function(){}();
(function(){ 0, function(){}(); })();
0, function(){ (function(){ 0, function(){}(); })(); }();
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