2009-03-25 13:42:47 +01:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2011-01-11 16:28:15 +01:00
|
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** No Commercial Usage
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** This file contains pre-release code and may not be distributed.
|
|
|
|
|
** You may use this file in accordance with the terms and conditions
|
|
|
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
|
|
|
** this package.
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
|
|
|
|
** 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.
|
|
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** 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.
|
|
|
|
|
**
|
|
|
|
|
** If you have questions regarding the use of this file, please contact
|
|
|
|
|
** Nokia at qt-info@nokia.com.
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "watchutils.h"
|
2010-04-26 18:54:08 +02:00
|
|
|
#include "watchdata.h"
|
|
|
|
|
#include "debuggerstringutils.h"
|
2009-09-18 16:55:17 +02:00
|
|
|
#include "gdb/gdbmi.h"
|
2010-04-26 18:54:08 +02:00
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2009-03-25 13:42:47 +01:00
|
|
|
|
2009-10-01 16:38:08 +02:00
|
|
|
#include <coreplugin/ifile.h>
|
|
|
|
|
|
2009-05-14 14:29:37 +02:00
|
|
|
#include <texteditor/basetexteditor.h>
|
|
|
|
|
#include <texteditor/basetextmark.h>
|
|
|
|
|
#include <texteditor/itexteditor.h>
|
|
|
|
|
#include <texteditor/texteditorconstants.h>
|
2010-11-04 11:46:16 +01:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2009-05-14 14:29:37 +02:00
|
|
|
|
|
|
|
|
#include <cpptools/cpptoolsconstants.h>
|
2010-12-03 13:49:35 +01:00
|
|
|
#include <cpptools/abstracteditorsupport.h>
|
2009-05-14 14:29:37 +02:00
|
|
|
|
2010-12-03 13:49:35 +01:00
|
|
|
#include <cplusplus/ModelManagerInterface.h>
|
2009-05-14 14:29:37 +02:00
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
2009-10-16 16:26:28 +02:00
|
|
|
#include <cplusplus/Overview.h>
|
|
|
|
|
#include <Symbols.h>
|
|
|
|
|
#include <Scope.h>
|
2009-05-14 14:29:37 +02:00
|
|
|
|
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
|
2009-03-25 13:42:47 +01:00
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtCore/QTime>
|
|
|
|
|
#include <QtCore/QStringList>
|
2009-04-29 14:15:09 +02:00
|
|
|
#include <QtCore/QCoreApplication>
|
|
|
|
|
#include <QtCore/QTextStream>
|
2009-10-16 16:26:28 +02:00
|
|
|
#include <QtCore/QHash>
|
2009-04-29 14:15:09 +02:00
|
|
|
|
2009-05-14 14:29:37 +02:00
|
|
|
#include <QtGui/QTextCursor>
|
|
|
|
|
#include <QtGui/QPlainTextEdit>
|
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
enum { debug = 0 };
|
2009-10-16 16:26:28 +02:00
|
|
|
|
|
|
|
|
// Debug helpers for code model. @todo: Move to some CppTools library?
|
|
|
|
|
namespace CPlusPlus {
|
|
|
|
|
|
|
|
|
|
static void debugCppSymbolRecursion(QTextStream &str, const Overview &o,
|
|
|
|
|
const Symbol &s, bool doRecurse = true,
|
|
|
|
|
int recursion = 0)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < recursion; i++)
|
|
|
|
|
str << " ";
|
|
|
|
|
str << "Symbol: " << o.prettyName(s.name()) << " at line " << s.line();
|
|
|
|
|
if (s.isFunction())
|
|
|
|
|
str << " function";
|
|
|
|
|
if (s.isClass())
|
|
|
|
|
str << " class";
|
|
|
|
|
if (s.isDeclaration())
|
|
|
|
|
str << " declaration";
|
|
|
|
|
if (s.isBlock())
|
|
|
|
|
str << " block";
|
2010-08-12 12:35:22 +02:00
|
|
|
if (doRecurse && s.isScope()) {
|
2010-08-11 12:26:02 +02:00
|
|
|
const Scope *scoped = s.asScope();
|
2009-10-16 16:26:28 +02:00
|
|
|
const int size = scoped->memberCount();
|
|
|
|
|
str << " scoped symbol of " << size << '\n';
|
|
|
|
|
for (int m = 0; m < size; m++)
|
|
|
|
|
debugCppSymbolRecursion(str, o, *scoped->memberAt(m), true, recursion + 1);
|
|
|
|
|
} else {
|
|
|
|
|
str << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDebug operator<<(QDebug d, const Symbol &s)
|
|
|
|
|
{
|
|
|
|
|
QString output;
|
|
|
|
|
CPlusPlus::Overview o;
|
|
|
|
|
QTextStream str(&output);
|
|
|
|
|
debugCppSymbolRecursion(str, o, s, true, 0);
|
|
|
|
|
d.nospace() << output;
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDebug operator<<(QDebug d, const Scope &scope)
|
|
|
|
|
{
|
|
|
|
|
QString output;
|
|
|
|
|
Overview o;
|
|
|
|
|
QTextStream str(&output);
|
2010-08-11 12:26:02 +02:00
|
|
|
const int size = scope.memberCount();
|
2009-10-16 16:26:28 +02:00
|
|
|
str << "Scope of " << size;
|
2010-08-11 12:26:02 +02:00
|
|
|
if (scope.isNamespace())
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " namespace";
|
2010-08-11 12:26:02 +02:00
|
|
|
if (scope.isClass())
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " class";
|
2010-08-11 12:26:02 +02:00
|
|
|
if (scope.isEnum())
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " enum";
|
2010-08-11 12:26:02 +02:00
|
|
|
if (scope.isBlock())
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " block";
|
2010-08-11 12:26:02 +02:00
|
|
|
if (scope.isFunction())
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " function";
|
2010-08-11 12:26:02 +02:00
|
|
|
if (scope.isFunction())
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " prototype";
|
2010-08-11 15:48:21 +02:00
|
|
|
#if 0 // ### port me
|
2010-08-11 12:26:02 +02:00
|
|
|
if (const Symbol *owner = &scope) {
|
2009-10-16 16:26:28 +02:00
|
|
|
str << " owner: ";
|
|
|
|
|
debugCppSymbolRecursion(str, o, *owner, false, 0);
|
|
|
|
|
} else {
|
|
|
|
|
str << " 0-owner\n";
|
|
|
|
|
}
|
2010-08-11 12:26:02 +02:00
|
|
|
#endif
|
2009-10-16 16:26:28 +02:00
|
|
|
for (int s = 0; s < size; s++)
|
2010-08-11 12:26:02 +02:00
|
|
|
debugCppSymbolRecursion(str, o, *scope.memberAt(s), true, 2);
|
2009-10-16 16:26:28 +02:00
|
|
|
d.nospace() << output;
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
} // namespace CPlusPlus
|
|
|
|
|
|
2009-03-25 13:42:47 +01:00
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray dotEscape(QByteArray str)
|
2009-03-25 13:42:47 +01:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
str.replace(' ', '.');
|
|
|
|
|
str.replace('\\', '.');
|
|
|
|
|
str.replace('/', '.');
|
2009-03-25 13:42:47 +01:00
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString currentTime()
|
|
|
|
|
{
|
|
|
|
|
return QTime::currentTime().toString(QLatin1String("hh:mm:ss.zzz"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isSkippableFunction(const QString &funcName, const QString &fileName)
|
|
|
|
|
{
|
|
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qobject.cpp")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("kernel/moc_qobject.cpp")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qmetaobject.cpp")))
|
|
|
|
|
return true;
|
2009-08-20 11:49:56 +02:00
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qmetaobject_p.h")))
|
|
|
|
|
return true;
|
2009-03-25 13:42:47 +01:00
|
|
|
if (fileName.endsWith(QLatin1String(".moc")))
|
|
|
|
|
return true;
|
|
|
|
|
|
2010-02-01 12:43:56 +01:00
|
|
|
if (funcName.endsWith(QLatin1String("::qt_metacall")))
|
2009-03-25 13:42:47 +01:00
|
|
|
return true;
|
2010-02-01 12:43:56 +01:00
|
|
|
if (funcName.endsWith(QLatin1String("::d_func")))
|
2009-08-20 11:49:56 +02:00
|
|
|
return true;
|
2010-02-01 12:43:56 +01:00
|
|
|
if (funcName.endsWith(QLatin1String("::q_func")))
|
2009-08-20 11:49:56 +02:00
|
|
|
return true;
|
2009-03-25 13:42:47 +01:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isLeavableFunction(const QString &funcName, const QString &fileName)
|
|
|
|
|
{
|
|
|
|
|
if (funcName.endsWith(QLatin1String("QObjectPrivate::setCurrentSender")))
|
|
|
|
|
return true;
|
2009-08-20 11:49:56 +02:00
|
|
|
if (funcName.endsWith(QLatin1String("QMutexPool::get")))
|
|
|
|
|
return true;
|
2011-02-02 11:52:39 +01:00
|
|
|
|
|
|
|
|
if (fileName.endsWith(QLatin1String(".cpp"))) {
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qmetaobject.cpp"))
|
|
|
|
|
&& funcName.endsWith(QLatin1String("QMetaObject::methodOffset")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qobject.cpp"))
|
|
|
|
|
&& (funcName.endsWith(QLatin1String("QObjectConnectionListVector::at"))
|
|
|
|
|
|| funcName.endsWith(QLatin1String("~QObject"))))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qmutex.cpp")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qthread.cpp")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qthread_unix.cpp")))
|
|
|
|
|
return true;
|
|
|
|
|
} else if (fileName.endsWith(QLatin1String(".h"))) {
|
|
|
|
|
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qobject.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qmutex.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qvector.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qlist.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qhash.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qmap.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qshareddata.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qstring.h")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.endsWith(QLatin1String("/qglobal.h")))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (fileName.contains(QLatin1String("/qbasicatomic")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.contains(QLatin1String("/qorderedmutexlocker_p")))
|
|
|
|
|
return true;
|
|
|
|
|
if (fileName.contains(QLatin1String("/qatomic")))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-03-25 13:42:47 +01:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
bool isLetterOrNumber(char c)
|
|
|
|
|
{
|
|
|
|
|
return (c >= 'a' && c <= 'z')
|
|
|
|
|
|| (c >= 'A' && c <= 'Z')
|
|
|
|
|
|| (c >= '0' && c <= '9');
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-25 13:42:47 +01:00
|
|
|
bool hasLetterOrNumber(const QString &exp)
|
|
|
|
|
{
|
|
|
|
|
const QChar underscore = QLatin1Char('_');
|
|
|
|
|
for (int i = exp.size(); --i >= 0; )
|
|
|
|
|
if (exp.at(i).isLetterOrNumber() || exp.at(i) == underscore)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasSideEffects(const QString &exp)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: complete?
|
|
|
|
|
return exp.contains(QLatin1String("-="))
|
|
|
|
|
|| exp.contains(QLatin1String("+="))
|
|
|
|
|
|| exp.contains(QLatin1String("/="))
|
2009-05-05 20:33:31 +02:00
|
|
|
|| exp.contains(QLatin1String("%="))
|
2009-03-25 13:42:47 +01:00
|
|
|
|| exp.contains(QLatin1String("*="))
|
|
|
|
|
|| exp.contains(QLatin1String("&="))
|
|
|
|
|
|| exp.contains(QLatin1String("|="))
|
|
|
|
|
|| exp.contains(QLatin1String("^="))
|
|
|
|
|
|| exp.contains(QLatin1String("--"))
|
|
|
|
|
|| exp.contains(QLatin1String("++"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isKeyWord(const QString &exp)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: incomplete
|
|
|
|
|
return exp == QLatin1String("class")
|
|
|
|
|
|| exp == QLatin1String("const")
|
|
|
|
|
|| exp == QLatin1String("do")
|
|
|
|
|
|| exp == QLatin1String("if")
|
|
|
|
|
|| exp == QLatin1String("return")
|
|
|
|
|
|| exp == QLatin1String("struct")
|
|
|
|
|
|| exp == QLatin1String("template")
|
|
|
|
|
|| exp == QLatin1String("void")
|
|
|
|
|
|| exp == QLatin1String("volatile")
|
|
|
|
|
|| exp == QLatin1String("while");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool startsWithDigit(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
return !str.isEmpty() && str.at(0).isDigit();
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArray stripPointerType(QByteArray type)
|
2009-03-25 13:42:47 +01:00
|
|
|
{
|
2010-09-01 17:36:09 +02:00
|
|
|
if (type.endsWith('*'))
|
2009-03-25 13:42:47 +01:00
|
|
|
type.chop(1);
|
2010-09-01 17:36:09 +02:00
|
|
|
if (type.endsWith("* const"))
|
2009-03-25 13:42:47 +01:00
|
|
|
type.chop(7);
|
2010-09-01 17:36:09 +02:00
|
|
|
if (type.endsWith(' '))
|
2009-03-25 13:42:47 +01:00
|
|
|
type.chop(1);
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-16 16:26:28 +02:00
|
|
|
/* getUninitializedVariables(): Get variables that are not initialized
|
|
|
|
|
* at a certain line of a function from the code model to be able to
|
|
|
|
|
* indicate them as not in scope in the locals view.
|
|
|
|
|
* Find document + function in the code model, do a double check and
|
|
|
|
|
* collect declarative symbols that are in the function past or on
|
|
|
|
|
* the current line. blockRecursion() recurses up the scopes
|
|
|
|
|
* and collect symbols declared past or on the current line.
|
|
|
|
|
* Recursion goes up from the innermost scope, keeping a map
|
|
|
|
|
* of occurrences seen, to be able to derive the names of
|
|
|
|
|
* shadowed variables as the debugger sees them:
|
|
|
|
|
\code
|
|
|
|
|
int x; // Occurrence (1), should be reported as "x <shadowed 1>"
|
|
|
|
|
if (true) {
|
|
|
|
|
int x = 5; (2) // Occurrence (2), should be reported as "x"
|
|
|
|
|
}
|
|
|
|
|
\endcode
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef QHash<QString, int> SeenHash;
|
|
|
|
|
|
|
|
|
|
static void blockRecursion(const CPlusPlus::Overview &overview,
|
|
|
|
|
const CPlusPlus::Scope *scope,
|
|
|
|
|
unsigned line,
|
|
|
|
|
QStringList *uninitializedVariables,
|
|
|
|
|
SeenHash *seenHash,
|
|
|
|
|
int level = 0)
|
|
|
|
|
{
|
2010-08-11 12:26:02 +02:00
|
|
|
const int size = scope->memberCount();
|
2009-10-16 16:26:28 +02:00
|
|
|
for (int s = 0; s < size; s++){
|
2010-08-11 12:26:02 +02:00
|
|
|
const CPlusPlus::Symbol *symbol = scope->memberAt(s);
|
2009-10-16 16:26:28 +02:00
|
|
|
if (symbol->isDeclaration()) {
|
|
|
|
|
// Find out about shadowed symbols by bookkeeping
|
|
|
|
|
// the already seen occurrences in a hash.
|
|
|
|
|
const QString name = overview.prettyName(symbol->name());
|
|
|
|
|
SeenHash::iterator it = seenHash->find(name);
|
|
|
|
|
if (it == seenHash->end()) {
|
|
|
|
|
it = seenHash->insert(name, 0);
|
|
|
|
|
} else {
|
|
|
|
|
++(it.value());
|
2010-01-29 21:33:57 +01:00
|
|
|
}
|
2009-10-16 16:26:28 +02:00
|
|
|
// Is the declaration on or past the current line, that is,
|
|
|
|
|
// the variable not initialized.
|
|
|
|
|
if (symbol->line() >= line)
|
|
|
|
|
uninitializedVariables->push_back(WatchData::shadowedName(name, it.value()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Next block scope.
|
2010-08-11 12:26:02 +02:00
|
|
|
if (const CPlusPlus::Scope *enclosingScope = scope->enclosingBlock())
|
2009-10-16 16:26:28 +02:00
|
|
|
blockRecursion(overview, enclosingScope, line, uninitializedVariables, seenHash, level + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inline helper with integer error return codes.
|
|
|
|
|
static inline
|
|
|
|
|
int getUninitializedVariablesI(const CPlusPlus::Snapshot &snapshot,
|
|
|
|
|
const QString &functionName,
|
|
|
|
|
const QString &file,
|
|
|
|
|
int line,
|
|
|
|
|
QStringList *uninitializedVariables)
|
|
|
|
|
{
|
|
|
|
|
uninitializedVariables->clear();
|
|
|
|
|
// Find document
|
2009-12-07 10:54:27 +01:00
|
|
|
if (snapshot.isEmpty() || functionName.isEmpty() || file.isEmpty() || line < 1)
|
2009-10-16 16:26:28 +02:00
|
|
|
return 1;
|
2009-12-07 10:54:27 +01:00
|
|
|
const CPlusPlus::Snapshot::const_iterator docIt = snapshot.find(file);
|
|
|
|
|
if (docIt == snapshot.end())
|
2009-10-16 16:26:28 +02:00
|
|
|
return 2;
|
|
|
|
|
const CPlusPlus::Document::Ptr doc = docIt.value();
|
|
|
|
|
// Look at symbol at line and find its function. Either it is the
|
|
|
|
|
// function itself or some expression/variable.
|
2010-05-17 13:01:56 +02:00
|
|
|
const CPlusPlus::Symbol *symbolAtLine = doc->lastVisibleSymbolAt(line, 0);
|
2009-10-16 16:26:28 +02:00
|
|
|
if (!symbolAtLine)
|
|
|
|
|
return 4;
|
2009-10-19 10:59:46 +02:00
|
|
|
// First figure out the function to do a safety name check
|
|
|
|
|
// and the innermost scope at cursor position
|
2009-10-16 16:26:28 +02:00
|
|
|
const CPlusPlus::Function *function = 0;
|
2009-10-19 10:59:46 +02:00
|
|
|
const CPlusPlus::Scope *innerMostScope = 0;
|
|
|
|
|
if (symbolAtLine->isFunction()) {
|
2009-10-16 16:26:28 +02:00
|
|
|
function = symbolAtLine->asFunction();
|
2009-10-19 10:59:46 +02:00
|
|
|
if (function->memberCount() == 1) // Skip over function block
|
|
|
|
|
if (CPlusPlus::Block *block = function->memberAt(0)->asBlock())
|
2010-08-11 12:26:02 +02:00
|
|
|
innerMostScope = block;
|
2009-10-16 16:26:28 +02:00
|
|
|
} else {
|
2010-08-11 12:26:02 +02:00
|
|
|
if (const CPlusPlus::Scope *functionScope = symbolAtLine->enclosingFunction()) {
|
|
|
|
|
function = functionScope->asFunction();
|
2009-10-19 10:59:46 +02:00
|
|
|
innerMostScope = symbolAtLine->isBlock() ?
|
2010-08-11 12:26:02 +02:00
|
|
|
symbolAtLine->asBlock() :
|
|
|
|
|
symbolAtLine->enclosingBlock();
|
2009-10-19 10:59:46 +02:00
|
|
|
}
|
2009-10-16 16:26:28 +02:00
|
|
|
}
|
2009-10-19 10:59:46 +02:00
|
|
|
if (!function || !innerMostScope)
|
2009-10-16 16:26:28 +02:00
|
|
|
return 7;
|
|
|
|
|
// Compare function names with a bit off fuzz,
|
|
|
|
|
// skipping modules from a CDB symbol "lib!foo" or namespaces
|
|
|
|
|
// that the code model does not show at this point
|
|
|
|
|
CPlusPlus::Overview overview;
|
|
|
|
|
const QString name = overview.prettyName(function->name());
|
|
|
|
|
if (!functionName.endsWith(name))
|
|
|
|
|
return 11;
|
|
|
|
|
if (functionName.size() > name.size()) {
|
|
|
|
|
const char previousChar = functionName.at(functionName.size() - name.size() - 1).toLatin1();
|
|
|
|
|
if (previousChar != ':' && previousChar != '!' )
|
|
|
|
|
return 11;
|
|
|
|
|
}
|
2009-10-19 10:59:46 +02:00
|
|
|
// Starting from the innermost block scope, collect declarations.
|
2009-10-16 16:26:28 +02:00
|
|
|
SeenHash seenHash;
|
|
|
|
|
blockRecursion(overview, innerMostScope, line, uninitializedVariables, &seenHash);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getUninitializedVariables(const CPlusPlus::Snapshot &snapshot,
|
|
|
|
|
const QString &function,
|
|
|
|
|
const QString &file,
|
|
|
|
|
int line,
|
|
|
|
|
QStringList *uninitializedVariables)
|
|
|
|
|
{
|
|
|
|
|
const int rc = getUninitializedVariablesI(snapshot, function, file, line, uninitializedVariables);
|
|
|
|
|
if (debug) {
|
|
|
|
|
QString msg;
|
|
|
|
|
QTextStream str(&msg);
|
|
|
|
|
str << "getUninitializedVariables() " << function << ' ' << file << ':' << line
|
|
|
|
|
<< " returns (int) " << rc << " '"
|
|
|
|
|
<< uninitializedVariables->join(QString(QLatin1Char(','))) << '\'';
|
|
|
|
|
if (rc)
|
2009-12-07 10:54:27 +01:00
|
|
|
str << " of " << snapshot.size() << " documents";
|
2009-10-16 16:26:28 +02:00
|
|
|
qDebug() << msg;
|
|
|
|
|
}
|
|
|
|
|
return rc == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArray gdbQuoteTypes(const QByteArray &type)
|
2009-03-25 13:42:47 +01:00
|
|
|
{
|
|
|
|
|
// gdb does not understand sizeof(Core::IFile*).
|
|
|
|
|
// "sizeof('Core::IFile*')" is also not acceptable,
|
|
|
|
|
// it needs to be "sizeof('Core::IFile'*)"
|
|
|
|
|
//
|
|
|
|
|
// We never will have a perfect solution here (even if we had a full blown
|
|
|
|
|
// C++ parser as we do not have information on what is a type and what is
|
|
|
|
|
// a variable name. So "a<b>::c" could either be two comparisons of values
|
|
|
|
|
// 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
|
|
|
|
|
// assume here it is the latter.
|
|
|
|
|
//return type;
|
|
|
|
|
|
|
|
|
|
// (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
|
|
|
|
|
// (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
|
|
|
|
|
if (isPointerType(type))
|
2010-09-01 17:36:09 +02:00
|
|
|
return gdbQuoteTypes(stripPointerType(type)) + '*';
|
2009-03-25 13:42:47 +01:00
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArray accu;
|
|
|
|
|
QByteArray result;
|
2009-03-25 13:42:47 +01:00
|
|
|
int templateLevel = 0;
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
const char colon = ':';
|
|
|
|
|
const char singleQuote = '\'';
|
|
|
|
|
const char lessThan = '<';
|
|
|
|
|
const char greaterThan = '>';
|
2009-03-25 13:42:47 +01:00
|
|
|
for (int i = 0; i != type.size(); ++i) {
|
2010-09-01 17:36:09 +02:00
|
|
|
const char c = type.at(i);
|
|
|
|
|
if (isLetterOrNumber(c) || c == '_' || c == colon || c == ' ') {
|
2009-03-25 13:42:47 +01:00
|
|
|
accu += c;
|
|
|
|
|
} else if (c == lessThan) {
|
|
|
|
|
++templateLevel;
|
|
|
|
|
accu += c;
|
|
|
|
|
} else if (c == greaterThan) {
|
|
|
|
|
--templateLevel;
|
|
|
|
|
accu += c;
|
|
|
|
|
} else if (templateLevel > 0) {
|
|
|
|
|
accu += c;
|
|
|
|
|
} else {
|
|
|
|
|
if (accu.contains(colon) || accu.contains(lessThan))
|
|
|
|
|
result += singleQuote + accu + singleQuote;
|
|
|
|
|
else
|
|
|
|
|
result += accu;
|
|
|
|
|
accu.clear();
|
|
|
|
|
result += c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (accu.contains(colon) || accu.contains(lessThan))
|
|
|
|
|
result += singleQuote + accu + singleQuote;
|
|
|
|
|
else
|
|
|
|
|
result += accu;
|
|
|
|
|
//qDebug() << "GDB_QUOTING" << type << " TO " << result;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
bool extractTemplate(const QByteArray &type, QByteArray *tmplate, QByteArray *inner)
|
2009-03-25 13:42:47 +01:00
|
|
|
{
|
|
|
|
|
// Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
|
|
|
|
|
// 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
|
|
|
|
|
// whether parsing was successful
|
2009-07-03 13:56:27 +02:00
|
|
|
// Gdb inserts a blank after each comma which we would like to avoid
|
|
|
|
|
tmplate->clear();
|
|
|
|
|
inner->clear();
|
2010-09-01 17:36:09 +02:00
|
|
|
if (!type.contains('<'))
|
2009-07-03 13:56:27 +02:00
|
|
|
return false;
|
2009-03-25 13:42:47 +01:00
|
|
|
int level = 0;
|
|
|
|
|
bool skipSpace = false;
|
2009-07-03 13:56:27 +02:00
|
|
|
const int size = type.size();
|
2009-03-25 13:42:47 +01:00
|
|
|
|
2009-07-03 13:56:27 +02:00
|
|
|
for (int i = 0; i != size; ++i) {
|
2010-09-01 17:36:09 +02:00
|
|
|
const char c = type.at(i);
|
|
|
|
|
switch (c) {
|
2009-07-03 13:56:27 +02:00
|
|
|
case '<':
|
2009-03-25 13:42:47 +01:00
|
|
|
*(level == 0 ? tmplate : inner) += c;
|
|
|
|
|
++level;
|
2009-07-03 13:56:27 +02:00
|
|
|
break;
|
|
|
|
|
case '>':
|
2009-03-25 13:42:47 +01:00
|
|
|
--level;
|
|
|
|
|
*(level == 0 ? tmplate : inner) += c;
|
2009-07-03 13:56:27 +02:00
|
|
|
break;
|
|
|
|
|
case ',':
|
2010-09-01 17:36:09 +02:00
|
|
|
*inner += (level == 1) ? '@' : ',';
|
2009-03-25 13:42:47 +01:00
|
|
|
skipSpace = true;
|
2009-07-03 13:56:27 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
2010-09-01 17:36:09 +02:00
|
|
|
if (!skipSpace || c != ' ') {
|
2009-07-03 13:56:27 +02:00
|
|
|
*(level == 0 ? tmplate : inner) += c;
|
|
|
|
|
skipSpace = false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-03-25 13:42:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*tmplate = tmplate->trimmed();
|
2010-09-01 17:36:09 +02:00
|
|
|
tmplate->replace("<>", "");
|
2009-03-25 13:42:47 +01:00
|
|
|
*inner = inner->trimmed();
|
2009-07-03 13:56:27 +02:00
|
|
|
// qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
|
2009-03-25 13:42:47 +01:00
|
|
|
return !inner->isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString extractTypeFromPTypeOutput(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
int pos0 = str.indexOf(QLatin1Char('='));
|
|
|
|
|
int pos1 = str.indexOf(QLatin1Char('{'));
|
|
|
|
|
int pos2 = str.lastIndexOf(QLatin1Char('}'));
|
|
|
|
|
QString res = str;
|
|
|
|
|
if (pos0 != -1 && pos1 != -1 && pos2 != -1)
|
|
|
|
|
res = str.mid(pos0 + 2, pos1 - 1 - pos0)
|
|
|
|
|
+ QLatin1String(" ... ") + str.right(str.size() - pos2);
|
|
|
|
|
return res.simplified();
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
bool isSymbianIntType(const QByteArray &type)
|
2009-09-15 18:21:40 +02:00
|
|
|
{
|
2010-09-01 17:36:09 +02:00
|
|
|
return type == "TInt" || type == "TBool";
|
2009-09-15 18:21:40 +02:00
|
|
|
}
|
|
|
|
|
|
2011-01-10 10:14:23 +01:00
|
|
|
QByteArray sizeofTypeExpression(const QByteArray &type)
|
2009-03-25 13:42:47 +01:00
|
|
|
{
|
2010-09-01 17:36:09 +02:00
|
|
|
if (type.endsWith('*'))
|
|
|
|
|
return "sizeof(void*)";
|
2011-01-10 10:14:23 +01:00
|
|
|
if (type.endsWith('>'))
|
2010-09-01 17:36:09 +02:00
|
|
|
return "sizeof(" + type + ')';
|
|
|
|
|
return "sizeof(" + gdbQuoteTypes(type) + ')';
|
2009-03-25 13:42:47 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
// Utilities to decode string data returned by the dumper helpers.
|
|
|
|
|
|
2009-05-08 15:37:41 +02:00
|
|
|
QString quoteUnprintableLatin1(const QByteArray &ba)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
QString res;
|
|
|
|
|
char buf[10];
|
|
|
|
|
for (int i = 0, n = ba.size(); i != n; ++i) {
|
|
|
|
|
const unsigned char c = ba.at(i);
|
|
|
|
|
if (isprint(c)) {
|
|
|
|
|
res += c;
|
|
|
|
|
} else {
|
|
|
|
|
qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
|
|
|
|
|
res += buf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-29 18:03:35 +02:00
|
|
|
QString decodeData(const QByteArray &ba, int encoding)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
switch (encoding) {
|
|
|
|
|
case 0: // unencoded 8 bit data
|
2009-04-29 18:03:35 +02:00
|
|
|
return quoteUnprintableLatin1(ba);
|
2009-04-29 14:15:09 +02:00
|
|
|
case 1: { // base64 encoded 8 bit data, used for QByteArray
|
|
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
QString rc = doubleQuote;
|
2009-04-29 18:03:35 +02:00
|
|
|
rc += quoteUnprintableLatin1(QByteArray::fromBase64(ba));
|
2009-04-29 14:15:09 +02:00
|
|
|
rc += doubleQuote;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
case 2: { // base64 encoded 16 bit data, used for QString
|
|
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
2009-05-04 12:12:45 +02:00
|
|
|
const QByteArray decodedBa = QByteArray::fromBase64(ba);
|
2009-04-29 14:15:09 +02:00
|
|
|
QString rc = doubleQuote;
|
2009-10-12 09:30:28 +02:00
|
|
|
rc += QString::fromUtf16(reinterpret_cast<const ushort *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 2);
|
2009-04-29 14:15:09 +02:00
|
|
|
rc += doubleQuote;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
case 3: { // base64 encoded 32 bit data
|
2009-05-04 12:12:45 +02:00
|
|
|
const QByteArray decodedBa = QByteArray::fromBase64(ba);
|
2009-04-29 14:15:09 +02:00
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
QString rc = doubleQuote;
|
2009-10-12 09:30:28 +02:00
|
|
|
rc += QString::fromUcs4(reinterpret_cast<const uint *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 4);
|
2009-04-29 14:15:09 +02:00
|
|
|
rc += doubleQuote;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
case 4: { // base64 encoded 16 bit data, without quotes (see 2)
|
2009-05-04 12:12:45 +02:00
|
|
|
const QByteArray decodedBa = QByteArray::fromBase64(ba);
|
2009-10-12 09:30:28 +02:00
|
|
|
return QString::fromUtf16(reinterpret_cast<const ushort *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 2);
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
2009-07-01 14:15:10 +02:00
|
|
|
case 5: { // base64 encoded 8 bit data, without quotes (see 1)
|
|
|
|
|
return quoteUnprintableLatin1(QByteArray::fromBase64(ba));
|
|
|
|
|
}
|
2010-03-11 18:55:52 +01:00
|
|
|
case 6: { // %02x encoded 8 bit Latin1 data
|
2009-10-21 16:41:18 +02:00
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
const QByteArray decodedBa = QByteArray::fromHex(ba);
|
|
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
|
|
|
|
return doubleQuote + QString::fromLatin1(decodedBa) + doubleQuote;
|
|
|
|
|
}
|
2010-03-05 11:10:58 +01:00
|
|
|
case 7: { // %04x encoded 16 bit data, Little Endian
|
2009-10-15 13:57:51 +02:00
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
2009-10-12 09:30:28 +02:00
|
|
|
const QByteArray decodedBa = QByteArray::fromHex(ba);
|
2009-10-12 15:22:11 +02:00
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
2009-10-15 13:57:51 +02:00
|
|
|
return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
|
2009-10-12 09:30:28 +02:00
|
|
|
}
|
2010-03-05 11:10:58 +01:00
|
|
|
case 8: { // %08x encoded 32 bit data, Little Endian
|
|
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
const QByteArray decodedBa = QByteArray::fromHex(ba);
|
|
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
|
|
|
|
return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
|
|
|
|
|
}
|
2010-03-11 18:55:52 +01:00
|
|
|
case 9: { // %02x encoded 8 bit Utf-8 data
|
|
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
const QByteArray decodedBa = QByteArray::fromHex(ba);
|
|
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
|
|
|
|
return doubleQuote + QString::fromUtf8(decodedBa) + doubleQuote;
|
|
|
|
|
}
|
|
|
|
|
case 10: { // %08x encoded 32 bit data, Big Endian
|
|
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
QByteArray decodedBa = QByteArray::fromHex(ba);
|
|
|
|
|
for (int i = 0; i < decodedBa.size(); i += 4) {
|
|
|
|
|
char c = decodedBa.at(i);
|
|
|
|
|
decodedBa[i] = decodedBa.at(i + 3);
|
|
|
|
|
decodedBa[i + 3] = c;
|
|
|
|
|
c = decodedBa.at(i + 1);
|
|
|
|
|
decodedBa[i + 1] = decodedBa.at(i + 2);
|
|
|
|
|
decodedBa[i + 2] = c;
|
|
|
|
|
}
|
|
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
|
|
|
|
return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
|
|
|
|
|
}
|
2010-03-12 11:14:50 +01:00
|
|
|
case 11: { // %04x encoded 16 bit data, Big Endian
|
2010-03-11 18:55:52 +01:00
|
|
|
const QChar doubleQuote(QLatin1Char('"'));
|
|
|
|
|
QByteArray decodedBa = QByteArray::fromHex(ba);
|
|
|
|
|
for (int i = 0; i < decodedBa.size(); i += 2) {
|
|
|
|
|
char c = decodedBa.at(i);
|
|
|
|
|
decodedBa[i] = decodedBa.at(i + 1);
|
|
|
|
|
decodedBa[i + 1] = c;
|
|
|
|
|
}
|
|
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
2010-07-09 11:00:20 +02:00
|
|
|
return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
|
2010-03-11 18:55:52 +01:00
|
|
|
}
|
2010-09-13 12:37:30 +02:00
|
|
|
case 12: { // %04x encoded 16 bit data, Little Endian, without quotes (see 7)
|
|
|
|
|
const QByteArray decodedBa = QByteArray::fromHex(ba);
|
|
|
|
|
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
|
|
|
|
|
return QString::fromUtf16(reinterpret_cast<const ushort *>
|
|
|
|
|
(decodedBa.data()), decodedBa.size() / 2);
|
|
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
2009-10-21 16:41:18 +02:00
|
|
|
qDebug() << "ENCODING ERROR: " << encoding;
|
2009-04-29 14:15:09 +02:00
|
|
|
return QCoreApplication::translate("Debugger", "<Encoding error>");
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-04 11:46:16 +01:00
|
|
|
TextEditor::ITextEditor *currentTextEditor()
|
|
|
|
|
{
|
|
|
|
|
if (const Core::EditorManager *editorManager = Core::EditorManager::instance())
|
|
|
|
|
if (Core::IEditor *editor = editorManager->currentEditor())
|
|
|
|
|
return qobject_cast<TextEditor::ITextEditor*>(editor);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-14 14:29:37 +02:00
|
|
|
// Editor tooltip support
|
|
|
|
|
bool isCppEditor(Core::IEditor *editor)
|
|
|
|
|
{
|
2010-08-27 16:36:35 +02:00
|
|
|
using namespace CppTools::Constants;
|
|
|
|
|
const Core::IFile *file = editor->file();
|
|
|
|
|
if (!file)
|
|
|
|
|
return false;
|
|
|
|
|
const QByteArray mimeType = file->mimeType().toLatin1();
|
|
|
|
|
return mimeType == C_SOURCE_MIMETYPE
|
|
|
|
|
|| mimeType == CPP_SOURCE_MIMETYPE
|
|
|
|
|
|| mimeType == CPP_HEADER_MIMETYPE
|
|
|
|
|
|| mimeType == OBJECTIVE_CPP_SOURCE_MIMETYPE;
|
2009-05-14 14:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-04 11:46:16 +01:00
|
|
|
bool currentTextEditorPosition(QString *fileNameIn /* = 0 */,
|
|
|
|
|
int *lineNumberIn /* = 0 */)
|
|
|
|
|
{
|
|
|
|
|
QString fileName;
|
2010-12-16 12:05:48 +01:00
|
|
|
int lineNumber = 0;
|
2010-11-04 11:46:16 +01:00
|
|
|
if (TextEditor::ITextEditor *textEditor = currentTextEditor()) {
|
|
|
|
|
if (const Core::IFile *file = textEditor->file()) {
|
|
|
|
|
fileName = file->fileName();
|
|
|
|
|
lineNumber = textEditor->currentLine();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (fileNameIn)
|
|
|
|
|
*fileNameIn = fileName;
|
|
|
|
|
if (lineNumberIn)
|
|
|
|
|
*lineNumberIn = lineNumber;
|
|
|
|
|
return !fileName.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-14 14:29:37 +02:00
|
|
|
// Return the Cpp expression, and, if desired, the function
|
|
|
|
|
QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos,
|
|
|
|
|
int *line, int *column, QString *function /* = 0 */)
|
|
|
|
|
{
|
2010-08-27 16:36:35 +02:00
|
|
|
using namespace CppTools;
|
2010-12-03 13:49:35 +01:00
|
|
|
using namespace CPlusPlus;
|
2009-05-14 14:29:37 +02:00
|
|
|
*line = *column = 0;
|
|
|
|
|
if (function)
|
|
|
|
|
function->clear();
|
|
|
|
|
|
|
|
|
|
const QPlainTextEdit *plaintext = qobject_cast<QPlainTextEdit*>(editor->widget());
|
|
|
|
|
if (!plaintext)
|
2010-09-01 17:36:09 +02:00
|
|
|
return QByteArray();
|
2009-05-14 14:29:37 +02:00
|
|
|
|
|
|
|
|
QString expr = plaintext->textCursor().selectedText();
|
2011-01-04 18:50:18 +01:00
|
|
|
CppModelManagerInterface *modelManager = CppModelManagerInterface::instance();
|
2010-06-07 13:06:21 +02:00
|
|
|
if (expr.isEmpty() && modelManager) {
|
2009-05-14 14:29:37 +02:00
|
|
|
QTextCursor tc(plaintext->document());
|
|
|
|
|
tc.setPosition(pos);
|
|
|
|
|
|
|
|
|
|
const QChar ch = editor->characterAt(pos);
|
|
|
|
|
if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord);
|
|
|
|
|
|
|
|
|
|
// Fetch the expression's code.
|
2010-06-29 17:47:59 +02:00
|
|
|
CPlusPlus::ExpressionUnderCursor expressionUnderCursor;
|
2009-05-14 14:29:37 +02:00
|
|
|
expr = expressionUnderCursor(tc);
|
2010-06-15 10:44:07 +02:00
|
|
|
*column = tc.positionInBlock();
|
2009-05-14 14:29:37 +02:00
|
|
|
*line = tc.blockNumber();
|
|
|
|
|
} else {
|
|
|
|
|
const QTextCursor tc = plaintext->textCursor();
|
2010-06-15 10:44:07 +02:00
|
|
|
*column = tc.positionInBlock();
|
2009-05-14 14:29:37 +02:00
|
|
|
*line = tc.blockNumber();
|
2009-07-03 13:56:27 +02:00
|
|
|
}
|
2009-05-14 14:29:37 +02:00
|
|
|
|
|
|
|
|
if (function && !expr.isEmpty())
|
|
|
|
|
if (const Core::IFile *file = editor->file())
|
2010-06-07 13:06:21 +02:00
|
|
|
if (modelManager)
|
2010-08-27 16:36:35 +02:00
|
|
|
*function = AbstractEditorSupport::functionAt(modelManager,
|
|
|
|
|
file->fileName(), *line, *column);
|
2009-05-14 14:29:37 +02:00
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
return expr.toUtf8();
|
2009-05-14 14:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-27 16:36:35 +02:00
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
// ----------------- QtDumperHelper::TypeData
|
|
|
|
|
QtDumperHelper::TypeData::TypeData() :
|
|
|
|
|
type(UnknownType),
|
|
|
|
|
isTemplate(false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QtDumperHelper::TypeData::clear()
|
|
|
|
|
{
|
|
|
|
|
isTemplate = false;
|
|
|
|
|
type = UnknownType;
|
|
|
|
|
tmplate.clear();
|
|
|
|
|
inner.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------- QtDumperHelper
|
|
|
|
|
QtDumperHelper::QtDumperHelper() :
|
2009-07-07 16:00:45 +02:00
|
|
|
m_qtVersion(0),
|
|
|
|
|
m_dumperVersion(1.0)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
2009-07-02 16:38:15 +02:00
|
|
|
qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
|
2010-09-01 17:36:09 +02:00
|
|
|
setQClassPrefixes(QByteArray());
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QtDumperHelper::clear()
|
|
|
|
|
{
|
|
|
|
|
m_nameTypeMap.clear();
|
|
|
|
|
m_qtVersion = 0;
|
2009-07-07 16:00:45 +02:00
|
|
|
m_dumperVersion = 1.0;
|
2009-04-29 14:15:09 +02:00
|
|
|
m_qtNamespace.clear();
|
2009-04-29 16:52:14 +02:00
|
|
|
m_sizeCache.clear();
|
2009-07-02 16:38:15 +02:00
|
|
|
qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
|
|
|
|
|
m_expressionCache.clear();
|
2010-09-01 17:36:09 +02:00
|
|
|
setQClassPrefixes(QByteArray());
|
2009-07-07 16:00:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString QtDumperHelper::msgDumperOutdated(double requiredVersion, double currentVersion)
|
|
|
|
|
{
|
|
|
|
|
return QCoreApplication::translate("QtDumperHelper",
|
2010-07-21 12:04:20 +02:00
|
|
|
"Found an outdated version of the debugging helper library (%1); "
|
|
|
|
|
"version %2 is required.").
|
|
|
|
|
arg(currentVersion).arg(requiredVersion);
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void formatQtVersion(int v, QTextStream &str)
|
|
|
|
|
{
|
|
|
|
|
str << ((v >> 16) & 0xFF) << '.' << ((v >> 8) & 0xFF) << '.' << (v & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString QtDumperHelper::toString(bool debug) const
|
|
|
|
|
{
|
|
|
|
|
if (debug) {
|
|
|
|
|
QString rc;
|
|
|
|
|
QTextStream str(&rc);
|
|
|
|
|
str << "version=";
|
|
|
|
|
formatQtVersion(m_qtVersion, str);
|
2009-07-06 17:36:50 +02:00
|
|
|
str << "dumperversion='" << m_dumperVersion << "' namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: ";
|
2009-04-29 14:15:09 +02:00
|
|
|
const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd();
|
|
|
|
|
for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) {
|
|
|
|
|
str <<",[" << it.key() << ',' << it.value() << ']';
|
|
|
|
|
}
|
2009-07-02 16:38:15 +02:00
|
|
|
str << "\nSpecial size: ";
|
|
|
|
|
for (int i = 0; i < SpecialSizeCount; i++)
|
|
|
|
|
str << ' ' << m_specialSizes[i];
|
|
|
|
|
str << "\nSize cache: ";
|
2009-04-29 16:52:14 +02:00
|
|
|
const SizeCache::const_iterator scend = m_sizeCache.constEnd();
|
|
|
|
|
for (SizeCache::const_iterator it = m_sizeCache.constBegin(); it != scend; ++it) {
|
2009-10-02 15:17:50 +02:00
|
|
|
str << ' ' << it.key() << '=' << it.value() << '\n';
|
2009-04-29 16:52:14 +02:00
|
|
|
}
|
2009-09-18 16:55:17 +02:00
|
|
|
str << "\nExpression cache: (" << m_expressionCache.size() << ")\n";
|
2010-09-01 17:36:09 +02:00
|
|
|
const ExpressionCache::const_iterator excend = m_expressionCache.constEnd();
|
|
|
|
|
for (ExpressionCache::const_iterator it = m_expressionCache.constBegin(); it != excend; ++it)
|
2009-09-18 16:55:17 +02:00
|
|
|
str << " " << it.key() << ' ' << it.value() << '\n';
|
2009-04-29 14:15:09 +02:00
|
|
|
return rc;
|
|
|
|
|
}
|
2010-08-27 17:24:13 +02:00
|
|
|
const QString nameSpace = m_qtNamespace.isEmpty()
|
|
|
|
|
? QCoreApplication::translate("QtDumperHelper", "<none>") : m_qtNamespace;
|
2009-04-29 14:15:09 +02:00
|
|
|
return QCoreApplication::translate("QtDumperHelper",
|
2010-07-21 12:04:20 +02:00
|
|
|
"%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3",
|
|
|
|
|
0, QCoreApplication::CodecForTr,
|
|
|
|
|
m_nameTypeMap.size()).arg(qtVersionString(), nameSpace).arg(m_dumperVersion);
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QtDumperHelper::Type QtDumperHelper::simpleType(const QByteArray &simpleType) const
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
return m_nameTypeMap.value(simpleType, UnknownType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int QtDumperHelper::qtVersion() const
|
|
|
|
|
{
|
|
|
|
|
return m_qtVersion;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArray QtDumperHelper::qtNamespace() const
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
return m_qtNamespace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int QtDumperHelper::typeCount() const
|
|
|
|
|
{
|
|
|
|
|
return m_nameTypeMap.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Look up unnamespaced 'std' types.
|
2010-08-27 17:24:13 +02:00
|
|
|
static QtDumperHelper::Type stdType(const QByteArray &type)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type == "vector")
|
2009-04-29 14:15:09 +02:00
|
|
|
return QtDumperHelper::StdVectorType;
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type == "deque")
|
2009-04-29 14:15:09 +02:00
|
|
|
return QtDumperHelper::StdDequeType;
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type == "set")
|
2009-04-29 14:15:09 +02:00
|
|
|
return QtDumperHelper::StdSetType;
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type == "stack")
|
2009-04-29 14:15:09 +02:00
|
|
|
return QtDumperHelper::StdStackType;
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type == "map")
|
2009-04-29 14:15:09 +02:00
|
|
|
return QtDumperHelper::StdMapType;
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type == "basic_string")
|
2009-04-29 14:15:09 +02:00
|
|
|
return QtDumperHelper::StdStringType;
|
|
|
|
|
return QtDumperHelper::UnknownType;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-27 17:24:13 +02:00
|
|
|
static QtDumperHelper::Type specialType(QByteArray type)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
// Std classes.
|
2010-08-27 17:24:13 +02:00
|
|
|
if (type.startsWith("std::"))
|
|
|
|
|
return stdType(type.mid(5));
|
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
// Strip namespace
|
2009-06-03 12:46:55 +02:00
|
|
|
// FIXME: that's not a good idea as it makes all namespaces equal.
|
2010-08-27 17:24:13 +02:00
|
|
|
const int namespaceIndex = type.lastIndexOf("::");
|
2009-04-29 14:15:09 +02:00
|
|
|
if (namespaceIndex == -1) {
|
|
|
|
|
// None ... check for std..
|
2010-08-27 17:24:13 +02:00
|
|
|
const QtDumperHelper::Type sType = stdType(type);
|
|
|
|
|
if (sType != QtDumperHelper::UnknownType)
|
2009-04-29 14:15:09 +02:00
|
|
|
return sType;
|
|
|
|
|
} else {
|
2010-08-27 17:24:13 +02:00
|
|
|
type = type.mid(namespaceIndex + 2);
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
2010-08-27 17:24:13 +02:00
|
|
|
|
|
|
|
|
if (type == "QAbstractItem")
|
|
|
|
|
return QtDumperHelper::QAbstractItemType;
|
|
|
|
|
if (type == "QMap")
|
|
|
|
|
return QtDumperHelper::QMapType;
|
|
|
|
|
if (type == "QMapNode")
|
|
|
|
|
return QtDumperHelper::QMapNodeType;
|
|
|
|
|
if (type == "QMultiMap")
|
|
|
|
|
return QtDumperHelper::QMultiMapType;
|
|
|
|
|
if (type == "QObject")
|
|
|
|
|
return QtDumperHelper::QObjectType;
|
|
|
|
|
if (type == "QObjectSignal")
|
|
|
|
|
return QtDumperHelper::QObjectSignalType;
|
|
|
|
|
if (type == "QObjectSlot")
|
|
|
|
|
return QtDumperHelper::QObjectSlotType;
|
|
|
|
|
if (type == "QStack")
|
|
|
|
|
return QtDumperHelper::QStackType;
|
|
|
|
|
if (type == "QVector")
|
|
|
|
|
return QtDumperHelper::QVectorType;
|
|
|
|
|
if (type == "QWidget")
|
|
|
|
|
return QtDumperHelper::QWidgetType;
|
|
|
|
|
return QtDumperHelper::UnknownType;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArray QtDumperHelper::qtVersionString() const
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
QString rc;
|
|
|
|
|
QTextStream str(&rc);
|
|
|
|
|
formatQtVersion(m_qtVersion, str);
|
2010-09-01 17:36:09 +02:00
|
|
|
return rc.toLatin1();
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse a list of types.
|
2010-08-27 17:24:13 +02:00
|
|
|
typedef QList<QByteArray> QByteArrayList;
|
2009-04-29 14:15:09 +02:00
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
static inline QByteArray qClassName(const QByteArray &qtNamespace, const char *className)
|
2009-07-07 16:00:45 +02:00
|
|
|
{
|
|
|
|
|
if (qtNamespace.isEmpty())
|
2010-09-01 17:36:09 +02:00
|
|
|
return className;
|
|
|
|
|
QByteArray rc = qtNamespace;
|
|
|
|
|
rc += "::";
|
|
|
|
|
rc += className;
|
2009-07-07 16:00:45 +02:00
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
void QtDumperHelper::setQClassPrefixes(const QByteArray &qNamespace)
|
2009-07-07 16:00:45 +02:00
|
|
|
{
|
|
|
|
|
// Prefixes with namespaces
|
|
|
|
|
m_qPointerPrefix = qClassName(qNamespace, "QPointer");
|
|
|
|
|
m_qSharedPointerPrefix = qClassName(qNamespace, "QSharedPointer");
|
|
|
|
|
m_qSharedDataPointerPrefix = qClassName(qNamespace, "QSharedDataPointer");
|
|
|
|
|
m_qWeakPointerPrefix = qClassName(qNamespace, "QWeakPointer");
|
2009-09-21 14:55:39 +02:00
|
|
|
m_qListPrefix = qClassName(qNamespace, "QList");
|
|
|
|
|
m_qLinkedListPrefix = qClassName(qNamespace, "QLinkedList");
|
|
|
|
|
m_qVectorPrefix = qClassName(qNamespace, "QVector");
|
2009-10-02 15:17:50 +02:00
|
|
|
m_qQueuePrefix = qClassName(qNamespace, "QQueue");
|
2009-07-07 16:00:45 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-18 16:55:17 +02:00
|
|
|
static inline double getDumperVersion(const GdbMi &contents)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
2009-09-18 16:55:17 +02:00
|
|
|
const GdbMi dumperVersionG = contents.findChild("dumperversion");
|
|
|
|
|
if (dumperVersionG.type() != GdbMi::Invalid) {
|
2009-07-07 16:00:45 +02:00
|
|
|
bool ok;
|
2009-09-18 16:55:17 +02:00
|
|
|
const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
|
2009-07-07 16:00:45 +02:00
|
|
|
if (ok)
|
2009-09-18 16:55:17 +02:00
|
|
|
return v;
|
2009-07-07 16:00:45 +02:00
|
|
|
}
|
2009-09-18 16:55:17 +02:00
|
|
|
return 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-27 17:24:13 +02:00
|
|
|
bool QtDumperHelper::parseQuery(const GdbMi &contents)
|
2009-09-18 16:55:17 +02:00
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
if (debug > 1)
|
|
|
|
|
qDebug() << "parseQuery" << contents.toString(true, 2);
|
|
|
|
|
|
|
|
|
|
// Common info, dumper version, etc
|
2011-01-17 15:11:11 +01:00
|
|
|
QByteArray ns = contents.findChild("namespace").data();
|
|
|
|
|
setQtNamespace(ns);
|
2009-09-18 16:55:17 +02:00
|
|
|
int qtv = 0;
|
|
|
|
|
const GdbMi qtversion = contents.findChild("qtversion");
|
|
|
|
|
if (qtversion.children().size() == 3) {
|
|
|
|
|
qtv = (qtversion.childAt(0).data().toInt() << 16)
|
|
|
|
|
+ (qtversion.childAt(1).data().toInt() << 8)
|
|
|
|
|
+ qtversion.childAt(2).data().toInt();
|
|
|
|
|
}
|
|
|
|
|
m_qtVersion = qtv;
|
|
|
|
|
// Get list of helpers
|
2010-08-27 17:24:13 +02:00
|
|
|
QByteArrayList availableSimpleDebuggingHelpers;
|
2009-09-18 16:55:17 +02:00
|
|
|
foreach (const GdbMi &item, contents.findChild("dumpers").children())
|
2010-08-27 17:24:13 +02:00
|
|
|
availableSimpleDebuggingHelpers.append(item.data());
|
|
|
|
|
|
|
|
|
|
// Parse types
|
|
|
|
|
m_nameTypeMap.clear();
|
|
|
|
|
foreach (const QByteArray &type, availableSimpleDebuggingHelpers) {
|
|
|
|
|
const Type t = specialType(type);
|
|
|
|
|
m_nameTypeMap.insert(type, t != UnknownType ? t : SupportedType);
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-18 16:55:17 +02:00
|
|
|
m_dumperVersion = getDumperVersion(contents);
|
|
|
|
|
// Parse sizes
|
|
|
|
|
foreach (const GdbMi &sizesList, contents.findChild("sizes").children()) {
|
|
|
|
|
const int childCount = sizesList.childCount();
|
|
|
|
|
if (childCount > 1) {
|
|
|
|
|
const int size = sizesList.childAt(0).data().toInt();
|
|
|
|
|
for (int c = 1; c < childCount; c++)
|
2010-09-01 17:36:09 +02:00
|
|
|
addSize(sizesList.childAt(c).data(), size);
|
2009-09-18 16:55:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Parse expressions
|
|
|
|
|
foreach (const GdbMi &exprList, contents.findChild("expressions").children())
|
|
|
|
|
if (exprList.childCount() == 2)
|
2010-09-01 17:36:09 +02:00
|
|
|
m_expressionCache.insert(exprList.childAt(0).data(),
|
|
|
|
|
exprList.childAt(1).data());
|
2009-04-29 14:15:09 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
void QtDumperHelper::addSize(const QByteArray &name, int size)
|
2009-04-29 16:52:14 +02:00
|
|
|
{
|
|
|
|
|
// Special interest cases
|
2010-09-01 17:36:09 +02:00
|
|
|
if (name == "char*") {
|
2009-07-02 16:38:15 +02:00
|
|
|
m_specialSizes[PointerSize] = size;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const SpecialSizeType st = specialSizeType(name);
|
|
|
|
|
if (st != SpecialSizeCount) {
|
|
|
|
|
m_specialSizes[st] = size;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-04-29 16:52:14 +02:00
|
|
|
do {
|
2009-07-03 13:56:27 +02:00
|
|
|
// CDB helpers
|
2010-09-01 17:36:09 +02:00
|
|
|
if (name == "std::string") {
|
|
|
|
|
m_sizeCache.insert("std::basic_string<char,std::char_traits<char>,std::allocator<char> >", size);
|
|
|
|
|
m_sizeCache.insert("basic_string<char,char_traits<char>,allocator<char> >", size);
|
2009-04-29 16:52:14 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2010-09-01 17:36:09 +02:00
|
|
|
if (name == "std::wstring") {
|
|
|
|
|
m_sizeCache.insert("basic_string<unsigned short,char_traits<unsignedshort>,allocator<unsignedshort> >", size);
|
|
|
|
|
m_sizeCache.insert("std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >", size);
|
2009-04-29 16:52:14 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (false);
|
|
|
|
|
m_sizeCache.insert(name, size);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QtDumperHelper::Type QtDumperHelper::type(const QByteArray &typeName) const
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
const QtDumperHelper::TypeData td = typeData(typeName);
|
|
|
|
|
return td.type;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QtDumperHelper::TypeData QtDumperHelper::typeData(const QByteArray &typeName) const
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
TypeData td;
|
|
|
|
|
td.type = UnknownType;
|
|
|
|
|
const Type st = simpleType(typeName);
|
|
|
|
|
if (st != UnknownType) {
|
|
|
|
|
td.isTemplate = false;
|
2009-06-03 12:46:55 +02:00
|
|
|
td.type = st;
|
2009-04-29 14:15:09 +02:00
|
|
|
return td;
|
|
|
|
|
}
|
|
|
|
|
// Try template
|
|
|
|
|
td.isTemplate = extractTemplate(typeName, &td.tmplate, &td.inner);
|
|
|
|
|
if (!td.isTemplate)
|
|
|
|
|
return td;
|
|
|
|
|
// Check the template type QMap<X,Y> -> 'QMap'
|
|
|
|
|
td.type = simpleType(td.tmplate);
|
|
|
|
|
return td;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-29 16:52:14 +02:00
|
|
|
// Format an expression to have the debugger query the
|
|
|
|
|
// size. Use size cache if possible
|
2011-01-10 10:14:23 +01:00
|
|
|
QByteArray QtDumperHelper::evaluationSizeofTypeExpression(const QByteArray &typeName) const
|
2009-04-29 16:52:14 +02:00
|
|
|
{
|
2009-07-02 16:38:15 +02:00
|
|
|
// Look up special size types
|
|
|
|
|
const SpecialSizeType st = specialSizeType(typeName);
|
|
|
|
|
if (st != SpecialSizeCount) {
|
|
|
|
|
if (const int size = m_specialSizes[st])
|
2010-09-01 17:36:09 +02:00
|
|
|
return QByteArray::number(size);
|
2009-07-02 16:38:15 +02:00
|
|
|
}
|
|
|
|
|
// Look up size cache
|
2009-04-29 16:52:14 +02:00
|
|
|
const SizeCache::const_iterator sit = m_sizeCache.constFind(typeName);
|
|
|
|
|
if (sit != m_sizeCache.constEnd())
|
2010-09-01 17:36:09 +02:00
|
|
|
return QByteArray::number(sit.value());
|
2009-04-29 16:52:14 +02:00
|
|
|
// Finally have the debugger evaluate
|
2011-01-10 10:14:23 +01:00
|
|
|
return sizeofTypeExpression(typeName);
|
2009-04-29 16:52:14 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QByteArray &typeName) const
|
2009-07-02 16:38:15 +02:00
|
|
|
{
|
|
|
|
|
if (isPointerType(typeName))
|
|
|
|
|
return PointerSize;
|
2010-09-01 17:36:09 +02:00
|
|
|
if (typeName == "int")
|
2009-07-02 16:38:15 +02:00
|
|
|
return IntSize;
|
2010-09-01 17:36:09 +02:00
|
|
|
if (typeName.startsWith("std::allocator"))
|
2009-07-02 16:38:15 +02:00
|
|
|
return StdAllocatorSize;
|
2009-07-07 16:00:45 +02:00
|
|
|
if (typeName.startsWith(m_qPointerPrefix))
|
2009-07-02 16:38:15 +02:00
|
|
|
return QPointerSize;
|
2009-07-07 16:00:45 +02:00
|
|
|
if (typeName.startsWith(m_qSharedPointerPrefix))
|
2009-07-02 16:38:15 +02:00
|
|
|
return QSharedPointerSize;
|
2009-07-07 16:00:45 +02:00
|
|
|
if (typeName.startsWith(m_qSharedDataPointerPrefix))
|
2009-07-02 16:38:15 +02:00
|
|
|
return QSharedDataPointerSize;
|
2009-07-07 16:00:45 +02:00
|
|
|
if (typeName.startsWith(m_qWeakPointerPrefix))
|
2009-07-02 16:38:15 +02:00
|
|
|
return QWeakPointerSize;
|
2009-09-21 14:55:39 +02:00
|
|
|
if (typeName.startsWith(m_qListPrefix))
|
|
|
|
|
return QListSize;
|
|
|
|
|
if (typeName.startsWith(m_qLinkedListPrefix))
|
|
|
|
|
return QLinkedListSize;
|
|
|
|
|
if (typeName.startsWith(m_qVectorPrefix))
|
|
|
|
|
return QVectorSize;
|
|
|
|
|
if (typeName.startsWith(m_qQueuePrefix))
|
|
|
|
|
return QQueueSize;
|
2009-07-02 16:38:15 +02:00
|
|
|
return SpecialSizeCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool isInteger(const QString &n)
|
|
|
|
|
{
|
|
|
|
|
const int size = n.size();
|
|
|
|
|
if (!size)
|
|
|
|
|
return false;
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
|
if (!n.at(i).isDigit())
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-10 10:14:23 +01:00
|
|
|
// Return debugger expression to get the offset of a map node.
|
|
|
|
|
static inline QByteArray qMapNodeValueOffsetExpression(const QByteArray &type)
|
|
|
|
|
{
|
|
|
|
|
return "(size_t)&(('" + type + "'*)0)->value";
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
void QtDumperHelper::evaluationParameters(const WatchData &data,
|
2011-01-10 10:14:23 +01:00
|
|
|
const TypeData &td, QByteArray *inBuffer, QByteArrayList *extraArgsIn) const
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
|
enum { maxExtraArgCount = 4 };
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArrayList &extraArgs = *extraArgsIn;
|
2009-04-29 14:15:09 +02:00
|
|
|
|
|
|
|
|
// See extractTemplate for parameters
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArrayList inners = td.inner.split('@');
|
2009-04-29 14:15:09 +02:00
|
|
|
if (inners.at(0).isEmpty())
|
|
|
|
|
inners.clear();
|
|
|
|
|
for (int i = 0; i != inners.size(); ++i)
|
|
|
|
|
inners[i] = inners[i].simplified();
|
|
|
|
|
|
|
|
|
|
QString outertype = td.isTemplate ? td.tmplate : data.type;
|
|
|
|
|
// adjust the data extract
|
2010-09-01 17:36:09 +02:00
|
|
|
if (outertype == m_qtNamespace + "QWidget")
|
|
|
|
|
outertype = m_qtNamespace + "QObject";
|
2009-04-29 14:15:09 +02:00
|
|
|
|
2009-06-03 12:46:55 +02:00
|
|
|
QString inner = td.inner;
|
2010-09-01 17:36:09 +02:00
|
|
|
const QByteArray zero = "0";
|
2009-06-03 12:46:55 +02:00
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
extraArgs.clear();
|
|
|
|
|
|
|
|
|
|
if (!inners.empty()) {
|
|
|
|
|
// "generic" template dumpers: passing sizeof(argument)
|
|
|
|
|
// gives already most information the dumpers need
|
|
|
|
|
const int count = qMin(int(maxExtraArgCount), inners.size());
|
|
|
|
|
for (int i = 0; i < count; i++)
|
2011-01-10 10:14:23 +01:00
|
|
|
extraArgs.push_back(evaluationSizeofTypeExpression(inners.at(i)));
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
2010-09-08 10:21:30 +02:00
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
// Pad with zeros
|
2010-09-08 10:21:30 +02:00
|
|
|
while (extraArgs.size() < maxExtraArgCount)
|
2010-09-01 17:36:09 +02:00
|
|
|
extraArgs.push_back("0");
|
2009-04-29 14:15:09 +02:00
|
|
|
|
|
|
|
|
// in rare cases we need more or less:
|
|
|
|
|
switch (td.type) {
|
2009-06-03 12:46:55 +02:00
|
|
|
case QAbstractItemType:
|
2010-09-22 11:19:35 +02:00
|
|
|
if (data.dumperFlags.isEmpty()) {
|
|
|
|
|
qWarning("Internal error: empty dumper state '%s'.", data.iname.constData());
|
|
|
|
|
} else {
|
|
|
|
|
inner = data.dumperFlags.mid(1);
|
|
|
|
|
}
|
2009-06-03 12:46:55 +02:00
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case QObjectSlotType:
|
|
|
|
|
case QObjectSignalType: {
|
|
|
|
|
// we need the number out of something like
|
|
|
|
|
// iname="local.ob.slots.2" // ".deleteLater()"?
|
|
|
|
|
const int pos = data.iname.lastIndexOf('.');
|
2010-09-01 17:36:09 +02:00
|
|
|
const QByteArray slotNumber = data.iname.mid(pos + 1);
|
2009-04-29 14:15:09 +02:00
|
|
|
QTC_ASSERT(slotNumber.toInt() != -1, /**/);
|
|
|
|
|
extraArgs[0] = slotNumber;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case QMapType:
|
|
|
|
|
case QMultiMapType: {
|
2010-09-01 17:36:09 +02:00
|
|
|
QByteArray nodetype;
|
2009-07-14 10:07:15 +02:00
|
|
|
if (m_qtVersion >= 0x040500) {
|
2010-09-01 17:36:09 +02:00
|
|
|
nodetype = m_qtNamespace + "QMapNode";
|
2009-04-29 14:15:09 +02:00
|
|
|
nodetype += data.type.mid(outertype.size());
|
2009-04-22 17:28:26 +02:00
|
|
|
} else {
|
2009-04-29 14:15:09 +02:00
|
|
|
// FIXME: doesn't work for QMultiMap
|
2010-09-01 17:36:09 +02:00
|
|
|
nodetype = data.type + "::Node";
|
2009-04-22 17:28:26 +02:00
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
//qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
|
|
|
|
|
// << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
|
2011-01-10 10:14:23 +01:00
|
|
|
extraArgs[2] = evaluationSizeofTypeExpression(nodetype);
|
|
|
|
|
extraArgs[3] = qMapNodeValueOffsetExpression(nodetype);
|
2009-04-22 17:28:26 +02:00
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
2009-07-14 10:07:15 +02:00
|
|
|
case QMapNodeType:
|
2011-01-10 10:14:23 +01:00
|
|
|
extraArgs[2] = evaluationSizeofTypeExpression(data.type);
|
|
|
|
|
extraArgs[3] = qMapNodeValueOffsetExpression(data.type);
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
|
case StdVectorType:
|
|
|
|
|
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
2010-09-01 17:36:09 +02:00
|
|
|
if (inners.at(0) == "bool")
|
|
|
|
|
outertype = "std::vector::bool";
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
|
case StdDequeType:
|
2010-09-01 17:36:09 +02:00
|
|
|
extraArgs[1] = "0";
|
2010-06-11 13:39:47 +10:00
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case StdStackType:
|
|
|
|
|
// remove 'std::allocator<...>':
|
2010-09-01 17:36:09 +02:00
|
|
|
extraArgs[1] = "0";
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
|
case StdSetType:
|
|
|
|
|
// remove 'std::less<...>':
|
2010-09-01 17:36:09 +02:00
|
|
|
extraArgs[1] = "0";
|
2009-04-29 14:15:09 +02:00
|
|
|
// remove 'std::allocator<...>':
|
2010-09-01 17:36:09 +02:00
|
|
|
extraArgs[2] = "0";
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
|
case StdMapType: {
|
2009-10-09 14:11:05 +02:00
|
|
|
// We need the offset of the second item in the value pair.
|
2009-04-29 14:15:09 +02:00
|
|
|
// We read the type of the pair from the allocator argument because
|
2009-10-09 14:11:05 +02:00
|
|
|
// that gets the constness "right" (in the sense that gdb/cdb can
|
2009-07-03 13:56:27 +02:00
|
|
|
// read it back: "std::allocator<std::pair<Key,Value> >"
|
|
|
|
|
// -> "std::pair<Key,Value>". Different debuggers have varying
|
|
|
|
|
// amounts of terminating blanks...
|
2009-10-09 14:11:05 +02:00
|
|
|
extraArgs[2].clear();
|
2010-09-01 17:36:09 +02:00
|
|
|
extraArgs[3] = "0";
|
|
|
|
|
QByteArray pairType = inners.at(3);
|
|
|
|
|
int bracketPos = pairType.indexOf('<');
|
2009-07-03 13:56:27 +02:00
|
|
|
if (bracketPos != -1)
|
|
|
|
|
pairType.remove(0, bracketPos + 1);
|
2009-10-09 14:11:05 +02:00
|
|
|
// We don't want the comparator and the allocator confuse gdb.
|
2010-09-01 17:36:09 +02:00
|
|
|
const char closingBracket = '>';
|
2009-07-07 16:00:45 +02:00
|
|
|
bracketPos = pairType.lastIndexOf(closingBracket);
|
|
|
|
|
if (bracketPos != -1)
|
|
|
|
|
bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1);
|
2009-07-03 13:56:27 +02:00
|
|
|
if (bracketPos != -1)
|
|
|
|
|
pairType.truncate(bracketPos + 1);
|
2011-01-10 10:14:23 +01:00
|
|
|
extraArgs[2] = "(size_t)&(('";
|
|
|
|
|
extraArgs[2] += pairType;
|
|
|
|
|
extraArgs[2] += "'*)0)->second";
|
|
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
|
case StdStringType:
|
|
|
|
|
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
2010-09-01 17:36:09 +02:00
|
|
|
if (inners.at(0) == "char")
|
|
|
|
|
outertype = "std::string";
|
|
|
|
|
else if (inners.at(0) == "wchar_t")
|
|
|
|
|
outertype = "std::wstring";
|
2009-04-29 14:15:09 +02:00
|
|
|
qFill(extraArgs, zero);
|
|
|
|
|
break;
|
|
|
|
|
case UnknownType:
|
|
|
|
|
qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO);
|
|
|
|
|
break;
|
|
|
|
|
case SupportedType:
|
2009-07-16 12:09:24 +02:00
|
|
|
case QVectorType:
|
2009-08-28 09:44:11 +02:00
|
|
|
case QStackType:
|
2009-07-06 17:36:50 +02:00
|
|
|
case QObjectType:
|
|
|
|
|
case QWidgetType:
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-02 16:38:15 +02:00
|
|
|
// Look up expressions in the cache
|
|
|
|
|
if (!m_expressionCache.empty()) {
|
2010-09-01 17:36:09 +02:00
|
|
|
const ExpressionCache::const_iterator excCend = m_expressionCache.constEnd();
|
|
|
|
|
const QByteArrayList::iterator eend = extraArgs.end();
|
|
|
|
|
for (QByteArrayList::iterator it = extraArgs.begin(); it != eend; ++it) {
|
|
|
|
|
QByteArray &e = *it;
|
2009-07-02 16:38:15 +02:00
|
|
|
if (!e.isEmpty() && e != zero && !isInteger(e)) {
|
2010-09-01 17:36:09 +02:00
|
|
|
const ExpressionCache::const_iterator eit = m_expressionCache.constFind(e);
|
2009-07-02 16:38:15 +02:00
|
|
|
if (eit != excCend)
|
|
|
|
|
e = eit.value();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
inBuffer->clear();
|
|
|
|
|
inBuffer->append(outertype.toUtf8());
|
|
|
|
|
inBuffer->append('\0');
|
2010-01-05 16:51:55 +01:00
|
|
|
inBuffer->append(data.iname);
|
2009-04-29 14:15:09 +02:00
|
|
|
inBuffer->append('\0');
|
2010-01-05 16:51:55 +01:00
|
|
|
inBuffer->append(data.exp);
|
2009-04-29 14:15:09 +02:00
|
|
|
inBuffer->append('\0');
|
2009-06-03 12:46:55 +02:00
|
|
|
inBuffer->append(inner.toUtf8());
|
2009-04-29 14:15:09 +02:00
|
|
|
inBuffer->append('\0');
|
2010-01-05 16:51:55 +01:00
|
|
|
inBuffer->append(data.iname);
|
2009-06-08 11:45:15 +02:00
|
|
|
inBuffer->append('\0');
|
2009-04-29 14:15:09 +02:00
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
|
qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-29 16:52:14 +02:00
|
|
|
QDebug operator<<(QDebug in, const QtDumperHelper::TypeData &d)
|
|
|
|
|
{
|
|
|
|
|
QDebug nsp = in.nospace();
|
|
|
|
|
nsp << " type=" << d.type << " tpl=" << d.isTemplate;
|
|
|
|
|
if (d.isTemplate)
|
|
|
|
|
nsp << d.tmplate << '<' << d.inner << '>';
|
|
|
|
|
return in;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-26 18:54:08 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// GdbMi interaction
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void setWatchDataValue(WatchData &data, const GdbMi &item)
|
|
|
|
|
{
|
|
|
|
|
GdbMi value = item.findChild("value");
|
|
|
|
|
if (value.isValid()) {
|
|
|
|
|
int encoding = item.findChild("valueencoded").data().toInt();
|
|
|
|
|
data.setValue(decodeData(value.data(), encoding));
|
|
|
|
|
} else {
|
|
|
|
|
data.setValueNeeded();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi,
|
|
|
|
|
int encoding)
|
|
|
|
|
{
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.setValueToolTip(decodeData(mi.data(), encoding));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.setHasChildren(mi.data().toInt() > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.data() == "true")
|
|
|
|
|
data.valueEnabled = true;
|
|
|
|
|
else if (mi.data() == "false")
|
|
|
|
|
data.valueEnabled = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataValueEditable(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.data() == "true")
|
|
|
|
|
data.valueEditable = true;
|
|
|
|
|
else if (mi.data() == "false")
|
|
|
|
|
data.valueEditable = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataExpression(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.exp = mi.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataAddress(WatchData &data, const GdbMi &mi)
|
|
|
|
|
{
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
setWatchDataAddressHelper(data, mi.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataAddressHelper(WatchData &data, const QByteArray &addr)
|
|
|
|
|
{
|
2010-09-22 11:19:35 +02:00
|
|
|
if (addr.startsWith("0x")) { // Item model dumpers pull tricks
|
|
|
|
|
data.setHexAddress(addr);
|
|
|
|
|
} else {
|
|
|
|
|
data.dumperFlags = addr;
|
|
|
|
|
}
|
|
|
|
|
if (data.exp.isEmpty() && !data.dumperFlags.startsWith('$'))
|
|
|
|
|
data.exp = "*(" + gdbQuoteTypes(data.type) + "*)" +data.hexAddress();
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the "type" and "displayedtype" children of root and set up type.
|
|
|
|
|
void setWatchDataType(WatchData &data, const GdbMi &item)
|
|
|
|
|
{
|
|
|
|
|
if (item.isValid())
|
2010-09-01 17:36:09 +02:00
|
|
|
data.setType(item.data());
|
2010-04-26 18:54:08 +02:00
|
|
|
else if (data.type.isEmpty())
|
|
|
|
|
data.setTypeNeeded();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setWatchDataDisplayedType(WatchData &data, const GdbMi &item)
|
|
|
|
|
{
|
|
|
|
|
if (item.isValid())
|
|
|
|
|
data.displayedType = _(item.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void parseWatchData(const QSet<QByteArray> &expandedINames,
|
2010-09-23 11:15:56 +02:00
|
|
|
const WatchData &data0, const GdbMi &item,
|
2010-10-08 14:55:57 +02:00
|
|
|
QList<WatchData> *list)
|
2010-04-26 18:54:08 +02:00
|
|
|
{
|
|
|
|
|
//qDebug() << "HANDLE CHILDREN: " << data0.toString() << item.toString();
|
|
|
|
|
WatchData data = data0;
|
|
|
|
|
bool isExpanded = expandedINames.contains(data.iname);
|
|
|
|
|
if (!isExpanded)
|
|
|
|
|
data.setChildrenUnneeded();
|
|
|
|
|
|
|
|
|
|
GdbMi children = item.findChild("children");
|
|
|
|
|
if (children.isValid() || !isExpanded)
|
|
|
|
|
data.setChildrenUnneeded();
|
|
|
|
|
|
|
|
|
|
setWatchDataType(data, item.findChild("type"));
|
|
|
|
|
GdbMi mi = item.findChild("editvalue");
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.editvalue = mi.data();
|
|
|
|
|
mi = item.findChild("editformat");
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.editformat = mi.data().toInt();
|
|
|
|
|
mi = item.findChild("typeformats");
|
|
|
|
|
if (mi.isValid())
|
|
|
|
|
data.typeFormats = QString::fromUtf8(mi.data());
|
|
|
|
|
|
|
|
|
|
setWatchDataValue(data, item);
|
|
|
|
|
setWatchDataAddress(data, item.findChild("addr"));
|
|
|
|
|
setWatchDataExpression(data, item.findChild("exp"));
|
|
|
|
|
setWatchDataValueEnabled(data, item.findChild("valueenabled"));
|
|
|
|
|
setWatchDataValueEditable(data, item.findChild("valueeditable"));
|
|
|
|
|
setWatchDataChildCount(data, item.findChild("numchild"));
|
|
|
|
|
//qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n";
|
|
|
|
|
list->append(data);
|
|
|
|
|
|
|
|
|
|
bool ok = false;
|
|
|
|
|
qulonglong addressBase = item.findChild("addrbase").data().toULongLong(&ok, 0);
|
|
|
|
|
qulonglong addressStep = item.findChild("addrstep").data().toULongLong();
|
|
|
|
|
|
|
|
|
|
// Try not to repeat data too often.
|
|
|
|
|
WatchData childtemplate;
|
|
|
|
|
setWatchDataType(childtemplate, item.findChild("childtype"));
|
|
|
|
|
setWatchDataChildCount(childtemplate, item.findChild("childnumchild"));
|
|
|
|
|
//qDebug() << "CHILD TEMPLATE:" << childtemplate.toString();
|
|
|
|
|
|
2010-10-12 11:30:34 +02:00
|
|
|
for (int i = 0, n = children.children().size(); i != n; ++i) {
|
|
|
|
|
const GdbMi &child = children.children().at(i);
|
2010-04-26 18:54:08 +02:00
|
|
|
WatchData data1 = childtemplate;
|
2010-10-12 11:30:34 +02:00
|
|
|
data1.sortId = i;
|
2010-04-26 18:54:08 +02:00
|
|
|
GdbMi name = child.findChild("name");
|
|
|
|
|
if (name.isValid())
|
|
|
|
|
data1.name = _(name.data());
|
|
|
|
|
else
|
|
|
|
|
data1.name = QString::number(i);
|
|
|
|
|
GdbMi iname = child.findChild("iname");
|
2010-09-23 11:15:56 +02:00
|
|
|
if (iname.isValid()) {
|
2010-04-26 18:54:08 +02:00
|
|
|
data1.iname = iname.data();
|
2010-09-23 11:15:56 +02:00
|
|
|
} else {
|
|
|
|
|
data1.iname = data.iname;
|
|
|
|
|
data1.iname += '.';
|
|
|
|
|
data1.iname += data1.name.toLatin1();
|
|
|
|
|
}
|
2010-04-26 18:54:08 +02:00
|
|
|
if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
|
|
|
|
|
data1.name = _c('[') + data1.name + _c(']');
|
|
|
|
|
if (addressStep) {
|
|
|
|
|
const QByteArray addr = "0x" + QByteArray::number(addressBase, 16);
|
|
|
|
|
setWatchDataAddressHelper(data1, addr);
|
|
|
|
|
addressBase += addressStep;
|
|
|
|
|
}
|
|
|
|
|
QByteArray key = child.findChild("key").data();
|
|
|
|
|
if (!key.isEmpty()) {
|
|
|
|
|
int encoding = child.findChild("keyencoded").data().toInt();
|
|
|
|
|
QString skey = decodeData(key, encoding);
|
|
|
|
|
if (skey.size() > 13) {
|
|
|
|
|
skey = skey.left(12);
|
|
|
|
|
skey += _("...");
|
|
|
|
|
}
|
|
|
|
|
//data1.name += " (" + skey + ")";
|
|
|
|
|
data1.name = skey;
|
|
|
|
|
}
|
2010-10-08 14:55:57 +02:00
|
|
|
parseWatchData(expandedINames, data1, child, list);
|
2010-04-26 18:54:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-05-05 10:22:44 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|