Commit 7c73cf32 authored by marja's avatar marja Committed by Commit bot

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=

Review-Url: https://codereview.chromium.org/2352593002
Cr-Commit-Position: refs/heads/master@{#39719}
parent a0484bc6
...@@ -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);
......
...@@ -837,6 +837,7 @@ DEFINE_BOOL(trace_maps, false, "trace map creation") ...@@ -837,6 +837,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:
...@@ -2683,7 +2689,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2683,7 +2689,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; }
// })(); // })();
...@@ -2695,17 +2703,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2695,17 +2703,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,
...@@ -2720,11 +2729,16 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2720,11 +2729,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) {
...@@ -2800,35 +2814,38 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2800,35 +2814,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);
...@@ -2838,6 +2855,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2838,6 +2855,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);
} }
} }
...@@ -2867,11 +2886,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -2867,11 +2886,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 =
...@@ -2922,14 +2936,16 @@ Expression* Parser::ParseAsyncFunctionExpression(bool* ok) { ...@@ -2922,14 +2936,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 =
...@@ -2956,7 +2972,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody( ...@@ -2956,7 +2972,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);
...@@ -2983,7 +2999,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody( ...@@ -2983,7 +2999,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;
...@@ -3255,8 +3271,10 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( ...@@ -3255,8 +3271,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());
...@@ -3427,7 +3445,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( ...@@ -3427,7 +3445,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) {
...@@ -3452,10 +3470,36 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( ...@@ -3452,10 +3470,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