Commit 96368496 authored by Christian Kandeler's avatar Christian Kandeler

Debugger: Refactor name demangler.

Now all the parsing logic as well as the stringification is in the tree
nodes.

Change-Id: Ie8222729c14c0102d94045026fc61a75cd31cc63
Reviewed-by: default avatarChristian Kandeler <christian.kandeler@nokia.com>
parent 91d22556
......@@ -265,7 +265,9 @@ QtcPlugin {
"namedemangler/namedemangler.h",
"namedemangler/parsetreenodes.cpp",
"namedemangler/parsetreenodes.h",
"namedemangler/demanglerexceptions.h"
"namedemangler/demanglerexceptions.h",
"namedemangler/globalparsestate.h",
"namedemangler/globalparsestate.cpp"
]
Group {
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "globalparsestate.h"
#include "demanglerexceptions.h"
#include "parsetreenodes.h"
namespace Debugger {
namespace Internal {
char GlobalParseState::peek(int ahead)
{
Q_ASSERT(m_pos >= 0);
if (m_pos + ahead < m_mangledName.size())
return m_mangledName[m_pos + ahead];
return eoi;
}
char GlobalParseState::advance(int steps)
{
Q_ASSERT(steps > 0);
if (m_pos + steps > m_mangledName.size())
throw ParseException(QLatin1String("Unexpected end of input"));
const char c = m_mangledName[m_pos];
m_pos += steps;
return c;
}
QByteArray GlobalParseState::readAhead(int charCount) const
{
QByteArray str;
if (m_pos + charCount <= m_mangledName.size())
str = m_mangledName.mid(m_pos, charCount);
else
str.fill(eoi, charCount);
return str;
}
void GlobalParseState::addSubstitution(const ParseTreeNode *node)
{
const QByteArray symbol = node->toByteArray();
if (!symbol.isEmpty() && !m_substitutions.contains(symbol))
m_substitutions.append(symbol);
}
} // namespace Internal
} // namespace Debugger
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#ifndef GLOBAL_PARSE_STATE_H
#define GLOBAL_PARSE_STATE_H
#include <QByteArray>
#include <QStack>
namespace Debugger {
namespace Internal {
class NameDemanglerPrivate;
class ParseTreeNode;
class GlobalParseState
{
friend class NameDemanglerPrivate;
public:
char peek(int ahead = 0);
char advance(int steps = 1);
QByteArray readAhead(int charCount) const;
int stackElementCount() const { return m_parseStack.count(); }
ParseTreeNode *stackTop() const { return m_parseStack.top(); }
ParseTreeNode *stackElementAt(int index) const { return m_parseStack.at(index); }
void pushToStack(ParseTreeNode *node) { m_parseStack.push(node); }
ParseTreeNode *popFromStack() { return m_parseStack.pop(); }
int substitutionCount() const { return m_substitutions.count(); }
QByteArray substitutionAt(int index) const { return m_substitutions.at(index); }
void addSubstitution(const ParseTreeNode *node);
int templateParamCount() const { return m_templateParams.count(); }
ParseTreeNode *templateParamAt(int index) const { return m_templateParams.at(index); }
void addTemplateParam(ParseTreeNode *node) { m_templateParams << node; }
void clearTemplateParams() { m_templateParams.clear(); }
// TODO: Can we get rid of this by analyzing the stack?
bool isConversionOperator() const { return m_isConversionOperator; }
void setIsConversionOperator(bool is) { m_isConversionOperator = is; }
private:
int m_pos;
QByteArray m_mangledName;
QList<QByteArray> m_substitutions;
QList<ParseTreeNode *> m_templateParams;
bool m_isConversionOperator;
QStack<ParseTreeNode *> m_parseStack;
static const char eoi = '$';
};
} // namespace Internal
} // namespace Debugger
#endif // GLOBAL_PARSE_STATE_H
......@@ -35,36 +35,8 @@
#include "demanglerexceptions.h"
#include "parsetreenodes.h"
#include <QByteArray>
#include <QList>
#include <QMap>
#include <QRegExp>
#include <QStack>
#include <QString>
#include <cctype>
#include <limits>
#define PARSE_RULE_AND_ADD_RESULT_AS_CHILD(rule, parentNode) \
do { \
parse##rule(); \
DEMANGLER_ASSERT(!m_parseStack.isEmpty()); \
DEMANGLER_ASSERT(dynamic_cast<rule##Node *>(m_parseStack.top())); \
popNodeFromStackAndAddAsChild(parentNode); \
} while (0)
// Debugging facility.
//#define DO_TRACE
#ifdef DO_TRACE
#define FUNC_START() \
qDebug("Function %s has started, input is at position %d.", Q_FUNC_INFO, m_pos)
#define FUNC_END() \
qDebug("Function %s has finished, input is at position %d..", m_pos)
#else
#define FUNC_START()
#define FUNC_END()
#endif // DO_TRACE
namespace Debugger {
namespace Internal {
......@@ -76,74 +48,9 @@ public:
const QString &demangledName() const { return m_demangledName; }
private:
char peek(int ahead = 0);
char advance(int steps = 1);
const QByteArray readAhead(int charCount);
void addSubstitution(const ParseTreeNode *node);
// One parse function per Non-terminal.
void parseArrayType();
void parseBareFunctionType();
void parseBuiltinType();
void parseCallOffset();
void parseClassEnumType();
void parseCtorDtorName();
void parseCvQualifiers();
void parseDigit();
void parseDiscriminator();
void parseEncoding();
void parseExpression();
void parseExprPrimary();
void parseFloatValue();
void parseFunctionType();
void parseLocalName();
void parseMangledName();
void parseName();
void parseNestedName();
void parseNonNegativeNumber(int base = 10);
void parseNumber(int base = 10);
void parseNvOffset();
void parseOperatorName();
void parsePointerToMemberType();
void parsePrefix();
void parsePrefix2();
void parseSpecialName();
void parseSourceName();
void parseSubstitution();
void parseTemplateArg();
void parseTemplateArgs();
int parseTemplateParam();
void parseType();
void parseUnqualifiedName();
void parseUnscopedName();
void parseVOffset();
const QByteArray getIdentifier(int len);
int getNonNegativeNumber(int base = 10);
template <typename T> T *allocateNodeAndAddToStack()
{
T * const node = new T;
m_parseStack.push(node);
return node;
}
void popNodeFromStackAndAddAsChild(ParseTreeNode *parentNode)
{
parentNode->addChild(m_parseStack.pop());
}
static const char eoi = '$';
int m_pos;
QByteArray m_mangledName;
GlobalParseState m_parseState;
QString m_errorString;
QString m_demangledName;
QList<QByteArray> m_substitutions;
QList<ParseTreeNode *> m_templateParams;
bool m_isConversionOperator;
QStack<ParseTreeNode *> m_parseStack;
};
......@@ -151,29 +58,28 @@ bool NameDemanglerPrivate::demangle(const QString &mangledName)
{
bool success;
try {
m_mangledName = mangledName.toAscii();
m_pos = 0;
m_isConversionOperator = false;
m_parseState.m_mangledName = mangledName.toAscii();
m_parseState.m_pos = 0;
m_parseState.m_isConversionOperator = false;
m_demangledName.clear();
if (!MangledNameNode::mangledRepresentationStartsWith(peek())) {
m_demangledName = m_mangledName;
if (!MangledNameNode::mangledRepresentationStartsWith(m_parseState.peek())) {
m_demangledName = m_parseState.m_mangledName;
return true;
}
parseMangledName();
if (m_pos != m_mangledName.size())
ParseTreeNode::parseRule<MangledNameNode>(&m_parseState);
if (m_parseState.m_pos != m_parseState.m_mangledName.size())
throw ParseException(QLatin1String("Unconsumed input"));
if (m_parseStack.count() != 1) {
if (m_parseState.m_parseStack.count() != 1) {
throw ParseException(QString::fromLocal8Bit("There are %1 elements on the parse stack; "
"expected one.").arg(m_parseStack.count()));
"expected one.").arg(m_parseState.m_parseStack.count()));
}
m_demangledName = m_parseStack.top()->toByteArray();
m_demangledName = m_parseState.m_parseStack.top()->toByteArray();
success = true;
} catch (const ParseException &p) {
m_errorString = QString::fromLocal8Bit("Parse error at index %1 of mangled name '%2': %3.")
.arg(m_pos).arg(mangledName, p.error);
.arg(m_parseState.m_pos).arg(mangledName, p.error);
success = false;
} catch (const InternalDemanglerException &e) {
m_errorString = QString::fromLocal8Bit("Internal demangler error at function %1, file %2, "
......@@ -181,1547 +87,11 @@ bool NameDemanglerPrivate::demangle(const QString &mangledName)
success = false;
}
qDeleteAll(m_parseStack);
m_parseStack.clear();
m_substitutions.clear();
m_templateParams.clear();
return success;
}
/*
* Grammar: http://www.codesourcery.com/public/cxx-abi/abi.html#mangling
* The grammar as given there is not LL(k), so a number of transformations
* were necessary, which we will document at the respective parsing function.
* <mangled-name> ::= _Z <encoding>
*/
void NameDemanglerPrivate::parseMangledName()
{
FUNC_START();
MangledNameNode * const node = allocateNodeAndAddToStack<MangledNameNode>();
advance(2);
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(Encoding, node);
FUNC_END();
}
/*
* <encoding> ::= <name> <bare-function-type>
* ::= <name>
* ::= <special-name>
*/
void NameDemanglerPrivate::parseEncoding()
{
FUNC_START();
EncodingNode * const encodingNode = allocateNodeAndAddToStack<EncodingNode>();
const char next = peek();
if (NameNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(Name, encodingNode);
if (BareFunctionTypeNode::mangledRepresentationStartsWith(peek()))
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(BareFunctionType, encodingNode);
addSubstitution(encodingNode);
m_templateParams.clear();
m_isConversionOperator = false;
} else if (SpecialNameNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(SpecialName, encodingNode);
} else {
throw ParseException(QString::fromLatin1("Invalid encoding"));
}
FUNC_END();
}
/*
* <name> ::= <nested-name>
* ::= <unscoped-name>
* ::= <unscoped-template-name> <template-args>
* ::= <local-name> # See Scope Encoding below
*
* We can't use this rule directly, because <unscoped-template-name>
* can expand to <unscoped-name>. We therefore integrate it directly
* into the production for <name>:
* <name> ::= <unscoped-name> [<template-args>]
* ::= <substitution> <template-args>
*
* Secondly, <substitution> shares an expansion ("St") with <unscoped-name>,
* so we have to look further ahead to see which one matches.
*/
void NameDemanglerPrivate::parseName()
{
FUNC_START();
NameNode * const node = allocateNodeAndAddToStack<NameNode>();
if ((readAhead(2) == "St" && UnqualifiedNameNode::mangledRepresentationStartsWith(peek(2)))
|| UnscopedNameNode::mangledRepresentationStartsWith(peek())) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(UnscopedName, node);
if (TemplateArgsNode::mangledRepresentationStartsWith(peek())) {
addSubstitution(node);
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateArgs, node);
}
} else {
const char next = peek();
if (NestedNameNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(NestedName, node);
} else if (SubstitutionNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(Substitution, node);
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateArgs, node);
} else if (LocalNameNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(LocalName, node);
} else {
throw ParseException(QString::fromLatin1("Invalid name"));
}
}
FUNC_END();
}
/*
* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
* ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
* <template-prefix> ::= <prefix> <unqualified-name>
* ::= <template-param>
* ::= <substitution>
*
* The <template-prefix> rule leads to an indirect recursion with <prefix>, so
* we integrate it into <nested-name>:
* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name>
* [<template-args>] E
* ::= N [<CV-qualifiers>] <template-param> <template-args> E
* ::= N [<CV-qualifiers>] <substitution> <template-args> E
*
* The occurrence of <prefix> in the first expansion makes this rule
* completely unmanageable, because <prefix>'s first and follow sets are
* not distinct and it also shares elements of its first set with
* <template-param> and <substitution>. However, <prefix> can expand
* to both the non-terminals it is followed by as well as the two competing
* non-terminal sequences in the other rules, so we can just write:
* <nested-name> ::= N [<CV-qualifiers>] <prefix> E
*
* That's not all, though: Both <operator-name> and <cv-qualifiers> can start
* with an 'r', so we have to do a two-character-look-ahead for that case.
*/
void NameDemanglerPrivate::parseNestedName()
{
FUNC_START();
if (!NestedNameNode::mangledRepresentationStartsWith(advance()))
throw ParseException(QString::fromLatin1("Invalid nested-name"));
NestedNameNode * const node = allocateNodeAndAddToStack<NestedNameNode>();
if (CvQualifiersNode::mangledRepresentationStartsWith(peek()) && peek(1) != 'm'
&& peek(1) != 'M' && peek(1) != 's' && peek(1) != 'S') {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(CvQualifiers, node);
}
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(Prefix, node);
if (advance() != 'E')
throw ParseException(QString::fromLatin1("Invalid nested-name"));
FUNC_END();
}
/*
* <prefix> ::= <prefix> <unqualified-name>
* ::= <template-prefix> <template-args>
* ::= <template-param>
* ::= # empty
* ::= <substitution>
*
* We have to eliminate the left-recursion and the template-prefix rule
* and end up with this:
* <prefix> ::= <template-param> [<template-args>] <prefix-2>
* ::= <substitution> [<template-args>] <prefix-2>
* ::= <prefix-2>
*/
void NameDemanglerPrivate::parsePrefix()
{
FUNC_START();
PrefixNode * const node = allocateNodeAndAddToStack<PrefixNode>();
const char next = peek();
if (TemplateParamNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateParam, node);
if (TemplateArgsNode::mangledRepresentationStartsWith(peek())) {
addSubstitution(node);
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateArgs, node);
}
if (UnqualifiedNameNode::mangledRepresentationStartsWith(peek())) {
addSubstitution(node);
parsePrefix2(); // Pops itself to child list.
}
} else if (SubstitutionNode::mangledRepresentationStartsWith(next)) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(Substitution, node);
if (TemplateArgsNode::mangledRepresentationStartsWith(peek())) {
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateArgs, node);
if (UnqualifiedNameNode::mangledRepresentationStartsWith(peek()))
addSubstitution(node);
}
parsePrefix2(); // Pops itself to child list.
} else {
parsePrefix2(); // Pops itself to child list.
}
FUNC_END();
}
/*
* <prefix-2> ::= <unqualified-name> [<template-args>] <prefix-2>
* ::= # empty
*/
void NameDemanglerPrivate::parsePrefix2()
{
FUNC_START();
// We need to do this so we can correctly add all substitutions, which always start
// with the representation of the prefix node.
ParseTreeNode * const prefixNode = m_parseStack.top();
Prefix2Node * const node = allocateNodeAndAddToStack<Prefix2Node>();
prefixNode->addChild(node);
bool firstRun = true;
while (UnqualifiedNameNode::mangledRepresentationStartsWith(peek())) {
if (!firstRun)
addSubstitution(prefixNode);
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(UnqualifiedName, node);
if (TemplateArgsNode::mangledRepresentationStartsWith(peek())) {
addSubstitution(prefixNode);
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateArgs, node);
}
firstRun = false;
}
m_parseStack.pop();
FUNC_END();
}
/*
* <template-args> ::= I <template-arg>+ E
*/
void NameDemanglerPrivate::parseTemplateArgs()
{
FUNC_START();
TemplateArgsNode * const node = allocateNodeAndAddToStack<TemplateArgsNode>();
if (!TemplateArgsNode::mangledRepresentationStartsWith(advance()))
throw ParseException(QString::fromLatin1("Invalid template args"));
do
PARSE_RULE_AND_ADD_RESULT_AS_CHILD(TemplateArg, node);
while (TemplateArgNode::mangledRepresentationStartsWith(peek()));
if (advance() != 'E')
throw ParseException(QString::fromLatin1("Invalid template args"));
FUNC_END();
}
/*
* <template-param> ::= T_ # first template parameter
* ::= T <non-negative-number> _
*/
int NameDemanglerPrivate::parseTemplateParam()
{
FUNC_START();
if (!TemplateParamNode::mangledRepresentationStartsWith(advance()))
throw ParseException(QString::fromLatin1("Invalid template-param"));
TemplateParamNode * const node = allocateNodeAndAddToStack<TemplateParamNode>();
int index;
if (peek() == '_')
index = 0;
else
index = getNonNegativeNumber() + 1;
if (advance() != '_')
throw ParseException(QString::fromLatin1("Invalid template-param"));
if (index >= m_templateParams.count()) {
if (!m_isConversionOperator) {
throw ParseException(QString::fromLocal8Bit("Invalid template parameter index %1")
.arg(index));
}
} else {
node->addChild(m_templateParams.at(index));
}
FUNC_END();
return index;
}
/* <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const */
void NameDemanglerPrivate::parseCvQualifiers()
{
FUNC_START();
CvQualifiersNode * const node = allocateNodeAndAddToStack<CvQualifiersNode>();
node->m_hasConst = false;
node->m_hasVolatile = false;
while (true) {
if (peek() == 'V') {
if (node->m_hasConst || node->m_hasVolatile)
throw ParseException(QLatin1String("Invalid qualifiers: unexpected 'volatile'"));
node->m_hasVolatile = true;
advance();
} else if (peek() == 'K') {
if (node->m_hasConst)
throw ParseException(QLatin1String("Invalid qualifiers: 'const' appears twice"));
node->m_hasConst = true;