diff --git a/pkg/analysis_server/analysis_server.iml b/pkg/analysis_server/analysis_server.iml
index ea34ce549c3..eeb9c7a6ff4 100644
--- a/pkg/analysis_server/analysis_server.iml
+++ b/pkg/analysis_server/analysis_server.iml
@@ -57,5 +57,6 @@
+
\ No newline at end of file
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 0b6aa79dde6..524f0cea7a4 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -437,6 +437,7 @@ const List errorCodeValues = const [
ParserErrorCode.REDIRECTING_CONSTRUCTOR_WITH_BODY,
ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR,
ParserErrorCode.SETTER_IN_FUNCTION,
+ ParserErrorCode.STACK_OVERFLOW,
ParserErrorCode.STATIC_AFTER_CONST,
ParserErrorCode.STATIC_AFTER_FINAL,
ParserErrorCode.STATIC_AFTER_VAR,
diff --git a/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart b/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
index c4b6f316e6d..35aab587527 100644
--- a/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
+++ b/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
@@ -834,6 +834,11 @@ class ParserErrorCode extends ErrorCode {
"Setters can't be defined within methods or functions.",
"Try moving the setter outside the method or function.");
+ static const ParserErrorCode STACK_OVERFLOW = const ParserErrorCode(
+ 'STACK_OVERFLOW',
+ "The file has too many nested expressions or statements.",
+ "Try simplifying the code.");
+
static const ParserErrorCode STATIC_AFTER_CONST = const ParserErrorCode(
'STATIC_AFTER_CONST',
"The modifier 'static' should be before the modifier 'const'.",
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index 33eba26d683..ea41c985f8a 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -170,6 +170,8 @@ class Parser {
static String _YIELD = Keyword.YIELD.syntax;
+ static const int _MAX_TREE_DEPTH = 300;
+
/**
* The source being parsed.
*/
@@ -214,6 +216,12 @@ class Parser {
*/
Token _currentToken;
+ /**
+ * The depth of the current AST. When this depth is too high, so we're at the
+ * risk of overflowing the stack, we stop parsing and report an error.
+ */
+ int _treeDepth = 0;
+
/**
* A flag indicating whether the parser is currently in a function body marked
* as being 'async'.
@@ -1960,8 +1968,16 @@ class Parser {
[_currentToken.lexeme]);
_advance();
} else {
- CompilationUnitMember member =
- parseCompilationUnitMember(commentAndMetadata);
+ CompilationUnitMember member;
+ try {
+ member = parseCompilationUnitMember(commentAndMetadata);
+ } on _TooDeepTreeError {
+ _reportErrorForToken(ParserErrorCode.STACK_OVERFLOW, _currentToken);
+ Token eof = new Token(TokenType.EOF, 0);
+ eof.previous = eof;
+ eof.setNext(eof);
+ return astFactory.compilationUnit(eof, null, null, null, eof);
+ }
if (member != null) {
declarations.add(member);
}
@@ -2712,37 +2728,45 @@ class Parser {
* | throwExpression
*/
Expression parseExpression2() {
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.THROW) {
- return parseThrowExpression();
- } else if (keyword == Keyword.RETHROW) {
- // TODO(brianwilkerson) Rethrow is a statement again.
- return parseRethrowExpression();
+ if (_treeDepth > _MAX_TREE_DEPTH) {
+ throw new _TooDeepTreeError();
}
- //
- // assignableExpression is a subset of conditionalExpression, so we can
- // parse a conditional expression and then determine whether it is followed
- // by an assignmentOperator, checking for conformance to the restricted
- // grammar after making that determination.
- //
- Expression expression = parseConditionalExpression();
- TokenType type = _currentToken.type;
- if (type == TokenType.PERIOD_PERIOD) {
- List cascadeSections = [];
- do {
- Expression section = parseCascadeSection();
- if (section != null) {
- cascadeSections.add(section);
- }
- } while (_currentToken.type == TokenType.PERIOD_PERIOD);
- return astFactory.cascadeExpression(expression, cascadeSections);
- } else if (type.isAssignmentOperator) {
- Token operator = getAndAdvance();
- _ensureAssignable(expression);
- return astFactory.assignmentExpression(
- expression, operator, parseExpression2());
+ _treeDepth++;
+ try {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.THROW) {
+ return parseThrowExpression();
+ } else if (keyword == Keyword.RETHROW) {
+ // TODO(brianwilkerson) Rethrow is a statement again.
+ return parseRethrowExpression();
+ }
+ //
+ // assignableExpression is a subset of conditionalExpression, so we can
+ // parse a conditional expression and then determine whether it is followed
+ // by an assignmentOperator, checking for conformance to the restricted
+ // grammar after making that determination.
+ //
+ Expression expression = parseConditionalExpression();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.PERIOD_PERIOD) {
+ List cascadeSections = [];
+ do {
+ Expression section = parseCascadeSection();
+ if (section != null) {
+ cascadeSections.add(section);
+ }
+ } while (_currentToken.type == TokenType.PERIOD_PERIOD);
+ return astFactory.cascadeExpression(expression, cascadeSections);
+ } else if (type.isAssignmentOperator) {
+ Token operator = getAndAdvance();
+ _ensureAssignable(expression);
+ return astFactory.assignmentExpression(
+ expression, operator, parseExpression2());
+ }
+ return expression;
+ } finally {
+ _treeDepth--;
}
- return expression;
}
/**
@@ -4795,20 +4819,29 @@ class Parser {
* label* nonLabeledStatement
*/
Statement parseStatement2() {
- List