Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index 8b7f04e430705086888d98a5ccd300c5af3059af..f638069697c7788d223fce7a67333dc4fe8ed309 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -125,6 +125,27 @@ class BoolScope : public ValueObject { |
}; |
+class RecursionChecker : public ValueObject { |
+ public: |
+ explicit RecursionChecker(Parser* p) : parser_(p) { |
+ parser_->recursion_counter_++; |
+ // No need to check the stack unless the parser is in an unusually deep |
+ // recurive state. Thus, we omit the more expensive stack checks in |
+ // the common case. |
+ const int kMaxUncheckedDepth = 100; // Somewhat arbitrary. |
+ if (parser_->recursion_counter_ > kMaxUncheckedDepth) { |
+ parser_->CheckStack(); |
+ } |
+ } |
+ ~RecursionChecker() { |
+ parser_->recursion_counter_--; |
+ } |
+ |
+ private: |
+ Parser* parser_; |
+}; |
+ |
+ |
static RawTypeArguments* NewTypeArguments( |
const GrowableArray<AbstractType*>& objs) { |
const TypeArguments& a = |
@@ -347,7 +368,8 @@ Parser::Parser(const Script& script, const Library& library, intptr_t token_pos) |
last_used_try_index_(0), |
unregister_pending_function_(false), |
async_temp_scope_(NULL), |
- trace_indent_(0) { |
+ trace_indent_(0), |
+ recursion_counter_(0) { |
ASSERT(tokens_iterator_.IsValid()); |
ASSERT(!library.IsNull()); |
} |
@@ -381,7 +403,8 @@ Parser::Parser(const Script& script, |
last_used_try_index_(0), |
unregister_pending_function_(false), |
async_temp_scope_(NULL), |
- trace_indent_(0) { |
+ trace_indent_(0), |
+ recursion_counter_(0) { |
ASSERT(tokens_iterator_.IsValid()); |
ASSERT(!current_function().IsNull()); |
EnsureExpressionTemp(); |
@@ -6112,6 +6135,17 @@ void Parser::ParseTopLevel() { |
} |
+void Parser::CheckStack() { |
+ volatile uword c_stack_pos = Isolate::GetCurrentStackPointer(); |
+ volatile uword c_stack_base = OSThread::Current()->stack_base(); |
+ volatile uword c_stack_limit = |
+ c_stack_base - OSThread::GetSpecifiedStackSize(); |
+ if ((c_stack_base > 0) && (c_stack_pos < c_stack_limit)) { |
rmacnak
2016/01/22 00:45:28
Add comment that c_stack_base > 0 handles the case
hausner
2016/01/22 18:38:14
Done.
|
+ ReportError("stack overflow while parsing"); |
+ } |
+} |
+ |
+ |
void Parser::ChainNewBlock(LocalScope* outer_scope) { |
Block* block = new(Z) Block( |
current_block_, |
@@ -8058,6 +8092,7 @@ void Parser::ParseStatementSequence() { |
TRACE_PARSER("ParseStatementSequence"); |
const bool dead_code_allowed = true; |
bool abrupt_completing_seen = false; |
+ RecursionChecker rc(this); |
while (CurrentToken() != Token::kRBRACE) { |
const intptr_t statement_pos = TokenPos(); |
AstNode* statement = ParseStatement(); |
@@ -8097,6 +8132,7 @@ SequenceNode* Parser::ParseNestedStatement(bool parsing_loop_body, |
ParseStatementSequence(); |
ExpectToken(Token::kRBRACE); |
} else { |
+ RecursionChecker rc(this); |
AstNode* statement = ParseStatement(); |
if (statement != NULL) { |
current_block_->statements->Add(statement); |
@@ -10835,6 +10871,8 @@ AstNode* Parser::ParseExpr(bool require_compiletime_const, |
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL; |
const intptr_t expr_pos = TokenPos(); |
+ RecursionChecker rc(this); |
+ |
if (CurrentToken() == Token::kTHROW) { |
if (require_compiletime_const) { |
ReportError("'throw expr' is not a valid compile-time constant"); |