Issue 25558. Report an error, but don't crash when AST it too deep.

R=brianwilkerson@google.com
BUG= https://github.com/dart-lang/sdk/issues/25558

Review-Url: https://codereview.chromium.org/2823993002 .
This commit is contained in:
Konstantin Shcheglov 2017-04-17 14:46:52 -07:00
parent 9ce608e89d
commit 62320cc838
5 changed files with 92 additions and 45 deletions

View file

@ -57,5 +57,6 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="application" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View file

@ -437,6 +437,7 @@ const List<ErrorCode> 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,

View file

@ -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'.",

View file

@ -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<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 (labels == null) {
labels = <Label>[label];
} else {
labels.add(label);
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) {
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 {}

View file

@ -14,8 +14,8 @@ regress_29025_test: StaticWarning # Issue 29081
# Runtime negative test. No static errors or warnings.
closure_call_wrong_argument_count_negative_test: skip
deep_nesting1_negative_test: Crash # Issue 25558
deep_nesting2_negative_test: Crash # Issue 25558
deep_nesting1_negative_test: CompileTimeError # Issue 25558
deep_nesting2_negative_test: CompileTimeError # Issue 25558
enum_syntax_test/05: Fail # 21649
enum_syntax_test/06: Fail # 21649