Chromium Code Reviews| Index: src/parsing/parser.cc |
| diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc |
| index 4973857c37ea0f2cf2854c35000be559db065e3a..7d31cc05b06e83fdc290e8712aea06750d56b462 100644 |
| --- a/src/parsing/parser.cc |
| +++ b/src/parsing/parser.cc |
| @@ -122,11 +122,17 @@ class DiscardableZoneScope { |
| if (use_temp_zone) { |
| parser_->fni_ = &fni_; |
| parser_->zone_ = temp_zone; |
| + if (parser_->reusable_preparser_ != nullptr) { |
| + parser_->reusable_preparser_->zone_ = temp_zone; |
| + } |
| } |
| } |
| ~DiscardableZoneScope() { |
| parser_->fni_ = prev_fni_; |
| parser_->zone_ = prev_zone_; |
| + if (parser_->reusable_preparser_ != nullptr) { |
| + parser_->reusable_preparser_->zone_ = prev_zone_; |
| + } |
| } |
| private: |
| @@ -2948,7 +2954,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| // These are all things we can know at this point, without looking at the |
| // function itself. |
| - // In addition, we need to distinguish between these cases: |
| + // 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() { |
| // bar = function() { return 1; } |
| // })(); |
| @@ -2960,17 +2968,19 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| // Now foo will be parsed eagerly and compiled eagerly (optimization: assume |
| // parenthesis before the function means that it will be called |
| - // immediately). The inner function *must* be parsed eagerly to resolve the |
| - // possible reference to the variable in foo's scope. However, it's possible |
| - // that it will be compiled lazily. |
| - |
| - // To make this additional case work, both Parser and PreParser implement a |
| - // logic where only top-level functions will be parsed lazily. |
| - bool is_lazily_parsed = mode() == PARSE_LAZILY && |
| - scope()->AllowsLazyParsing() && |
| - !function_state_->next_function_is_parenthesized(); |
| - |
| - // Determine whether the function body can be discarded after parsing. |
| + // immediately). bar can be parsed lazily, but we need to parse it in a mode |
| + // that tracks unresolved variables. |
| + DCHECK_IMPLIES(mode() == PARSE_LAZILY, FLAG_lazy); |
| + DCHECK_IMPLIES(mode() == PARSE_LAZILY, allow_lazy()); |
| + DCHECK_IMPLIES(mode() == PARSE_LAZILY, !allow_natives()); |
| + DCHECK_IMPLIES(mode() == PARSE_LAZILY, extension_ == nullptr); |
| + |
| + bool is_lazy_top_level_function = |
| + mode() == PARSE_LAZILY && |
| + eager_compile_hint == FunctionLiteral::kShouldLazyCompile && |
| + scope()->AllowsLazyParsingWithoutUnresolvedVariables(); |
|
vogelheim
2016/09/16 08:13:04
Does this eliminate the force-eager-compile-for-pa
marja
2016/09/16 08:38:15
As discussed offline, the logic is intact (eager_c
|
| + |
| + // Determine whether we can still do the inner function lazy parsing. |
| // The preconditions are: |
| // - Lazy compilation has to be enabled. |
| // - Neither V8 natives nor native function declarations can be allowed, |
| @@ -2985,15 +2995,19 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| // - The function literal shouldn't be hinted to eagerly compile. |
| // - For asm.js functions the body needs to be available when module |
| // validation is active, because we examine the entire module at once. |
| - bool use_temp_zone = |
| - !is_lazily_parsed && FLAG_lazy && !allow_natives() && |
| + |
| + // Inner functions will be parsed by 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 is_lazy_inner_function = |
| + !is_lazy_top_level_function && FLAG_lazy && !allow_natives() && |
| extension_ == NULL && allow_lazy() && |
| function_type == FunctionLiteral::kDeclaration && |
| eager_compile_hint != FunctionLiteral::kShouldEagerCompile && |
| !(FLAG_validate_asm && scope()->IsAsmModule()); |
| DeclarationScope* main_scope = nullptr; |
| - if (use_temp_zone) { |
| + if (is_lazy_inner_function) { |
| // This Scope lives in the main Zone; we'll migrate data into it later. |
| main_scope = NewFunctionScope(kind); |
| } |
| @@ -3018,11 +3032,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| // to do scope analysis correctly after full parsing, we migrate needed |
| // information from scope into main_scope when the function has been parsed. |
| Zone temp_zone(zone()->allocator()); |
| - DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone); |
| + DiscardableZoneScope zone_scope(this, &temp_zone, is_lazy_inner_function); |
| DeclarationScope* scope = NewFunctionScope(kind); |
| SetLanguageMode(scope, language_mode); |
| - if (!use_temp_zone) { |
| + if (!is_lazy_inner_function) { |
| main_scope = scope; |
| } else { |
| DCHECK(main_scope->zone() != scope->zone()); |
| @@ -3066,18 +3080,17 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| // which says whether we need to create an arguments adaptor frame). |
| if (formals.has_rest) arity--; |
| - // Eager or lazy parse? |
| - // If is_lazily_parsed, we'll parse lazily. We'll call SkipLazyFunctionBody, |
| - // which may decide to abort lazy parsing if it suspects that wasn't a good |
| - // idea. If so (in which case the parser is expected to have backtracked), |
| - // or if we didn't try to lazy parse in the first place, we'll have to parse |
| - // eagerly. |
| - if (is_lazily_parsed) { |
| + // Eager or lazy parse? If is_lazy_top_level_function, we'll parse |
| + // lazily. We'll call SkipLazyFunctionBody, which may decide to abort lazy |
| + // parsing if it suspects that wasn't a good idea. If so (in which case the |
| + // parser is expected to have backtracked), or if we didn't try to lazy |
| + // parse in the first place, we'll have to parse eagerly. |
| + if (is_lazy_top_level_function) { |
|
vogelheim
2016/09/16 08:13:04
For my understanding..
this: DCHECK(is_lazy_top_l
marja
2016/09/16 08:38:15
The latter will work. It's also possible both are
|
| Scanner::BookmarkScope bookmark(scanner()); |
| bool may_abort = bookmark.Set(); |
| - LazyParsingResult result = |
| - SkipLazyFunctionBody(&materialized_literal_count, |
| - &expected_property_count, may_abort, CHECK_OK); |
| + LazyParsingResult result = SkipLazyFunctionBody( |
| + &materialized_literal_count, &expected_property_count, false, |
| + may_abort, CHECK_OK); |
| materialized_literal_count += formals.materialized_literals_count + |
| function_state.materialized_literal_count(); |
| @@ -3085,7 +3098,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| if (result == kLazyParsingAborted) { |
| bookmark.Reset(); |
| // 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 |
| // should also eager-compile this function, and that we expect it to be |
| @@ -3094,17 +3107,29 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| should_be_used_once_hint = true; |
| } |
| } |
| - if (!is_lazily_parsed) { |
| + if (is_lazy_inner_function) { |
| + if (FLAG_lazy_inner_functions) { |
| + LazyParsingResult result = SkipLazyFunctionBody( |
| + &materialized_literal_count, &expected_property_count, true, false, |
| + CHECK_OK); |
| + materialized_literal_count += |
| + formals.materialized_literals_count + |
| + function_state.materialized_literal_count(); |
| + DCHECK(result != kLazyParsingAborted); |
| + USE(result); |
| + } else { |
| + 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(); |
| + } |
| + } else if (!is_lazy_top_level_function) { |
|
vogelheim
2016/09/16 08:13:04
style nitpick: This code was awkward before, since
marja
2016/09/16 08:38:15
Will be changed after the flag is gone.
|
| body = ParseEagerFunctionBody(function_name, pos, formals, kind, |
| function_type, CHECK_OK); |
| materialized_literal_count = function_state.materialized_literal_count(); |
| expected_property_count = function_state.expected_property_count(); |
| - if (use_temp_zone) { |
| - // If the preconditions are correct the function body should never be |
| - // accessed, but do this anyway for better behaviour if they're wrong. |
| - body = nullptr; |
| - } |
| } |
| // Parsing the body may change the language mode in our scope. |
| @@ -3138,7 +3163,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral( |
| has_duplicate_parameters = |
| !classifier()->is_valid_formal_parameter_list_without_duplicates(); |
| - if (use_temp_zone) { |
| + if (is_lazy_inner_function) { |
| DCHECK(main_scope != scope); |
| scope->AnalyzePartially(main_scope, &previous_zone_ast_node_factory); |
| } |
| @@ -3192,13 +3217,15 @@ Expression* Parser::ParseAsyncFunctionExpression(bool* ok) { |
| Parser::LazyParsingResult Parser::SkipLazyFunctionBody( |
| 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_); |
| int function_block_pos = position(); |
| DeclarationScope* scope = this->scope()->AsDeclarationScope(); |
| DCHECK(scope->is_function_scope()); |
| - 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 |
| // data contains the information we need to construct the lazy function. |
| FunctionEntry entry = |
| @@ -3225,7 +3252,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody( |
| // AST. This gathers the data needed to build a lazy function. |
| SingletonLogger logger; |
| PreParser::PreParseResult result = |
| - ParseLazyFunctionBodyWithPreParser(&logger, may_abort); |
| + ParseLazyFunctionBodyWithPreParser(&logger, is_inner_function, may_abort); |
| // Return immediately if pre-parser decided to abort parsing. |
| if (result == PreParser::kPreParseAbort) { |
| return kLazyParsingAborted; |
| @@ -3251,7 +3278,7 @@ Parser::LazyParsingResult Parser::SkipLazyFunctionBody( |
| SetLanguageMode(scope, logger.language_mode()); |
| if (logger.uses_super_property()) scope->RecordSuperPropertyUsage(); |
| if (logger.calls_eval()) scope->RecordEvalCall(); |
| - if (produce_cached_parse_data()) { |
| + if (!is_inner_function && produce_cached_parse_data()) { |
| DCHECK(log_); |
| // Position right after terminal '}'. |
| int body_end = scanner()->location().end_pos; |
| @@ -3553,11 +3580,12 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( |
| const AstRawString* function_name, int pos, |
| const ParserFormalParameters& parameters, FunctionKind kind, |
| FunctionLiteral::FunctionType function_type, bool* ok) { |
| - // Everything inside an eagerly parsed function will be parsed eagerly |
| - // (see comment above). |
| + // Everything inside an eagerly parsed function will be parsed eagerly (see |
| + // 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); |
| ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone()); |
| - |
| static const int kFunctionNameAssignmentIndex = 0; |
| if (function_type == FunctionLiteral::kNamedExpression) { |
| DCHECK(function_name != NULL); |
| @@ -3713,7 +3741,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody( |
| } |
| 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 |
| // main thread preparse times. |
| if (pre_parse_timer_ != NULL) { |
| @@ -3737,10 +3765,36 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( |
| SET_ALLOW(harmony_trailing_commas); |
| #undef SET_ALLOW |
| } |
| - PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction( |
| - language_mode(), function_state_->kind(), |
| - scope()->AsDeclarationScope()->has_simple_parameters(), parsing_module_, |
| - logger, may_abort, use_counts_); |
| + // Aborting inner function preparsing would leave scopes in an inconsistent |
| + // state; we don't parse inner functions in the abortable mode anyway. |
| + DCHECK(!is_inner_function || !may_abort); |
| + |
| + 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) { |
| pre_parse_timer_->Stop(); |
| } |