forked from qt-creator/qt-creator
C++: Cache parsing of template ids
...in order to stop memory intensive parsing for invalid code. Parsing the test data/snippet "hard" led to a memory consumption of about 5.5MB and this could easily get up to hundreds/gigabytes by adding some more "if_<bool_<true>,\n" lines. With the caching, we are at about 1.0MB, even if more lines are added. The "memory consumption" was measured with valgrind-massif. The stated numbers are the reported peaks. Task-number: QTCREATORBUG-12890 Change-Id: Ie7eb00cfc7915552d29bb27410a6b13a486f486e Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
This commit is contained in:
11
src/libs/3rdparty/cplusplus/Parser.cpp
vendored
11
src/libs/3rdparty/cplusplus/Parser.cpp
vendored
@@ -165,6 +165,7 @@ public:
|
|||||||
Expression,
|
Expression,
|
||||||
ExpressionList,
|
ExpressionList,
|
||||||
ParameterDeclarationClause,
|
ParameterDeclarationClause,
|
||||||
|
TemplateId,
|
||||||
TypeId
|
TypeId
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -510,6 +511,7 @@ bool Parser::parseClassOrNamespaceName(NameAST *&node)
|
|||||||
bool Parser::parseTemplateId(NameAST *&node, unsigned template_token)
|
bool Parser::parseTemplateId(NameAST *&node, unsigned template_token)
|
||||||
{
|
{
|
||||||
DEBUG_THIS_RULE();
|
DEBUG_THIS_RULE();
|
||||||
|
CHECK_CACHE(ASTCache::TemplateId, NameAST);
|
||||||
|
|
||||||
const unsigned start = cursor();
|
const unsigned start = cursor();
|
||||||
|
|
||||||
@@ -523,14 +525,17 @@ bool Parser::parseTemplateId(NameAST *&node, unsigned template_token)
|
|||||||
if (maybeSplitGreaterGreaterToken() || LA() == T_GREATER) {
|
if (maybeSplitGreaterGreaterToken() || LA() == T_GREATER) {
|
||||||
ast->greater_token = consumeToken();
|
ast->greater_token = consumeToken();
|
||||||
node = ast;
|
node = ast;
|
||||||
return true;
|
const bool result = true;
|
||||||
|
_astCache->insert(ASTCache::TemplateId, start, node, cursor(), result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool result = false;
|
||||||
|
_astCache->insert(ASTCache::TemplateId, start, 0, cursor(), result);
|
||||||
rewind(start);
|
rewind(start);
|
||||||
|
return result;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::parseNestedNameSpecifier(NestedNameSpecifierListAST *&node,
|
bool Parser::parseNestedNameSpecifier(NestedNameSpecifierListAST *&node,
|
||||||
|
@@ -199,6 +199,7 @@ private slots:
|
|||||||
void unnamed_class();
|
void unnamed_class();
|
||||||
void unnamed_class_data();
|
void unnamed_class_data();
|
||||||
void expensiveExpression();
|
void expensiveExpression();
|
||||||
|
void invalidCode_data();
|
||||||
void invalidCode();
|
void invalidCode();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1857,13 +1858,66 @@ void tst_AST::expensiveExpression()
|
|||||||
QVERIFY(unit->ast());
|
QVERIFY(unit->ast());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_AST::invalidCode_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("source");
|
||||||
|
|
||||||
|
typedef QByteArray _;
|
||||||
|
QTest::newRow("simple") <<
|
||||||
|
_("static inValidLine()\n");
|
||||||
|
|
||||||
|
QTest::newRow("hard") <<
|
||||||
|
_("typedef\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true>,\n"
|
||||||
|
"if_<bool_<true {}\n");
|
||||||
|
}
|
||||||
|
|
||||||
void tst_AST::invalidCode()
|
void tst_AST::invalidCode()
|
||||||
{
|
{
|
||||||
const QByteArray invalidCode = "static inValidLine()\n"
|
QFETCH(QByteArray, source);
|
||||||
"class Foo {};\n";
|
|
||||||
|
|
||||||
QSharedPointer<TranslationUnit> unit(parse(invalidCode, TranslationUnit::ParseTranlationUnit,
|
source += "\nclass Foo {};\n";
|
||||||
false, false, false));
|
const QSharedPointer<TranslationUnit> unit(parse(source, TranslationUnit::ParseTranlationUnit,
|
||||||
|
false, false, true));
|
||||||
|
|
||||||
|
// Check that we find the class coming after the invalid garbage.
|
||||||
QVERIFY(unit->ast());
|
QVERIFY(unit->ast());
|
||||||
TranslationUnitAST *unitAST = unit->ast()->asTranslationUnit();
|
TranslationUnitAST *unitAST = unit->ast()->asTranslationUnit();
|
||||||
QVERIFY(unitAST->declaration_list);
|
QVERIFY(unitAST->declaration_list);
|
||||||
|
Reference in New Issue
Block a user