Index: pkg/analyzer/lib/src/generated/parser.dart |
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart |
index 33eba26d683875fa8ab9bdf729882c49f13f2e89..ea41c985f8a75d59658d1c4392f2600a62ac7507 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. |
*/ |
@@ -215,6 +217,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<Expression> cascadeSections = <Expression>[]; |
- 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<Expression> cascadeSections = <Expression>[]; |
+ 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<Label> labels = null; |
- while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) { |
- Label label = parseLabel(isDeclaration: true); |
+ if (_treeDepth > _MAX_TREE_DEPTH) { |
+ throw new _TooDeepTreeError(); |
+ } |
+ _treeDepth++; |
+ try { |
+ List<Label> labels = null; |
+ while ( |
+ _matchesIdentifier() && _currentToken.next.type == TokenType.COLON) { |
+ Label label = parseLabel(isDeclaration: true); |
+ if (labels == null) { |
+ labels = <Label>[label]; |
+ } else { |
+ labels.add(label); |
+ } |
+ } |
+ Statement statement = parseNonLabeledStatement(); |
if (labels == null) { |
- labels = <Label>[label]; |
- } else { |
- labels.add(label); |
+ return statement; |
} |
+ return astFactory.labeledStatement(labels, statement); |
+ } finally { |
+ _treeDepth--; |
} |
- Statement statement = parseNonLabeledStatement(); |
- if (labels == null) { |
- return statement; |
- } |
- return astFactory.labeledStatement(labels, statement); |
} |
/** |
@@ -8567,3 +8600,10 @@ class Parser { |
} |
} |
} |
+ |
+/** |
+ * Instances of this class are thrown when the parser detects that AST has |
+ * too many nested expressions to be parsed safely and avoid possibility of |
+ * [StackOverflowError] in the parser or during later analysis. |
+ */ |
+class _TooDeepTreeError {} |