2009-03-25 13:42:47 +01:00
|
|
|
/**************************************************************************
|
|
|
|
**
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
**
|
|
|
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
|
|
|
** 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
|
2009-08-14 09:30:56 +02:00
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
2009-03-25 13:42:47 +01:00
|
|
|
**
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
#include "watchutils.h"
|
2009-04-29 14:15:09 +02:00
|
|
|
#include "watchhandler.h"
|
|
|
|
#include <utils/qtcassert.h>
|
2009-03-25 13:42:47 +01:00
|
|
|
|
2009-05-14 14:29:37 +02:00
|
|
|
#include <texteditor/basetexteditor.h>
|
|
|
|
#include <texteditor/basetextmark.h>
|
|
|
|
#include <texteditor/itexteditor.h>
|
|
|
|
#include <texteditor/texteditorconstants.h>
|
|
|
|
|
|
|
|
#include <cpptools/cppmodelmanagerinterface.h>
|
|
|
|
#include <cpptools/cpptoolsconstants.h>
|
|
|
|
|
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
|
|
|
|
|
|
|
#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-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-03-25 13:42:47 +01:00
|
|
|
namespace Debugger {
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
QString dotEscape(QString str)
|
|
|
|
{
|
2009-03-26 08:42:27 +01:00
|
|
|
const QChar dot = QLatin1Char('.');
|
2009-03-25 13:42:47 +01:00
|
|
|
str.replace(QLatin1Char(' '), dot);
|
|
|
|
str.replace(QLatin1Char('\\'), dot);
|
|
|
|
str.replace(QLatin1Char('/'), dot);
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (funcName.endsWith("::qt_metacall"))
|
|
|
|
return true;
|
2009-08-20 11:49:56 +02:00
|
|
|
if (funcName.endsWith("::d_func"))
|
|
|
|
return true;
|
|
|
|
if (funcName.endsWith("::q_func"))
|
|
|
|
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;
|
2009-03-25 13:42:47 +01:00
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qmetaobject.cpp"))
|
|
|
|
&& funcName.endsWith(QLatin1String("QMetaObject::methodOffset")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qobject.h")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qobject.cpp"))
|
|
|
|
&& funcName.endsWith(QLatin1String("QObjectConnectionListVector::at")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("kernel/qobject.cpp"))
|
|
|
|
&& funcName.endsWith(QLatin1String("~QObject")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("thread/qmutex.cpp")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("thread/qthread.cpp")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("thread/qthread_unix.cpp")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("thread/qmutex.h")))
|
|
|
|
return true;
|
|
|
|
if (fileName.contains(QLatin1String("thread/qbasicatomic")))
|
|
|
|
return true;
|
|
|
|
if (fileName.contains(QLatin1String("thread/qorderedmutexlocker_p")))
|
|
|
|
return true;
|
|
|
|
if (fileName.contains(QLatin1String("arch/qatomic")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("tools/qvector.h")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("tools/qlist.h")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("tools/qhash.h")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("tools/qmap.h")))
|
|
|
|
return true;
|
2009-08-20 11:49:56 +02:00
|
|
|
if (fileName.endsWith(QLatin1String("tools/qshareddata.h")))
|
|
|
|
return true;
|
2009-03-25 13:42:47 +01:00
|
|
|
if (fileName.endsWith(QLatin1String("tools/qstring.h")))
|
|
|
|
return true;
|
|
|
|
if (fileName.endsWith(QLatin1String("global/qglobal.h")))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 isPointerType(const QString &type)
|
|
|
|
{
|
|
|
|
return type.endsWith(QLatin1Char('*')) || type.endsWith(QLatin1String("* const"));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAccessSpecifier(const QString &str)
|
|
|
|
{
|
2009-04-29 18:03:35 +02:00
|
|
|
static const QStringList items = QStringList()
|
|
|
|
<< QLatin1String("private")
|
|
|
|
<< QLatin1String("protected")
|
|
|
|
<< QLatin1String("public");
|
2009-03-25 13:42:47 +01:00
|
|
|
return items.contains(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool startsWithDigit(const QString &str)
|
|
|
|
{
|
|
|
|
return !str.isEmpty() && str.at(0).isDigit();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString stripPointerType(QString type)
|
|
|
|
{
|
|
|
|
if (type.endsWith(QLatin1Char('*')))
|
|
|
|
type.chop(1);
|
|
|
|
if (type.endsWith(QLatin1String("* const")))
|
|
|
|
type.chop(7);
|
|
|
|
if (type.endsWith(QLatin1Char(' ')))
|
|
|
|
type.chop(1);
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString gdbQuoteTypes(const QString &type)
|
|
|
|
{
|
|
|
|
// 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))
|
|
|
|
return gdbQuoteTypes(stripPointerType(type)) + QLatin1Char('*');
|
|
|
|
|
|
|
|
QString accu;
|
|
|
|
QString result;
|
|
|
|
int templateLevel = 0;
|
|
|
|
|
|
|
|
const QChar colon = QLatin1Char(':');
|
|
|
|
const QChar singleQuote = QLatin1Char('\'');
|
|
|
|
const QChar lessThan = QLatin1Char('<');
|
|
|
|
const QChar greaterThan = QLatin1Char('>');
|
|
|
|
for (int i = 0; i != type.size(); ++i) {
|
|
|
|
const QChar c = type.at(i);
|
|
|
|
if (c.isLetterOrNumber() || c == QLatin1Char('_') || c == colon || c == QLatin1Char(' ')) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
|
|
|
|
{
|
|
|
|
// 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();
|
|
|
|
if (!type.contains(QLatin1Char('<')))
|
|
|
|
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) {
|
2009-03-25 13:42:47 +01:00
|
|
|
const QChar c = type.at(i);
|
2009-07-03 13:56:27 +02:00
|
|
|
const char asciiChar = c.toAscii();
|
|
|
|
switch (asciiChar) {
|
|
|
|
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 ',':
|
2009-03-25 13:42:47 +01:00
|
|
|
*inner += (level == 1) ? QLatin1Char('@') : QLatin1Char(',');
|
|
|
|
skipSpace = true;
|
2009-07-03 13:56:27 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!skipSpace || asciiChar != ' ') {
|
|
|
|
*(level == 0 ? tmplate : inner) += c;
|
|
|
|
skipSpace = false;
|
|
|
|
}
|
|
|
|
break;
|
2009-03-25 13:42:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*tmplate = tmplate->trimmed();
|
|
|
|
*tmplate = tmplate->remove(QLatin1String("<>"));
|
|
|
|
*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();
|
|
|
|
}
|
|
|
|
|
2009-07-03 13:25:51 +02:00
|
|
|
bool isIntType(const QString &type)
|
2009-03-25 13:42:47 +01:00
|
|
|
{
|
|
|
|
static const QStringList types = QStringList()
|
|
|
|
<< QLatin1String("char") << QLatin1String("int") << QLatin1String("short")
|
2009-07-03 13:25:51 +02:00
|
|
|
<< QLatin1String("long") << QLatin1String("bool")
|
|
|
|
<< QLatin1String("signed char") << QLatin1String("unsigned")
|
2009-03-25 13:42:47 +01:00
|
|
|
<< QLatin1String("unsigned char")
|
|
|
|
<< QLatin1String("unsigned int") << QLatin1String("unsigned long")
|
|
|
|
<< QLatin1String("long long") << QLatin1String("unsigned long long");
|
|
|
|
return types.contains(type);
|
|
|
|
}
|
|
|
|
|
2009-09-15 18:21:40 +02:00
|
|
|
bool isSymbianIntType(const QString &type)
|
|
|
|
{
|
|
|
|
static const QStringList types = QStringList()
|
|
|
|
<< QLatin1String("TInt") << QLatin1String("TBool");
|
|
|
|
return types.contains(type);
|
|
|
|
}
|
|
|
|
|
2009-07-03 13:25:51 +02:00
|
|
|
bool isIntOrFloatType(const QString &type)
|
|
|
|
{
|
|
|
|
static const QStringList types = QStringList()
|
|
|
|
<< QLatin1String("float") << QLatin1String("double");
|
|
|
|
return isIntType(type) || types.contains(type);
|
|
|
|
}
|
|
|
|
|
2009-07-14 11:21:52 +02:00
|
|
|
GuessChildrenResult guessChildren(const QString &type)
|
|
|
|
{
|
|
|
|
if (isIntOrFloatType(type))
|
|
|
|
return HasNoChildren;
|
|
|
|
if (isPointerType(type))
|
|
|
|
return HasChildren;
|
|
|
|
if (type.endsWith(QLatin1String("QString")))
|
|
|
|
return HasNoChildren;
|
|
|
|
return HasPossiblyChildren;
|
|
|
|
}
|
|
|
|
|
2009-03-25 13:42:47 +01:00
|
|
|
QString sizeofTypeExpression(const QString &type)
|
|
|
|
{
|
|
|
|
if (type.endsWith(QLatin1Char('*')))
|
|
|
|
return QLatin1String("sizeof(void*)");
|
|
|
|
if (type.endsWith(QLatin1Char('>')))
|
|
|
|
return QLatin1String("sizeof(") + type + QLatin1Char(')');
|
|
|
|
return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')');
|
|
|
|
}
|
|
|
|
|
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-05-04 12:12:45 +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-05-04 12:12:45 +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);
|
|
|
|
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));
|
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
return QCoreApplication::translate("Debugger", "<Encoding error>");
|
|
|
|
}
|
|
|
|
|
2009-05-14 14:29:37 +02:00
|
|
|
// Editor tooltip support
|
|
|
|
bool isCppEditor(Core::IEditor *editor)
|
|
|
|
{
|
|
|
|
static QStringList cppMimeTypes;
|
|
|
|
if (cppMimeTypes.empty()) {
|
|
|
|
cppMimeTypes << QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)
|
|
|
|
<< QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)
|
|
|
|
<< QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)
|
|
|
|
<< QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
|
|
|
|
}
|
|
|
|
if (const Core::IFile *file = editor->file())
|
|
|
|
return cppMimeTypes.contains(file->mimeType());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the function the cursor is in to use a scope.
|
|
|
|
|
|
|
|
|
2009-07-03 13:56:27 +02:00
|
|
|
|
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 */)
|
|
|
|
{
|
|
|
|
|
|
|
|
*line = *column = 0;
|
|
|
|
if (function)
|
|
|
|
function->clear();
|
|
|
|
|
|
|
|
const QPlainTextEdit *plaintext = qobject_cast<QPlainTextEdit*>(editor->widget());
|
|
|
|
if (!plaintext)
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
QString expr = plaintext->textCursor().selectedText();
|
|
|
|
if (expr.isEmpty()) {
|
|
|
|
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.
|
|
|
|
CPlusPlus::ExpressionUnderCursor expressionUnderCursor;
|
|
|
|
expr = expressionUnderCursor(tc);
|
|
|
|
*column = tc.columnNumber();
|
|
|
|
*line = tc.blockNumber();
|
|
|
|
} else {
|
|
|
|
const QTextCursor tc = plaintext->textCursor();
|
|
|
|
*column = tc.columnNumber();
|
|
|
|
*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())
|
|
|
|
if (CppTools::CppModelManagerInterface *modelManager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>())
|
|
|
|
*function = CppTools::AbstractEditorSupport::functionAt(modelManager, file->fileName(), *line, *column);
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
// --------------- QtDumperResult
|
|
|
|
|
|
|
|
QtDumperResult::Child::Child() :
|
2009-07-03 13:56:27 +02:00
|
|
|
keyEncoded(0),
|
2009-05-05 16:39:51 +02:00
|
|
|
valueEncoded(0),
|
2009-07-06 17:36:50 +02:00
|
|
|
childCount(-1),
|
2009-08-31 09:14:04 +02:00
|
|
|
valueEnabled(true),
|
2009-07-06 17:36:50 +02:00
|
|
|
valueEncountered(false)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QtDumperResult::QtDumperResult() :
|
2009-07-06 17:36:50 +02:00
|
|
|
valueEncountered(false),
|
2009-04-29 14:15:09 +02:00
|
|
|
valueEncoded(0),
|
2009-08-31 09:14:04 +02:00
|
|
|
valueEnabled(true),
|
2009-07-06 17:36:50 +02:00
|
|
|
childCount(-1),
|
|
|
|
internal(false),
|
|
|
|
childChildCount(-1)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtDumperResult::clear()
|
|
|
|
{
|
|
|
|
iname.clear();
|
|
|
|
value.clear();
|
|
|
|
address.clear();
|
2009-07-06 17:36:50 +02:00
|
|
|
addressInfo.clear();
|
2009-04-29 14:15:09 +02:00
|
|
|
type.clear();
|
2009-07-03 13:56:27 +02:00
|
|
|
extra.clear();
|
2009-05-05 16:39:51 +02:00
|
|
|
displayedType.clear();
|
2009-04-29 14:15:09 +02:00
|
|
|
valueEncoded = 0;
|
2009-08-31 09:14:04 +02:00
|
|
|
valueEncountered = false;
|
|
|
|
valueEnabled = false;
|
2009-07-06 17:36:50 +02:00
|
|
|
childCount = -1;
|
2009-04-29 14:15:09 +02:00
|
|
|
internal = false;
|
|
|
|
childType.clear();
|
|
|
|
children.clear();
|
2009-07-06 17:36:50 +02:00
|
|
|
childChildCount = -1;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QList<WatchData> QtDumperResult::toWatchData(int source) const
|
|
|
|
{
|
|
|
|
QList<WatchData> rc;
|
|
|
|
rc.push_back(WatchData());
|
|
|
|
WatchData &root = rc.front();
|
|
|
|
root.iname = iname;
|
|
|
|
const QChar dot = QLatin1Char('.');
|
|
|
|
const int lastDotIndex = root.iname.lastIndexOf(dot);
|
|
|
|
root.exp = root.name = lastDotIndex == -1 ? iname : iname.mid(lastDotIndex + 1);
|
2009-07-06 17:36:50 +02:00
|
|
|
if (valueEncountered) {
|
|
|
|
root.setValue(decodeData(value, valueEncoded));
|
2009-08-31 09:14:04 +02:00
|
|
|
root.valueEnabled = valueEnabled;
|
2009-07-06 17:36:50 +02:00
|
|
|
}
|
2009-08-12 11:21:44 +02:00
|
|
|
root.setType(type);
|
|
|
|
if (!displayedType.isEmpty())
|
|
|
|
root.displayedType = displayedType;
|
2009-04-29 14:15:09 +02:00
|
|
|
root.setAddress(address);
|
|
|
|
root.source = source;
|
2009-07-06 17:36:50 +02:00
|
|
|
if (childCount >= 0)
|
|
|
|
root.setHasChildren(childCount > 0);
|
|
|
|
// Children. Sanity check after parsing sets childcount to list size
|
|
|
|
// if list is not empty
|
|
|
|
if (children.empty()) {
|
|
|
|
if (childCount > 0)
|
|
|
|
root.setChildrenNeeded();
|
|
|
|
} else {
|
|
|
|
root.setChildrenUnneeded();
|
|
|
|
for (int c = 0; c < childCount; c++) {
|
|
|
|
const Child &dchild = children.at(c);
|
|
|
|
rc.push_back(WatchData());
|
|
|
|
WatchData &wchild = rc.back();
|
|
|
|
wchild.source = source;
|
|
|
|
wchild.iname = iname;
|
2009-07-15 16:07:57 +02:00
|
|
|
// Name can be empty for array-like things
|
|
|
|
const QString iname = dchild.name.isEmpty() ? QString::number(c) : dchild.name;
|
2009-07-06 17:36:50 +02:00
|
|
|
// Use key entry as name (which is used for map nodes)
|
|
|
|
if (dchild.key.isEmpty()) {
|
2009-07-15 16:07:57 +02:00
|
|
|
wchild.name = iname;
|
2009-07-06 17:36:50 +02:00
|
|
|
} else {
|
2009-07-15 16:07:57 +02:00
|
|
|
// Do not use map keys as iname since they might contain quotes.
|
2009-07-06 17:36:50 +02:00
|
|
|
wchild.name = decodeData(dchild.key, dchild.keyEncoded);
|
|
|
|
if (wchild.name.size() > 13) {
|
|
|
|
wchild.name.truncate(12);
|
|
|
|
wchild.name += QLatin1String("...");
|
2009-07-03 13:56:27 +02:00
|
|
|
}
|
2009-07-06 17:36:50 +02:00
|
|
|
}
|
2009-07-15 16:07:57 +02:00
|
|
|
// Append iname to total iname.
|
|
|
|
wchild.iname += dot;
|
|
|
|
wchild.iname += iname;
|
2009-07-06 17:36:50 +02:00
|
|
|
wchild.exp = dchild.exp;
|
|
|
|
if (dchild.valueEncountered) {
|
2009-08-31 09:14:04 +02:00
|
|
|
wchild.valueEnabled = dchild.valueEnabled;
|
2009-04-29 14:15:09 +02:00
|
|
|
wchild.setValue(decodeData(dchild.value, dchild.valueEncoded));
|
2009-07-06 17:36:50 +02:00
|
|
|
}
|
|
|
|
wchild.setAddress(dchild.address);
|
2009-07-24 16:05:46 +02:00
|
|
|
// The type setter sets hasChildren for known types.
|
|
|
|
wchild.setType(dchild.type.isEmpty() ? childType : dchild.type);
|
2009-08-12 11:21:44 +02:00
|
|
|
if (!dchild.displayedType.isEmpty())
|
|
|
|
wchild.displayedType = dchild.displayedType;
|
|
|
|
// Child overrides.
|
|
|
|
const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount;
|
|
|
|
switch (effectiveChildChildCount) {
|
|
|
|
case -1: // In this case, trust WatchData::setType().
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
wchild.setHasChildren(false);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wchild.setHasChildren(true);
|
|
|
|
wchild.setChildrenNeeded();
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-06 17:36:50 +02:00
|
|
|
if (debug) {
|
|
|
|
QDebug nospace = qDebug().nospace();
|
|
|
|
nospace << "QtDumperResult::toWatchData" << *this << '\n';
|
|
|
|
foreach(const WatchData &wd, rc)
|
|
|
|
nospace << " " << wd.toString() << '\n';
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDebug operator<<(QDebug in, const QtDumperResult &d)
|
|
|
|
{
|
|
|
|
QDebug nospace = in.nospace();
|
2009-08-31 09:14:04 +02:00
|
|
|
nospace << " iname=" << d.iname << " type=" << d.type
|
|
|
|
<< " displayed=" << d.displayedType
|
2009-07-06 17:36:50 +02:00
|
|
|
<< " address=" << d.address;
|
|
|
|
if (!d.addressInfo.isEmpty())
|
|
|
|
nospace << " addressInfo=" << d.addressInfo;
|
|
|
|
if (d.valueEncountered) {
|
|
|
|
nospace << " encoded=" << d.valueEncoded
|
|
|
|
<< " value=" << d.value
|
2009-08-31 09:14:04 +02:00
|
|
|
<< " enabled=" << d.valueEnabled;
|
2009-07-06 17:36:50 +02:00
|
|
|
} else {
|
|
|
|
nospace << " <no value>";
|
|
|
|
}
|
|
|
|
nospace << " childnumchild=" << d.childChildCount
|
|
|
|
<< " internal=" << d.internal
|
2009-07-03 13:56:27 +02:00
|
|
|
<< " extra='" << d.extra << "'\n";
|
2009-07-06 17:36:50 +02:00
|
|
|
|
2009-05-05 16:39:51 +02:00
|
|
|
const int realChildCount = d.children.size();
|
|
|
|
if (d.childCount || realChildCount) {
|
2009-07-03 13:56:27 +02:00
|
|
|
nospace << "childCount=" << d.childCount << '/' << realChildCount
|
2009-04-29 14:15:09 +02:00
|
|
|
<< " childType=" << d.childType << '\n';
|
2009-05-05 16:39:51 +02:00
|
|
|
for (int i = 0; i < realChildCount; i++) {
|
2009-04-29 14:15:09 +02:00
|
|
|
const QtDumperResult::Child &c = d.children.at(i);
|
|
|
|
nospace << " #" << i << " addr=" << c.address
|
2009-08-31 09:14:04 +02:00
|
|
|
<< " enabled=" << c.valueEnabled
|
2009-05-06 09:22:05 +02:00
|
|
|
<< " type=" << c.type << " exp=" << c.exp
|
2009-07-03 13:56:27 +02:00
|
|
|
<< " name=" << c.name;
|
|
|
|
if (!c.key.isEmpty())
|
|
|
|
nospace << " keyencoded=" << c.keyEncoded << " key=" << c.key;
|
2009-07-06 17:36:50 +02:00
|
|
|
if (c.valueEncountered) {
|
|
|
|
nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value;
|
|
|
|
} else {
|
|
|
|
nospace << " <no value>";
|
|
|
|
}
|
|
|
|
nospace << "childcount=" << c.childCount << '\n';
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return in;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------- 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);
|
2009-07-07 16:00:45 +02:00
|
|
|
setQClassPrefixes(QString());
|
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();
|
2009-07-07 16:00:45 +02:00
|
|
|
setQClassPrefixes(QString());
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QtDumperHelper::msgDumperOutdated(double requiredVersion, double currentVersion)
|
|
|
|
{
|
|
|
|
return QCoreApplication::translate("QtDumperHelper",
|
|
|
|
"Found a too-old 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) {
|
|
|
|
str << ' ' << it.key() << '=' << it.value();
|
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("QtDumperHelper", "<none>") : m_qtNamespace;
|
|
|
|
return QCoreApplication::translate("QtDumperHelper",
|
2009-07-06 17:36:50 +02:00
|
|
|
"%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3",
|
2009-04-29 14:15:09 +02:00
|
|
|
0, QCoreApplication::CodecForTr,
|
2009-07-07 16:00:45 +02:00
|
|
|
m_nameTypeMap.size()).arg(qtVersionString(), nameSpace).arg(m_dumperVersion);
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QtDumperHelper::Type QtDumperHelper::simpleType(const QString &simpleType) const
|
|
|
|
{
|
|
|
|
return m_nameTypeMap.value(simpleType, UnknownType);
|
|
|
|
}
|
|
|
|
|
|
|
|
int QtDumperHelper::qtVersion() const
|
|
|
|
{
|
|
|
|
return m_qtVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QtDumperHelper::qtNamespace() const
|
|
|
|
{
|
|
|
|
return m_qtNamespace;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtDumperHelper::setQtNamespace(const QString &qtNamespace)
|
|
|
|
{
|
|
|
|
m_qtNamespace = qtNamespace;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QtDumperHelper::typeCount() const
|
|
|
|
{
|
|
|
|
return m_nameTypeMap.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look up unnamespaced 'std' types.
|
|
|
|
static inline QtDumperHelper::Type stdType(const QString &s)
|
|
|
|
{
|
|
|
|
if (s == QLatin1String("vector"))
|
|
|
|
return QtDumperHelper::StdVectorType;
|
|
|
|
if (s == QLatin1String("deque"))
|
|
|
|
return QtDumperHelper::StdDequeType;
|
|
|
|
if (s == QLatin1String("set"))
|
|
|
|
return QtDumperHelper::StdSetType;
|
|
|
|
if (s == QLatin1String("stack"))
|
|
|
|
return QtDumperHelper::StdStackType;
|
|
|
|
if (s == QLatin1String("map"))
|
|
|
|
return QtDumperHelper::StdMapType;
|
|
|
|
if (s == QLatin1String("basic_string"))
|
|
|
|
return QtDumperHelper::StdStringType;
|
|
|
|
return QtDumperHelper::UnknownType;
|
|
|
|
}
|
|
|
|
|
|
|
|
QtDumperHelper::Type QtDumperHelper::specialType(QString s)
|
|
|
|
{
|
|
|
|
// Std classes.
|
|
|
|
if (s.startsWith(QLatin1String("std::")))
|
|
|
|
return stdType(s.mid(5));
|
|
|
|
// Strip namespace
|
2009-06-03 12:46:55 +02:00
|
|
|
// FIXME: that's not a good idea as it makes all namespaces equal.
|
2009-04-29 14:15:09 +02:00
|
|
|
const int namespaceIndex = s.lastIndexOf(QLatin1String("::"));
|
|
|
|
if (namespaceIndex == -1) {
|
|
|
|
// None ... check for std..
|
|
|
|
const Type sType = stdType(s);
|
|
|
|
if (sType != UnknownType)
|
|
|
|
return sType;
|
|
|
|
} else {
|
2009-06-03 12:46:55 +02:00
|
|
|
s = s.mid(namespaceIndex + 2);
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
2009-06-03 12:46:55 +02:00
|
|
|
if (s == QLatin1String("QAbstractItem"))
|
|
|
|
return QAbstractItemType;
|
2009-04-29 14:15:09 +02:00
|
|
|
if (s == QLatin1String("QMap"))
|
|
|
|
return QMapType;
|
|
|
|
if (s == QLatin1String("QMapNode"))
|
|
|
|
return QMapNodeType;
|
2009-08-28 09:44:11 +02:00
|
|
|
if (s == QLatin1String("QMultiMap"))
|
|
|
|
return QMultiMapType;
|
|
|
|
if (s == QLatin1String("QObject"))
|
|
|
|
return QObjectType;
|
|
|
|
if (s == QLatin1String("QObjectSignal"))
|
|
|
|
return QObjectSignalType;
|
|
|
|
if (s == QLatin1String("QObjectSlot"))
|
|
|
|
return QObjectSlotType;
|
|
|
|
if (s == QLatin1String("QStack"))
|
|
|
|
return QStackType;
|
|
|
|
if (s == QLatin1String("QVector"))
|
|
|
|
return QVectorType;
|
|
|
|
if (s == QLatin1String("QWidget"))
|
|
|
|
return QWidgetType;
|
2009-04-29 14:15:09 +02:00
|
|
|
return UnknownType;
|
|
|
|
}
|
|
|
|
|
2009-07-02 16:38:15 +02:00
|
|
|
QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t)
|
2009-04-29 14:15:09 +02:00
|
|
|
{
|
2009-07-06 17:36:50 +02:00
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
switch (t) {
|
2009-07-02 16:38:15 +02:00
|
|
|
case QAbstractItemType:
|
|
|
|
return NeedsComplexExpression;
|
|
|
|
case QMapType:
|
|
|
|
case QMultiMapType:
|
|
|
|
case QMapNodeType:
|
2009-07-07 16:00:45 +02:00
|
|
|
case StdMapType:
|
2009-07-02 16:38:15 +02:00
|
|
|
return NeedsCachedExpression;
|
|
|
|
default:
|
2009-07-06 17:36:50 +02:00
|
|
|
// QObjectSlotType, QObjectSignalType need the signal number, which is numeric
|
2009-07-02 16:38:15 +02:00
|
|
|
break;
|
2009-04-29 18:03:35 +02:00
|
|
|
}
|
2009-07-02 16:38:15 +02:00
|
|
|
return NeedsNoExpression;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QtDumperHelper::qtVersionString() const
|
|
|
|
{
|
|
|
|
QString rc;
|
|
|
|
QTextStream str(&rc);
|
|
|
|
formatQtVersion(m_qtVersion, str);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtDumperHelper::setQtVersion(int v)
|
|
|
|
{
|
|
|
|
m_qtVersion = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtDumperHelper::setQtVersion(const QString &v)
|
|
|
|
{
|
|
|
|
m_qtVersion = 0;
|
|
|
|
const QStringList vl = v.split(QLatin1Char('.'));
|
|
|
|
if (vl.size() == 3) {
|
|
|
|
const int major = vl.at(0).toInt();
|
|
|
|
const int minor = vl.at(1).toInt();
|
|
|
|
const int patch = vl.at(2).toInt();
|
|
|
|
m_qtVersion = (major << 16) | (minor << 8) | patch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a list of types.
|
|
|
|
void QtDumperHelper::parseQueryTypes(const QStringList &l, Debugger debugger)
|
|
|
|
{
|
|
|
|
m_nameTypeMap.clear();
|
|
|
|
const int count = l.count();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
const Type t = specialType(l.at(i));
|
|
|
|
if (t != UnknownType) {
|
|
|
|
// Exclude types that require expression syntax for CDB
|
2009-07-02 16:38:15 +02:00
|
|
|
if (debugger == GdbDebugger || expressionRequirements(t) != NeedsComplexExpression)
|
2009-04-29 14:15:09 +02:00
|
|
|
m_nameTypeMap.insert(l.at(i), t);
|
|
|
|
} else {
|
|
|
|
m_nameTypeMap.insert(l.at(i), SupportedType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A parse for dumper output:
|
|
|
|
* "iname="local.sl",addr="0x0012BA84",value="<3 items>",valuedisabled="true",
|
|
|
|
* numchild="3",childtype="QString",childnumchild="0",children=[{name="0",value="<binhex>",
|
|
|
|
* valueencoded="2"},{name="1",value="dAB3AG8A",valueencoded="2"},{name="2",
|
|
|
|
* value="dABoAHIAZQBlAA==",valueencoded="2"}]"
|
|
|
|
* Default implementation can be used for debugging purposes. */
|
|
|
|
|
2009-04-29 18:03:35 +02:00
|
|
|
class DumperParser
|
|
|
|
{
|
2009-04-29 14:15:09 +02:00
|
|
|
public:
|
|
|
|
explicit DumperParser(const char *s) : m_s(s) {}
|
|
|
|
bool run();
|
2009-07-03 13:56:27 +02:00
|
|
|
virtual ~DumperParser() {}
|
2009-04-29 14:15:09 +02:00
|
|
|
|
|
|
|
protected:
|
|
|
|
// handle 'key="value"'
|
|
|
|
virtual bool handleKeyword(const char *k, int size);
|
|
|
|
virtual bool handleListStart();
|
|
|
|
virtual bool handleListEnd();
|
|
|
|
virtual bool handleHashStart();
|
|
|
|
virtual bool handleHashEnd();
|
|
|
|
virtual bool handleValue(const char *k, int size);
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool parseHash(int level, const char *&pos);
|
|
|
|
bool parseValue(int level, const char *&pos);
|
|
|
|
bool parseStringValue(const char *&ptr, int &size, const char *&pos) const;
|
|
|
|
|
|
|
|
const char *m_s;
|
|
|
|
};
|
|
|
|
|
|
|
|
// get a string value with pos at the opening double quote
|
|
|
|
bool DumperParser::parseStringValue(const char *&ptr, int &size, const char *&pos) const
|
|
|
|
{
|
|
|
|
pos++;
|
|
|
|
const char *endValuePtr = strchr(pos, '"');
|
|
|
|
if (!endValuePtr)
|
|
|
|
return false;
|
|
|
|
size = endValuePtr - pos;
|
|
|
|
ptr = pos;
|
|
|
|
pos = endValuePtr + 1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::run()
|
|
|
|
{
|
|
|
|
const char *ptr = m_s;
|
|
|
|
const bool rc = parseHash(0, ptr);
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO << '\n' << m_s << rc;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a non-empty hash with pos at the first keyword.
|
|
|
|
// Curly braces are present at level 0 only.
|
|
|
|
// '{a="X", b="X"}'
|
|
|
|
bool DumperParser::parseHash(int level, const char *&pos)
|
|
|
|
{
|
|
|
|
while (true) {
|
|
|
|
switch (*pos) {
|
|
|
|
case '\0': // EOS is acceptable at level 0 only
|
|
|
|
return level == 0;
|
|
|
|
case '}':
|
|
|
|
pos++;
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const char *equalsPtr = strchr(pos, '=');
|
|
|
|
if (!equalsPtr)
|
|
|
|
return false;
|
|
|
|
const int keywordLen = equalsPtr - pos;
|
|
|
|
if (!handleKeyword(pos, keywordLen))
|
|
|
|
return false;
|
|
|
|
pos = equalsPtr + 1;
|
|
|
|
if (!*pos)
|
2009-07-03 13:56:27 +02:00
|
|
|
return false;
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!parseValue(level + 1, pos))
|
2009-07-03 13:56:27 +02:00
|
|
|
return false;
|
2009-04-29 14:15:09 +02:00
|
|
|
if (*pos == ',')
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::parseValue(int level, const char *&pos)
|
|
|
|
{
|
|
|
|
// Simple string literal
|
|
|
|
switch (*pos) {
|
|
|
|
case '"': {
|
|
|
|
const char *valuePtr;
|
|
|
|
int valueSize;
|
|
|
|
return parseStringValue(valuePtr, valueSize, pos) && handleValue(valuePtr, valueSize);
|
|
|
|
}
|
|
|
|
// A List. Note that it has a trailing comma '["a",]'
|
|
|
|
case '[': {
|
|
|
|
if (!handleListStart())
|
|
|
|
return false;
|
|
|
|
pos++;
|
|
|
|
while (true) {
|
|
|
|
switch (*pos) {
|
|
|
|
case ']':
|
|
|
|
pos++;
|
|
|
|
return handleListEnd();
|
|
|
|
case '\0':
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!parseValue(level + 1, pos))
|
|
|
|
return false;
|
|
|
|
if (*pos == ',')
|
|
|
|
pos++;
|
|
|
|
}
|
2009-07-03 13:56:27 +02:00
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
return false;
|
|
|
|
// A hash '{a="b",b="c"}'
|
|
|
|
case '{': {
|
|
|
|
if (!handleHashStart())
|
|
|
|
return false;
|
|
|
|
pos++;
|
|
|
|
if (!parseHash(level + 1, pos))
|
2009-07-03 13:56:27 +02:00
|
|
|
return false;
|
2009-04-29 14:15:09 +02:00
|
|
|
return handleHashEnd();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::handleKeyword(const char *k, int size)
|
|
|
|
{
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO << '\n' << QByteArray(k, size);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::handleListStart()
|
|
|
|
{
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::handleListEnd()
|
|
|
|
{
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::handleHashStart()
|
|
|
|
{
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::handleHashEnd()
|
|
|
|
{
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DumperParser::handleValue(const char *k, int size)
|
|
|
|
{
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-04-29 14:15:09 +02:00
|
|
|
qDebug() << Q_FUNC_INFO << '\n' << QByteArray(k, size);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-04-22 17:28:26 +02:00
|
|
|
/* Parse 'query' (1) protocol response of the custom dumpers:
|
|
|
|
* "'dumpers=["QByteArray","QDateTime",..."std::basic_string",],
|
|
|
|
* qtversion=["4","5","1"],namespace="""' */
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
class QueryDumperParser : public DumperParser {
|
|
|
|
public:
|
2009-04-29 16:52:14 +02:00
|
|
|
typedef QPair<QString, int> SizeEntry;
|
2009-04-29 14:15:09 +02:00
|
|
|
explicit QueryDumperParser(const char *s);
|
|
|
|
|
|
|
|
struct Data {
|
|
|
|
QString qtNameSpace;
|
|
|
|
QString qtVersion;
|
2009-07-06 17:36:50 +02:00
|
|
|
QString dumperVersion;
|
2009-04-29 14:15:09 +02:00
|
|
|
QStringList types;
|
2009-04-29 16:52:14 +02:00
|
|
|
QList<SizeEntry> sizes;
|
2009-07-02 16:38:15 +02:00
|
|
|
QMap<QString, QString> expressionCache;
|
2009-04-29 14:15:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
inline Data data() const { return m_data; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool handleKeyword(const char *k, int size);
|
2009-07-03 13:56:27 +02:00
|
|
|
virtual bool handleListStart();
|
2009-04-29 14:15:09 +02:00
|
|
|
virtual bool handleListEnd();
|
2009-04-29 16:52:14 +02:00
|
|
|
virtual bool handleHashEnd();
|
2009-04-29 14:15:09 +02:00
|
|
|
virtual bool handleValue(const char *k, int size);
|
|
|
|
|
|
|
|
private:
|
2009-07-06 17:36:50 +02:00
|
|
|
enum Mode { None, ExpectingDumpers, ExpectingQtVersion, ExpectingDumperVersion,
|
2009-07-02 16:38:15 +02:00
|
|
|
ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache };
|
2009-04-29 14:15:09 +02:00
|
|
|
Mode m_mode;
|
|
|
|
Data m_data;
|
2009-04-29 16:52:14 +02:00
|
|
|
QString m_lastSizeType;
|
2009-07-02 16:38:15 +02:00
|
|
|
QString m_lastExpression;
|
2009-04-29 14:15:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
QueryDumperParser::QueryDumperParser(const char *s) :
|
|
|
|
DumperParser(s),
|
|
|
|
m_mode(None)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-07-03 13:56:27 +02:00
|
|
|
bool QueryDumperParser::handleKeyword(const char *k, int size)
|
2009-07-02 16:38:15 +02:00
|
|
|
{
|
|
|
|
switch (m_mode) {
|
|
|
|
case ExpectingSizes:
|
2009-04-29 16:52:14 +02:00
|
|
|
m_lastSizeType = QString::fromLatin1(k, size);
|
|
|
|
return true;
|
2009-07-02 16:38:15 +02:00
|
|
|
case ExpectingExpressionCache:
|
|
|
|
m_lastExpression = QString::fromLatin1(k, size);
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
2009-04-29 16:52:14 +02:00
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!qstrncmp(k, "dumpers", size)) {
|
|
|
|
m_mode = ExpectingDumpers;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!qstrncmp(k, "qtversion", size)) {
|
2009-07-06 17:36:50 +02:00
|
|
|
m_mode = ExpectingQtVersion;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!qstrncmp(k, "dumperversion", size)) {
|
|
|
|
m_mode = ExpectingDumperVersion;
|
2009-04-29 14:15:09 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!qstrncmp(k, "namespace", size)) {
|
|
|
|
m_mode = ExpectingNameSpace;
|
|
|
|
return true;
|
|
|
|
}
|
2009-04-29 16:52:14 +02:00
|
|
|
if (!qstrncmp(k, "sizes", size)) {
|
|
|
|
m_mode = ExpectingSizes;
|
|
|
|
return true;
|
|
|
|
}
|
2009-07-02 16:38:15 +02:00
|
|
|
if (!qstrncmp(k, "expressions", size)) {
|
|
|
|
m_mode = ExpectingExpressionCache;
|
|
|
|
return true;
|
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
qWarning("%s Unexpected keyword %s.\n", Q_FUNC_INFO, QByteArray(k, size).constData());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QueryDumperParser::handleListStart()
|
|
|
|
{
|
2009-07-06 17:36:50 +02:00
|
|
|
return m_mode == ExpectingDumpers || m_mode == ExpectingQtVersion;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QueryDumperParser::handleListEnd()
|
|
|
|
{
|
|
|
|
m_mode = None;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-04-29 16:52:14 +02:00
|
|
|
bool QueryDumperParser::handleHashEnd()
|
|
|
|
{
|
|
|
|
m_mode = None; // Size hash
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
bool QueryDumperParser::handleValue(const char *k, int size)
|
|
|
|
{
|
|
|
|
switch (m_mode) {
|
2009-04-29 16:52:14 +02:00
|
|
|
case None:
|
2009-04-29 14:15:09 +02:00
|
|
|
return false;
|
2009-04-29 16:52:14 +02:00
|
|
|
case ExpectingDumpers:
|
2009-04-29 14:15:09 +02:00
|
|
|
m_data.types.push_back(QString::fromLatin1(k, size));
|
|
|
|
break;
|
2009-04-29 16:52:14 +02:00
|
|
|
case ExpectingNameSpace:
|
2009-04-29 14:15:09 +02:00
|
|
|
m_data.qtNameSpace = QString::fromLatin1(k, size);
|
|
|
|
break;
|
2009-07-06 17:36:50 +02:00
|
|
|
case ExpectingDumperVersion:
|
|
|
|
m_data.dumperVersion = QString::fromLatin1(k, size);
|
|
|
|
break;
|
|
|
|
case ExpectingQtVersion: // ["4","1","5"]
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!m_data.qtVersion.isEmpty())
|
|
|
|
m_data.qtVersion += QLatin1Char('.');
|
|
|
|
m_data.qtVersion += QString::fromLatin1(k, size);
|
|
|
|
break;
|
2009-04-29 16:52:14 +02:00
|
|
|
case ExpectingSizes:
|
|
|
|
m_data.sizes.push_back(SizeEntry(m_lastSizeType, QString::fromLatin1(k, size).toInt()));
|
|
|
|
break;
|
2009-07-02 16:38:15 +02:00
|
|
|
case ExpectingExpressionCache:
|
|
|
|
m_data.expressionCache.insert(m_lastExpression, QString::fromLatin1(k, size));
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-07-07 16:00:45 +02:00
|
|
|
static inline QString qClassName(const QString &qtNamespace, const char *className)
|
|
|
|
{
|
|
|
|
if (qtNamespace.isEmpty())
|
|
|
|
return QString::fromAscii(className);
|
|
|
|
QString rc = qtNamespace;
|
|
|
|
rc += QLatin1String("::");
|
|
|
|
rc += QString::fromAscii(className);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtDumperHelper::setQClassPrefixes(const QString &qNamespace)
|
|
|
|
{
|
|
|
|
// Prefixes with namespaces
|
|
|
|
m_qPointerPrefix = qClassName(qNamespace, "QPointer");
|
|
|
|
m_qSharedPointerPrefix = qClassName(qNamespace, "QSharedPointer");
|
|
|
|
m_qSharedDataPointerPrefix = qClassName(qNamespace, "QSharedDataPointer");
|
|
|
|
m_qWeakPointerPrefix = qClassName(qNamespace, "QWeakPointer");
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
// parse a query
|
|
|
|
bool QtDumperHelper::parseQuery(const char *data, Debugger debugger)
|
|
|
|
{
|
|
|
|
QueryDumperParser parser(data);
|
|
|
|
if (!parser.run())
|
|
|
|
return false;
|
|
|
|
clear();
|
|
|
|
m_qtNamespace = parser.data().qtNameSpace;
|
|
|
|
setQtVersion(parser.data().qtVersion);
|
2009-07-07 16:00:45 +02:00
|
|
|
setQClassPrefixes(m_qtNamespace);
|
2009-04-29 14:15:09 +02:00
|
|
|
parseQueryTypes(parser.data().types, debugger);
|
2009-04-29 16:52:14 +02:00
|
|
|
foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes)
|
|
|
|
addSize(se.first, se.second);
|
2009-07-02 16:38:15 +02:00
|
|
|
m_expressionCache = parser.data().expressionCache;
|
2009-07-07 16:00:45 +02:00
|
|
|
// Version
|
|
|
|
if (!parser.data().dumperVersion.isEmpty()) {
|
|
|
|
double dumperVersion;
|
|
|
|
bool ok;
|
|
|
|
dumperVersion = parser.data().dumperVersion.toDouble(&ok);
|
|
|
|
if (ok)
|
|
|
|
m_dumperVersion = dumperVersion;
|
|
|
|
}
|
2009-04-29 14:15:09 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-07-07 16:00:45 +02:00
|
|
|
void QtDumperHelper::addExpression(const QString &expression, const QString &value)
|
|
|
|
{
|
|
|
|
m_expressionCache.insert(expression, value);
|
|
|
|
}
|
|
|
|
|
2009-04-29 16:52:14 +02:00
|
|
|
void QtDumperHelper::addSize(const QString &name, int size)
|
|
|
|
{
|
|
|
|
// Special interest cases
|
2009-07-02 16:38:15 +02:00
|
|
|
if (name == QLatin1String("char*")) {
|
|
|
|
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
|
2009-04-29 16:52:14 +02:00
|
|
|
if (name == QLatin1String("std::string")) {
|
2009-07-03 13:56:27 +02:00
|
|
|
m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char> >"), size);
|
|
|
|
m_sizeCache.insert(QLatin1String("basic_string<char,char_traits<char>,allocator<char> >"), size);
|
2009-04-29 16:52:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (name == QLatin1String("std::wstring")) {
|
2009-07-03 13:56:27 +02:00
|
|
|
m_sizeCache.insert(QLatin1String("basic_string<unsigned short,char_traits<unsignedshort>,allocator<unsignedshort> >"), size);
|
|
|
|
m_sizeCache.insert(QLatin1String("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);
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
QtDumperHelper::Type QtDumperHelper::type(const QString &typeName) const
|
|
|
|
{
|
|
|
|
const QtDumperHelper::TypeData td = typeData(typeName);
|
|
|
|
return td.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const
|
|
|
|
{
|
|
|
|
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
|
|
|
|
QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
|
|
|
|
Debugger /* debugger */) const
|
|
|
|
{
|
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])
|
|
|
|
return QString::number(size);
|
|
|
|
}
|
|
|
|
// 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())
|
|
|
|
return QString::number(sit.value());
|
|
|
|
// Finally have the debugger evaluate
|
|
|
|
return sizeofTypeExpression(typeName);
|
|
|
|
}
|
|
|
|
|
2009-07-07 16:00:45 +02:00
|
|
|
QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) const
|
2009-07-02 16:38:15 +02:00
|
|
|
{
|
|
|
|
if (isPointerType(typeName))
|
|
|
|
return PointerSize;
|
|
|
|
static const QString intType = QLatin1String("int");
|
|
|
|
static const QString stdAllocatorPrefix = QLatin1String("std::allocator");
|
|
|
|
if (typeName == intType)
|
|
|
|
return IntSize;
|
|
|
|
if (typeName.startsWith(stdAllocatorPrefix))
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
void QtDumperHelper::evaluationParameters(const WatchData &data,
|
|
|
|
const TypeData &td,
|
2009-04-29 16:52:14 +02:00
|
|
|
Debugger debugger,
|
2009-04-29 14:15:09 +02:00
|
|
|
QByteArray *inBuffer,
|
|
|
|
QStringList *extraArgsIn) const
|
|
|
|
{
|
|
|
|
enum { maxExtraArgCount = 4 };
|
|
|
|
|
|
|
|
QStringList &extraArgs = *extraArgsIn;
|
|
|
|
|
|
|
|
// See extractTemplate for parameters
|
|
|
|
QStringList inners = td.inner.split(QLatin1Char('@'));
|
|
|
|
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
|
|
|
|
if (outertype == m_qtNamespace + QLatin1String("QWidget"))
|
|
|
|
outertype = m_qtNamespace + QLatin1String("QObject");
|
|
|
|
|
2009-06-03 12:46:55 +02:00
|
|
|
QString inner = td.inner;
|
|
|
|
|
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++)
|
2009-04-29 16:52:14 +02:00
|
|
|
extraArgs.push_back(evaluationSizeofTypeExpression(inners.at(i), debugger));
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
int extraArgCount = extraArgs.size();
|
|
|
|
// Pad with zeros
|
|
|
|
const QString zero = QString(QLatin1Char('0'));
|
|
|
|
const int extraPad = maxExtraArgCount - extraArgCount;
|
|
|
|
for (int i = 0; i < extraPad; i++)
|
|
|
|
extraArgs.push_back(zero);
|
|
|
|
|
|
|
|
// in rare cases we need more or less:
|
|
|
|
switch (td.type) {
|
2009-06-03 12:46:55 +02:00
|
|
|
case QAbstractItemType:
|
|
|
|
inner = data.addr.mid(1);
|
|
|
|
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('.');
|
|
|
|
const QString slotNumber = data.iname.mid(pos + 1);
|
|
|
|
QTC_ASSERT(slotNumber.toInt() != -1, /**/);
|
|
|
|
extraArgs[0] = slotNumber;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case QMapType:
|
|
|
|
case QMultiMapType: {
|
|
|
|
QString nodetype;
|
2009-07-14 10:07:15 +02:00
|
|
|
if (m_qtVersion >= 0x040500) {
|
2009-04-29 14:15:09 +02:00
|
|
|
nodetype = m_qtNamespace + QLatin1String("QMapNode");
|
|
|
|
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
|
|
|
|
nodetype = data.type + QLatin1String("::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);
|
2009-04-29 16:52:14 +02:00
|
|
|
extraArgs[2] = evaluationSizeofTypeExpression(nodetype, debugger);
|
2009-04-29 14:15:09 +02:00
|
|
|
extraArgs[3] = QLatin1String("(size_t)&(('");
|
|
|
|
extraArgs[3] += nodetype;
|
|
|
|
extraArgs[3] += QLatin1String("'*)0)->value");
|
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:
|
2009-04-29 16:52:14 +02:00
|
|
|
extraArgs[2] = evaluationSizeofTypeExpression(data.type, debugger);
|
2009-04-29 14:15:09 +02:00
|
|
|
extraArgs[3] = QLatin1String("(size_t)&(('");
|
|
|
|
extraArgs[3] += data.type;
|
|
|
|
extraArgs[3] += QLatin1String("'*)0)->value");
|
|
|
|
break;
|
|
|
|
case StdVectorType:
|
|
|
|
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
|
|
|
if (inners.at(0) == QLatin1String("bool")) {
|
|
|
|
outertype = QLatin1String("std::vector::bool");
|
|
|
|
} else {
|
2009-04-29 16:52:14 +02:00
|
|
|
//extraArgs[extraArgCount++] = evaluationSizeofTypeExpression(data.type, debugger);
|
2009-04-29 14:15:09 +02:00
|
|
|
//extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StdDequeType:
|
|
|
|
extraArgs[1] = zero;
|
|
|
|
case StdStackType:
|
|
|
|
// remove 'std::allocator<...>':
|
|
|
|
extraArgs[1] = zero;
|
|
|
|
break;
|
|
|
|
case StdSetType:
|
|
|
|
// remove 'std::less<...>':
|
|
|
|
extraArgs[1] = zero;
|
|
|
|
// remove 'std::allocator<...>':
|
|
|
|
extraArgs[2] = zero;
|
|
|
|
break;
|
|
|
|
case StdMapType: {
|
|
|
|
// We don't want the comparator and the allocator confuse gdb.
|
|
|
|
// But we need the offset of the second item in the value pair.
|
|
|
|
// We read the type of the pair from the allocator argument because
|
|
|
|
// that gets the constness "right" (in the sense that gdb 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-04-29 14:15:09 +02:00
|
|
|
QString pairType = inners.at(3);
|
2009-07-03 13:56:27 +02:00
|
|
|
int bracketPos = pairType.indexOf(QLatin1Char('<'));
|
|
|
|
if (bracketPos != -1)
|
|
|
|
pairType.remove(0, bracketPos + 1);
|
2009-07-07 16:00:45 +02:00
|
|
|
const QChar closingBracket = QLatin1Char('>');
|
|
|
|
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);
|
2009-04-29 14:15:09 +02:00
|
|
|
extraArgs[2] = QLatin1String("(size_t)&(('");
|
|
|
|
extraArgs[2] += pairType;
|
|
|
|
extraArgs[2] += QLatin1String("'*)0)->second");
|
|
|
|
extraArgs[3] = zero;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StdStringType:
|
|
|
|
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
|
|
|
if (inners.at(0) == QLatin1String("char")) {
|
|
|
|
outertype = QLatin1String("std::string");
|
|
|
|
} else if (inners.at(0) == QLatin1String("wchar_t")) {
|
|
|
|
outertype = QLatin1String("std::wstring");
|
|
|
|
}
|
|
|
|
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()) {
|
|
|
|
const QMap<QString, QString>::const_iterator excCend = m_expressionCache.constEnd();
|
|
|
|
const QStringList::iterator eend = extraArgs.end();
|
|
|
|
for (QStringList::iterator it = extraArgs.begin(); it != eend; ++it) {
|
|
|
|
QString &e = *it;
|
|
|
|
if (!e.isEmpty() && e != zero && !isInteger(e)) {
|
|
|
|
const QMap<QString, QString>::const_iterator eit = m_expressionCache.constFind(e);
|
|
|
|
if (eit != excCend)
|
|
|
|
e = eit.value();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
inBuffer->clear();
|
|
|
|
inBuffer->append(outertype.toUtf8());
|
|
|
|
inBuffer->append('\0');
|
|
|
|
inBuffer->append(data.iname.toUtf8());
|
|
|
|
inBuffer->append('\0');
|
|
|
|
inBuffer->append(data.exp.toUtf8());
|
|
|
|
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');
|
2009-06-08 11:45:15 +02:00
|
|
|
inBuffer->append(data.iname.toUtf8());
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse value:
|
|
|
|
* "iname="local.sl",addr="0x0012BA84",value="<3 items>",valuedisabled="true",
|
2009-04-29 18:03:35 +02:00
|
|
|
* numchild="3",childtype="QString",childnumchild="0",
|
|
|
|
* children=[{name="0",value="<binhex>",valueencoded="2"},
|
|
|
|
* {name="1",value="dAB3AG8A",valueencoded="2"},
|
|
|
|
* {name="2",value="dABoAHIAZQBlAA==",valueencoded="2"}]" */
|
2009-04-29 14:15:09 +02:00
|
|
|
|
2009-04-29 18:03:35 +02:00
|
|
|
class ValueDumperParser : public DumperParser
|
|
|
|
{
|
2009-04-29 14:15:09 +02:00
|
|
|
public:
|
|
|
|
explicit ValueDumperParser(const char *s);
|
|
|
|
|
|
|
|
inline QtDumperResult result() const { return m_result; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool handleKeyword(const char *k, int size);
|
|
|
|
virtual bool handleHashStart();
|
|
|
|
virtual bool handleValue(const char *k, int size);
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum Mode { None, ExpectingIName, ExpectingAddress, ExpectingValue,
|
2009-05-05 16:39:51 +02:00
|
|
|
ExpectingType, ExpectingDisplayedType, ExpectingInternal,
|
2009-08-31 09:14:04 +02:00
|
|
|
ExpectingValueEnabled, ExpectingValueEncoded,
|
2009-05-05 16:39:51 +02:00
|
|
|
ExpectingCommonChildType, ExpectingChildCount,
|
2009-07-06 17:36:50 +02:00
|
|
|
ExpectingChildChildOverrideCount,
|
2009-07-03 13:56:27 +02:00
|
|
|
ExpectingExtra,
|
2009-04-29 14:15:09 +02:00
|
|
|
IgnoreNext,
|
|
|
|
ChildModeStart,
|
|
|
|
ExpectingChildren,ExpectingChildName, ExpectingChildAddress,
|
2009-05-05 16:39:51 +02:00
|
|
|
ExpectingChildExpression, ExpectingChildType,
|
2009-08-12 11:21:44 +02:00
|
|
|
ExpectingChildDisplayedType,
|
2009-07-03 13:56:27 +02:00
|
|
|
ExpectingChildKey, ExpectingChildKeyEncoded,
|
2009-05-05 16:39:51 +02:00
|
|
|
ExpectingChildValue, ExpectingChildValueEncoded,
|
2009-08-31 09:14:04 +02:00
|
|
|
ExpectingChildValueEnabled, ExpectingChildChildCount,
|
2009-07-03 13:56:27 +02:00
|
|
|
IgnoreNextChildMode
|
2009-05-05 16:39:51 +02:00
|
|
|
};
|
2009-04-29 14:15:09 +02:00
|
|
|
|
|
|
|
static inline Mode nextMode(Mode in, const char *keyword, int size);
|
|
|
|
|
|
|
|
Mode m_mode;
|
|
|
|
QtDumperResult m_result;
|
|
|
|
};
|
|
|
|
|
|
|
|
ValueDumperParser::ValueDumperParser(const char *s) :
|
|
|
|
DumperParser(s),
|
|
|
|
m_mode(None)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check key words
|
|
|
|
ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword, int size)
|
|
|
|
{
|
|
|
|
// Careful with same prefix
|
|
|
|
switch (size) {
|
2009-05-05 16:39:51 +02:00
|
|
|
case 3:
|
|
|
|
if (!qstrncmp(keyword, "exp", size))
|
|
|
|
return ExpectingChildExpression;
|
2009-07-03 13:56:27 +02:00
|
|
|
if (!qstrncmp(keyword, "key", size))
|
|
|
|
return ExpectingChildKey;
|
2009-05-05 16:39:51 +02:00
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case 4:
|
|
|
|
if (!qstrncmp(keyword, "addr", size))
|
|
|
|
return in > ChildModeStart ? ExpectingChildAddress : ExpectingAddress;
|
|
|
|
if (!qstrncmp(keyword, "type", size))
|
2009-05-05 16:39:51 +02:00
|
|
|
return in > ChildModeStart ? ExpectingChildType : ExpectingType;
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!qstrncmp(keyword, "name", size))
|
|
|
|
return ExpectingChildName;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
if (!qstrncmp(keyword, "iname", size))
|
|
|
|
return ExpectingIName;
|
|
|
|
if (!qstrncmp(keyword, "value", size))
|
|
|
|
return in > ChildModeStart ? ExpectingChildValue : ExpectingValue;
|
2009-07-03 13:56:27 +02:00
|
|
|
if (!qstrncmp(keyword, "extra", size))
|
|
|
|
return ExpectingExtra;
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (!qstrncmp(keyword, "children", size))
|
|
|
|
return ExpectingChildren;
|
|
|
|
if (!qstrncmp(keyword, "numchild", size))
|
2009-05-05 16:39:51 +02:00
|
|
|
return in > ChildModeStart ? ExpectingChildChildCount : ExpectingChildCount;
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!qstrncmp(keyword, "internal", size))
|
|
|
|
return ExpectingInternal;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if (!qstrncmp(keyword, "childtype", size))
|
2009-05-05 16:39:51 +02:00
|
|
|
return ExpectingCommonChildType;
|
2009-07-03 13:56:27 +02:00
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (!qstrncmp(keyword, "keyencoded", size))
|
|
|
|
return ExpectingChildKeyEncoded;
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case 12:
|
|
|
|
if (!qstrncmp(keyword, "valueencoded", size))
|
|
|
|
return in > ChildModeStart ? ExpectingChildValueEncoded : ExpectingValueEncoded;
|
|
|
|
break;
|
|
|
|
case 13:
|
2009-08-31 09:14:04 +02:00
|
|
|
if (!qstrncmp(keyword, "valueenabled", size))
|
|
|
|
return in > ChildModeStart ? ExpectingChildValueEnabled : ExpectingValueEnabled;
|
2009-05-05 16:39:51 +02:00
|
|
|
if (!qstrncmp(keyword, "displayedtype", size))
|
2009-08-12 11:21:44 +02:00
|
|
|
return in > ChildModeStart ? ExpectingChildDisplayedType : ExpectingDisplayedType;
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!qstrncmp(keyword, "childnumchild", size))
|
2009-07-06 17:36:50 +02:00
|
|
|
return ExpectingChildChildOverrideCount;
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
}
|
2009-07-03 13:56:27 +02:00
|
|
|
return in > ChildModeStart ? IgnoreNextChildMode : IgnoreNext;
|
2009-04-29 14:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueDumperParser::handleKeyword(const char *k, int size)
|
|
|
|
{
|
|
|
|
const Mode newMode = nextMode(m_mode, k, size);
|
|
|
|
if (debug && newMode == IgnoreNext)
|
|
|
|
qWarning("%s Unexpected keyword %s.\n", Q_FUNC_INFO, QByteArray(k, size).constData());
|
|
|
|
m_mode = newMode;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueDumperParser::handleHashStart()
|
|
|
|
{
|
|
|
|
m_result.children.push_back(QtDumperResult::Child());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValueDumperParser::handleValue(const char *k, int size)
|
|
|
|
{
|
|
|
|
const QByteArray valueBA(k, size);
|
|
|
|
switch (m_mode) {
|
|
|
|
case None:
|
|
|
|
case ChildModeStart:
|
|
|
|
return false;
|
|
|
|
case ExpectingIName:
|
|
|
|
m_result.iname = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-07-06 17:36:50 +02:00
|
|
|
case ExpectingAddress: {
|
|
|
|
const QString address = QString::fromLatin1(valueBA);
|
|
|
|
if (address.startsWith(QLatin1String("0x"))) {
|
|
|
|
m_result.address = address;
|
|
|
|
} else {
|
|
|
|
m_result.addressInfo = address;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case ExpectingValue:
|
2009-07-06 17:36:50 +02:00
|
|
|
m_result.valueEncountered = true;
|
2009-04-29 14:15:09 +02:00
|
|
|
m_result.value = valueBA;
|
|
|
|
break;
|
2009-08-31 09:14:04 +02:00
|
|
|
case ExpectingValueEnabled:
|
|
|
|
m_result.valueEnabled = valueBA == "true";
|
2009-04-29 14:15:09 +02:00
|
|
|
break;
|
|
|
|
case ExpectingValueEncoded:
|
|
|
|
m_result.valueEncoded = QString::fromLatin1(valueBA).toInt();
|
|
|
|
break;
|
|
|
|
case ExpectingType:
|
|
|
|
m_result.type = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-05-05 16:39:51 +02:00
|
|
|
case ExpectingDisplayedType:
|
|
|
|
m_result.displayedType = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-07-03 13:56:27 +02:00
|
|
|
case ExpectingExtra:
|
|
|
|
m_result.extra = valueBA;
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case ExpectingInternal:
|
|
|
|
m_result.internal = valueBA == "true";
|
|
|
|
break;
|
2009-05-05 16:39:51 +02:00
|
|
|
case ExpectingCommonChildType:
|
2009-04-29 14:15:09 +02:00
|
|
|
m_result.childType = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
|
|
|
case ExpectingChildCount:
|
|
|
|
m_result.childCount = QString::fromLatin1(valueBA).toInt();
|
|
|
|
break;
|
2009-07-06 17:36:50 +02:00
|
|
|
case ExpectingChildChildOverrideCount:
|
|
|
|
m_result.childChildCount = QString::fromLatin1(valueBA).toInt();
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case ExpectingChildren:
|
2009-07-03 13:56:27 +02:00
|
|
|
case IgnoreNextChildMode:
|
2009-04-29 14:15:09 +02:00
|
|
|
case IgnoreNext:
|
|
|
|
break;
|
|
|
|
case ExpectingChildName:
|
|
|
|
m_result.children.back().name = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
|
|
|
case ExpectingChildAddress:
|
|
|
|
m_result.children.back().address = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-07-03 13:56:27 +02:00
|
|
|
case ExpectingChildKeyEncoded:
|
|
|
|
m_result.children.back().keyEncoded = QString::fromLatin1(valueBA).toInt();
|
|
|
|
break;
|
|
|
|
case ExpectingChildKey:
|
|
|
|
m_result.children.back().key = valueBA;
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case ExpectingChildValue:
|
2009-07-06 17:36:50 +02:00
|
|
|
m_result.children.back().valueEncountered = true;
|
2009-04-29 14:15:09 +02:00
|
|
|
m_result.children.back().value = valueBA;
|
|
|
|
break;
|
2009-05-05 16:39:51 +02:00
|
|
|
case ExpectingChildExpression:
|
|
|
|
m_result.children.back().exp = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-04-29 14:15:09 +02:00
|
|
|
case ExpectingChildValueEncoded:
|
|
|
|
m_result.children.back().valueEncoded = QString::fromLatin1(valueBA).toInt();
|
|
|
|
break;
|
2009-08-31 09:14:04 +02:00
|
|
|
case ExpectingChildValueEnabled:
|
|
|
|
m_result.children.back().valueEnabled = valueBA == "true";
|
2009-05-05 16:39:51 +02:00
|
|
|
break;
|
|
|
|
case ExpectingChildType:
|
|
|
|
m_result.children.back().type = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-08-12 11:21:44 +02:00
|
|
|
case ExpectingChildDisplayedType:
|
|
|
|
m_result.children.back().displayedType = QString::fromLatin1(valueBA);
|
|
|
|
break;
|
2009-05-05 16:39:51 +02:00
|
|
|
case ExpectingChildChildCount:
|
|
|
|
m_result.children.back().childCount = QString::fromLatin1(valueBA).toInt();
|
|
|
|
break;
|
2009-04-22 17:28:26 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
bool QtDumperHelper::parseValue(const char *data, QtDumperResult *r)
|
2009-07-03 13:56:27 +02:00
|
|
|
{
|
2009-04-29 14:15:09 +02:00
|
|
|
ValueDumperParser parser(data);
|
2009-07-06 17:36:50 +02:00
|
|
|
|
2009-04-29 14:15:09 +02:00
|
|
|
if (!parser.run())
|
|
|
|
return false;
|
|
|
|
*r = parser.result();
|
2009-05-05 16:39:51 +02:00
|
|
|
// Sanity
|
2009-07-06 17:36:50 +02:00
|
|
|
if (!r->children.empty() && r->childCount != r->children.size())
|
|
|
|
r->childCount = r->children.size();
|
2009-08-12 11:21:44 +02:00
|
|
|
if (debug > 1)
|
2009-07-03 13:56:27 +02:00
|
|
|
qDebug() << '\n' << data << '\n' << *r;
|
2009-04-29 14:15:09 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-05-05 10:22:44 +02:00
|
|
|
} // namespace Internal
|
|
|
|
} // namespace Debugger
|