diff options
-rw-r--r-- | src/libs/3rdparty/cplusplus/Parser.cpp | 130 | ||||
-rw-r--r-- | src/libs/3rdparty/cplusplus/Parser.h | 4 | ||||
-rw-r--r-- | tests/auto/cplusplus/ast/tst_ast.cpp | 41 |
3 files changed, 167 insertions, 8 deletions
diff --git a/src/libs/3rdparty/cplusplus/Parser.cpp b/src/libs/3rdparty/cplusplus/Parser.cpp index 7ccc85c940d..a8ee2d9fbdc 100644 --- a/src/libs/3rdparty/cplusplus/Parser.cpp +++ b/src/libs/3rdparty/cplusplus/Parser.cpp @@ -26,8 +26,13 @@ #include "Literals.h" #include "ObjectiveCTypeQualifiers.h" #include "QtContextKeywords.h" + +#include <unordered_map> +#include <utility> + #include <string> #include <cstdio> // for putchar + #if defined(_MSC_VER) && (_MSC_VER < 1800) # define va_copy(dst, src) ((dst) = (src)) #elif defined(__INTEL_COMPILER) && !defined(va_copy) @@ -43,9 +48,9 @@ using namespace CPlusPlus; namespace { class DebugRule { +public: static int depth; -public: DebugRule(const char *name, const char *spell, unsigned idx, bool blocked) { for (int i = 0; i <= depth; ++i) @@ -150,12 +155,93 @@ inline bool isRightAssociative(int tokenKind) } // end of anonymous namespace +class Parser::ASTCache +{ + ASTCache(const ASTCache &other); + void operator =(const ASTCache &other); + +public: + enum ASTKind { + Expression, + ExpressionList, + ParameterDeclarationClause, + TypeId + }; + +public: + ASTCache() {} + + void insert(ASTKind astKind, unsigned tokenIndexBeforeParsing, + AST *resultingAST, unsigned resultingTokenIndex) + { + const auto key = std::make_pair(astKind, tokenIndexBeforeParsing); + const auto value = std::make_pair(resultingAST, resultingTokenIndex); + const auto keyValue = std::make_pair(key, value); + _cache.insert(keyValue); + } + + AST *find(ASTKind astKind, unsigned tokenIndex, + unsigned *resultingTokenIndex, bool *foundInCache) const + { + const auto key = std::make_pair(astKind, tokenIndex); + const auto it = _cache.find(key); + if (it == _cache.end()) { + *foundInCache = false; + return 0; + } else { + *foundInCache = true; + *resultingTokenIndex = it->second.second; + return it->second.first; + } + } + + void clear() + { + _cache.clear(); + } + +private: + struct KeyHasher { + size_t operator()(const std::pair<int, unsigned> &key) const + { return std::hash<int>()(key.first) ^ std::hash<unsigned>()(key.second); } + }; + + typedef std::pair<int, unsigned> ASTKindAndTokenIndex; + typedef std::pair<AST *, unsigned> ASTAndTokenIndex; + std::unordered_map<ASTKindAndTokenIndex, ASTAndTokenIndex, KeyHasher> _cache; +}; + #ifndef CPLUSPLUS_NO_DEBUG_RULE # define DEBUG_THIS_RULE() DebugRule __debug_rule__(__func__, tok().spell(), cursor(), _translationUnit->blockErrors()) +inline void debugPrintCheckCache(bool goodCase) +{ + for (int i = 0; i <= DebugRule::depth - 1; ++i) + fputc('-', stderr); + if (goodCase) + fprintf(stderr, " CACHE: Re-using AST from Cache.\n"); + else + fprintf(stderr, " CACHE: Already tried to parse this, skipping.\n"); +} #else # define DEBUG_THIS_RULE() do {} while (0) +inline void debugPrintCheckCache(bool) {} #endif +#define CHECK_CACHE(ASTKind, ASTType, returnValueInBadCase) \ + do { \ + bool foundInCache; \ + unsigned newTokenIndex; \ + if (AST *ast = _astCache->find(ASTKind, cursor(), &newTokenIndex, &foundInCache)) { \ + debugPrintCheckCache(true); \ + node = (ASTType *) ast; \ + _tokenIndex = newTokenIndex; \ + return true; \ + } else if (foundInCache) { \ + debugPrintCheckCache(false); \ + return returnValueInBadCase; \ + } \ + } while (0) + #define PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, minPrecedence) { \ if (LA() == T_THROW) { \ if (!parseThrowExpression(node)) \ @@ -177,11 +263,16 @@ Parser::Parser(TranslationUnit *unit) _inFunctionBody(false), _inExpressionStatement(false), _expressionDepth(0), - _statementDepth(0) + _statementDepth(0), + _astCache(new ASTCache), + _expressionStatementAstCache(new ASTCache) { } Parser::~Parser() -{ } +{ + delete _expressionStatementAstCache; + delete _astCache; +} bool Parser::switchTemplateArguments(bool templateArguments) { @@ -1841,6 +1932,8 @@ bool Parser::parseTypeParameter(DeclarationAST *&node) bool Parser::parseTypeId(ExpressionAST *&node) { DEBUG_THIS_RULE(); + CHECK_CACHE(ASTCache::TypeId, ExpressionAST, false); + SpecifierListAST *type_specifier = 0; if (parseTypeSpecifier(type_specifier)) { TypeIdAST *ast = new (_pool) TypeIdAST; @@ -1857,6 +1950,8 @@ bool Parser::parseParameterDeclarationClause(ParameterDeclarationClauseAST *&nod DEBUG_THIS_RULE(); if (LA() == T_RPAREN) return true; // nothing to do + CHECK_CACHE(ASTCache::ParameterDeclarationClause, ParameterDeclarationClauseAST, true); + const unsigned initialCursor = cursor(); ParameterDeclarationListAST *parameter_declarations = 0; @@ -1881,6 +1976,7 @@ bool Parser::parseParameterDeclarationClause(ParameterDeclarationClauseAST *&nod node = ast; } + _astCache->insert(ASTCache::ParameterDeclarationClause, initialCursor, node, cursor()); return true; } @@ -2797,9 +2893,14 @@ bool Parser::parseTypeIdList(ExpressionListAST *&node) bool Parser::parseExpressionList(ExpressionListAST *&node) { DEBUG_THIS_RULE(); + CHECK_CACHE(ASTCache::ExpressionList, ExpressionListAST, false); + unsigned initialCursor = cursor(); - if (_languageFeatures.cxx11Enabled) - return parseInitializerList0x(node); + if (_languageFeatures.cxx11Enabled) { + bool result = parseInitializerList0x(node); + _astCache->insert(ASTCache::ExpressionList, initialCursor, (AST *) node, cursor()); + return result; + } ExpressionListAST **expression_list_ptr = &node; ExpressionAST *expression = 0; @@ -2816,9 +2917,11 @@ bool Parser::parseExpressionList(ExpressionListAST *&node) expression_list_ptr = &(*expression_list_ptr)->next; } } + _astCache->insert(ASTCache::ExpressionList, initialCursor, (AST *) node, cursor()); return true; } + _astCache->insert(ASTCache::ExpressionList, initialCursor, 0, cursor()); return false; } @@ -2981,9 +3084,11 @@ bool Parser::parseExpressionStatement(StatementAST *&node) const bool wasInExpressionStatement = _inExpressionStatement; _inExpressionStatement = true; - // switch to the temp pool + // switch to the temp pool and cache MemoryPool *previousPool = _pool; _pool = &_expressionStatementTempPool; + ASTCache *previousASTCache = _astCache; + _astCache = _expressionStatementAstCache; bool parsed = false; @@ -3000,12 +3105,15 @@ bool Parser::parseExpressionStatement(StatementAST *&node) _inExpressionStatement = wasInExpressionStatement; if (! _inExpressionStatement) { - // rewind the memory pool after parsing a toplevel expression statement. + // rewind the memory pool and cache after parsing a toplevel expression statement. _expressionStatementTempPool.reset(); + _astCache->clear(); } - // restore the pool + // restore the pool and cache _pool = previousPool; + _astCache = previousASTCache; + return parsed; } @@ -5246,6 +5354,7 @@ bool Parser::parseCastExpression(ExpressionAST *&node) DEBUG_THIS_RULE(); if (LA() == T_LPAREN) { unsigned lparen_token = consumeToken(); + unsigned initialCursor = cursor(); ExpressionAST *type_id = 0; if (parseTypeId(type_id) && LA() == T_RPAREN) { @@ -5300,6 +5409,7 @@ bool Parser::parseCastExpression(ExpressionAST *&node) } parse_as_unary_expression: + _astCache->insert(ASTCache::TypeId, initialCursor, 0, cursor()); rewind(lparen_token); } @@ -5400,6 +5510,8 @@ bool Parser::parseConstantExpression(ExpressionAST *&node) bool Parser::parseExpression(ExpressionAST *&node) { DEBUG_THIS_RULE(); + CHECK_CACHE(ASTCache::Expression, ExpressionAST, false); + unsigned initialCursor = cursor(); if (_expressionDepth > MAX_EXPRESSION_DEPTH) return false; @@ -5407,6 +5519,8 @@ bool Parser::parseExpression(ExpressionAST *&node) ++_expressionDepth; bool success = parseCommaExpression(node); --_expressionDepth; + + _astCache->insert(ASTCache::Expression, initialCursor, node, cursor()); return success; } diff --git a/src/libs/3rdparty/cplusplus/Parser.h b/src/libs/3rdparty/cplusplus/Parser.h index 87007c8f753..4a2a6dfb7e0 100644 --- a/src/libs/3rdparty/cplusplus/Parser.h +++ b/src/libs/3rdparty/cplusplus/Parser.h @@ -325,6 +325,10 @@ private: MemoryPool _expressionStatementTempPool; std::map<unsigned, TemplateArgumentListEntry> _templateArgumentList; + class ASTCache; + ASTCache *_astCache; + ASTCache *_expressionStatementAstCache; + private: Parser(const Parser& source); void operator =(const Parser& source); diff --git a/tests/auto/cplusplus/ast/tst_ast.cpp b/tests/auto/cplusplus/ast/tst_ast.cpp index 56371e477bc..ebd9e468e14 100644 --- a/tests/auto/cplusplus/ast/tst_ast.cpp +++ b/tests/auto/cplusplus/ast/tst_ast.cpp @@ -193,6 +193,7 @@ private slots: void incomplete_ast(); void unnamed_class(); void unnamed_class_data(); + void expensiveExpression(); }; void tst_AST::gcc_attributes_1() @@ -1790,6 +1791,46 @@ void tst_AST::unnamed_class_data() QTest::newRow("unnamed-__attribute__") << _("class __attribute__((aligned(8))){};"); } +void tst_AST::expensiveExpression() +{ + QSharedPointer<TranslationUnit> unit(parseStatement( + "void f()\n" + "{\n" + "(g1\n" + "(g2\n" + "(g3\n" + "(g4\n" + "(g5\n" + "(g6\n" + "(g7\n" + "(g8\n" + "(g9\n" + "(g10\n" + "(g11\n" + "(g12\n" + "(g13\n" + "(g14\n" + "(g15\n" + "(g16\n" + "(g17\n" + "(g18\n" + "(g19\n" + "(g20\n" + "(g21\n" + "(g22\n" + "(g23\n" + "(g24\n" + "(g25\n" + "(g26\n" + "(g27\n" + "(g28\n" + "(g29\n" + "(g30\n" + "(g31(0))))))))))))))))))))))))))))))));\n" + "}\n")); + QVERIFY(unit->ast()); +} + void tst_AST::initTestCase() { control.setDiagnosticClient(&diag); |