// 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 #include "src/allocation.h" #include "src/base/logging.h" #include "src/conversions-inl.h" #include "src/conversions.h" #include "src/globals.h" #include "src/parsing/duplicate-finder.h" #include "src/parsing/parser-base.h" #include "src/parsing/preparsed-scope-data.h" #include "src/parsing/preparser.h" #include "src/unicode.h" #include "src/utils.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- // The CHECK_OK macro is a convenient macro to enforce error // handling for functions that may fail (by returning !*ok). // // CAUTION: This macro appends extra statements after a call, // thus it must never be used where only a single statement // is correct (e.g. an if statement branch w/o braces)! #define CHECK_OK_VALUE(x) ok); \ if (!*ok) return x; \ ((void)0 #define DUMMY ) // to make indentation work #undef DUMMY #define CHECK_OK CHECK_OK_VALUE(Expression::Default()) #define CHECK_OK_VOID CHECK_OK_VALUE(this->Void()) namespace { PreParserIdentifier GetSymbolHelper(Scanner* scanner) { // 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; } switch (scanner->current_contextual_token()) { case Token::CONSTRUCTOR: return PreParserIdentifier::Constructor(); case Token::NAME: return PreParserIdentifier::Name(); default: break; } if (scanner->literal_contains_escapes()) { return PreParserIdentifier::Default(); } switch (scanner->current_contextual_token()) { case Token::EVAL: return PreParserIdentifier::Eval(); case Token::ARGUMENTS: return PreParserIdentifier::Arguments(); default: break; } 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::PreParseProgram() { DCHECK_NULL(scope_); DeclarationScope* scope = NewScriptScope(); #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 (parsing_module_) scope = NewModuleScope(scope); FunctionState top_scope(&function_state_, &scope_, scope); original_scope_ = scope_; bool ok = true; int start_position = scanner()->peek_location().beg_pos; PreParserStatementList body; ParseStatementList(body, Token::EOS, &ok); original_scope_ = nullptr; if (stack_overflow()) return kPreParseStackOverflow; if (!ok) { ReportUnexpectedToken(scanner()->current_token()); } else if (is_strict(language_mode())) { CheckStrictOctalLiteral(start_position, scanner()->location().end_pos, &ok); } return kPreParseSuccess; } PreParser::PreParseResult PreParser::PreParseFunction( const AstRawString* function_name, FunctionKind kind, FunctionLiteral::FunctionType function_type, DeclarationScope* function_scope, bool is_inner_function, bool may_abort, int* use_counts, ProducedPreParsedScopeData** produced_preparsed_scope_data, int script_id) { DCHECK_EQ(FUNCTION_SCOPE, function_scope->scope_type()); use_counts_ = use_counts; DCHECK(!track_unresolved_variables_); track_unresolved_variables_ = is_inner_function; set_script_id(script_id); #ifdef DEBUG function_scope->set_is_being_lazily_parsed(true); #endif // Start collecting data for a new function which might contain skippable // functions. std::unique_ptr produced_preparsed_scope_data_scope; if (FLAG_preparser_scope_analysis && !IsArrowFunction(kind)) { track_unresolved_variables_ = true; produced_preparsed_scope_data_scope.reset( new ProducedPreParsedScopeData::DataGatheringScope(function_scope, this)); } // 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); // This indirection is needed so that we can use the CHECK_OK macros. bool ok_holder = true; bool* ok = &ok_holder; PreParserFormalParameters formals(function_scope); DuplicateFinder duplicate_finder; std::unique_ptr formals_classifier; // Parse non-arrow function parameters. For arrow functions, the parameters // have already been parsed. if (!IsArrowFunction(kind)) { formals_classifier.reset(new ExpressionClassifier(this, &duplicate_finder)); // We return kPreParseSuccess in failure cases too - errors are retrieved // separately by Parser::SkipLazyFunctionBody. ParseFormalParameterList(&formals, CHECK_OK_VALUE(kPreParseSuccess)); Expect(Token::RPAREN, CHECK_OK_VALUE(kPreParseSuccess)); int formals_end_position = scanner()->location().end_pos; CheckArityRestrictions( formals.arity, kind, formals.has_rest, function_scope->start_position(), formals_end_position, CHECK_OK_VALUE(kPreParseSuccess)); } Expect(Token::LBRACE, CHECK_OK_VALUE(kPreParseSuccess)); DeclarationScope* inner_scope = function_scope; LazyParsingResult result; if (!formals.is_simple) { inner_scope = NewVarblockScope(); inner_scope->set_start_position(scanner()->location().beg_pos); } { BlockState block_state(&scope_, inner_scope); result = ParseStatementListAndLogFunction(&formals, may_abort, ok); } if (!formals.is_simple) { BuildParameterInitializationBlock(formals, ok); 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); inner_scope->FinalizeBlockScope(); } else { if (is_sloppy(function_scope->language_mode())) { function_scope->HoistSloppyBlockFunctions(nullptr); } } if (!IsArrowFunction(kind) && track_unresolved_variables_ && result == kLazyParsingComplete) { // 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_type, function_scope); } use_counts_ = nullptr; track_unresolved_variables_ = false; if (result == kLazyParsingAborted) { return kPreParseAbort; } else if (stack_overflow()) { return kPreParseStackOverflow; } else if (!*ok) { 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. const bool allow_duplicate_parameters = is_sloppy(function_scope->language_mode()) && formals.is_simple && !IsConciseMethod(kind); ValidateFormalParameters(function_scope->language_mode(), allow_duplicate_parameters, CHECK_OK_VALUE(kPreParseSuccess)); *produced_preparsed_scope_data = produced_preparsed_scope_data_; } if (is_strict(function_scope->language_mode())) { int end_pos = scanner()->location().end_pos; CheckStrictOctalLiteral(function_scope->start_position(), end_pos, ok); } } 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, FunctionLiteral::FunctionType function_type, LanguageMode language_mode, ZonePtrList* arguments_for_wrapped_function, bool* ok) { // Wrapped functions are not parsed in the preparser. DCHECK_NULL(arguments_for_wrapped_function); DCHECK_NE(FunctionLiteral::kWrapped, function_type); // Function :: // '(' FormalParameterList? ')' '{' FunctionBody '}' const RuntimeCallCounterId counters[2][2] = { {RuntimeCallCounterId::kPreParseBackgroundNoVariableResolution, RuntimeCallCounterId::kPreParseNoVariableResolution}, {RuntimeCallCounterId::kPreParseBackgroundWithVariableResolution, RuntimeCallCounterId::kPreParseWithVariableResolution}}; RuntimeCallTimerScope runtime_timer( runtime_call_stats_, counters[track_unresolved_variables_][parsing_on_main_thread_]); base::ElapsedTimer timer; if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); DeclarationScope* function_scope = NewFunctionScope(kind); function_scope->SetLanguageMode(language_mode); // Start collecting data for a new function which might contain skippable // functions. std::unique_ptr produced_preparsed_scope_data_scope; if (!function_state_->next_function_is_likely_called() && produced_preparsed_scope_data_ != nullptr) { DCHECK(FLAG_preparser_scope_analysis); DCHECK(track_unresolved_variables_); produced_preparsed_scope_data_scope.reset( new ProducedPreParsedScopeData::DataGatheringScope(function_scope, this)); } FunctionState function_state(&function_state_, &scope_, function_scope); DuplicateFinder duplicate_finder; ExpressionClassifier formals_classifier(this, &duplicate_finder); int func_id = GetNextFunctionLiteralId(); Expect(Token::LPAREN, CHECK_OK); int start_position = scanner()->location().beg_pos; function_scope->set_start_position(start_position); PreParserFormalParameters formals(function_scope); ParseFormalParameterList(&formals, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); int formals_end_position = scanner()->location().end_pos; CheckArityRestrictions(formals.arity, kind, formals.has_rest, start_position, formals_end_position, CHECK_OK); Expect(Token::LBRACE, CHECK_OK); // Parse function body. PreParserStatementList body; int pos = function_token_pos == kNoSourcePosition ? peek_position() : function_token_pos; ParseFunctionBody(body, function_name, pos, formals, kind, function_type, CHECK_OK); // Parsing the body may change the language mode in our scope. language_mode = function_scope->language_mode(); if (is_sloppy(language_mode)) { function_scope->HoistSloppyBlockFunctions(nullptr); } // 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, CHECK_OK); const bool allow_duplicate_parameters = is_sloppy(language_mode) && formals.is_simple && !IsConciseMethod(kind); ValidateFormalParameters(language_mode, allow_duplicate_parameters, CHECK_OK); int end_position = scanner()->location().end_pos; if (is_strict(language_mode)) { CheckStrictOctalLiteral(start_position, end_position, CHECK_OK); } if (produced_preparsed_scope_data_scope) { produced_preparsed_scope_data_scope->MarkFunctionAsSkippable( end_position, GetLastFunctionLiteralId() - func_id); } if (V8_UNLIKELY(FLAG_log_function_events)) { double ms = timer.Elapsed().InMillisecondsF(); const char* event_name = track_unresolved_variables_ ? "preparse-resolution" : "preparse-no-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; const AstRawString* string = function_name.string_; if (string != nullptr) { name = reinterpret_cast(string->raw_data()); name_byte_length = string->byte_length(); } logger_->FunctionEvent( event_name, script_id(), ms, function_scope->start_position(), function_scope->end_position(), name, name_byte_length); } return Expression::Default(); } PreParser::LazyParsingResult PreParser::ParseStatementListAndLogFunction( PreParserFormalParameters* formals, bool may_abort, bool* ok) { PreParserStatementList body; LazyParsingResult result = ParseStatementList( body, Token::RBRACE, may_abort, CHECK_OK_VALUE(kLazyParsingComplete)); if (result == kLazyParsingAborted) return result; // Position right after terminal '}'. DCHECK_EQ(Token::RBRACE, scanner()->peek()); 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(), GetLastFunctionLiteralId()); return kLazyParsingComplete; } PreParserStatement PreParser::BuildParameterInitializationBlock( const PreParserFormalParameters& parameters, bool* ok) { DCHECK(!parameters.is_simple); DCHECK(scope()->is_function_scope()); if (FLAG_preparser_scope_analysis && scope()->AsDeclarationScope()->calls_sloppy_eval() && produced_preparsed_scope_data_ != 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 (produced_preparsed_scope_data_->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.) produced_preparsed_scope_data_->parent()->Bailout(); } else { // Lazy parsing started at the current function; it cannot contain // skippable functions. produced_preparsed_scope_data_->Bailout(); } } return PreParserStatement::Default(); } PreParserExpression PreParser::ExpressionFromIdentifier( const PreParserIdentifier& name, int start_position, InferName infer) { VariableProxy* proxy = nullptr; if (track_unresolved_variables_) { DCHECK_NOT_NULL(name.string_); proxy = scope()->NewUnresolved(factory()->ast_node_factory(), name.string_, start_position, NORMAL_VARIABLE); } return PreParserExpression::FromIdentifier(name, proxy, zone()); } void PreParser::DeclareAndInitializeVariables( PreParserStatement block, const DeclarationDescriptor* declaration_descriptor, const DeclarationParsingResult::Declaration* declaration, ZonePtrList* names, bool* ok) { if (declaration->pattern.variables_ != nullptr) { DCHECK(FLAG_lazy_inner_functions); DCHECK(track_unresolved_variables_); for (auto variable : *(declaration->pattern.variables_)) { declaration_descriptor->scope->RemoveUnresolved(variable); Variable* var = scope()->DeclareVariableName( variable->raw_name(), declaration_descriptor->mode); if (FLAG_preparser_scope_analysis) { MarkLoopVariableAsAssigned(declaration_descriptor->scope, var, declaration_descriptor->declaration_kind); // This is only necessary if there is an initializer, but we don't have // that information here. Consequently, the preparser sometimes says // maybe-assigned where the parser (correctly) says never-assigned. } if (names) { names->Add(variable->raw_name(), zone()); } } } } #undef CHECK_OK #undef CHECK_OK_CUSTOM } // namespace internal } // namespace v8