Index: src/parsing/parser-base.h |
diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h |
index 96741fb4364a97f0639dd52f303f2a7651d6e5e5..caf020787ac142c603fa032dc2b77099662872eb 100644 |
--- a/src/parsing/parser-base.h |
+++ b/src/parsing/parser-base.h |
@@ -186,10 +186,14 @@ struct FormalParametersBase { |
// typedef ExpressionList; |
// typedef PropertyList; |
// typedef FormalParameters; |
+// typedef Statement; |
// typedef StatementList; |
// typedef Block; |
// // For constructing objects returned by the traversing functions. |
// typedef Factory; |
+// // For other implementation-specific tasks. |
+// typedef Target; |
+// typedef TargetScope; |
// }; |
template <typename Impl> |
@@ -207,6 +211,7 @@ class ParserBase { |
typedef typename Types::ExpressionList ExpressionListT; |
typedef typename Types::PropertyList PropertyListT; |
typedef typename Types::FormalParameters FormalParametersT; |
+ typedef typename Types::Statement StatementT; |
typedef typename Types::StatementList StatementListT; |
typedef typename Types::Block BlockT; |
typedef typename v8::internal::ExpressionClassifier<Types> |
@@ -916,6 +921,10 @@ class ParserBase { |
return scope()->GetReceiverScope(); |
} |
LanguageMode language_mode() { return scope()->language_mode(); } |
+ void RaiseLanguageMode(LanguageMode mode) { |
+ LanguageMode old = scope()->language_mode(); |
+ impl()->SetLanguageMode(scope(), old > mode ? old : mode); |
+ } |
bool is_generator() const { return function_state_->is_generator(); } |
bool is_async_function() const { |
return function_state_->is_async_function(); |
@@ -1072,7 +1081,12 @@ class ParserBase { |
classifier()->RecordArrowFormalParametersError(location, message, arg); |
} |
- // Recursive descent functions: |
+ // Recursive descent functions. |
+ // All ParseXXX functions take as the last argument an *ok parameter |
+ // which is set to false if parsing failed; it is unchanged otherwise. |
+ // By making the 'exception handling' explicit, we are forced to check |
+ // for failure at the call sites. The family of CHECK_OK* macros can |
+ // be useful for this. |
// Parses an identifier that is valid for the current scope, in particular it |
// fails on strict mode future reserved keywords in a strict scope. If |
@@ -1179,6 +1193,32 @@ class ParserBase { |
ZoneList<const AstRawString*>* names, |
bool* ok); |
+ // Under some circumstances, we allow preparsing to abort if the preparsed |
+ // function is "long and trivial", and fully parse instead. Our current |
+ // definition of "long and trivial" is: |
+ // - over kLazyParseTrialLimit statements |
+ // - all starting with an identifier (i.e., no if, for, while, etc.) |
+ static const int kLazyParseTrialLimit = 200; |
+ |
+ // TODO(nikolaos, marja): The first argument should not really be passed |
+ // by value. The method is expected to add the parsed statements to the |
+ // list. This works because in the case of the parser, StatementListT is |
+ // a pointer whereas the preparser does not really modify the body. |
+ V8_INLINE void ParseStatementList(StatementListT body, int end_token, |
+ bool* ok) { |
+ LazyParsingResult result = ParseStatementList(body, end_token, false, ok); |
+ USE(result); |
+ DCHECK_EQ(result, kLazyParsingComplete); |
+ } |
+ LazyParsingResult ParseStatementList(StatementListT body, int end_token, |
+ bool may_abort, bool* ok); |
+ StatementT ParseStatementListItem(bool* ok); |
+ StatementT ParseStatement(ZoneList<const AstRawString*>* labels, |
+ AllowLabelledFunctionStatement allow_function, |
+ bool* ok); |
+ StatementT ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels, |
+ bool* ok); |
+ |
bool IsNextLetKeyword(); |
bool IsTrivialExpression(); |
@@ -3913,6 +3953,263 @@ void ParserBase<Impl>::CheckDestructuringElement(ExpressionT expression, |
} |
} |
+// Redefinition of CHECK_OK for parsing statements. |
+#undef CHECK_OK |
+#define CHECK_OK CHECK_OK_CUSTOM(NullStatement) |
+ |
+template <typename Impl> |
+typename ParserBase<Impl>::LazyParsingResult |
+ParserBase<Impl>::ParseStatementList(StatementListT body, int end_token, |
+ bool may_abort, bool* ok) { |
+ // StatementList :: |
+ // (StatementListItem)* <end_token> |
+ |
+ // Allocate a target stack to use for this set of source |
+ // elements. This way, all scripts and functions get their own |
+ // target stack thus avoiding illegal breaks and continues across |
+ // functions. |
+ typename Types::TargetScope target_scope(this); |
+ int count_statements = 0; |
+ |
+ DCHECK(!impl()->IsNullStatementList(body)); |
+ bool directive_prologue = true; // Parsing directive prologue. |
+ |
+ while (peek() != end_token) { |
+ if (directive_prologue && peek() != Token::STRING) { |
+ directive_prologue = false; |
+ } |
+ |
+ bool starts_with_identifier = peek() == Token::IDENTIFIER; |
+ Scanner::Location token_loc = scanner()->peek_location(); |
+ StatementT stat = impl()->ParseStatementListItem( |
+ CHECK_OK_CUSTOM(Return, kLazyParsingComplete)); |
+ |
+ if (impl()->IsNullOrEmptyStatement(stat)) { |
+ directive_prologue = false; // End of directive prologue. |
+ continue; |
+ } |
+ |
+ if (directive_prologue) { |
+ // The length of the token is used to distinguish between strings literals |
+ // that evaluate equal to directives but contain either escape sequences |
+ // (e.g., "use \x73trict") or line continuations (e.g., "use \(newline) |
+ // strict"). |
+ if (impl()->IsUseStrictDirective(stat) && |
+ token_loc.end_pos - token_loc.beg_pos == sizeof("use strict") + 1) { |
+ // Directive "use strict" (ES5 14.1). |
+ RaiseLanguageMode(STRICT); |
+ if (!scope()->HasSimpleParameters()) { |
+ // TC39 deemed "use strict" directives to be an error when occurring |
+ // in the body of a function with non-simple parameter list, on |
+ // 29/7/2015. https://goo.gl/ueA7Ln |
+ impl()->ReportMessageAt( |
+ token_loc, MessageTemplate::kIllegalLanguageModeDirective, |
+ "use strict"); |
+ *ok = false; |
+ return kLazyParsingComplete; |
+ } |
+ // Because declarations in strict eval code don't leak into the scope |
+ // of the eval call, it is likely that functions declared in strict |
+ // eval code will be used within the eval code, so lazy parsing is |
+ // probably not a win. |
+ if (scope()->is_eval_scope()) mode_ = PARSE_EAGERLY; |
+ } else if (impl()->IsUseAsmDirective(stat) && |
+ token_loc.end_pos - token_loc.beg_pos == |
+ sizeof("use asm") + 1) { |
+ // Directive "use asm". |
+ impl()->SetAsmModule(); |
+ } else if (impl()->IsStringLiteral(stat)) { |
+ // Possibly an unknown directive. |
+ // TODO(nikolaos): Check if the following is really what we want! |
+ // """Should not change mode, but will increment UseCounter |
+ // if appropriate. Ditto usages below.""" ??? |
+ RaiseLanguageMode(SLOPPY); |
Dan Ehrenberg
2016/09/06 16:57:03
The comparison logic in RaiseLanguageMode which yo
|
+ } else { |
+ // End of the directive prologue. |
+ directive_prologue = false; |
+ // TODO(nikolaos): Check if the following is really what we want! |
+ RaiseLanguageMode(SLOPPY); |
+ } |
+ } else { |
+ // TODO(nikolaos): Check if the following is really what we want! |
+ RaiseLanguageMode(SLOPPY); |
+ } |
+ |
+ // If we're allowed to abort, we will do so when we see a "long and |
+ // trivial" function. Our current definition of "long and trivial" is: |
+ // - over kLazyParseTrialLimit statements |
+ // - all starting with an identifier (i.e., no if, for, while, etc.) |
+ if (may_abort) { |
+ if (!starts_with_identifier) { |
+ may_abort = false; |
+ } else if (++count_statements > kLazyParseTrialLimit) { |
+ return kLazyParsingAborted; |
+ } |
+ } |
+ |
+ body->Add(stat, zone()); |
+ } |
+ return kLazyParsingComplete; |
+} |
+ |
+template <typename Impl> |
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatementListItem( |
+ bool* ok) { |
+ // ECMA 262 6th Edition |
+ // StatementListItem[Yield, Return] : |
+ // Statement[?Yield, ?Return] |
+ // Declaration[?Yield] |
+ // |
+ // Declaration[Yield] : |
+ // HoistableDeclaration[?Yield] |
+ // ClassDeclaration[?Yield] |
+ // LexicalDeclaration[In, ?Yield] |
+ // |
+ // HoistableDeclaration[Yield, Default] : |
+ // FunctionDeclaration[?Yield, ?Default] |
+ // GeneratorDeclaration[?Yield, ?Default] |
+ // |
+ // LexicalDeclaration[In, Yield] : |
+ // LetOrConst BindingList[?In, ?Yield] ; |
+ |
+ switch (peek()) { |
+ case Token::FUNCTION: |
+ return impl()->ParseHoistableDeclaration(nullptr, false, ok); |
+ case Token::CLASS: |
+ Consume(Token::CLASS); |
+ return impl()->ParseClassDeclaration(nullptr, false, ok); |
+ case Token::VAR: |
+ case Token::CONST: |
+ return impl()->ParseVariableStatement(kStatementListItem, nullptr, ok); |
+ case Token::LET: |
+ if (IsNextLetKeyword()) { |
+ return impl()->ParseVariableStatement(kStatementListItem, nullptr, ok); |
+ } |
+ break; |
+ case Token::ASYNC: |
+ if (allow_harmony_async_await() && PeekAhead() == Token::FUNCTION && |
+ !scanner()->HasAnyLineTerminatorAfterNext()) { |
+ Consume(Token::ASYNC); |
+ return impl()->ParseAsyncFunctionDeclaration(nullptr, false, ok); |
+ } |
+ /* falls through */ |
+ default: |
+ break; |
+ } |
+ return impl()->ParseStatement(nullptr, kAllowLabelledFunctionStatement, ok); |
+} |
+ |
+template <typename Impl> |
+typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement( |
+ ZoneList<const AstRawString*>* labels, |
+ AllowLabelledFunctionStatement allow_function, bool* ok) { |
+ // Statement :: |
+ // Block |
+ // VariableStatement |
+ // EmptyStatement |
+ // ExpressionStatement |
+ // IfStatement |
+ // IterationStatement |
+ // ContinueStatement |
+ // BreakStatement |
+ // ReturnStatement |
+ // WithStatement |
+ // LabelledStatement |
+ // SwitchStatement |
+ // ThrowStatement |
+ // TryStatement |
+ // DebuggerStatement |
+ |
+ // Note: Since labels can only be used by 'break' and 'continue' |
+ // statements, which themselves are only valid within blocks, |
+ // iterations or 'switch' statements (i.e., BreakableStatements), |
+ // labels can be simply ignored in all other cases; except for |
+ // trivial labeled break statements 'label: break label' which is |
+ // parsed into an empty statement. |
+ switch (peek()) { |
+ case Token::LBRACE: |
+ return impl()->ParseBlock(labels, ok); |
+ case Token::SEMICOLON: |
+ Next(); |
+ return factory()->NewEmptyStatement(kNoSourcePosition); |
+ case Token::IF: |
+ return impl()->ParseIfStatement(labels, ok); |
+ case Token::DO: |
+ return impl()->ParseDoWhileStatement(labels, ok); |
+ case Token::WHILE: |
+ return impl()->ParseWhileStatement(labels, ok); |
+ case Token::FOR: |
+ return impl()->ParseForStatement(labels, ok); |
+ case Token::CONTINUE: |
+ case Token::BREAK: |
+ case Token::RETURN: |
+ case Token::THROW: |
+ case Token::TRY: { |
+ // These statements must have their labels preserved in an enclosing |
+ // block, as the corresponding AST nodes do not currently store their |
+ // labels. |
+ // TODO(nikolaos, marja): Consider adding the labels to the AST nodes. |
+ if (labels == nullptr) { |
+ return ParseStatementAsUnlabelled(labels, ok); |
+ } else { |
+ BlockT result = |
+ factory()->NewBlock(labels, 1, false, kNoSourcePosition); |
+ typename Types::Target target(this, result); |
+ StatementT statement = ParseStatementAsUnlabelled(labels, CHECK_OK); |
+ result->statements()->Add(statement, zone()); |
+ return result; |
+ } |
+ } |
+ case Token::WITH: |
+ return impl()->ParseWithStatement(labels, ok); |
+ case Token::SWITCH: |
+ return impl()->ParseSwitchStatement(labels, ok); |
+ case Token::FUNCTION: |
+ // FunctionDeclaration only allowed as a StatementListItem, not in |
+ // an arbitrary Statement position. Exceptions such as |
+ // ES#sec-functiondeclarations-in-ifstatement-statement-clauses |
+ // are handled by calling ParseScopedStatement rather than |
+ // ParseStatement directly. |
+ impl()->ReportMessageAt(scanner()->peek_location(), |
+ is_strict(language_mode()) |
+ ? MessageTemplate::kStrictFunction |
+ : MessageTemplate::kSloppyFunction); |
+ *ok = false; |
+ return impl()->NullStatement(); |
+ case Token::DEBUGGER: |
+ return impl()->ParseDebuggerStatement(ok); |
+ case Token::VAR: |
+ return impl()->ParseVariableStatement(kStatement, nullptr, ok); |
+ default: |
+ return impl()->ParseExpressionOrLabelledStatement(labels, allow_function, |
+ ok); |
+ } |
+} |
+ |
+// This method parses a subset of statements (break, continue, return, throw, |
+// try) which are to be grouped because they all require their labeles to be |
+// preserved in an enclosing block. |
+template <typename Impl> |
+typename ParserBase<Impl>::StatementT |
+ParserBase<Impl>::ParseStatementAsUnlabelled( |
+ ZoneList<const AstRawString*>* labels, bool* ok) { |
+ switch (peek()) { |
+ case Token::CONTINUE: |
+ return impl()->ParseContinueStatement(ok); |
+ case Token::BREAK: |
+ return impl()->ParseBreakStatement(labels, ok); |
+ case Token::RETURN: |
+ return impl()->ParseReturnStatement(ok); |
+ case Token::THROW: |
+ return impl()->ParseThrowStatement(ok); |
+ case Token::TRY: |
+ return impl()->ParseTryStatement(ok); |
+ default: |
+ UNREACHABLE(); |
+ return impl()->NullStatement(); |
+ } |
+} |
+ |
#undef CHECK_OK |
#undef CHECK_OK_CUSTOM |