Commit 1f9863aa authored by cbruni's avatar cbruni Committed by Commit bot

Reland of Preparse inner functions (new try) (patchset #1 id:1 of...

Reland of Preparse inner functions (new try) (patchset #1 id:1 of https://codereview.chromium.org/2373443003/ )

Reason for revert:
Stability thief found, relanding speculative reverts.

Original issue's description:
> Revert of Preparse inner functions (new try) (patchset #21 id:420001 of https://codereview.chromium.org/2352593002/ )
>
> Reason for revert:
> We currently have some stability issues on Canary. Let's reland this after we verified that we "fixed" Canary again.
>
> Original issue's description:
> > Preparse inner functions (new try)
> >
> > This is an overly pessimistic approach where PreParser only keeps
> > track of unresolved variables, but doesn't declare anything. This
> > will result in context-allocating variables in the outer function
> > unnecessarily, if the variable names clash with variable names
> > used by the inner function (even if the variables are not the
> > same). However, we have been unable to prove that this approach
> > wouldn't be good enough for the practical purposes.
> >
> > Fixes after the previous try ( https://codereview.chromium.org/2322243002/ ):
> > Keep the context-allocation decision stable when compiling fully eagerly.
> >
> > Tests which exercise this functionality:
> > mjsunit/fixed-context-shapes-when-recompiling.js
> >
> > Design document (chromium):
> >
> > https://docs.google.com/a/chromium.org/document/d/1rRv5JJZ0JpOZAZN2CSUwZPFJiBAdRnTiSYhazseNHFg/edit?usp=sharing
> >
> > BUG=
> >
> > Committed: https://crrev.com/7c73cf32c60484cdf37c84f1d61b4640e87068d7
> > Cr-Commit-Position: refs/heads/master@{#39719}
>
> TBR=verwaest@chromium.org,adamk@chromium.org,marja@chromium.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=
>
> Committed: https://crrev.com/1e6296b2a7cfc307fd9e722e619f42965da4a267
> Cr-Commit-Position: refs/heads/master@{#39730}

TBR=verwaest@chromium.org,adamk@chromium.org,marja@chromium.org,hablich@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=

Review-Url: https://codereview.chromium.org/2377513006
Cr-Commit-Position: refs/heads/master@{#39755}
parent f72b4a08
...@@ -1073,12 +1073,14 @@ void DeclarationScope::AllocateVariables(ParseInfo* info, AnalyzeMode mode) { ...@@ -1073,12 +1073,14 @@ void DeclarationScope::AllocateVariables(ParseInfo* info, AnalyzeMode mode) {
} }
} }
bool Scope::AllowsLazyParsing() const { bool Scope::AllowsLazyParsingWithoutUnresolvedVariables() const {
// If we are inside a block scope, we must parse eagerly to find out how // If we are inside a block scope, we must find unresolved variables in the
// to allocate variables on the block scope. At this point, declarations may // inner scopes to find out how to allocate variables on the block scope. At
// not have yet been parsed. // this point, declarations may not have yet been parsed.
for (const Scope* s = this; s != nullptr; s = s->outer_scope_) { for (const Scope* s = this; s != nullptr; s = s->outer_scope_) {
if (s->is_block_scope()) return false; if (s->is_block_scope()) return false;
// TODO(marja): Refactor parsing modes: also add s->is_function_scope()
// here.
} }
return true; return true;
} }
...@@ -1178,7 +1180,7 @@ Scope* Scope::GetOuterScopeWithContext() { ...@@ -1178,7 +1180,7 @@ Scope* Scope::GetOuterScopeWithContext() {
Handle<StringSet> DeclarationScope::CollectNonLocals( Handle<StringSet> DeclarationScope::CollectNonLocals(
ParseInfo* info, Handle<StringSet> non_locals) { ParseInfo* info, Handle<StringSet> non_locals) {
VariableProxy* free_variables = FetchFreeVariables(this, info); VariableProxy* free_variables = FetchFreeVariables(this, true, info);
for (VariableProxy* proxy = free_variables; proxy != nullptr; for (VariableProxy* proxy = free_variables; proxy != nullptr;
proxy = proxy->next_unresolved()) { proxy = proxy->next_unresolved()) {
non_locals = StringSet::Add(non_locals, proxy->name()); non_locals = StringSet::Add(non_locals, proxy->name());
...@@ -1191,8 +1193,9 @@ void DeclarationScope::AnalyzePartially(DeclarationScope* migrate_to, ...@@ -1191,8 +1193,9 @@ void DeclarationScope::AnalyzePartially(DeclarationScope* migrate_to,
// Try to resolve unresolved variables for this Scope and migrate those which // 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 // cannot be resolved inside. It doesn't make sense to try to resolve them in
// the outer Scopes here, because they are incomplete. // the outer Scopes here, because they are incomplete.
for (VariableProxy* proxy = FetchFreeVariables(this); proxy != nullptr; for (VariableProxy* proxy =
proxy = proxy->next_unresolved()) { FetchFreeVariables(this, !FLAG_lazy_inner_functions);
proxy != nullptr; proxy = proxy->next_unresolved()) {
DCHECK(!proxy->is_resolved()); DCHECK(!proxy->is_resolved());
VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy); VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy);
migrate_to->AddUnresolved(copy); migrate_to->AddUnresolved(copy);
...@@ -1515,6 +1518,29 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy) { ...@@ -1515,6 +1518,29 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy) {
DCHECK(!proxy->is_resolved()); DCHECK(!proxy->is_resolved());
Variable* var = LookupRecursive(proxy, nullptr); Variable* var = LookupRecursive(proxy, nullptr);
ResolveTo(info, proxy, var); 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_;
}
}
} }
void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) { void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) {
...@@ -1560,13 +1586,16 @@ void Scope::ResolveVariablesRecursively(ParseInfo* info) { ...@@ -1560,13 +1586,16 @@ void Scope::ResolveVariablesRecursively(ParseInfo* info) {
} }
VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope, VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
ParseInfo* info, bool try_to_resolve, ParseInfo* info,
VariableProxy* stack) { VariableProxy* stack) {
for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr; for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr;
proxy = next) { proxy = next) {
next = proxy->next_unresolved(); next = proxy->next_unresolved();
DCHECK(!proxy->is_resolved()); DCHECK(!proxy->is_resolved());
Variable* var = LookupRecursive(proxy, max_outer_scope->outer_scope()); Variable* var = nullptr;
if (try_to_resolve) {
var = LookupRecursive(proxy, max_outer_scope->outer_scope());
}
if (var == nullptr) { if (var == nullptr) {
proxy->set_next_unresolved(stack); proxy->set_next_unresolved(stack);
stack = proxy; stack = proxy;
...@@ -1579,7 +1608,8 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope, ...@@ -1579,7 +1608,8 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
unresolved_ = nullptr; unresolved_ = nullptr;
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) { for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
stack = scope->FetchFreeVariables(max_outer_scope, info, stack); stack =
scope->FetchFreeVariables(max_outer_scope, try_to_resolve, info, stack);
} }
return stack; return stack;
......
...@@ -342,8 +342,9 @@ class Scope: public ZoneObject { ...@@ -342,8 +342,9 @@ class Scope: public ZoneObject {
int StackLocalCount() const; int StackLocalCount() const;
int ContextLocalCount() const; int ContextLocalCount() const;
// Determine if we can parse a function literal in this scope lazily. // Determine if we can parse a function literal in this scope lazily without
bool AllowsLazyParsing() const; // caring about the unresolved variables within.
bool AllowsLazyParsingWithoutUnresolvedVariables() const;
// The number of contexts between this and scope; zero if this == scope. // The number of contexts between this and scope; zero if this == scope.
int ContextChainLength(Scope* scope) const; int ContextChainLength(Scope* scope) const;
...@@ -538,6 +539,7 @@ class Scope: public ZoneObject { ...@@ -538,6 +539,7 @@ class Scope: public ZoneObject {
// list along the way, so full resolution cannot be done afterwards. // list along the way, so full resolution cannot be done afterwards.
// If a ParseInfo* is passed, non-free variables will be resolved. // If a ParseInfo* is passed, non-free variables will be resolved.
VariableProxy* FetchFreeVariables(DeclarationScope* max_outer_scope, VariableProxy* FetchFreeVariables(DeclarationScope* max_outer_scope,
bool try_to_resolve = true,
ParseInfo* info = nullptr, ParseInfo* info = nullptr,
VariableProxy* stack = nullptr); VariableProxy* stack = nullptr);
......
...@@ -838,6 +838,7 @@ DEFINE_BOOL(trace_maps, false, "trace map creation") ...@@ -838,6 +838,7 @@ DEFINE_BOOL(trace_maps, false, "trace map creation")
// parser.cc // parser.cc
DEFINE_BOOL(allow_natives_syntax, false, "allow natives syntax") DEFINE_BOOL(allow_natives_syntax, false, "allow natives syntax")
DEFINE_BOOL(trace_parse, false, "trace parsing and preparsing") DEFINE_BOOL(trace_parse, false, "trace parsing and preparsing")
DEFINE_BOOL(lazy_inner_functions, true, "enable lazy parsing inner functions")
// simulator-arm.cc, simulator-arm64.cc and simulator-mips.cc // simulator-arm.cc, simulator-arm64.cc and simulator-mips.cc
DEFINE_BOOL(trace_sim, false, "Trace simulator execution") DEFINE_BOOL(trace_sim, false, "Trace simulator execution")
......
...@@ -3915,13 +3915,17 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( ...@@ -3915,13 +3915,17 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
// Multiple statement body // Multiple statement body
Consume(Token::LBRACE); Consume(Token::LBRACE);
DCHECK_EQ(scope(), formal_parameters.scope); DCHECK_EQ(scope(), formal_parameters.scope);
bool is_lazily_parsed = (mode() == PARSE_LAZILY && bool is_lazily_parsed =
formal_parameters.scope->AllowsLazyParsing()); (mode() == PARSE_LAZILY &&
formal_parameters.scope
->AllowsLazyParsingWithoutUnresolvedVariables());
// TODO(marja): consider lazy-parsing inner arrow functions too. is_this
// handling in Scope::ResolveVariable needs to change.
if (is_lazily_parsed) { if (is_lazily_parsed) {
Scanner::BookmarkScope bookmark(scanner()); Scanner::BookmarkScope bookmark(scanner());
bookmark.Set(); bookmark.Set();
LazyParsingResult result = impl()->SkipLazyFunctionBody( LazyParsingResult result = impl()->SkipLazyFunctionBody(
&materialized_literal_count, &expected_property_count, true, &materialized_literal_count, &expected_property_count, false, true,
CHECK_OK); CHECK_OK);
if (formal_parameters.materialized_literals_count > 0) { if (formal_parameters.materialized_literals_count > 0) {
......
...@@ -122,11 +122,17 @@ class DiscardableZoneScope { ...@@ -122,11 +122,17 @@ class DiscardableZoneScope {
if (use_temp_zone) { if (use_temp_zone) {
parser_->fni_ = &fni_; parser_->fni_ = &fni_;
parser_->zone_ = temp_zone; parser_->zone_ = temp_zone;
if (parser_->reusable_preparser_ != nullptr) {
parser_->reusable_preparser_->zone_ = temp_zone;
}
} }
} }
~DiscardableZoneScope() { ~DiscardableZoneScope() {
parser_->fni_ = prev_fni_; parser_->fni_ = prev_fni_;
parser_->zone_ = prev_zone_; parser_->zone_ = prev_zone_;
if (parser_->reusable_preparser_ != nullptr) {
parser_->reusable_preparser_->zone_ = prev_zone_;
}
} }
private: private:
...@@ -2687,7 +2693,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2687,7 +2693,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// These are all things we can know at this point, without looking at the // These are all things we can know at this point, without looking at the
// function itself. // function itself.
// In addition, we need to distinguish between these cases: // We separate between lazy parsing top level functions and lazy parsing inner
// functions, because the latter needs to do more work. In particular, we need
// to track unresolved variables to distinguish between these cases:
// (function foo() { // (function foo() {
// bar = function() { return 1; } // bar = function() { return 1; }
// })(); // })();
...@@ -2699,17 +2707,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2699,17 +2707,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// Now foo will be parsed eagerly and compiled eagerly (optimization: assume // Now foo will be parsed eagerly and compiled eagerly (optimization: assume
// parenthesis before the function means that it will be called // parenthesis before the function means that it will be called
// immediately). The inner function *must* be parsed eagerly to resolve the // immediately). bar can be parsed lazily, but we need to parse it in a mode
// possible reference to the variable in foo's scope. However, it's possible // that tracks unresolved variables.
// that it will be compiled lazily. DCHECK_IMPLIES(mode() == PARSE_LAZILY, FLAG_lazy);
DCHECK_IMPLIES(mode() == PARSE_LAZILY, allow_lazy());
// To make this additional case work, both Parser and PreParser implement a DCHECK_IMPLIES(mode() == PARSE_LAZILY, extension_ == nullptr);
// logic where only top-level functions will be parsed lazily.
bool is_lazily_parsed = mode() == PARSE_LAZILY && bool is_lazy_top_level_function =
scope()->AllowsLazyParsing() && mode() == PARSE_LAZILY &&
!function_state_->next_function_is_parenthesized(); eager_compile_hint == FunctionLiteral::kShouldLazyCompile &&
scope()->AllowsLazyParsingWithoutUnresolvedVariables();
// Determine whether the function body can be discarded after parsing.
// Determine whether we can still lazy parse the inner function.
// The preconditions are: // The preconditions are:
// - Lazy compilation has to be enabled. // - Lazy compilation has to be enabled.
// - Neither V8 natives nor native function declarations can be allowed, // - Neither V8 natives nor native function declarations can be allowed,
...@@ -2724,11 +2733,16 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2724,11 +2733,16 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// - The function literal shouldn't be hinted to eagerly compile. // - The function literal shouldn't be hinted to eagerly compile.
// - For asm.js functions the body needs to be available when module // - For asm.js functions the body needs to be available when module
// validation is active, because we examine the entire module at once. // validation is active, because we examine the entire module at once.
// Inner functions will be parsed using a temporary Zone. After parsing, we
// will migrate unresolved variable into a Scope in the main Zone.
// TODO(marja): Refactor parsing modes: simplify this.
bool use_temp_zone = bool use_temp_zone =
!is_lazily_parsed && allow_lazy() && !is_lazy_top_level_function && allow_lazy() &&
function_type == FunctionLiteral::kDeclaration && function_type == FunctionLiteral::kDeclaration &&
eager_compile_hint != FunctionLiteral::kShouldEagerCompile && eager_compile_hint != FunctionLiteral::kShouldEagerCompile &&
!(FLAG_validate_asm && scope()->IsAsmModule()); !(FLAG_validate_asm && scope()->IsAsmModule());
bool is_lazy_inner_function = use_temp_zone && FLAG_lazy_inner_functions;
DeclarationScope* main_scope = nullptr; DeclarationScope* main_scope = nullptr;
if (use_temp_zone) { if (use_temp_zone) {
...@@ -2804,35 +2818,38 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2804,35 +2818,38 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// which says whether we need to create an arguments adaptor frame). // which says whether we need to create an arguments adaptor frame).
if (formals.has_rest) arity--; if (formals.has_rest) arity--;
// Eager or lazy parse? // Eager or lazy parse? If is_lazy_top_level_function, we'll parse
// If is_lazily_parsed, we'll parse lazily. We'll call SkipLazyFunctionBody, // lazily. We'll call SkipLazyFunctionBody, which may decide to abort lazy
// which may decide to abort lazy parsing if it suspects that wasn't a good // parsing if it suspects that wasn't a good idea. If so (in which case the
// idea. If so (in which case the parser is expected to have backtracked), // parser is expected to have backtracked), or if we didn't try to lazy
// or if we didn't try to lazy parse in the first place, we'll have to parse // parse in the first place, we'll have to parse eagerly.
// eagerly. if (is_lazy_top_level_function || is_lazy_inner_function) {
if (is_lazily_parsed) {
Scanner::BookmarkScope bookmark(scanner()); Scanner::BookmarkScope bookmark(scanner());
bookmark.Set(); bookmark.Set();
LazyParsingResult result = LazyParsingResult result = SkipLazyFunctionBody(
SkipLazyFunctionBody(&materialized_literal_count, &materialized_literal_count, &expected_property_count,
&expected_property_count, true, CHECK_OK); is_lazy_inner_function, is_lazy_top_level_function, CHECK_OK);
materialized_literal_count += formals.materialized_literals_count + materialized_literal_count += formals.materialized_literals_count +
function_state.materialized_literal_count(); function_state.materialized_literal_count();
if (result == kLazyParsingAborted) { if (result == kLazyParsingAborted) {
DCHECK(is_lazy_top_level_function);
bookmark.Apply(); bookmark.Apply();
// Trigger eager (re-)parsing, just below this block. // Trigger eager (re-)parsing, just below this block.
is_lazily_parsed = false; is_lazy_top_level_function = false;
// This is probably an initialization function. Inform the compiler it // This is probably an initialization function. Inform the compiler it
// should also eager-compile this function, and that we expect it to be // should also eager-compile this function, and that we expect it to be
// used once. // used once.
eager_compile_hint = FunctionLiteral::kShouldEagerCompile; eager_compile_hint = FunctionLiteral::kShouldEagerCompile;
should_be_used_once_hint = true; should_be_used_once_hint = true;
} else if (is_lazy_inner_function) {
DCHECK(main_scope != scope);
scope->AnalyzePartially(main_scope, &previous_zone_ast_node_factory);
} }
} }
if (!is_lazily_parsed) { if (!is_lazy_top_level_function && !is_lazy_inner_function) {
body = ParseEagerFunctionBody(function_name, pos, formals, kind, body = ParseEagerFunctionBody(function_name, pos, formals, kind,
function_type, CHECK_OK); function_type, CHECK_OK);
...@@ -2842,6 +2859,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2842,6 +2859,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// If the preconditions are correct the function body should never be // If the preconditions are correct the function body should never be
// accessed, but do this anyway for better behaviour if they're wrong. // accessed, but do this anyway for better behaviour if they're wrong.
body = nullptr; body = nullptr;
DCHECK(main_scope != scope);
scope->AnalyzePartially(main_scope, &previous_zone_ast_node_factory);
} }
} }
...@@ -2871,11 +2890,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2871,11 +2890,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
} }
has_duplicate_parameters = has_duplicate_parameters =
!classifier()->is_valid_formal_parameter_list_without_duplicates(); !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. } // DiscardableZoneScope goes out of scope.
FunctionLiteral::ParameterFlag duplicate_parameters = FunctionLiteral::ParameterFlag duplicate_parameters =
...@@ -2926,14 +2940,16 @@ Expression* Parser::ParseAsyncFunctionExpression(bool* ok) { ...@@ -2926,14 +2940,16 @@ Expression* Parser::ParseAsyncFunctionExpression(bool* ok) {
Parser::LazyParsingResult Parser::SkipLazyFunctionBody( Parser::LazyParsingResult Parser::SkipLazyFunctionBody(
int* materialized_literal_count, int* expected_property_count, int* materialized_literal_count, int* expected_property_count,
bool may_abort, bool* ok) { bool is_inner_function, bool may_abort, bool* ok) {
if (produce_cached_parse_data()) CHECK(log_); if (produce_cached_parse_data()) CHECK(log_);
int function_block_pos = position(); int function_block_pos = position();
DeclarationScope* scope = this->scope()->AsDeclarationScope(); DeclarationScope* scope = this->scope()->AsDeclarationScope();
DCHECK(scope->is_function_scope()); DCHECK(scope->is_function_scope());
scope->set_is_lazily_parsed(true); scope->set_is_lazily_parsed(true);
if (consume_cached_parse_data() && !cached_parse_data_->rejected()) { // Inner functions are not part of the cached data.
if (!is_inner_function && consume_cached_parse_data() &&
!cached_parse_data_->rejected()) {
// If we have cached data, we use it to skip parsing the function body. The // If we have cached data, we use it to skip parsing the function body. The
// data contains the information we need to construct the lazy function. // data contains the information we need to construct the lazy function.
FunctionEntry entry = FunctionEntry entry =
...@@ -2960,7 +2976,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody( ...@@ -2960,7 +2976,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody(
// AST. This gathers the data needed to build a lazy function. // AST. This gathers the data needed to build a lazy function.
SingletonLogger logger; SingletonLogger logger;
PreParser::PreParseResult result = PreParser::PreParseResult result =
ParseLazyFunctionBodyWithPreParser(&logger, may_abort); ParseLazyFunctionBodyWithPreParser(&logger, is_inner_function, may_abort);
// Return immediately if pre-parser decided to abort parsing. // Return immediately if pre-parser decided to abort parsing.
if (result == PreParser::kPreParseAbort) { if (result == PreParser::kPreParseAbort) {
scope->set_is_lazily_parsed(false); scope->set_is_lazily_parsed(false);
...@@ -2987,7 +3003,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody( ...@@ -2987,7 +3003,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody(
SetLanguageMode(scope, logger.language_mode()); SetLanguageMode(scope, logger.language_mode());
if (logger.uses_super_property()) scope->RecordSuperPropertyUsage(); if (logger.uses_super_property()) scope->RecordSuperPropertyUsage();
if (logger.calls_eval()) scope->RecordEvalCall(); if (logger.calls_eval()) scope->RecordEvalCall();
if (produce_cached_parse_data()) { if (!is_inner_function && produce_cached_parse_data()) {
DCHECK(log_); DCHECK(log_);
// Position right after terminal '}'. // Position right after terminal '}'.
int body_end = scanner()->location().end_pos; int body_end = scanner()->location().end_pos;
...@@ -3259,8 +3275,10 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( ...@@ -3259,8 +3275,10 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
const AstRawString* function_name, int pos, const AstRawString* function_name, int pos,
const ParserFormalParameters& parameters, FunctionKind kind, const ParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok) { FunctionLiteral::FunctionType function_type, bool* ok) {
// Everything inside an eagerly parsed function will be parsed eagerly // Everything inside an eagerly parsed function will be parsed eagerly (see
// (see comment above). // comment above). Lazy inner functions are handled separately and they won't
// require the mode to be PARSE_LAZILY (see ParseFunctionLiteral).
// TODO(marja): Refactor parsing modes: remove this.
ParsingModeScope parsing_mode(this, PARSE_EAGERLY); ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone()); ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone());
...@@ -3431,7 +3449,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( ...@@ -3431,7 +3449,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
} }
PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
SingletonLogger* logger, bool may_abort) { SingletonLogger* logger, bool is_inner_function, bool may_abort) {
// This function may be called on a background thread too; record only the // This function may be called on a background thread too; record only the
// main thread preparse times. // main thread preparse times.
if (pre_parse_timer_ != NULL) { if (pre_parse_timer_ != NULL) {
...@@ -3456,10 +3474,36 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( ...@@ -3456,10 +3474,36 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
SET_ALLOW(harmony_class_fields); SET_ALLOW(harmony_class_fields);
#undef SET_ALLOW #undef SET_ALLOW
} }
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction( // Aborting inner function preparsing would leave scopes in an inconsistent
language_mode(), function_state_->kind(), // state; we don't parse inner functions in the abortable mode anyway.
scope()->AsDeclarationScope()->has_simple_parameters(), parsing_module_, DCHECK(!is_inner_function || !may_abort);
logger, may_abort, use_counts_);
FunctionKind kind = function_state_->kind();
PreParser::PreParseResult result;
if (!is_inner_function) {
// If we don't need to look at the scope, construct a dummy scope chain
// which is not connected to the real scope chain.
LanguageMode mode = language_mode();
bool has_simple_parameters =
scope()->AsDeclarationScope()->has_simple_parameters();
DeclarationScope* top_scope = NewScriptScope();
top_scope->SetLanguageMode(mode);
FunctionState top_state(&function_state_, &scope_state_, top_scope,
kNormalFunction);
DeclarationScope* function_scope = NewFunctionScope(kind);
if (!has_simple_parameters) {
function_scope->SetHasNonSimpleParameters();
}
result = reusable_preparser_->PreParseLazyFunction(
kind, function_scope, parsing_module_, logger, is_inner_function,
may_abort, use_counts_);
} else {
// Detaching the scopes created by PreParser from the Scope chain must be
// done above (see ParseFunctionLiteral & AnalyzePartially).
result = reusable_preparser_->PreParseLazyFunction(
kind, scope()->AsDeclarationScope(), parsing_module_, logger,
is_inner_function, may_abort, use_counts_);
}
if (pre_parse_timer_ != NULL) { if (pre_parse_timer_ != NULL) {
pre_parse_timer_->Stop(); pre_parse_timer_->Stop();
} }
......
...@@ -484,10 +484,11 @@ class Parser : public ParserBase<Parser> { ...@@ -484,10 +484,11 @@ class Parser : public ParserBase<Parser> {
// in order to force the function to be eagerly parsed, after all. // in order to force the function to be eagerly parsed, after all.
LazyParsingResult SkipLazyFunctionBody(int* materialized_literal_count, LazyParsingResult SkipLazyFunctionBody(int* materialized_literal_count,
int* expected_property_count, int* expected_property_count,
bool may_abort, bool* ok); bool is_inner_function, bool may_abort,
bool* ok);
PreParser::PreParseResult ParseLazyFunctionBodyWithPreParser( PreParser::PreParseResult ParseLazyFunctionBodyWithPreParser(
SingletonLogger* logger, bool may_abort); SingletonLogger* logger, bool is_inner_function, bool may_abort);
Block* BuildParameterInitializationBlock( Block* BuildParameterInitializationBlock(
const ParserFormalParameters& parameters, bool* ok); const ParserFormalParameters& parameters, bool* ok);
...@@ -1063,6 +1064,9 @@ class Parser : public ParserBase<Parser> { ...@@ -1063,6 +1064,9 @@ class Parser : public ParserBase<Parser> {
} }
// Parser's private field members. // Parser's private field members.
friend class DiscardableZoneScope; // Uses reusable_preparser_.
// FIXME(marja): Make reusable_preparser_ always use its own temp Zone (call
// DeleteAll after each function), so this won't be needed.
Scanner scanner_; Scanner scanner_;
PreParser* reusable_preparser_; PreParser* reusable_preparser_;
......
...@@ -38,8 +38,10 @@ namespace internal { ...@@ -38,8 +38,10 @@ namespace internal {
#define CHECK_OK CHECK_OK_VALUE(Statement::Default()) #define CHECK_OK CHECK_OK_VALUE(Statement::Default())
#define CHECK_OK_VOID CHECK_OK_VALUE(this->Void()) #define CHECK_OK_VOID CHECK_OK_VALUE(this->Void())
PreParserIdentifier PreParser::GetSymbol() const { namespace {
switch (scanner()->current_token()) {
PreParserIdentifier GetSymbolHelper(Scanner* scanner) {
switch (scanner->current_token()) {
case Token::ENUM: case Token::ENUM:
return PreParserIdentifier::Enum(); return PreParserIdentifier::Enum();
case Token::AWAIT: case Token::AWAIT:
...@@ -55,34 +57,46 @@ PreParserIdentifier PreParser::GetSymbol() const { ...@@ -55,34 +57,46 @@ PreParserIdentifier PreParser::GetSymbol() const {
case Token::ASYNC: case Token::ASYNC:
return PreParserIdentifier::Async(); return PreParserIdentifier::Async();
default: default:
if (scanner()->UnescapedLiteralMatches("eval", 4)) if (scanner->UnescapedLiteralMatches("eval", 4))
return PreParserIdentifier::Eval(); return PreParserIdentifier::Eval();
if (scanner()->UnescapedLiteralMatches("arguments", 9)) if (scanner->UnescapedLiteralMatches("arguments", 9))
return PreParserIdentifier::Arguments(); return PreParserIdentifier::Arguments();
if (scanner()->UnescapedLiteralMatches("undefined", 9)) if (scanner->UnescapedLiteralMatches("undefined", 9))
return PreParserIdentifier::Undefined(); return PreParserIdentifier::Undefined();
if (scanner()->LiteralMatches("prototype", 9)) if (scanner->LiteralMatches("prototype", 9))
return PreParserIdentifier::Prototype(); return PreParserIdentifier::Prototype();
if (scanner()->LiteralMatches("constructor", 11)) if (scanner->LiteralMatches("constructor", 11))
return PreParserIdentifier::Constructor(); return PreParserIdentifier::Constructor();
return PreParserIdentifier::Default(); return PreParserIdentifier::Default();
} }
} }
} // unnamed namespace
PreParserIdentifier PreParser::GetSymbol() const {
PreParserIdentifier symbol = GetSymbolHelper(scanner());
if (track_unresolved_variables_) {
const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory());
DCHECK_NOT_NULL(result);
symbol.string_ = result;
}
return symbol;
}
PreParser::PreParseResult PreParser::PreParseLazyFunction( PreParser::PreParseResult PreParser::PreParseLazyFunction(
LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters, FunctionKind kind, DeclarationScope* function_scope, bool parsing_module,
bool parsing_module, ParserRecorder* log, bool may_abort, int* use_counts) { ParserRecorder* log, bool is_inner_function, bool may_abort,
int* use_counts) {
parsing_module_ = parsing_module; parsing_module_ = parsing_module;
log_ = log; log_ = log;
use_counts_ = use_counts; use_counts_ = use_counts;
// Lazy functions always have trivial outer scopes (no with/catch scopes). DCHECK(!track_unresolved_variables_);
track_unresolved_variables_ = is_inner_function;
// The caller passes the function_scope which is not yet inserted into the
// scope_state_. All scopes above the function_scope are ignored by the
// PreParser.
DCHECK_NULL(scope_state_); DCHECK_NULL(scope_state_);
DeclarationScope* top_scope = NewScriptScope();
FunctionState top_state(&function_state_, &scope_state_, top_scope,
kNormalFunction);
scope()->SetLanguageMode(language_mode);
DeclarationScope* function_scope = NewFunctionScope(kind);
if (!has_simple_parameters) function_scope->SetHasNonSimpleParameters();
FunctionState function_state(&function_state_, &scope_state_, function_scope, FunctionState function_state(&function_state_, &scope_state_, function_scope,
kind); kind);
DCHECK_EQ(Token::LBRACE, scanner()->current_token()); DCHECK_EQ(Token::LBRACE, scanner()->current_token());
...@@ -90,6 +104,7 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction( ...@@ -90,6 +104,7 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction(
int start_position = peek_position(); int start_position = peek_position();
LazyParsingResult result = ParseLazyFunctionLiteralBody(may_abort, &ok); LazyParsingResult result = ParseLazyFunctionLiteralBody(may_abort, &ok);
use_counts_ = nullptr; use_counts_ = nullptr;
track_unresolved_variables_ = false;
if (result == kLazyParsingAborted) { if (result == kLazyParsingAborted) {
return kPreParseAbort; return kPreParseAbort;
} else if (stack_overflow()) { } else if (stack_overflow()) {
...@@ -340,6 +355,21 @@ void PreParser::ParseAsyncArrowSingleExpressionBody(PreParserStatementList body, ...@@ -340,6 +355,21 @@ void PreParser::ParseAsyncArrowSingleExpressionBody(PreParserStatementList body,
body->Add(PreParserStatement::ExpressionStatement(return_value), zone()); body->Add(PreParserStatement::ExpressionStatement(return_value), zone());
} }
PreParserExpression PreParser::ExpressionFromIdentifier(
PreParserIdentifier name, int start_position, int end_position,
InferName infer) {
if (track_unresolved_variables_) {
AstNodeFactory factory(ast_value_factory());
// Setting the Zone is necessary because zone_ might be the temp Zone, and
// AstValueFactory doesn't know about it.
factory.set_zone(zone());
DCHECK_NOT_NULL(name.string_);
scope()->NewUnresolved(&factory, name.string_, start_position, end_position,
NORMAL_VARIABLE);
}
return PreParserExpression::FromIdentifier(name);
}
#undef CHECK_OK #undef CHECK_OK
#undef CHECK_OK_CUSTOM #undef CHECK_OK_CUSTOM
......
...@@ -112,10 +112,12 @@ class PreParserIdentifier { ...@@ -112,10 +112,12 @@ class PreParserIdentifier {
kAsyncIdentifier kAsyncIdentifier
}; };
explicit PreParserIdentifier(Type type) : type_(type) {} explicit PreParserIdentifier(Type type) : type_(type), string_(nullptr) {}
Type type_; Type type_;
// Only non-nullptr when PreParser.track_unresolved_variables_ is true.
const AstRawString* string_;
friend class PreParserExpression; friend class PreParserExpression;
friend class PreParser;
}; };
...@@ -774,7 +776,8 @@ class PreParser : public ParserBase<PreParser> { ...@@ -774,7 +776,8 @@ class PreParser : public ParserBase<PreParser> {
ParserRecorder* log, uintptr_t stack_limit) ParserRecorder* log, uintptr_t stack_limit)
: ParserBase<PreParser>(zone, scanner, stack_limit, NULL, : ParserBase<PreParser>(zone, scanner, stack_limit, NULL,
ast_value_factory, log), ast_value_factory, log),
use_counts_(nullptr) {} use_counts_(nullptr),
track_unresolved_variables_(false) {}
// Pre-parse the program from the character stream; returns true on // Pre-parse the program from the character stream; returns true on
// success (even if parsing failed, the pre-parse data successfully // success (even if parsing failed, the pre-parse data successfully
...@@ -820,10 +823,10 @@ class PreParser : public ParserBase<PreParser> { ...@@ -820,10 +823,10 @@ class PreParser : public ParserBase<PreParser> {
// keyword and parameters, and have consumed the initial '{'. // keyword and parameters, and have consumed the initial '{'.
// At return, unless an error occurred, the scanner is positioned before the // At return, unless an error occurred, the scanner is positioned before the
// the final '}'. // the final '}'.
PreParseResult PreParseLazyFunction(LanguageMode language_mode, PreParseResult PreParseLazyFunction(FunctionKind kind,
FunctionKind kind, DeclarationScope* function_scope,
bool has_simple_parameters,
bool parsing_module, ParserRecorder* log, bool parsing_module, ParserRecorder* log,
bool track_unresolved_variables,
bool may_abort, int* use_counts); bool may_abort, int* use_counts);
private: private:
...@@ -850,9 +853,9 @@ class PreParser : public ParserBase<PreParser> { ...@@ -850,9 +853,9 @@ class PreParser : public ParserBase<PreParser> {
const PreParserFormalParameters& parameters, FunctionKind kind, const PreParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok); FunctionLiteral::FunctionType function_type, bool* ok);
V8_INLINE LazyParsingResult V8_INLINE LazyParsingResult SkipLazyFunctionBody(
SkipLazyFunctionBody(int* materialized_literal_count, int* materialized_literal_count, int* expected_property_count,
int* expected_property_count, bool may_abort, bool* ok) { bool track_unresolved_variables, bool may_abort, bool* ok) {
UNREACHABLE(); UNREACHABLE();
return kLazyParsingComplete; return kLazyParsingComplete;
} }
...@@ -1304,11 +1307,9 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1304,11 +1307,9 @@ class PreParser : public ParserBase<PreParser> {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
V8_INLINE PreParserExpression ExpressionFromIdentifier( PreParserExpression ExpressionFromIdentifier(
PreParserIdentifier name, int start_position, int end_position, PreParserIdentifier name, int start_position, int end_position,
InferName infer = InferName::kYes) { InferName infer = InferName::kYes);
return PreParserExpression::FromIdentifier(name);
}
V8_INLINE PreParserExpression ExpressionFromString(int pos) { V8_INLINE PreParserExpression ExpressionFromString(int pos) {
if (scanner()->UnescapedLiteralMatches("use strict", 10)) { if (scanner()->UnescapedLiteralMatches("use strict", 10)) {
...@@ -1417,6 +1418,7 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1417,6 +1418,7 @@ class PreParser : public ParserBase<PreParser> {
// Preparser's private field members. // Preparser's private field members.
int* use_counts_; int* use_counts_;
bool track_unresolved_variables_;
}; };
PreParserExpression PreParser::SpreadCall(PreParserExpression function, PreParserExpression PreParser::SpreadCall(PreParserExpression function,
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "src/ast/ast.h" #include "src/ast/ast.h"
#include "src/compiler.h" #include "src/compiler.h"
#include "src/execution.h" #include "src/execution.h"
#include "src/flags.h"
#include "src/isolate.h" #include "src/isolate.h"
#include "src/objects.h" #include "src/objects.h"
#include "src/parsing/parse-info.h" #include "src/parsing/parse-info.h"
...@@ -3416,7 +3417,14 @@ TEST(InnerAssignment) { ...@@ -3416,7 +3417,14 @@ TEST(InnerAssignment) {
bool expected = outers[i].assigned || inners[j].assigned; bool expected = outers[i].assigned || inners[j].assigned;
CHECK(var != NULL); CHECK(var != NULL);
CHECK(var->is_used() || !expected); CHECK(var->is_used() || !expected);
CHECK((var->maybe_assigned() == i::kMaybeAssigned) == expected); bool is_maybe_assigned = var->maybe_assigned() == i::kMaybeAssigned;
if (i::FLAG_lazy_inner_functions) {
// If we parse inner functions lazily, allow being pessimistic about
// maybe_assigned.
CHECK(is_maybe_assigned || (is_maybe_assigned == expected));
} else {
CHECK(is_maybe_assigned == expected);
}
} }
} }
} }
......
...@@ -87,7 +87,6 @@ var f3 = (function F1(invisible_parameter) { ...@@ -87,7 +87,6 @@ var f3 = (function F1(invisible_parameter) {
var invisible2 = 2; var invisible2 = 2;
return (function F3() { return (function F3() {
var visible2 = 20; var visible2 = 20;
var invisible2 = 3;
return (function () {return visible1 + visible2 + visible1a;}); return (function () {return visible1 + visible2 + visible1a;});
})(); })();
})(); })();
......
...@@ -157,20 +157,20 @@ function CheckScopeChainNames(names, exec_state) { ...@@ -157,20 +157,20 @@ function CheckScopeChainNames(names, exec_state) {
} }
// Check that the content of the scope is as expected. For functions just check // Check that the scope contains at least minimum_content. For functions just
// that there is a function. // check that there is a function.
function CheckScopeContent(content, number, exec_state) { function CheckScopeContent(minimum_content, number, exec_state) {
var scope = exec_state.frame().scope(number); var scope = exec_state.frame().scope(number);
var count = 0; var minimum_count = 0;
for (var p in content) { for (var p in minimum_content) {
var property_mirror = scope.scopeObject().property(p); var property_mirror = scope.scopeObject().property(p);
assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope');
if (typeof(content[p]) === 'function') { if (typeof(minimum_content[p]) === 'function') {
assertTrue(property_mirror.value().isFunction()); assertTrue(property_mirror.value().isFunction());
} else { } else {
assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); assertEquals(minimum_content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value');
} }
count++; minimum_count++;
} }
// 'arguments' and might be exposed in the local and closure scope. Just // 'arguments' and might be exposed in the local and closure scope. Just
...@@ -186,14 +186,14 @@ function CheckScopeContent(content, number, exec_state) { ...@@ -186,14 +186,14 @@ function CheckScopeContent(content, number, exec_state) {
// Temporary variables introduced by the parser have not been materialized. // Temporary variables introduced by the parser have not been materialized.
assertTrue(scope.scopeObject().property('').isUndefined()); assertTrue(scope.scopeObject().property('').isUndefined());
if (count != scope_size) { if (scope_size < minimum_count) {
print('Names found in scope:'); print('Names found in scope:');
var names = scope.scopeObject().propertyNames(); var names = scope.scopeObject().propertyNames();
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
print(names[i]); print(names[i]);
} }
} }
assertEquals(count, scope_size); assertTrue(scope_size >= minimum_count);
// Get the debug command processor. // Get the debug command processor.
var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
......
// 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 1
(function TestLazyInnerFunctionCallsEval() {
var i = (function eager_outer() {
var a = 41; // Should be context-allocated
function lazy_inner() {
return eval("a");
}
return lazy_inner;
})();
assertEquals(41, i());
})();
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