diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 674bf5a5731..3cdc4f16f56 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -1,9 +1,8 @@ TEMPLATE = lib TARGET = Debugger -#DEFINES += QT_USE_FAST_OPERATOR_PLUS -#DEFINES += QT_USE_FAST_CONCATENATION - +# DEFINES += QT_USE_FAST_OPERATOR_PLUS +# DEFINES += QT_USE_FAST_CONCATENATION # CONFIG += single include(../../qtcreatorplugin.pri) include(../../plugins/coreplugin/coreplugin.pri) @@ -14,11 +13,10 @@ include(../../plugins/texteditor/texteditor.pri) include(../../libs/cplusplus/cplusplus.pri) include(../../libs/utils/utils.pri) INCLUDEPATH += $$PWD/../../libs/utils - -QT += gui network script - -HEADERS += \ - breakhandler.h \ +QT += gui \ + network \ + script +HEADERS += breakhandler.h \ breakwindow.h \ debuggeragents.h \ debuggeractions.h \ @@ -44,9 +42,8 @@ HEADERS += \ threadswindow.h \ watchhandler.h \ watchwindow.h \ - -SOURCES += \ - breakhandler.cpp \ + name_demangler.h +SOURCES += breakhandler.cpp \ breakwindow.cpp \ breakwindow.h \ debuggeragents.cpp \ @@ -70,7 +67,7 @@ SOURCES += \ threadswindow.cpp \ watchhandler.cpp \ watchwindow.cpp \ - + name_demangler.cpp FORMS += attachexternaldialog.ui \ attachcoredialog.ui \ attachtcfdialog.ui \ @@ -79,21 +76,16 @@ FORMS += attachexternaldialog.ui \ dumperoptionpage.ui \ commonoptionspage.ui \ startexternaldialog.ui \ - startremotedialog.ui \ - + startremotedialog.ui RESOURCES += debugger.qrc - -false { -SOURCES += $$PWD/modeltest.cpp -HEADERS += $$PWD/modeltest.h -DEFINES += USE_MODEL_TEST=1 +false { + SOURCES += $$PWD/modeltest.cpp + HEADERS += $$PWD/modeltest.h + DEFINES += USE_MODEL_TEST=1 } - include(cdb/cdb.pri) include(gdb/gdb.pri) include(script/script.pri) include(tcf/tcf.pri) - include(shared/shared.pri) - OTHER_FILES += Debugger.pluginspec diff --git a/src/plugins/debugger/name_demangler.cpp b/src/plugins/debugger/name_demangler.cpp new file mode 100644 index 00000000000..c1fb514e4ab --- /dev/null +++ b/src/plugins/debugger/name_demangler.cpp @@ -0,0 +1,2131 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "name_demangler.h" + +// Debugging facility. +//#define DO_TRACE +#ifdef DO_TRACE +#define FUNC_START() \ + qDebug("Function %s has started, input is at position %d.", __func__, pos) +#define FUNC_END(result) \ + qDebug("Function %s has finished, result is '%s'.", __func__, qPrintable(result)) +#else +#define FUNC_START() +#define FUNC_END(result) +#endif // DO_TRACE + +QT_BEGIN_NAMESPACE + +class NameDemanglerPrivate +{ + Q_DECLARE_TR_FUNCTIONS(NameDemanglerPrivate) +public: + NameDemanglerPrivate(); + ~NameDemanglerPrivate(); + + bool demangle(const QString &mangledName); + const QString &errorString() const { return m_errorString; } + const QString &demangledName() const { return m_demangledName; } + +private: + class Operator + { + public: + enum OpType { UnaryOp, BinaryOp, TernaryOp }; + Operator(const QString &code, const QString &repr) + : code(code), repr(repr) { } + virtual const QString makeExpr(const QStringList &exprs) const=0; + virtual OpType type() const=0; + + const QString code; + QString repr; + }; + + class UnaryOperator : public Operator + { + public: + UnaryOperator(const QString &code, const QString &repr) + : Operator(code, repr) { } + virtual const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 1); + return repr + exprs.first(); + } + OpType type() const { return UnaryOp; } + }; + + class FunctionCallOperator : public UnaryOperator + { + public: + FunctionCallOperator() : UnaryOperator("cl", "") { } + const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 1); + return exprs.first() + "()"; + } + }; + + class SizeAlignOfOperator : public UnaryOperator + { + public: + SizeAlignOfOperator(const QString &code, const QString &repr) + : UnaryOperator(code, repr) { } + const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 1); + return repr + "(" + exprs.first() + ")"; + } + }; + + class BinaryOperator : public Operator + { + public: + BinaryOperator(const QString &code, const QString &repr) + : Operator(code, repr) { } + virtual const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 2); + return exprs.first() + " " + repr + " " + exprs.at(1); + } + OpType type() const { return BinaryOp; } + }; + + class ArrayNewOperator : public BinaryOperator + { + public: + ArrayNewOperator() : BinaryOperator("na", "") { } + const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 2); + return "new " + exprs.first() + "[" + exprs.at(1) + "]"; + } + }; + + class BinOpWithNoSpaces : public BinaryOperator + { + public: + BinOpWithNoSpaces(const QString &code, const QString &repr) + : BinaryOperator(code, repr) { } + virtual const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 2); + return exprs.first() + repr + exprs.at(1); + } + }; + + class ArrayAccessOperator : public BinaryOperator + { + public: + ArrayAccessOperator() : BinaryOperator("ix", "") { } + const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 2); + return exprs.first() + "[" + exprs.at(1) + "]"; + } + }; + + class QuestionMarkOperator : public Operator + { + public: + QuestionMarkOperator() : Operator("qu", "") { } + virtual const QString makeExpr(const QStringList &exprs) const + { + Q_ASSERT(exprs.size() == 2); + return exprs.first() + " ? " + exprs.at(1) + " : " + exprs.at(2); + } + OpType type() const { return TernaryOp; } + }; + + void setupFirstSets(); + void setupOps(); + + QChar peek(int ahead = 0); + QChar advance(int steps = 1); + const QString readAhead(int charCount); + + void addSubstitution(const QString &symbol); + + /* + * One parse function per Non-terminal. + * The functions return their unmangled representation, except where noted. + */ + const QString parseArrayType(); + + /* + * Returns the list of argument types, the first of which may be + * the return type, depending on context. + */ + const QStringList parseBareFunctionType(); + + const QString parseBuiltinType(); + void parseCallOffset(); + const QString parseClassEnumType(); + const QString parseCtorDtorName(); + const QString parseCvQualifiers(); + int parseDigit(); + int parseDiscriminator(); + const QString parseEncoding(); + const QString parseExpression(); + const QString parseExprPrimary(); + double parseFloat(); + const QString parseFunctionType(); + const QString parseIdentifier(int len); + const QString parseLocalName(); + const QString parseMangledName(); + const QString parseName(); + const QString parseNestedName(); + int parseNonNegativeNumber(int base = 10); + int parseNumber(); + void parseNvOffset(); + const Operator &parseOperatorName(); + const QString parsePointerToMemberType(); + const QString parsePrefix(); + const QString parsePrefix2(const QString &oldPrefix); + int parseSeqId(); + const QString parseSpecialName(); + const QString parseSourceName(); + const QString parseSubstitution(); + const QString parseTemplateArg(); + const QString parseTemplateArgs(); + const QString parseTemplateParam(); + const QString parseType(); + const QString parseUnqualifiedName(); + const QString parseUnscopedName(); + void parseVOffset(); + + void insertQualifier(QString &type, const QString &qualifier); + + void error(const QString &errorSpec); + + static const QChar eoi; + bool parseError; + int pos; + QString mangledName; + QString m_errorString; + QString m_demangledName; + QStringList substitutions; + QStringList templateParams; + + QMap ops; + + // The first-sets for all non-terminals. + QSet firstSetArrayType; + QSet firstSetBareFunctionType; + QSet firstSetBuiltinType; + QSet firstSetCallOffset; + QSet firstSetClassEnumType; + QSet firstSetDiscriminator; + QSet firstSetCtorDtorName; + QSet firstSetCvQualifiers; + QSet firstSetEncoding; + QSet firstSetExpression; + QSet firstSetExprPrimary; + QSet firstSetFunctionType; + QSet firstSetLocalName; + QSet firstSetMangledName; + QSet firstSetName; + QSet firstSetNestedName; + QSet firstSetNonNegativeNumber; + QSet firstSetNumber; + QSet firstSetOperatorName; + QSet firstSetPointerToMemberType; + QSet firstSetPositiveNumber; + QSet firstSetPrefix; + QSet firstSetPrefix2; + QSet firstSetSeqId; + QSet firstSetSourceName; + QSet firstSetSpecialName; + QSet firstSetSubstitution; + QSet firstSetTemplateArg; + QSet firstSetTemplateArgs; + QSet firstSetTemplateParam; + QSet firstSetType; + QSet firstSetUnqualifiedName; + QSet firstSetUnscopedName; +}; + + +const QChar NameDemanglerPrivate::eoi('$'); + +NameDemanglerPrivate::NameDemanglerPrivate() +{ + setupFirstSets(); + setupOps(); +} + +NameDemanglerPrivate::~NameDemanglerPrivate() +{ + qDeleteAll(ops); +} + +bool NameDemanglerPrivate::demangle(const QString &mangledName) +{ + this->mangledName = mangledName; + pos = 0; + parseError = false; + m_demangledName.clear(); + substitutions.clear(); + templateParams.clear(); + m_demangledName = parseMangledName(); + m_demangledName.replace(QRegExp("([^a-zA-Z\\d>)])::"), "\\1"); + if (m_demangledName.startsWith("::")) + m_demangledName.remove(0, 2); + if (!parseError && pos != mangledName.size()) + error(tr("Premature end of input")); + +#ifdef DO_TRACE + qDebug("%d", substitutions.size()); + foreach (QString s, substitutions) + qDebug(qPrintable(s)); +#endif + return !parseError; +} + +/* + * 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. + * ::= _Z + */ +const QString NameDemanglerPrivate::parseMangledName() +{ + FUNC_START(); + QString name; + if (readAhead(2) != "_Z") { + name = mangledName; + advance(mangledName.size()); + } else { + advance(2); + name = parseEncoding(); + } + FUNC_END(name); + return name; +} + +/* + * ::= + * ::= + * ::= ')) { // Template instantiation. + start = 1; + encoding.prepend(signature.first() + " "); + } else { // Normal function. + start = 0; + } + encoding += "("; + for (int i = start; i < signature.size(); ++i) { + if (i > start) + encoding += ", "; + const QString &type = signature.at(i); + if (type != "void") + encoding += type; + } + encoding += ")"; + encoding += qualifiers; + addSubstitution(encoding); + } else { + addSubstitution(encoding); + } + templateParams.clear(); + } else if (firstSetSpecialName.contains(next)) { + encoding = parseSpecialName(); + } else { + error(tr("Invalid encoding")); + } + + FUNC_END(encoding); + return encoding; +} + + +/* + * ::= + * ::= + * ::= + * ::= # See Scope Encoding below + * + * We can't use this rule directly, because + * can expand to . We therefore integrate it directly + * into the production for : + * ::= [] + * ::= + * + * Secondly, shares an expansion ("St") with , + * so we have to look further ahead to see which one matches. + */ +const QString NameDemanglerPrivate::parseName() +{ + FUNC_START(); + Q_ASSERT((firstSetNestedName & firstSetUnscopedName).isEmpty()); + Q_ASSERT((firstSetNestedName & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetNestedName & firstSetLocalName).isEmpty()); + Q_ASSERT((firstSetUnscopedName & firstSetSubstitution).size() == 1); // 'S' + Q_ASSERT((firstSetUnscopedName & firstSetLocalName).isEmpty()); + Q_ASSERT((firstSetSubstitution & firstSetLocalName).isEmpty()); + + QString name; + if ((readAhead(2) == "St" && firstSetUnqualifiedName.contains(peek(2))) + || firstSetUnscopedName.contains(peek())) { + name = parseUnscopedName(); + if (!parseError && firstSetTemplateArgs.contains(peek())) { + addSubstitution(name); + name += parseTemplateArgs(); + } + } else { + QChar next = peek(); + if (firstSetNestedName.contains(next)) { + name = parseNestedName(); + } else if (firstSetSubstitution.contains(next)) { + name = parseSubstitution(); + if (!parseError) { + name += parseTemplateArgs(); + } + } else if (firstSetLocalName.contains(next)) { + name = parseLocalName(); + } else { + error(tr("Invalid name")); + } + } + + FUNC_END(name); + return name; +} + +/* + * ::= N [] E + * ::= N [] E + * ::= + * ::= + * ::= + * + * The rule leads to an indirect recursion with , so + * we integrate it into : + * ::= N [] + * [] E + * ::= N [] E + * ::= N [] E + * + * The occurrence of in the first expansion makes this rule + * completely unmanageable, because 's first and follow sets are + * not distinct and it also shares elements of its first set with + * and . However, 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: + * ::= N [] E + * + * That's not all, though: Both and can start + * with an 'r', so we have to do a two-character-look-ahead for that case. + */ +const QString NameDemanglerPrivate::parseNestedName() +{ + FUNC_START(); + Q_ASSERT((firstSetCvQualifiers & firstSetPrefix).size() == 1); + + QString name; + if (advance() != 'N') { + error(tr("Invalid nested-name")); + } else { + QString cvQualifiers; + if (firstSetCvQualifiers.contains(peek()) && peek(1) != 'm' + && peek(1) != 'M' && peek(1) != 's' && peek(1) != 'S') + cvQualifiers = parseCvQualifiers(); + if (!parseError) { + name = parsePrefix(); + if (!parseError && advance() != 'E') + error(tr("Invalid nested-name")); + + /* + * These are member function qualifiers which will have to + * be moved to the back of the whole declaration later on, + * so we mark them with the '@' character to ne able to easily + * spot them. + */ + if (!cvQualifiers.isEmpty()) + name += "@" + cvQualifiers; + } + } + + FUNC_END(name); + return name; +} + +/* + * ::= + * ::= + * ::= + * ::= # empty + * ::= + * + * We have to eliminate the left-recursion and the template-prefix rule + * and end up with this: + * ::= [] + * ::= [] + * ::= + */ +const QString NameDemanglerPrivate::parsePrefix() +{ + FUNC_START(); + Q_ASSERT((firstSetTemplateParam & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetTemplateArgs & firstSetPrefix2).isEmpty()); + Q_ASSERT((firstSetTemplateParam & firstSetPrefix2).isEmpty()); + Q_ASSERT((firstSetSubstitution & firstSetPrefix2).isEmpty()); + + QString prefix; + QChar next = peek(); + if (firstSetTemplateParam.contains(next)) { + prefix = parseTemplateParam(); + if (!parseError && firstSetTemplateArgs.contains(peek())) { + addSubstitution(prefix); + prefix += parseTemplateArgs(); + } + if (!parseError) { + if (firstSetUnqualifiedName.contains(peek())) { + addSubstitution(prefix); + prefix = parsePrefix2(prefix); + } + } + } else if (firstSetSubstitution.contains(next)) { + prefix = parseSubstitution(); + QString templateArgs; + if (!parseError && firstSetTemplateArgs.contains(peek())) { + templateArgs = parseTemplateArgs(); + prefix += templateArgs; + } + if (!parseError) { + if (firstSetUnqualifiedName.contains(peek()) && + !templateArgs.isEmpty()) + addSubstitution(prefix); + prefix = parsePrefix2(prefix); + } + } else { + prefix = parsePrefix2(prefix); + } + + FUNC_END(prefix); + return prefix; +} + +/* + * ::= [] + * ::= # empty + */ +const QString NameDemanglerPrivate::parsePrefix2(const QString &oldPrefix) +{ + FUNC_START(); + Q_ASSERT((firstSetTemplateArgs & firstSetPrefix2).isEmpty()); + + QString prefix = oldPrefix; + bool firstRun = true; + while (!parseError && firstSetUnqualifiedName.contains(peek())) { + if (!firstRun) + addSubstitution(prefix); + prefix += parseUnqualifiedName(); + if (!parseError) { + if (firstSetTemplateArgs.contains(peek())) { + addSubstitution(prefix); + prefix += parseTemplateArgs(); + } + } + firstRun = false; + } + + FUNC_END(prefix); + return prefix; +} + +/* + * ::= I + E + */ +const QString NameDemanglerPrivate::parseTemplateArgs() +{ + FUNC_START(); + Q_ASSERT(!firstSetTemplateArg.contains('E')); + + QString args = "<"; + if (advance() != 'I') { + error(tr("Invalid template args")); + } else { + do { + if (args.length() > 1) + args += ", "; + args += parseTemplateArg(); + } while (!parseError && firstSetTemplateArg.contains(peek())); + if (!parseError && advance() != 'E') + error(tr("Invalid template args")); + } + + args += '>'; + FUNC_END(args); + return args; +} + +/* + * ::= T_ # first template parameter + * ::= T _ + */ +const QString NameDemanglerPrivate::parseTemplateParam() +{ + FUNC_START(); + + QString param; + if (advance() != 'T') { + error(tr("Invalid template-param")); + } else { + int index; + if (peek() == '_') + index = 0; + else + index = parseNonNegativeNumber() + 1; + if (!parseError && advance() != '_') + error(tr("Invalid template-param")); + param = templateParams.at(index); + } + + FUNC_END(param); + return param; +} + +/* ::= [r] [V] [K] # restrict (C99), volatile, const */ +const QString NameDemanglerPrivate::parseCvQualifiers() +{ + FUNC_START(); + + QString qualifiers; + bool volatileFound = false; + bool constFound = false; + while (true) { + if (peek() == 'V') { + if (volatileFound || constFound) { + error(tr("Invalid qualifiers: unexpected 'volatile'")); + break; + } else { + volatileFound = true; + qualifiers += " volatile"; + advance(); + } + } else if (peek() == 'K') { + if (constFound) { + error(tr("Invalid qualifiers: 'const' appears twice")); + break; + } else { + constFound = true; + qualifiers += " const"; + advance(); + } + } else { + break; + } + } + + FUNC_END(qualifiers); + return qualifiers; +} + + +int NameDemanglerPrivate::parseNumber() +{ + FUNC_START(); + + bool negative = false; + if (peek() == 'n') { + negative = true; + advance(); + } + int val = parseNonNegativeNumber(); + int number = negative ? -val : val; + + FUNC_END(QString::number(number)); + return number; +} + + +int NameDemanglerPrivate::parseNonNegativeNumber(int base) +{ + FUNC_START(); + + int startPos = pos; + while (peek().isDigit()) + advance(); + int number; + if (pos == startPos) { + error(tr("Invalid non-negative number")); + number = 0; + } else { + number = mangledName.mid(startPos, pos - startPos).toInt(0, base); + } + + FUNC_END(QString::number(number)); + return number; +} + +/* + * Floating-point literals are encoded using a fixed-length lowercase + * hexadecimal string corresponding to the internal representation + * (IEEE on Itanium), high-order bytes first, without leading zeroes. + * For example: "Lf bf800000 E" is -1.0f on Itanium. + */ +double NameDemanglerPrivate::parseFloat() +{ + FUNC_START(); + + // TODO: Implementation! + Q_ASSERT(0); + + FUNC_END(QString()); + return 0.0; +} + +/* + * ::= # type or template + * ::= X E # expression + * ::= # simple expressions + * ::= I * E # argument pack + * ::= sp # pack expansion of (C++0x) + */ +const QString NameDemanglerPrivate::parseTemplateArg() +{ + FUNC_START(); + Q_ASSERT(!firstSetType.contains('X') && !firstSetType.contains('I') + /* && !firstSetType.contains('s') */); + Q_ASSERT((firstSetType & firstSetExprPrimary).isEmpty()); + Q_ASSERT(!firstSetExprPrimary.contains('X') + && !firstSetExprPrimary.contains('I') + && !firstSetExprPrimary.contains('s')); + Q_ASSERT(!firstSetTemplateArg.contains('E')); + + QString arg; + QChar next = peek(); + if (readAhead(2) == "sp") { + advance(2); + arg = parseExpression(); + } else if (firstSetType.contains(next)) { + arg = parseType(); + } else if (firstSetExprPrimary.contains(next)) { + arg = parseExprPrimary(); + } else if (next == 'X') { + advance(); + arg = parseExpression(); + if (!parseError && advance() != 'E') + error(tr("Invalid template-arg")); + } else if (next == 'I') { + advance(); + while (!parseError && firstSetTemplateArg.contains(peek())) { + if (!arg.isEmpty()) + arg += ", "; // TODO: is this correct? + arg += parseTemplateArg(); + } + if (!parseError && advance() != 'E') + error(tr("Invalid template-arg")); + } else { + error(tr("Invalid template-arg")); + } + + templateParams.append(arg); + FUNC_END(arg); + return arg; +} + +/* + * ::= + * ::= + * ::= + * ::= cl * E # call + * ::= cv expression # conversion with one argument + * ::= cv _ * E # conversion with a different number of arguments + * ::= st # sizeof (a type) + * ::= at # alignof (a type) + * ::= + * ::= + * ::= sr # dependent name + * ::= sr # dependent template-id + * ::= sZ # size of a parameter pack + * ::= + * + * Note that the grammar is missing the definition of . This + * has not been a problem in the test cases so far. + */ +const QString NameDemanglerPrivate::parseExpression() +{ + FUNC_START(); + Q_ASSERT((firstSetOperatorName & firstSetTemplateParam).isEmpty()); +// Q_ASSERT((firstSetOperatorName & firstSetFunctionParam).isEmpty()); + Q_ASSERT((firstSetOperatorName & firstSetExprPrimary).isEmpty()); +// Q_ASSERT((firstSetTemplateParam & firstSetFunctionParam).isEmpty()); + Q_ASSERT((firstSetTemplateParam & firstSetExprPrimary).isEmpty()); + Q_ASSERT(!firstSetTemplateParam.contains('c') + && !firstSetTemplateParam.contains('s') + && !firstSetTemplateParam.contains('a')); +// Q_ASSERT((firstSetFunctionParam & firstSetExprPrimary).isEmpty()); +/* + Q_ASSERT(!firstSetFunctionParam.contains('c') + && !firstSetFunctionParam.contains('s') + && !firstSetFunctionParam.contains('a')); +*/ + Q_ASSERT(!firstSetExprPrimary.contains('c') + && !firstSetExprPrimary.contains('s') + && !firstSetExprPrimary.contains('a')); + Q_ASSERT(!firstSetExpression.contains('E')); + Q_ASSERT(!firstSetExpression.contains('_')); + + QString expr; + + /* + * Some of the terminals in the productions of + * also appear in the productions of operator-name. We assume the direct + * productions to have higher precedence and check them first to prevent + * them being parsed by parseOperatorName(). + */ + QString str = readAhead(2); + if (str == "cl") { + advance(2); + while (!parseError && firstSetExpression.contains(peek())) + expr += parseExpression(); + if (!parseError && advance() != 'E') + error(tr("Invalid expression")); + } else if (str == "cv") { + advance(2); + expr = parseType() + "("; + if (!parseError) { + if (peek() == '_') { + advance(); + for (int numArgs = 0; + !parseError && firstSetExpression.contains(peek()); + ++numArgs) { + if (numArgs > 0) + expr += ", "; + expr += parseExpression(); + } + if (!parseError && advance() != 'E') + error(tr("Invalid expression")); + } else { + expr += parseExpression(); + } + } + expr += ")"; + } else if (str == "st") { + advance(2); + expr = "sizeof(" + parseType() + ")"; + } else if (str == "at") { + advance(2); + expr = "alignof(" + parseType() + ")"; + } else if (str == "sr") { // TODO: Which syntax to use here? + advance(2); + expr = parseType(); + if (!parseError) + expr += parseUnqualifiedName(); + if (!parseError && firstSetTemplateArgs.contains(peek())) + parseTemplateArgs(); + } else if (str == "sZ") { + expr = parseTemplateParam(); // TODO: Syntax? + } else { + QChar next = peek(); + if (firstSetOperatorName.contains(next)) { + const Operator &op = parseOperatorName(); + QStringList exprs; + if (!parseError) + exprs.append(parseExpression()); + if (!parseError && op.type() != Operator::UnaryOp) + exprs.append(parseExpression()); + if (!parseError && op.type() == Operator::TernaryOp) { + exprs.append(parseExpression()); + } + expr = op.makeExpr(exprs); + } else if (firstSetTemplateParam.contains(next)) { + expr = parseTemplateParam(); +#if 0 + } else if (firstSetFunctionParam.contains(next)) { + expr = parseFunctionParam(); +#endif + } else if (firstSetExprPrimary.contains(next)) { + expr = parseExprPrimary(); + } else { + error(tr("Invalid expression")); + } + } + + FUNC_END(expr); + return expr; +} + +/* + * ::= L E # integer literal + * ::= L E # floating literal + * ::= L E # external name + */ +const QString NameDemanglerPrivate::parseExprPrimary() +{ + FUNC_START(); + Q_ASSERT((firstSetType & firstSetMangledName).isEmpty()); + + QString expr; + if (advance() != 'L') { + error(tr("Invalid primary expression")); + } else { + QChar next = peek(); + if (firstSetType.contains(next)) { + const QString type = parseType(); + if (!parseError) { + if (true /* type just parsed indicates integer */) + expr += QString::number(parseNumber()); + else if (true /* type just parsed indicates float */) + expr += QString::number(parseFloat()); + else + error(tr("Invalid expr-primary")); + } + } else if (firstSetMangledName.contains(next)) { + expr = parseMangledName(); + } else { + error(tr("Invalid expr-primary")); + } + if (!parseError && advance() != 'E') + error(tr("Invalid expr-primary")); + } + + FUNC_END(expr); + return expr; +} + +/* + * ::= + * ::= + * ::= + * ::= + * ::= + * ::= + * ::= + * ::= # See Compression below + * ::= + * ::= P # pointer-to + * ::= R # reference-to + * ::= O # rvalue reference-to (C++0x) + * ::= C # complex pair (C 2000) + * ::= G # imaginary (C 2000) + * ::= U # vendor extended type qualifier + * ::= Dp # pack expansion of (C++0x) + * ::= Dt E # decltype of an id-expression or class member access (C++0x) + * ::= DT E # decltype of an expression (C++0x) + * + * Because can expand to , we have to + * do a slight transformation: We get rid of and + * integrate its rhs into 's rhs. This leads to the following + * identical prefixes: + * ::= + * ::= + * ::= + * ::= + * + * Also, the first set of has some overlap with + * direct productions of , so these have to be worked around as well. + */ +const QString NameDemanglerPrivate::parseType() +{ + FUNC_START(); + Q_ASSERT((firstSetBuiltinType & firstSetFunctionType).isEmpty()); + Q_ASSERT((firstSetBuiltinType & firstSetClassEnumType).size() == 1); + Q_ASSERT((firstSetBuiltinType & firstSetArrayType).isEmpty()); + Q_ASSERT((firstSetBuiltinType & firstSetPointerToMemberType).isEmpty()); + Q_ASSERT((firstSetBuiltinType & firstSetTemplateParam).isEmpty()); + Q_ASSERT((firstSetBuiltinType & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetBuiltinType & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetBuiltinType.contains('P') + && !firstSetBuiltinType.contains('R') + && !firstSetBuiltinType.contains('O') + && !firstSetBuiltinType.contains('C') + && !firstSetBuiltinType.contains('G') + && !firstSetBuiltinType.contains('U')); + Q_ASSERT((firstSetFunctionType & firstSetClassEnumType).isEmpty()); + Q_ASSERT((firstSetFunctionType & firstSetArrayType).isEmpty()); + Q_ASSERT((firstSetFunctionType & firstSetPointerToMemberType).isEmpty()); + Q_ASSERT((firstSetFunctionType & firstSetTemplateParam).isEmpty()); + Q_ASSERT((firstSetFunctionType & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetFunctionType & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetFunctionType.contains('P') + && !firstSetFunctionType.contains('R') + && !firstSetFunctionType.contains('O') + && !firstSetFunctionType.contains('C') + && !firstSetFunctionType.contains('G') + && !firstSetFunctionType.contains('U') + && !firstSetFunctionType.contains('D')); + Q_ASSERT((firstSetClassEnumType & firstSetArrayType).isEmpty()); + Q_ASSERT((firstSetClassEnumType & firstSetPointerToMemberType).isEmpty()); + Q_ASSERT((firstSetClassEnumType & firstSetTemplateParam).isEmpty()); + Q_ASSERT((firstSetClassEnumType & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetClassEnumType & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetClassEnumType.contains('P') + && !firstSetClassEnumType.contains('R') + && !firstSetClassEnumType.contains('O') + && !firstSetClassEnumType.contains('C') + && !firstSetClassEnumType.contains('G') + && !firstSetClassEnumType.contains('U') + /* && !firstSetClassEnumType.contains('D') */); + Q_ASSERT((firstSetArrayType & firstSetPointerToMemberType).isEmpty()); + Q_ASSERT((firstSetArrayType & firstSetTemplateParam).isEmpty()); + Q_ASSERT((firstSetArrayType & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetArrayType & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetArrayType.contains('P') + && !firstSetArrayType.contains('R') + && !firstSetArrayType.contains('O') + && !firstSetArrayType.contains('C') + && !firstSetArrayType.contains('G') + && !firstSetArrayType.contains('U') + && !firstSetArrayType.contains('D')); + Q_ASSERT((firstSetPointerToMemberType & firstSetTemplateParam).isEmpty()); + Q_ASSERT((firstSetPointerToMemberType & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetPointerToMemberType & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetPointerToMemberType.contains('P') + && !firstSetPointerToMemberType.contains('R') + && !firstSetPointerToMemberType.contains('O') + && !firstSetPointerToMemberType.contains('C') + && !firstSetPointerToMemberType.contains('G') + && !firstSetPointerToMemberType.contains('U') + && !firstSetPointerToMemberType.contains('D')); + Q_ASSERT((firstSetTemplateParam & firstSetSubstitution).isEmpty()); + Q_ASSERT((firstSetTemplateParam & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetTemplateParam.contains('P') + && !firstSetTemplateParam.contains('R') + && !firstSetTemplateParam.contains('O') + && !firstSetTemplateParam.contains('C') + && !firstSetTemplateParam.contains('G') + && !firstSetTemplateParam.contains('U') + && !firstSetTemplateParam.contains('D')); + Q_ASSERT((firstSetSubstitution & firstSetCvQualifiers).isEmpty()); + Q_ASSERT(!firstSetSubstitution.contains('P') + && !firstSetSubstitution.contains('R') + && !firstSetSubstitution.contains('O') + && !firstSetSubstitution.contains('C') + && !firstSetSubstitution.contains('G') + && !firstSetSubstitution.contains('U') + && !firstSetSubstitution.contains('D')); + Q_ASSERT(!firstSetCvQualifiers.contains('P') + && !firstSetCvQualifiers.contains('R') + && !firstSetCvQualifiers.contains('O') + && !firstSetCvQualifiers.contains('C') + && !firstSetCvQualifiers.contains('G') + && !firstSetCvQualifiers.contains('U') + && !firstSetCvQualifiers.contains('D')); + + QString type; + QString str = readAhead(2); + if (str == "Dp") { + advance(2); + type = parseType(); // TODO: Probably needs augmentation + } else if (str == "Dt") { + advance(2); + type = parseExpression(); // TODO: See above + if (!parseError && advance() != 'E') + error(tr("Invalid type")); + } else if (str == "DT") { + advance(2); + type = parseExpression(); // TODO: See above + if (!parseError && advance() != 'E') + error(tr("Invalid type")); + } else { + QChar next = peek(); + if (str == "Dd" || str == "De" || str == "Df" || str == "Dh" + || str == "Di" || str == "Ds" + || (next != 'D' && firstSetBuiltinType.contains(next))) { + type = parseBuiltinType(); + } else if (firstSetFunctionType.contains(next)) { + type = parseFunctionType(); + addSubstitution(type); + } else if (firstSetClassEnumType.contains(next)) { + type = parseClassEnumType(); + addSubstitution(type); + } else if (firstSetArrayType.contains(next)) { + type = parseArrayType(); + } else if (firstSetPointerToMemberType.contains(next)) { + type = parsePointerToMemberType(); + } else if (firstSetTemplateParam.contains(next)) { + type = parseTemplateParam(); + addSubstitution(type); + if (!parseError && firstSetTemplateArgs.contains(peek())) { + type += parseTemplateArgs(); + addSubstitution(type); + } + } else if (firstSetSubstitution.contains(next)) { + type = parseSubstitution(); + if (!parseError && firstSetTemplateArgs.contains(peek())) { + type += parseTemplateArgs(); + addSubstitution(type); + } + } else if (firstSetCvQualifiers.contains(next)) { + const QString cvQualifiers = parseCvQualifiers(); + if (!parseError) { + type = parseType(); + if (!parseError && !cvQualifiers.isEmpty()) { + insertQualifier(type, cvQualifiers); + addSubstitution(type); + } + } + } else if (next == 'P') { + advance(); + type = parseType(); + insertQualifier(type, "*"); + addSubstitution(type); + } else if (next == 'R') { + advance(); + type = parseType(); + insertQualifier(type, "&"); + addSubstitution(type); + } else if (next == 'O') { + advance(); + type = parseType() + "&&"; // TODO: Correct notation? + addSubstitution(type); + } else if (next == 'C') { + advance(); + type = parseType(); // TODO: What to do append here? + addSubstitution(type); + } else if (next == 'G') { + advance(); + type = parseType(); // TODO: see above + addSubstitution(type); + } else if (next == 'U') { + advance(); + type = parseSourceName(); + if (!parseError) + type += parseType(); // TODO: handle this correctly + } else { + error(tr("Invalid type")); + } + } + + FUNC_END(type); + return type; +} + +/* ::= */ +const QString NameDemanglerPrivate::parseSourceName() +{ + FUNC_START(); + + int idLen = parseNonNegativeNumber(); + QString sourceName; + if (!parseError) + sourceName = parseIdentifier(idLen); + + FUNC_END(sourceName); + return sourceName; +} + +/* + * ::= v # void + * ::= w # wchar_t + * ::= b # bool + * ::= c # char + * ::= a # signed char + * ::= h # unsigned char + * ::= s # short + * ::= t # unsigned short + * ::= i # int + * ::= j # unsigned int + * ::= l # long + * ::= m # unsigned long + * ::= x # long long, __int64 + * ::= y # unsigned long long, __int64 + * ::= n # __int128 + * ::= o # unsigned __int128 + * ::= f # float + * ::= d # double + * ::= e # long double, __float80 + * ::= g # __float128 + * ::= z # ellipsis + * ::= Dd # IEEE 754r decimal floating point (64 bits) + * ::= De # IEEE 754r decimal floating point (128 bits) + * ::= Df # IEEE 754r decimal floating point (32 bits) + * ::= Dh # IEEE 754r half-precision floating point (16 bits) + * ::= Di # char32_t + * ::= Ds # char16_t + * ::= u # vendor extended type + */ +const QString NameDemanglerPrivate::parseBuiltinType() +{ + FUNC_START(); + + // TODO: Perhaps a map would be more appropriate here. + QString type; + switch (advance().toAscii()) { + case 'v': + type = "void"; + break; + case 'w': + type = "wchar_t"; + break; + case 'b': + type = "bool"; + break; + case 'c': + type = "char"; + break; + case 'a': + type = "signed char"; + break; + case 'h': + type = "unsigned char"; + break; + case 's': + type = "short"; + break; + case 't': + type = "unsigned short"; + break; + case 'i': + type = "int"; + break; + case 'j': + type = "unsigned int"; + break; + case 'l': + type = "long"; + break; + case 'm': + type = "unsigned long"; + break; + case 'x': + type = "long long"; + break; + case 'y': + type = "unsigned long long"; + break; + case 'n': + type = "__int128"; + break; + case 'o': + type = "unsigned __int128"; + break; + case 'f': + type = "float"; + break; + case 'd': + type = "double"; + break; + case 'e': + type = "long double"; + break; + case 'g': + type = "__float128"; + break; + case 'z': + type = "..."; + break; + case 'D': + switch (advance().toAscii()) { + case 'd': + case 'e': + case 'f': + case 'h': + type = "IEEE_special_float"; + break; + case 'i': + type = "char32_t"; + break; + case 's': + type = "char16_t"; + break; + break; + default: + error(tr("Invalid built-in type")); + } + break; + case 'u': + type = parseSourceName(); + break; + default: + error(tr("Invalid builtin-type")); + } + + FUNC_END(type); + return type; +} + +/* ::= F [Y] E */ +const QString NameDemanglerPrivate::parseFunctionType() +{ + FUNC_START(); + + // TODO: Check how we get here. Do we always have a return type or not? + QString funcType; + bool externC = false; + if (advance() != 'F') { + error(tr("Invalid function type")); + } else { + if (peek() == 'Y') { + advance(); + externC = true; + } + const QStringList &signature = parseBareFunctionType(); + if (!parseError && advance() != 'E') + error(tr("Invalid function type")); + if (!parseError) { + QString returnType = signature.first(); + QString argList = "("; + for (int i = 1; i < signature.size(); ++i) { + if (i > 1) + argList.append(", "); + const QString &type = signature.at(i); + if (type != "void") + argList.append(type); + } + argList.append(")"); + bool retTypeIsFuncPtr = false; + const int firstClosingParenIndex = returnType.indexOf(')'); + if (firstClosingParenIndex != -1) { + const int firstOpeningParenIndex = + returnType.lastIndexOf('(', firstClosingParenIndex); + const QChar next = returnType[firstOpeningParenIndex + 1]; + if (next == '*' || next =='&') { + retTypeIsFuncPtr = true; + funcType = returnType.left(firstOpeningParenIndex + 2) + + argList + returnType.mid(firstOpeningParenIndex + 2); + } + } + if (!retTypeIsFuncPtr) + funcType = returnType + " " + argList; + } + } + + if (externC) + funcType.prepend("extern \"C\" "); + FUNC_END(funcType); + return funcType; +} + +/* ::= + */ +const QStringList NameDemanglerPrivate::parseBareFunctionType() +{ + FUNC_START(); + + QStringList signature; + do + signature.append(parseType()); + while (!parseError && firstSetType.contains(peek())); + + FUNC_END(signature.join(":")); + return signature; +} + +/* ::= */ +const QString NameDemanglerPrivate::parseClassEnumType() +{ + FUNC_START(); + const QString &name = parseName(); + FUNC_END(name); + return name; +} + +/* + * ::= + * ::= + * ::= + */ +const QString NameDemanglerPrivate::parseUnqualifiedName() +{ + FUNC_START(); + Q_ASSERT((firstSetOperatorName & firstSetCtorDtorName).isEmpty()); + Q_ASSERT((firstSetOperatorName & firstSetSourceName).isEmpty()); + Q_ASSERT((firstSetCtorDtorName & firstSetSourceName).isEmpty()); + + QString name; + QChar next = peek(); + if (firstSetOperatorName.contains(next)) + name = "::operator" + parseOperatorName().repr; + else if (firstSetCtorDtorName.contains(next)) + name = "::" + parseCtorDtorName(); + else if (firstSetSourceName.contains(next)) + name = "::" + parseSourceName(); + else + error(tr("Invalid unqualified-name")); + + FUNC_END(name); + return name; +} + +/* + * ::= nw # new + * ::= na # new[] + * ::= dl # delete + * ::= da # delete[] + * ::= ps # + (unary) + * ::= ng # - (unary) + * ::= ad # & (unary) + * ::= de # * (unary) + * ::= co # ~ + * ::= pl # + + * ::= mi # - + * ::= ml # * + * ::= dv # / + * ::= rm # % + * ::= an # & + * ::= or # | + * ::= eo # ^ + * ::= aS # = + * ::= pL # += + * ::= mI # -= + * ::= mL # *= + * ::= dV # /= + * ::= rM # %= + * ::= aN # &= + * ::= oR # |= + * ::= eO # ^= + * ::= ls # << + * ::= rs # >> + * ::= lS # <<= + * ::= rS # >>= + * ::= eq # == + * ::= ne # != + * ::= lt # < + * ::= gt # > + * ::= le # <= + * ::= ge # >= + * ::= nt # ! + * ::= aa # && + * ::= oo # || + * ::= pp # ++ + * ::= mm # -- + * ::= cm # , + * ::= pm # ->* + * ::= pt # -> + * ::= cl # () + * ::= ix # [] + * ::= qu # ? + * ::= st # sizeof (a type) + * ::= sz # sizeof (an expression) + * ::= at # alignof (a type) + * ::= az # alignof (an expression) + * ::= cv # (cast) + * ::= v # vendor extended operator + */ +const NameDemanglerPrivate::Operator &NameDemanglerPrivate::parseOperatorName() +{ + FUNC_START(); + + const Operator *op; + if (peek() == 'v') { + // TODO: Implement vendor-extended operators. + static const UnaryOperator vendorOp("v", "[unimplemented]"); + advance(); + int numExprs = parseDigit(); + Q_UNUSED(numExprs); + if (!parseError) + parseSourceName(); + op = &vendorOp; + } else { + const QString id = readAhead(2); + advance(2); + if (id == "cv") { + static UnaryOperator castOp("cv", ""); + QString type = parseType(); + castOp.repr = "(" + type + ")"; + } else { + op = ops.value(id); + if (op == 0) { + static const UnaryOperator pseudoOp("invalid", "invalid"); + op = &pseudoOp; + error(tr("Invalid operator-name '%s'").arg(id)); + } + } + } + + FUNC_END(op->repr); + return *op; +} + +/* + * ::= A _ + * ::= A [] _ + */ +const QString NameDemanglerPrivate::parseArrayType() +{ + FUNC_START(); + Q_ASSERT((firstSetNonNegativeNumber & firstSetExpression).isEmpty()); + Q_ASSERT(!firstSetNonNegativeNumber.contains('_')); + Q_ASSERT(!firstSetExpression.contains('_')); + + QString type; + if (advance() != 'A') { + error(tr("Invalid array-type")); + } else { + QChar next = peek(); + QString dimension; + if (firstSetNonNegativeNumber.contains(next)) { + dimension = QString::number(parseNonNegativeNumber()); + } else if (firstSetExpression.contains(next)){ + dimension = parseExpression(); + } + if (!parseError && advance() != '_') + error(tr("Invalid array-type")); + if (!parseError) + type = parseType() + "[" + dimension + "]"; + } + + FUNC_END(type); + return type; +} + +/* ::= M */ +const QString NameDemanglerPrivate::parsePointerToMemberType() +{ + FUNC_START(); + + QString type; + if (advance() != 'M') { + error(tr("Invalid pointer-to-member-type")); + } else { + const QString classType = parseType(); + QString memberType; + if (!parseError) + memberType = parseType(); + if (!parseError) { + if (memberType.contains(')')) { // Function? + int parenIndex = memberType.indexOf('('); + QString returnType = memberType.left(parenIndex); + memberType.remove(0, parenIndex); + type = returnType + "(" + classType + "::*)" + memberType; + } else { + type = memberType + " " + classType + "::*"; + } + } + } + + FUNC_END(type); + return type; +} + +/* + * ::= S _ + * ::= S_ + * ::= St # ::std:: + * ::= Sa # ::std::allocator + * ::= Sb # ::std::basic_string + * ::= Ss # ::std::basic_string < char, + * ::std::char_traits, + * ::std::allocator > + * ::= Si # ::std::basic_istream > + * ::= So # ::std::basic_ostream > + * ::= Sd # ::std::basic_iostream > + */ +const QString NameDemanglerPrivate::parseSubstitution() +{ + FUNC_START(); + Q_ASSERT(!firstSetSeqId.contains('_') && !firstSetSeqId.contains('t') + && !firstSetSeqId.contains('a') && !firstSetSeqId.contains('b') + && !firstSetSeqId.contains('s') && !firstSetSeqId.contains('i') + && !firstSetSeqId.contains('o') && !firstSetSeqId.contains('d')); + + QString substitution; + if (advance() != 'S') { + error(tr("Invalid substitution")); + } else if (firstSetSeqId.contains(peek())) { + int substIndex = parseSeqId() + 1; + if (!parseError && substIndex >= substitutions.size()) + error(tr("Invalid substitution: element %1 was requested, " + "but there are only %2"). + arg(substIndex + 1).arg(substitutions.size())); + else + substitution = substitutions.at(substIndex); + if (!parseError && advance() != '_') + error(tr("Invalid substitution")); + } else { + switch (advance().toAscii()) { + case '_': + if (substitutions.isEmpty()) + error(tr("Invalid substitution: There are no elements")); + else + substitution = substitutions.first(); + break; + case 't': + substitution = "::std::"; + break; + case 'a': + substitution = "::std::allocator"; + break; + case 'b': + substitution = "::std::basic_string"; + break; + case 's': + substitution = "::std::basic_string, ::std::allocator >"; + break; + case 'i': + substitution = + "::std::basic_istream >"; + break; + case 'o': + substitution = + "::std::basic_ostream >"; + break; + case 'd': + substitution = + "::std::basic_iostream >"; + break; + default: + error(tr("Invalid substitution")); + } + } + + FUNC_END(substitution); + return substitution; +} + + +/* + * The is a sequence number in base 36, using digits + * and upper case letters. + */ +int NameDemanglerPrivate::parseSeqId() +{ + FUNC_START(); + int seqId = parseNonNegativeNumber(36); + FUNC_END(QString::number(seqId)); + return seqId; +} + +/* + * ::= TV # virtual table + * ::= TT # VTT structure (construction vtable index) + * ::= TI # typeinfo structure + * ::= TS # typeinfo name (null-terminated byte string) + * ::= GV # Guard variable for one-time initialization + * ::= T + * ::= Tc + * # base is the nominal target function of thunk + * # first call-offset is 'this' adjustment + * # second call-offset is result adjustment + */ +const QString NameDemanglerPrivate::parseSpecialName() +{ + FUNC_START(); + Q_ASSERT(!firstSetCallOffset.contains('V') + && !firstSetCallOffset.contains('T') + && !firstSetCallOffset.contains('I') + && !firstSetCallOffset.contains('S') + && !firstSetCallOffset.contains('c')); + + QString name; + QString str = readAhead(2); + if (str == "TV") { + advance(2); + name = "[virtual table of " + parseType() + "]"; + } else if (str == "TT") { + advance(2); + name = "[VTT struct of " + parseType() + "]"; + } else if (str == "TI") { + advance(2); + name = "typeid(" + parseType() + ")"; + } else if (str == "TS") { + advance(2); + name = "typeid(" + parseType() + ").name()"; + } else if (str == "GV") { + advance(2); + name = "guard variable of " + parseName() + "]"; + } else if (str == "Tc") { + advance(2); + parseCallOffset(); + if (!parseError) + parseCallOffset(); + if (!parseError) + parseEncoding(); + } else if (advance() == 'T') { + parseCallOffset(); + if (!parseError) + parseEncoding(); + } else { + error(tr("Invalid special-name")); + } + + FUNC_END(name); + return name; +} + +/* + * ::= + * ::= St # ::std:: + */ +const QString NameDemanglerPrivate::parseUnscopedName() +{ + FUNC_START(); + Q_ASSERT(!firstSetUnqualifiedName.contains('S')); + + QString name; + if (readAhead(2) == "St") { + advance(2); + name = "::std" + parseUnqualifiedName(); + } else if (firstSetUnqualifiedName.contains(peek())) { + name = parseUnqualifiedName(); + } else { + error(tr("Invalid unqualified-name")); + } + + FUNC_END(name); + return name; +} + + +/* + * := Z E [] + * := Z E s [] + * + * Note that can start with 's', so we need to to read-ahead. + */ +const QString NameDemanglerPrivate::parseLocalName() +{ + FUNC_START(); + + QString name; + if (advance() != 'Z') { + error(tr("Invalid local-name")); + } else { + name = parseEncoding(); + if (!parseError && advance() != 'E') { + error(tr("Invalid local-name")); + } else { + QString str = readAhead(2); + QChar next = peek(); + if (str == "sp" || str == "sr" || str == "st" || str == "sz" + || str == "sZ" || (next != 's' && firstSetName.contains(next))) + name += parseName(); + else if (next == 's') { + advance(); + name += "::\"string literal\""; + } else { + error(tr("Invalid local-name")); + } + if (!parseError && firstSetDiscriminator.contains(peek())) + parseDiscriminator(); + } + } + + FUNC_END(name); + return name; +} + +/* := _ */ +int NameDemanglerPrivate::parseDiscriminator() +{ + int index; + if (advance() != '_') { + error(tr("Invalid discriminator")); + index = -1; + } else { + index = parseNonNegativeNumber(); + } + + FUNC_END(QString::number(index)); + return index; +} + +/* + * ::= C1 # complete object constructor + * ::= C2 # base object constructor + * ::= C3 # complete object allocating constructor + * ::= D0 # deleting destructor + * ::= D1 # complete object destructor + * ::= D2 # base object destructor + */ +const QString NameDemanglerPrivate::parseCtorDtorName() +{ + FUNC_START(); + + QString name; + bool destructor = false; + switch (advance().toAscii()) { + case 'C': + switch (advance().toAscii()) { + case '1': + case '2': + case '3': + break; + default: + error(tr("Invalid ctor-dtor-name")); + } + break; + case 'D': + switch (advance().toAscii()) { + case '0': + case '1': + case '2': + destructor = true; + break; + default: + error(tr("Invalid ctor-dtor-name")); + } + break; + default: + error(tr("Invalid ctor-dtor-name")); + } + + if (!parseError) { + name = substitutions.last(); + int templateArgsStart = name.indexOf('<'); + if (templateArgsStart != -1) + name.remove(templateArgsStart, + name.indexOf('>') - templateArgsStart + 1); + int lastComponentStart = name.lastIndexOf("::"); + if (lastComponentStart != -1) + name.remove(0, lastComponentStart + 2); + if (destructor) + name.prepend('~'); + } + + FUNC_END(name); + return name; +} + +/* This will probably need the number of characters to read. */ +const QString NameDemanglerPrivate::parseIdentifier(int len) +{ + FUNC_START(); + + const QString id = mangledName.mid(pos, len); + advance(len); + + FUNC_END(id); + return id; +} + +/* + * ::= h _ + * ::= v _ + */ +void NameDemanglerPrivate::parseCallOffset() +{ + FUNC_START(); + + switch (advance().toAscii()) { + case 'h': + parseNvOffset(); + break; + case 'v': + parseVOffset(); + break; + default: + error(tr("Invalid call-offset")); + } + if (!parseError && advance() != '_') + error(tr("Invalid call-offset")); + FUNC_END(QString()); +} + +/* ::= # non-virtual base override */ +void NameDemanglerPrivate::parseNvOffset() +{ + FUNC_START(); + parseNumber(); + FUNC_END(QString()); +} + +/* + * ::= _ + * # virtual base override, with vcall offset + */ +void NameDemanglerPrivate::parseVOffset() +{ + FUNC_START(); + + parseNumber(); + if (advance() != '_') + error(tr("Invalid v-offset")); + parseNumber(); + + FUNC_END(QString()); +} + +int NameDemanglerPrivate::parseDigit() +{ + FUNC_START(); + + int digit = advance().digitValue(); + if (digit == -1) + error(tr("Invalid digit")); + + FUNC_END(QString::number(digit)); + return digit; +} + +void NameDemanglerPrivate::error(const QString &errorSpec) +{ + parseError = true; + m_errorString = tr("At position %1: ").arg(pos) + errorSpec; +} + +QChar NameDemanglerPrivate::peek(int ahead) +{ + Q_ASSERT(pos >= 0); + if (pos + ahead < mangledName.size()) { + return mangledName[pos + ahead]; + } else { + return eoi; + } +} + +QChar NameDemanglerPrivate::advance(int steps) +{ + Q_ASSERT(steps > 0); + if (pos + steps <= mangledName.size()) { + QChar c = mangledName[pos]; + pos += steps; + return c; + } else { + pos = mangledName.size(); + parseError = true; + return eoi; + } +} + +const QString NameDemanglerPrivate::readAhead(int charCount) +{ + QString str; + if (pos + charCount < mangledName.size()) + str = mangledName.mid(pos, charCount); + else + str.fill(eoi, charCount); + return str; +} + +void NameDemanglerPrivate::setupFirstSets() +{ + firstSetMangledName << '_'; + firstSetNestedName << 'N'; + firstSetFunctionType << 'F'; + firstSetArrayType << 'A'; + firstSetPointerToMemberType << 'M'; + firstSetTemplateParam << 'T'; + firstSetSubstitution << 'S'; + firstSetLocalName << 'Z'; + firstSetTemplateArgs << 'I'; + firstSetExprPrimary << 'L'; + firstSetDiscriminator << '_'; + firstSetSpecialName << 'T' << 'G'; + firstSetCtorDtorName << 'C' << 'D'; + firstSetCallOffset << 'h' << 'v'; + firstSetCvQualifiers << 'K' << 'V' << 'r'; + firstSetOperatorName << 'n' << 'd' << 'p' << 'a' << 'c' << 'm' << 'r' + << 'o' << 'e' << 'l' << 'g' << 'i' << 'q' << 's' + << 'v'; + firstSetBuiltinType << 'v' << 'w' << 'b' << 'c' << 'a' << 'h' << 's' << 't' + << 'i' << 'j' << 'l' << 'm' << 'x' << 'y' << 'n' << 'o' + << 'f' << 'g' << 'e' << 'd' << 'z' << 'D' << 'u'; + firstSetPositiveNumber + << '1' << '2' << '3' << '4' << '5' << '6' << '7' << '8' << '9'; + (firstSetNonNegativeNumber += firstSetPositiveNumber) << '0'; + (firstSetNumber += firstSetPositiveNumber) << 'n'; + firstSetSourceName = firstSetPositiveNumber; + firstSetSeqId += firstSetNonNegativeNumber; + for (char c = 'A'; c != 'Z'; ++c) + firstSetSeqId << c; + (((firstSetExpression += firstSetOperatorName) += firstSetTemplateParam) + /* += firstSetFunctionParam */ += firstSetExprPrimary) + << 'c' << 's' << 'a'; + firstSetUnqualifiedName = firstSetOperatorName | firstSetCtorDtorName + | firstSetSourceName; + firstSetPrefix2 = firstSetUnqualifiedName; + firstSetPrefix = firstSetTemplateParam | firstSetSubstitution + | firstSetPrefix2; + firstSetUnscopedName = firstSetUnqualifiedName | (QSet() << 'S'); + firstSetName = firstSetNestedName | firstSetUnscopedName + | firstSetSubstitution | firstSetLocalName; + + /* + * The first set of is much smaller than + * the grammar claims. + * firstSetClassEnumType = firstSetName; + */ + (firstSetClassEnumType += firstSetNonNegativeNumber) << 'N' << 'D' << 'Z'; + + firstSetEncoding = firstSetName | firstSetSpecialName; + ((((((((firstSetType += firstSetBuiltinType) += firstSetFunctionType) + += firstSetClassEnumType) += firstSetArrayType) + += firstSetPointerToMemberType) += firstSetTemplateParam) + += firstSetSubstitution) += firstSetCvQualifiers) << 'P' << 'R' + << 'O' << 'C' << 'G' << 'U' << 'D'; + firstSetBareFunctionType = firstSetType; + ((firstSetTemplateArg += firstSetType) += firstSetExprPrimary) + << 'X' << 'I' << 's'; + +#if 0 + foreach(QChar c, firstSetType) + qDebug("'%c'", c.toAscii()); + qDebug("\n"); + foreach(QChar c, firstSetExprPrimary) + qDebug("'%c'", c.toAscii()); +#endif +} + +void NameDemanglerPrivate::setupOps() +{ + ops["nw"] = new UnaryOperator("nw", "new "); + ops["na"] = new ArrayNewOperator; + ops["dl"] = new UnaryOperator("dl", "delete "); + ops["da"] = new UnaryOperator("da", "delete[] "); + ops["ps"] = new UnaryOperator("ps", "+"); + ops["ng"] = new UnaryOperator("ng", "-"); + ops["ad"] = new UnaryOperator("ad", "&"); + ops["de"] = new UnaryOperator("de", "*"); + ops["co"] = new UnaryOperator("co", "~"); + ops["pl"] = new BinaryOperator("pl", "+"); + ops["mi"] = new BinaryOperator("mi", "-"); + ops["ml"] = new BinaryOperator("ml", "*"); + ops["dv"] = new BinaryOperator("dv", "/"); + ops["rm"] = new BinaryOperator("rm", "%"); + ops["an"] = new BinaryOperator("an", "&"); + ops["or"] = new BinaryOperator("or", "|"); + ops["eo"] = new BinaryOperator("eo", "^"); + ops["aS"] = new BinaryOperator("aS", "="); + ops["pL"] = new BinaryOperator("pl", "+="); + ops["mI"] = new BinaryOperator("mI", "-="); + ops["mL"] = new BinaryOperator("mL", "*="); + ops["dV"] = new BinaryOperator("dV", "/="); + ops["rM"] = new BinaryOperator("rM", "%="); + ops["aN"] = new BinaryOperator("aN", "&="); + ops["oR"] = new BinaryOperator("oR", "|="); + ops["eO"] = new BinaryOperator("eO", "^="); + ops["ls"] = new BinaryOperator("ls", "<<"); + ops["rs"] = new BinaryOperator("rs", ">>"); + ops["lS"] = new BinaryOperator("lS", "<<="); + ops["rS"] = new BinaryOperator("rS", ">>="); + ops["eq"] = new BinaryOperator("eq", "=="); + ops["ne"] = new BinaryOperator("ne", "!="); + ops["lt"] = new BinaryOperator("lt", "<"); + ops["gt"] = new BinaryOperator("gt", ">"); + ops["le"] = new BinaryOperator("le", "<="); + ops["ge"] = new BinaryOperator("ge", ">="); + ops["nt"] = new UnaryOperator("nt", "!"); + ops["aa"] = new BinaryOperator("aa", "&&"); + ops["oo"] = new BinaryOperator("oo", "||"); + ops["pp"] = new UnaryOperator("pp", "++"); // Prefix? + ops["mm"] = new UnaryOperator("mm", "--"); // Prefix? + ops["cm"] = new BinaryOperator("cm", ","); + ops["pm"] = new BinOpWithNoSpaces("pm", "->*"); + ops["pt"] = new BinOpWithNoSpaces("pm", "->"); + ops["cl"] = new FunctionCallOperator; + ops["ix"] = new ArrayAccessOperator; + ops["qu"] = new QuestionMarkOperator; + ops["st"] = new SizeAlignOfOperator("st", "sizeof"); + ops["sz"] = new SizeAlignOfOperator("sz", "sizeof"); + ops["at"] = new SizeAlignOfOperator("at", "alignof"); + ops["az"] = new SizeAlignOfOperator("az", "alignof"); +} + +void NameDemanglerPrivate::addSubstitution(const QString &symbol) +{ + if (!symbol.isEmpty() && !substitutions.contains(symbol)) + substitutions.append(symbol); +} + +void NameDemanglerPrivate::insertQualifier(QString &type, + const QString &qualifier) +{ + const int funcAnchor = type.indexOf(QRegExp("\\([^*&]")); + const int qualAnchor = type.indexOf(QRegExp("(\\*|\\&|const|volatile)\\)")); + int insertionPos; + QString insertionString = qualifier; + if (funcAnchor == -1) { + if (qualAnchor == -1) { + insertionPos = type.size(); + } else { + insertionPos = type.indexOf(')', qualAnchor);; + } + } else if (qualAnchor == -1 || funcAnchor < qualAnchor) { + if (qualifier == "*" || qualifier == "&") { + insertionPos = funcAnchor; + insertionString = "(" + qualifier + ")"; + } else { + insertionPos = type.size(); // Qualifier for pointer to member. + } + } else { + insertionPos = type.indexOf(')', qualAnchor);; + } + if ((insertionString == "*" || insertionString == "&") + && (type[insertionPos - 1] != '*' && type[insertionPos - 1] != '&')) + insertionString.prepend(' '); + type.insert(insertionPos, insertionString); +} + +NameDemangler::NameDemangler() : pImpl(new NameDemanglerPrivate) { } + +NameDemangler::~NameDemangler() +{ + delete pImpl; +} + +bool NameDemangler::demangle(const QString &mangledName) +{ + return pImpl->demangle(mangledName); +} + +const QString &NameDemangler::errorString() const +{ + return pImpl->errorString(); +} + +const QString &NameDemangler::demangledName() const +{ + return pImpl->demangledName(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/debugger/name_demangler.h b/src/plugins/debugger/name_demangler.h new file mode 100644 index 00000000000..9c53aa470ed --- /dev/null +++ b/src/plugins/debugger/name_demangler.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef NAME_DEMANGLER_H +#define NAME_DEMANGLER_H + +QT_BEGIN_NAMESPACE + +class QString; + +class NameDemanglerPrivate; + +class NameDemangler +{ +public: + NameDemangler(); + ~NameDemangler(); + + /* + * Demangles a mangled name. Also accepts a non-demangled name, + * in which case it is not transformed. + * Returns true <=> the name is not mangled or it is mangled correctly + * according to the specification. + */ + bool demangle(const QString &mangledName); + + /* + * A textual description of the error encountered, if there was one. + * Only valid if demangle() returned false. + */ + const QString &errorString() const; + + /* + * The demangled name. If the original name was not mangled, this + * is identical to the input. + * Only valid if demangle() returned true. + */ + const QString &demangledName() const; + +private: + NameDemanglerPrivate *pImpl; +}; + +QT_END_NAMESPACE + +#endif // Include guard.