| // Copyright 2011 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. |
| |
| #include <cmath> |
| |
| #include "src/base/logging.h" |
| #include "src/common/globals.h" |
| #include "src/logging/counters.h" |
| #include "src/numbers/conversions-inl.h" |
| #include "src/numbers/conversions.h" |
| #include "src/parsing/parser-base.h" |
| #include "src/parsing/preparse-data.h" |
| #include "src/parsing/preparser.h" |
| #include "src/strings/unicode.h" |
| #include "src/utils/allocation.h" |
| #include "src/utils/utils.h" |
| #include "src/zone/zone-list-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| PreParserIdentifier GetIdentifierHelper(Scanner* scanner, |
| const AstRawString* string, |
| AstValueFactory* avf) { |
| // These symbols require slightly different treatement: |
| // - regular keywords (async, await, etc.; treated in 1st switch.) |
| // - 'contextual' keywords (and may contain escaped; treated in 2nd switch.) |
| // - 'contextual' keywords, but may not be escaped (3rd switch). |
| switch (scanner->current_token()) { |
| case Token::AWAIT: |
| return PreParserIdentifier::Await(); |
| case Token::ASYNC: |
| return PreParserIdentifier::Async(); |
| case Token::PRIVATE_NAME: |
| return PreParserIdentifier::PrivateName(); |
| default: |
| break; |
| } |
| if (string == avf->constructor_string()) { |
| return PreParserIdentifier::Constructor(); |
| } |
| if (string == avf->name_string()) { |
| return PreParserIdentifier::Name(); |
| } |
| if (scanner->literal_contains_escapes()) { |
| return PreParserIdentifier::Default(); |
| } |
| if (string == avf->eval_string()) { |
| return PreParserIdentifier::Eval(); |
| } |
| if (string == avf->arguments_string()) { |
| return PreParserIdentifier::Arguments(); |
| } |
| return PreParserIdentifier::Default(); |
| } |
| |
| } // namespace |
| |
| PreParserIdentifier PreParser::GetIdentifier() const { |
| const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory()); |
| PreParserIdentifier symbol = |
| GetIdentifierHelper(scanner(), result, ast_value_factory()); |
| DCHECK_NOT_NULL(result); |
| symbol.string_ = result; |
| return symbol; |
| } |
| |
| PreParser::PreParseResult PreParser::PreParseProgram() { |
| DCHECK_NULL(scope_); |
| DeclarationScope* scope = NewScriptScope(REPLMode::kNo); |
| #ifdef DEBUG |
| scope->set_is_being_lazily_parsed(true); |
| #endif |
| |
| // ModuleDeclarationInstantiation for Source Text Module Records creates a |
| // new Module Environment Record whose outer lexical environment record is |
| // the global scope. |
| if (flags().is_module()) scope = NewModuleScope(scope); |
| |
| FunctionState top_scope(&function_state_, &scope_, scope); |
| original_scope_ = scope_; |
| int start_position = peek_position(); |
| PreParserScopedStatementList body(pointer_buffer()); |
| ParseStatementList(&body, Token::EOS); |
| CheckConflictingVarDeclarations(scope); |
| original_scope_ = nullptr; |
| if (stack_overflow()) return kPreParseStackOverflow; |
| if (is_strict(language_mode())) { |
| CheckStrictOctalLiteral(start_position, scanner()->location().end_pos); |
| } |
| return kPreParseSuccess; |
| } |
| |
| void PreParserFormalParameters::ValidateDuplicate(PreParser* preparser) const { |
| if (has_duplicate_) preparser->ReportUnidentifiableError(); |
| } |
| |
| void PreParserFormalParameters::ValidateStrictMode(PreParser* preparser) const { |
| if (strict_parameter_error_) preparser->ReportUnidentifiableError(); |
| } |
| |
| PreParser::PreParseResult PreParser::PreParseFunction( |
| const AstRawString* function_name, FunctionKind kind, |
| FunctionSyntaxKind function_syntax_kind, DeclarationScope* function_scope, |
| int* use_counts, ProducedPreparseData** produced_preparse_data) { |
| DCHECK_EQ(FUNCTION_SCOPE, function_scope->scope_type()); |
| use_counts_ = use_counts; |
| #ifdef DEBUG |
| function_scope->set_is_being_lazily_parsed(true); |
| #endif |
| |
| PreParserFormalParameters formals(function_scope); |
| |
| // In the preparser, we use the function literal ids to count how many |
| // FunctionLiterals were encountered. The PreParser doesn't actually persist |
| // FunctionLiterals, so there IDs don't matter. |
| ResetFunctionLiteralId(); |
| |
| // The caller passes the function_scope which is not yet inserted into the |
| // scope stack. All scopes above the function_scope are ignored by the |
| // PreParser. |
| DCHECK_NULL(function_state_); |
| DCHECK_NULL(scope_); |
| FunctionState function_state(&function_state_, &scope_, function_scope); |
| |
| // Start collecting data for a new function which might contain skippable |
| // functions. |
| PreparseDataBuilder::DataGatheringScope preparse_data_builder_scope(this); |
| |
| if (IsArrowFunction(kind)) { |
| formals.is_simple = function_scope->has_simple_parameters(); |
| } else { |
| preparse_data_builder_scope.Start(function_scope); |
| |
| // Parse non-arrow function parameters. For arrow functions, the parameters |
| // have already been parsed. |
| ParameterDeclarationParsingScope formals_scope(this); |
| // We return kPreParseSuccess in failure cases too - errors are retrieved |
| // separately by Parser::SkipLazyFunctionBody. |
| ParseFormalParameterList(&formals); |
| if (formals_scope.has_duplicate()) formals.set_has_duplicate(); |
| if (!formals.is_simple) { |
| BuildParameterInitializationBlock(formals); |
| } |
| |
| Expect(Token::RPAREN); |
| int formals_end_position = scanner()->location().end_pos; |
| |
| CheckArityRestrictions(formals.arity, kind, formals.has_rest, |
| function_scope->start_position(), |
| formals_end_position); |
| } |
| |
| Expect(Token::LBRACE); |
| DeclarationScope* inner_scope = function_scope; |
| |
| if (!formals.is_simple) { |
| inner_scope = NewVarblockScope(); |
| inner_scope->set_start_position(position()); |
| } |
| |
| { |
| BlockState block_state(&scope_, inner_scope); |
| ParseStatementListAndLogFunction(&formals); |
| } |
| |
| bool allow_duplicate_parameters = false; |
| CheckConflictingVarDeclarations(inner_scope); |
| |
| if (!has_error()) { |
| if (formals.is_simple) { |
| if (is_sloppy(function_scope->language_mode())) { |
| function_scope->HoistSloppyBlockFunctions(nullptr); |
| } |
| |
| allow_duplicate_parameters = |
| is_sloppy(function_scope->language_mode()) && !IsConciseMethod(kind); |
| } else { |
| if (is_sloppy(inner_scope->language_mode())) { |
| inner_scope->HoistSloppyBlockFunctions(nullptr); |
| } |
| |
| SetLanguageMode(function_scope, inner_scope->language_mode()); |
| inner_scope->set_end_position(scanner()->peek_location().end_pos); |
| if (inner_scope->FinalizeBlockScope() != nullptr) { |
| const AstRawString* conflict = inner_scope->FindVariableDeclaredIn( |
| function_scope, VariableMode::kLastLexicalVariableMode); |
| if (conflict != nullptr) |
| ReportVarRedeclarationIn(conflict, inner_scope); |
| } |
| } |
| } |
| |
| use_counts_ = nullptr; |
| |
| if (stack_overflow()) { |
| return kPreParseStackOverflow; |
| } else if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { |
| return kPreParseNotIdentifiableError; |
| } else if (has_error()) { |
| DCHECK(pending_error_handler()->has_pending_error()); |
| } else { |
| DCHECK_EQ(Token::RBRACE, scanner()->peek()); |
| |
| if (!IsArrowFunction(kind)) { |
| // Validate parameter names. We can do this only after parsing the |
| // function, since the function can declare itself strict. |
| ValidateFormalParameters(language_mode(), formals, |
| allow_duplicate_parameters); |
| if (has_error()) { |
| if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { |
| return kPreParseNotIdentifiableError; |
| } else { |
| return kPreParseSuccess; |
| } |
| } |
| |
| // Declare arguments after parsing the function since lexical |
| // 'arguments' masks the arguments object. Declare arguments before |
| // declaring the function var since the arguments object masks 'function |
| // arguments'. |
| function_scope->DeclareArguments(ast_value_factory()); |
| |
| DeclareFunctionNameVar(function_name, function_syntax_kind, |
| function_scope); |
| |
| if (preparse_data_builder_->HasData()) { |
| *produced_preparse_data = |
| ProducedPreparseData::For(preparse_data_builder_, main_zone()); |
| } |
| } |
| |
| if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { |
| return kPreParseNotIdentifiableError; |
| } |
| |
| if (is_strict(function_scope->language_mode())) { |
| int end_pos = scanner()->location().end_pos; |
| CheckStrictOctalLiteral(function_scope->start_position(), end_pos); |
| } |
| } |
| |
| DCHECK(!pending_error_handler()->has_error_unidentifiable_by_preparser()); |
| return kPreParseSuccess; |
| } |
| |
| // Preparsing checks a JavaScript program and emits preparse-data that helps |
| // a later parsing to be faster. |
| // See preparser-data.h for the data. |
| |
| // The PreParser checks that the syntax follows the grammar for JavaScript, |
| // and collects some information about the program along the way. |
| // The grammar check is only performed in order to understand the program |
| // sufficiently to deduce some information about it, that can be used |
| // to speed up later parsing. Finding errors is not the goal of pre-parsing, |
| // rather it is to speed up properly written and correct programs. |
| // That means that contextual checks (like a label being declared where |
| // it is used) are generally omitted. |
| |
| PreParser::Expression PreParser::ParseFunctionLiteral( |
| Identifier function_name, Scanner::Location function_name_location, |
| FunctionNameValidity function_name_validity, FunctionKind kind, |
| int function_token_pos, FunctionSyntaxKind function_syntax_kind, |
| LanguageMode language_mode, |
| ZonePtrList<const AstRawString>* arguments_for_wrapped_function) { |
| FunctionParsingScope function_parsing_scope(this); |
| // Wrapped functions are not parsed in the preparser. |
| DCHECK_NULL(arguments_for_wrapped_function); |
| DCHECK_NE(FunctionSyntaxKind::kWrapped, function_syntax_kind); |
| // Function :: |
| // '(' FormalParameterList? ')' '{' FunctionBody '}' |
| RuntimeCallTimerScope runtime_timer( |
| runtime_call_stats_, |
| RuntimeCallCounterId::kPreParseWithVariableResolution, |
| RuntimeCallStats::kThreadSpecific); |
| |
| base::ElapsedTimer timer; |
| if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); |
| |
| DeclarationScope* function_scope = NewFunctionScope(kind); |
| function_scope->SetLanguageMode(language_mode); |
| int func_id = GetNextFunctionLiteralId(); |
| bool skippable_function = false; |
| |
| // Start collecting data for a new function which might contain skippable |
| // functions. |
| { |
| PreparseDataBuilder::DataGatheringScope preparse_data_builder_scope(this); |
| skippable_function = !function_state_->next_function_is_likely_called() && |
| preparse_data_builder_ != nullptr; |
| if (skippable_function) { |
| preparse_data_builder_scope.Start(function_scope); |
| } |
| |
| FunctionState function_state(&function_state_, &scope_, function_scope); |
| |
| Expect(Token::LPAREN); |
| int start_position = position(); |
| function_scope->set_start_position(start_position); |
| PreParserFormalParameters formals(function_scope); |
| { |
| ParameterDeclarationParsingScope formals_scope(this); |
| ParseFormalParameterList(&formals); |
| if (formals_scope.has_duplicate()) formals.set_has_duplicate(); |
| } |
| Expect(Token::RPAREN); |
| int formals_end_position = scanner()->location().end_pos; |
| |
| CheckArityRestrictions(formals.arity, kind, formals.has_rest, |
| start_position, formals_end_position); |
| |
| Expect(Token::LBRACE); |
| |
| // Parse function body. |
| PreParserScopedStatementList body(pointer_buffer()); |
| int pos = function_token_pos == kNoSourcePosition ? peek_position() |
| : function_token_pos; |
| AcceptINScope scope(this, true); |
| ParseFunctionBody(&body, function_name, pos, formals, kind, |
| function_syntax_kind, FunctionBodyType::kBlock); |
| |
| // Parsing the body may change the language mode in our scope. |
| language_mode = function_scope->language_mode(); |
| |
| // Validate name and parameter names. We can do this only after parsing the |
| // function, since the function can declare itself strict. |
| CheckFunctionName(language_mode, function_name, function_name_validity, |
| function_name_location); |
| |
| if (is_strict(language_mode)) { |
| CheckStrictOctalLiteral(start_position, end_position()); |
| } |
| if (skippable_function) { |
| preparse_data_builder_scope.SetSkippableFunction( |
| function_scope, formals.function_length, |
| GetLastFunctionLiteralId() - func_id); |
| } |
| } |
| |
| if (V8_UNLIKELY(FLAG_log_function_events)) { |
| double ms = timer.Elapsed().InMillisecondsF(); |
| const char* event_name = "preparse-resolution"; |
| // We might not always get a function name here. However, it can be easily |
| // reconstructed from the script id and the byte range in the log processor. |
| const char* name = ""; |
| size_t name_byte_length = 0; |
| bool is_one_byte = true; |
| const AstRawString* string = function_name.string_; |
| if (string != nullptr) { |
| name = reinterpret_cast<const char*>(string->raw_data()); |
| name_byte_length = string->byte_length(); |
| is_one_byte = string->is_one_byte(); |
| } |
| logger_->FunctionEvent( |
| event_name, flags().script_id(), ms, function_scope->start_position(), |
| function_scope->end_position(), name, name_byte_length, is_one_byte); |
| } |
| |
| return Expression::Default(); |
| } |
| |
| void PreParser::ParseStatementListAndLogFunction( |
| PreParserFormalParameters* formals) { |
| PreParserScopedStatementList body(pointer_buffer()); |
| ParseStatementList(&body, Token::RBRACE); |
| |
| // Position right after terminal '}'. |
| DCHECK_IMPLIES(!has_error(), scanner()->peek() == Token::RBRACE); |
| int body_end = scanner()->peek_location().end_pos; |
| DCHECK_EQ(this->scope()->is_function_scope(), formals->is_simple); |
| log_.LogFunction(body_end, formals->num_parameters(), |
| formals->function_length, GetLastFunctionLiteralId()); |
| } |
| |
| PreParserBlock PreParser::BuildParameterInitializationBlock( |
| const PreParserFormalParameters& parameters) { |
| DCHECK(!parameters.is_simple); |
| DCHECK(scope()->is_function_scope()); |
| if (scope()->AsDeclarationScope()->sloppy_eval_can_extend_vars() && |
| preparse_data_builder_ != nullptr) { |
| // We cannot replicate the Scope structure constructed by the Parser, |
| // because we've lost information whether each individual parameter was |
| // simple or not. Give up trying to produce data to skip inner functions. |
| if (preparse_data_builder_->parent() != nullptr) { |
| // Lazy parsing started before the current function; the function which |
| // cannot contain skippable functions is the parent function. (Its inner |
| // functions cannot either; they are implicitly bailed out.) |
| preparse_data_builder_->parent()->Bailout(); |
| } else { |
| // Lazy parsing started at the current function; it cannot contain |
| // skippable functions. |
| preparse_data_builder_->Bailout(); |
| } |
| } |
| |
| return PreParserBlock::Default(); |
| } |
| |
| bool PreParser::IdentifierEquals(const PreParserIdentifier& identifier, |
| const AstRawString* other) { |
| return identifier.string_ == other; |
| } |
| |
| } // namespace internal |
| } // namespace v8 |