| Index: runtime/vm/parser.cc
|
| diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
|
| index 1004d0d7db3c1b6178c1bcb9a53ab7314b0765b1..42af886a4cdc7251a6b185c8a4120db731473848 100644
|
| --- a/runtime/vm/parser.cc
|
| +++ b/runtime/vm/parser.cc
|
| @@ -127,6 +127,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 =
|
| @@ -349,7 +370,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());
|
| }
|
| @@ -383,7 +405,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();
|
| @@ -6047,6 +6070,18 @@ 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();
|
| + // Note: during early initialization the stack_base() can return 0.
|
| + if ((c_stack_base > 0) && (c_stack_pos < c_stack_limit)) {
|
| + ReportError("stack overflow while parsing");
|
| + }
|
| +}
|
| +
|
| +
|
| void Parser::ChainNewBlock(LocalScope* outer_scope) {
|
| Block* block = new(Z) Block(
|
| current_block_,
|
| @@ -7950,6 +7985,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();
|
| @@ -7989,6 +8025,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);
|
| @@ -10740,6 +10777,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");
|
|
|