forked from qt-creator/qt-creator
2447 lines
73 KiB
C++
2447 lines
73 KiB
C++
/****************************************************************************
|
|
** This file is part of Qt Creator
|
|
**
|
|
** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).*
|
|
**
|
|
** Contact: Qt Software Information (qt-info@nokia.com)**
|
|
**
|
|
** No Commercial Usage
|
|
** This file contains pre-release code and may only be used for evaluation
|
|
** and testing purposes. It may not be used for commercial development. You
|
|
** may use this file in accordance with the terms and conditions contained in
|
|
** the either Technology Preview License Agreement or the Beta Release
|
|
** License Agreement.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
** Public License versions 2.0 or 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the packaging
|
|
** of this file. Please review the following information to ensure GNU
|
|
** General Public Licensing requirements will be met:
|
|
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
|
|
** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
|
|
** exception, Nokia gives you certain additional rights. These rights are
|
|
** described in the Nokia Qt GPL Exception version 1.2, included in the file
|
|
** GPL_EXCEPTION.txt in this package.
|
|
**
|
|
** If you have questions regarding the use of this file, please contact Nokia
|
|
** at qt-info@nokia.com
|
|
****************************************************************************/
|
|
|
|
// {[(
|
|
|
|
#include <qglobal.h>
|
|
|
|
// this relies on contents copied from qobject_p.h
|
|
#define PRIVATE_OBJECT_ALLOWED 1
|
|
|
|
#include <QDateTime>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QHash>
|
|
#include <QLocale>
|
|
#include <QMap>
|
|
#include <QMetaObject>
|
|
#include <QMetaProperty>
|
|
#include <QModelIndex>
|
|
#include <QObject>
|
|
#include <QPointer>
|
|
#include <QString>
|
|
#include <QTextCodec>
|
|
#include <QVector>
|
|
|
|
|
|
/*!
|
|
\class QDumper
|
|
\brief Helper class for producing "nice" output in Qt Creator's debugger.
|
|
|
|
\internal
|
|
|
|
The whole "custom dumper" implementation is currently far less modular
|
|
than it could be. But as the code is still in a flux, making it nicer
|
|
from a pure archtectural point of view seems still be a waste of resources.
|
|
|
|
Some hints:
|
|
|
|
New dumpers for non-templated classes should be mentioned in
|
|
\c{qDumpObjectData440()} in the \c{protocolVersion == 1} branch.
|
|
|
|
Templated classes need extra support on the IDE level
|
|
(see plugins/debugger/gdbengine.cpp) and should not be mentiond in
|
|
\c{qDumpObjectData440()}.
|
|
|
|
In any case, dumper processesing should end up in
|
|
\c{handleProtocolVersion2and3()} and needs an entry in the bis switch there.
|
|
|
|
Next step is to create a suitable \c{static void qDumpFoo(QDumper &d)}
|
|
function. At the bare minimum it should contain something like:
|
|
|
|
|
|
\c{
|
|
const Foo &foo = *reinterpret_cast<const Foo *>(d.data);
|
|
|
|
P(d, "value", ...);
|
|
P(d, "type", "Foo");
|
|
P(d, "numchild", "0");
|
|
}
|
|
|
|
|
|
'P(d, name, value)' roughly expands to:
|
|
d << (name) << "=\"" << value << "\"";
|
|
|
|
Useful (i.e. understood by the IDE) names include:
|
|
|
|
\list
|
|
\o "name" shows up in the first column in the Locals&Watchers view.
|
|
\o "value" shows up in the second column.
|
|
\o "valueencoded" should be set to "1" if the value is base64 encoded.
|
|
Always base64-encode values that might use unprintable or otherwise
|
|
"confuse" the protocol (like spaces and quotes). [A-Za-z0-9] is "safe".
|
|
A value of "3" is used for base64-encoded UCS4, "2" denotes
|
|
base64-encoded UTF16.
|
|
\o "numchild" return the number of children in the view. Effectively, only
|
|
0 and != 0 will be used, so don't try too hard to get the number right.
|
|
\endlist
|
|
|
|
If the current item has children, it might be queried to produce information
|
|
about thes children. In this case the dumper should use something like
|
|
|
|
\c{
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
}
|
|
|
|
*/
|
|
|
|
int qtGhVersion = QT_VERSION;
|
|
|
|
#ifdef QT_GUI_LIB
|
|
# include <QPixmap>
|
|
# include <QImage>
|
|
#endif
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
|
|
//#include <sys/types.h>
|
|
|
|
#ifdef Q_OS_WIN
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
#undef NS
|
|
#ifdef QT_NAMESPACE
|
|
# define STRINGIFY0(s) #s
|
|
# define STRINGIFY1(s) STRINGIFY0(s)
|
|
# define NS STRINGIFY1(QT_NAMESPACE) "::"
|
|
# define NSX "'" STRINGIFY1(QT_NAMESPACE) "::"
|
|
# define NSY "'"
|
|
#else
|
|
# define NS ""
|
|
# define NSX ""
|
|
# define NSY ""
|
|
#endif
|
|
|
|
|
|
#if PRIVATE_OBJECT_ALLOWED
|
|
|
|
#if defined(QT_BEGIN_NAMESPACE)
|
|
QT_BEGIN_NAMESPACE
|
|
#endif
|
|
|
|
class QVariant;
|
|
class QThreadData;
|
|
class QObjectConnectionListVector;
|
|
|
|
class QObjectPrivate : public QObjectData
|
|
{
|
|
Q_DECLARE_PUBLIC(QObject)
|
|
|
|
public:
|
|
QObjectPrivate() {}
|
|
virtual ~QObjectPrivate() {}
|
|
|
|
// preserve binary compatibility with code compiled without Qt 3 support
|
|
QList<QObject *> pendingChildInsertedEvents; // unused
|
|
|
|
// id of the thread that owns the object
|
|
QThreadData *threadData;
|
|
void moveToThread_helper();
|
|
void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);
|
|
void _q_reregisterTimers(void *pointer);
|
|
|
|
struct Sender
|
|
{
|
|
QObject *sender;
|
|
int signal;
|
|
int ref;
|
|
};
|
|
|
|
Sender *currentSender; // object currently activating the object
|
|
QObject *currentChildBeingDeleted;
|
|
|
|
QList<QPointer<QObject> > eventFilters;
|
|
|
|
struct ExtraData
|
|
{
|
|
#ifndef QT_NO_USERDATA
|
|
QVector<QObjectUserData *> userData;
|
|
#endif
|
|
QList<QByteArray> propertyNames;
|
|
QList<QVariant> propertyValues;
|
|
};
|
|
ExtraData *extraData;
|
|
mutable quint32 connectedSignals;
|
|
|
|
QString objectName;
|
|
|
|
// Note: you must hold the signalSlotLock() before accessing the lists below or calling the functions
|
|
struct Connection
|
|
{
|
|
QObject *receiver;
|
|
int method;
|
|
uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
|
|
QBasicAtomicPointer<int> argumentTypes;
|
|
};
|
|
typedef QList<Connection> ConnectionList;
|
|
|
|
QObjectConnectionListVector *connectionLists;
|
|
QList<Sender> senders;
|
|
int *deleteWatch;
|
|
|
|
static QObjectPrivate *get(QObject *o) { return o->d_func(); }
|
|
};
|
|
|
|
#if defined(QT_BEGIN_NAMESPACE)
|
|
QT_END_NAMESPACE
|
|
#endif
|
|
|
|
#endif // PRIVATE_OBJECT_ALLOWED
|
|
|
|
|
|
// this can be mangled typenames of nested templates, each char-by-char
|
|
// comma-separated integer list
|
|
static char qDumpInBuffer[10000];
|
|
static char qDumpBuffer[1000];
|
|
|
|
namespace {
|
|
|
|
static bool isPointerType(const QByteArray &type)
|
|
{
|
|
return type.endsWith("*") || type.endsWith("* const");
|
|
}
|
|
|
|
static QByteArray stripPointerType(QByteArray type)
|
|
{
|
|
if (type.endsWith("*"))
|
|
type.chop(1);
|
|
if (type.endsWith("* const"))
|
|
type.chop(7);
|
|
if (type.endsWith(' '))
|
|
type.chop(1);
|
|
return type;
|
|
}
|
|
|
|
// This is used to abort evaluation of custom data dumpers in a "coordinated"
|
|
// way. Abortion will happen anyway when we try to access a non-initialized
|
|
// non-trivial object, so there is no way to prevent this from occuring at all
|
|
// conceptionally. Gdb will catch SIGSEGV and return to the calling frame.
|
|
// This is just fine provided we only _read_ memory in the custom handlers
|
|
// below.
|
|
|
|
volatile int qProvokeSegFaultHelper;
|
|
|
|
static const void *addOffset(const void *p, int offset)
|
|
{
|
|
return offset + reinterpret_cast<const char *>(p);
|
|
}
|
|
|
|
static const void *skipvtable(const void *p)
|
|
{
|
|
return sizeof(void*) + reinterpret_cast<const char *>(p);
|
|
}
|
|
|
|
static const void *deref(const void *p)
|
|
{
|
|
return *reinterpret_cast<const char* const*>(p);
|
|
}
|
|
|
|
static const void *dfunc(const void *p)
|
|
{
|
|
return deref(skipvtable(p));
|
|
}
|
|
|
|
static bool isEqual(const char *s, const char *t)
|
|
{
|
|
return qstrcmp(s, t) == 0;
|
|
}
|
|
|
|
static bool startsWith(const char *s, const char *t)
|
|
{
|
|
return qstrncmp(s, t, strlen(t)) == 0;
|
|
}
|
|
|
|
// provoke segfault when address is not readable
|
|
#define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
|
#define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
|
// provoke segfault unconditionally
|
|
#define qCheck(b) do { if (!b) qProvokeSegFaultHelper = *(char*)0; } while (0)
|
|
|
|
const char *stripNamespace(const char *type)
|
|
{
|
|
static const size_t nslen = strlen(NS);
|
|
return startsWith(type, NS) ? type + nslen : type;
|
|
}
|
|
|
|
static bool isSimpleType(const char *type)
|
|
{
|
|
switch (type[0]) {
|
|
case 'c':
|
|
return isEqual(type, "char");
|
|
case 'd':
|
|
return isEqual(type, "double");
|
|
case 'f':
|
|
return isEqual(type, "float");
|
|
case 'i':
|
|
return isEqual(type, "int");
|
|
case 'l':
|
|
return isEqual(type, "long") || startsWith(type, "long ");
|
|
case 's':
|
|
return isEqual(type, "short") || isEqual(type, "signed")
|
|
|| startsWith(type, "signed ");
|
|
case 'u':
|
|
return isEqual(type, "unsigned") || startsWith(type, "unsigned ");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isShortKey(const char *type)
|
|
{
|
|
return isSimpleType(type) || isEqual(type, "QString");
|
|
}
|
|
|
|
static bool isMovableType(const char *type)
|
|
{
|
|
if (isPointerType(type))
|
|
return true;
|
|
|
|
if (isSimpleType(type))
|
|
return true;
|
|
|
|
type = stripNamespace(type);
|
|
|
|
switch (type[1]) {
|
|
case 'B':
|
|
return isEqual(type, "QBrush")
|
|
|| isEqual(type, "QBitArray")
|
|
|| isEqual(type, "QByteArray") ;
|
|
case 'C':
|
|
return isEqual(type, "QCustomTypeInfo");
|
|
case 'D':
|
|
return isEqual(type, "QDate")
|
|
|| isEqual(type, "QDateTime");
|
|
case 'F':
|
|
return isEqual(type, "QFileInfo")
|
|
|| isEqual(type, "QFixed")
|
|
|| isEqual(type, "QFixedPoint")
|
|
|| isEqual(type, "QFixedSize");
|
|
case 'H':
|
|
return isEqual(type, "QHashDummyValue");
|
|
case 'I':
|
|
return isEqual(type, "QIcon")
|
|
|| isEqual(type, "QImage");
|
|
case 'L':
|
|
return isEqual(type, "QLine")
|
|
|| isEqual(type, "QLineF")
|
|
|| isEqual(type, "QLocal");
|
|
case 'M':
|
|
return isEqual(type, "QMatrix")
|
|
|| isEqual(type, "QModelIndex");
|
|
case 'P':
|
|
return isEqual(type, "QPoint")
|
|
|| isEqual(type, "QPointF")
|
|
|| isEqual(type, "QPen")
|
|
|| isEqual(type, "QPersistentModelIndex");
|
|
case 'R':
|
|
return isEqual(type, "QResourceRoot")
|
|
|| isEqual(type, "QRect")
|
|
|| isEqual(type, "QRectF")
|
|
|| isEqual(type, "QRegExp");
|
|
case 'S':
|
|
return isEqual(type, "QSize")
|
|
|| isEqual(type, "QSizeF")
|
|
|| isEqual(type, "QString");
|
|
case 'T':
|
|
return isEqual(type, "QTime")
|
|
|| isEqual(type, "QTextBlock");
|
|
case 'U':
|
|
return isEqual(type, "QUrl");
|
|
case 'V':
|
|
return isEqual(type, "QVariant");
|
|
case 'X':
|
|
return isEqual(type, "QXmlStreamAttribute")
|
|
|| isEqual(type, "QXmlStreamNamespaceDeclaration")
|
|
|| isEqual(type, "QXmlStreamNotationDeclaration")
|
|
|| isEqual(type, "QXmlStreamEntityDeclaration");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct QDumper
|
|
{
|
|
explicit QDumper();
|
|
~QDumper();
|
|
void flush();
|
|
void checkFill();
|
|
QDumper &operator<<(long c);
|
|
QDumper &operator<<(int i);
|
|
QDumper &operator<<(double d);
|
|
QDumper &operator<<(float d);
|
|
QDumper &operator<<(unsigned long c);
|
|
QDumper &operator<<(unsigned int i);
|
|
QDumper &operator<<(const void *p);
|
|
QDumper &operator<<(qulonglong c);
|
|
void put(char c);
|
|
void addCommaIfNeeded();
|
|
void putBase64Encoded(const char *buf, int n);
|
|
QDumper &operator<<(const char *str);
|
|
QDumper &operator<<(const QByteArray &ba);
|
|
QDumper &operator<<(const QString &str);
|
|
void disarm();
|
|
|
|
void beginHash(); // start of data hash output
|
|
void endHash(); // start of data hash output
|
|
|
|
void write(const void *buf, int len); // raw write to stdout
|
|
|
|
// the dumper arguments
|
|
int protocolVersion; // dumper protocol version
|
|
int token; // some token to show on success
|
|
const char *outertype; // object type
|
|
const char *iname; // object name used for display
|
|
const char *exp; // object expression
|
|
const char *innertype; // 'inner type' for class templates
|
|
const void *data; // pointer to raw data
|
|
bool dumpChildren; // do we want to see children?
|
|
|
|
// handling of nested templates
|
|
void setupTemplateParameters();
|
|
enum { maxTemplateParameters = 10 };
|
|
const char *templateParameters[maxTemplateParameters + 1];
|
|
int templateParametersCount;
|
|
|
|
// internal state
|
|
bool success; // are we finished?
|
|
int pos;
|
|
|
|
int extraInt[4];
|
|
};
|
|
|
|
|
|
QDumper::QDumper()
|
|
{
|
|
success = false;
|
|
pos = 0;
|
|
}
|
|
|
|
QDumper::~QDumper()
|
|
{
|
|
flush();
|
|
|
|
char buf[30];
|
|
int len = qsnprintf(buf, sizeof(buf) - 1, "%d^done\n", token);
|
|
write(buf, len);
|
|
}
|
|
|
|
void QDumper::write(const void *buf, int len)
|
|
{
|
|
::fwrite(buf, len, 1, stdout);
|
|
::fflush(stdout);
|
|
}
|
|
|
|
void QDumper::flush()
|
|
{
|
|
if (pos != 0) {
|
|
char buf[30];
|
|
int len = qsnprintf(buf, sizeof(buf) - 1, "%d#%d,", token, pos);
|
|
write(buf, len);
|
|
write(qDumpBuffer, pos);
|
|
write("\n", 1);
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
void QDumper::setupTemplateParameters()
|
|
{
|
|
char *s = const_cast<char *>(innertype);
|
|
|
|
templateParametersCount = 1;
|
|
templateParameters[0] = s;
|
|
for (int i = 1; i != maxTemplateParameters + 1; ++i)
|
|
templateParameters[i] = 0;
|
|
|
|
while (*s) {
|
|
while (*s && *s != '@')
|
|
++s;
|
|
if (*s) {
|
|
*s = '\0';
|
|
++s;
|
|
templateParameters[templateParametersCount++] = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(unsigned long long c)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%llu", c);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(unsigned long c)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%lu", c);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(float d)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%f", d);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(double d)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%f", d);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(unsigned int i)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%u", i);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(long c)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%ld", c);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(int i)
|
|
{
|
|
checkFill();
|
|
pos += sprintf(qDumpBuffer + pos, "%d", i);
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(const void *p)
|
|
{
|
|
static char buf[100];
|
|
if (p) {
|
|
sprintf(buf, "%p", p);
|
|
// we get a '0x' prefix only on some implementations.
|
|
// if it isn't there, write it out manually.
|
|
if (buf[1] != 'x') {
|
|
put('0');
|
|
put('x');
|
|
}
|
|
*this << buf;
|
|
} else {
|
|
*this << "<null>";
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void QDumper::checkFill()
|
|
{
|
|
if (pos >= int(sizeof(qDumpBuffer)) - 100)
|
|
flush();
|
|
}
|
|
|
|
void QDumper::put(char c)
|
|
{
|
|
checkFill();
|
|
qDumpBuffer[pos++] = c;
|
|
}
|
|
|
|
void QDumper::addCommaIfNeeded()
|
|
{
|
|
if (pos == 0)
|
|
return;
|
|
char c = qDumpBuffer[pos - 1];
|
|
if (c == '}' || c == '"' || c == ']')
|
|
put(',');
|
|
}
|
|
|
|
void QDumper::putBase64Encoded(const char *buf, int n)
|
|
{
|
|
const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
|
|
"ghijklmn" "opqrstuv" "wxyz0123" "456789+/";
|
|
const char padchar = '=';
|
|
int padlen = 0;
|
|
|
|
//int tmpsize = ((n * 4) / 3) + 3;
|
|
|
|
int i = 0;
|
|
while (i < n) {
|
|
int chunk = 0;
|
|
chunk |= int(uchar(buf[i++])) << 16;
|
|
if (i == n) {
|
|
padlen = 2;
|
|
} else {
|
|
chunk |= int(uchar(buf[i++])) << 8;
|
|
if (i == n)
|
|
padlen = 1;
|
|
else
|
|
chunk |= int(uchar(buf[i++]));
|
|
}
|
|
|
|
int j = (chunk & 0x00fc0000) >> 18;
|
|
int k = (chunk & 0x0003f000) >> 12;
|
|
int l = (chunk & 0x00000fc0) >> 6;
|
|
int m = (chunk & 0x0000003f);
|
|
put(alphabet[j]);
|
|
put(alphabet[k]);
|
|
put(padlen > 1 ? padchar : alphabet[l]);
|
|
put(padlen > 0 ? padchar : alphabet[m]);
|
|
}
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(const char *str)
|
|
{
|
|
if (!str)
|
|
return *this << "<null>";
|
|
while (*str)
|
|
put(*(str++));
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(const QByteArray &ba)
|
|
{
|
|
putBase64Encoded(ba.constData(), ba.size());
|
|
return *this;
|
|
}
|
|
|
|
QDumper &QDumper::operator<<(const QString &str)
|
|
{
|
|
QByteArray ba = str.toUtf8();
|
|
putBase64Encoded(ba.constData(), ba.size());
|
|
return *this;
|
|
}
|
|
|
|
void QDumper::disarm()
|
|
{
|
|
flush();
|
|
success = true;
|
|
}
|
|
|
|
void QDumper::beginHash()
|
|
{
|
|
addCommaIfNeeded();
|
|
put('{');
|
|
}
|
|
|
|
void QDumper::endHash()
|
|
{
|
|
put('}');
|
|
}
|
|
|
|
|
|
//
|
|
// Some helpers to keep the dumper code short
|
|
//
|
|
|
|
// dump property=value pair
|
|
#undef P
|
|
#define P(dumper,name,value) \
|
|
do { \
|
|
dumper.addCommaIfNeeded(); \
|
|
dumper << (name) << "=\"" << value << "\""; \
|
|
} while (0)
|
|
|
|
// simple string property
|
|
#undef S
|
|
#define S(dumper, name, value) \
|
|
dumper.beginHash(); \
|
|
P(dumper, "name", name); \
|
|
P(dumper, "value", value); \
|
|
P(dumper, "type", NS"QString"); \
|
|
P(dumper, "numchild", "0"); \
|
|
P(dumper, "valueencoded", "1"); \
|
|
dumper.endHash();
|
|
|
|
// simple integer property
|
|
#undef I
|
|
#define I(dumper, name, value) \
|
|
dumper.beginHash(); \
|
|
P(dumper, "name", name); \
|
|
P(dumper, "value", value); \
|
|
P(dumper, "type", "int"); \
|
|
P(dumper, "numchild", "0"); \
|
|
dumper.endHash();
|
|
|
|
// simple boolean property
|
|
#undef BL
|
|
#define BL(dumper, name, value) \
|
|
dumper.beginHash(); \
|
|
P(dumper, "name", name); \
|
|
P(dumper, "value", (value ? "true" : "false")); \
|
|
P(dumper, "type", "bool"); \
|
|
P(dumper, "numchild", "0"); \
|
|
dumper.endHash();
|
|
|
|
|
|
// a single QChar
|
|
#undef QC
|
|
#define QC(dumper, name, value) \
|
|
dumper.beginHash(); \
|
|
P(dumper, "name", name); \
|
|
P(dumper, "value", QString(QLatin1String("'%1' (%2, 0x%3)")) \
|
|
.arg(value).arg(value.unicode()).arg(value.unicode(), 0, 16)); \
|
|
P(dumper, "valueencoded", "1"); \
|
|
P(dumper, "type", NS"QChar"); \
|
|
P(dumper, "numchild", "0"); \
|
|
dumper.endHash();
|
|
|
|
#undef TT
|
|
#define TT(type, value) \
|
|
"<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>"
|
|
|
|
static void qDumpUnknown(QDumper &d)
|
|
{
|
|
P(d, "iname", d.iname);
|
|
P(d, "addr", d.data);
|
|
P(d, "value", "<internal error>");
|
|
P(d, "type", d.outertype);
|
|
P(d, "numchild", "0");
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpInnerValueHelper(QDumper &d, const char *type, const void *addr,
|
|
const char *key = "value")
|
|
{
|
|
type = stripNamespace(type);
|
|
switch (type[1]) {
|
|
case 'l':
|
|
if (isEqual(type, "float"))
|
|
P(d, key, *(float*)addr);
|
|
return;
|
|
case 'n':
|
|
if (isEqual(type, "int"))
|
|
P(d, key, *(int*)addr);
|
|
else if (isEqual(type, "unsigned"))
|
|
P(d, key, *(unsigned int*)addr);
|
|
else if (isEqual(type, "unsigned int"))
|
|
P(d, key, *(unsigned int*)addr);
|
|
else if (isEqual(type, "unsigned long"))
|
|
P(d, key, *(unsigned long*)addr);
|
|
else if (isEqual(type, "unsigned long long"))
|
|
P(d, key, *(qulonglong*)addr);
|
|
return;
|
|
case 'o':
|
|
if (isEqual(type, "bool"))
|
|
switch (*(bool*)addr) {
|
|
case 0: P(d, key, "false"); break;
|
|
case 1: P(d, key, "true"); break;
|
|
default: P(d, key, *(bool*)addr); break;
|
|
}
|
|
else if (isEqual(type, "double"))
|
|
P(d, key, *(double*)addr);
|
|
else if (isEqual(type, "long"))
|
|
P(d, key, *(long*)addr);
|
|
else if (isEqual(type, "long long"))
|
|
P(d, key, *(qulonglong*)addr);
|
|
return;
|
|
case 'B':
|
|
if (isEqual(type, "QByteArray")) {
|
|
d << key << "encoded=\"1\",";
|
|
P(d, key, *(QByteArray*)addr);
|
|
}
|
|
return;
|
|
case 'L':
|
|
if (startsWith(type, "QList<")) {
|
|
const QListData *ldata = reinterpret_cast<const QListData*>(addr);
|
|
P(d, "value", "<" << ldata->size() << " items>");
|
|
P(d, "valuedisabled", "true");
|
|
P(d, "numchild", ldata->size());
|
|
}
|
|
return;
|
|
case 'O':
|
|
if (isEqual(type, "QObject *")) {
|
|
if (addr) {
|
|
const QObject *ob = reinterpret_cast<const QObject *>(addr);
|
|
P(d, "addr", ob);
|
|
P(d, "value", ob->objectName());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QObject");
|
|
P(d, "displayedtype", ob->metaObject()->className());
|
|
} else {
|
|
P(d, "value", "0x0");
|
|
P(d, "type", NS"QObject *");
|
|
}
|
|
}
|
|
return;
|
|
case 'S':
|
|
if (isEqual(type, "QString")) {
|
|
d << key << "encoded=\"1\",";
|
|
P(d, key, *(QString*)addr);
|
|
}
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void qDumpInnerValue(QDumper &d, const char *type, const void *addr)
|
|
{
|
|
P(d, "addr", addr);
|
|
P(d, "type", type);
|
|
|
|
if (!type[0])
|
|
return;
|
|
|
|
qDumpInnerValueHelper(d, type, addr);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void qDumpQByteArray(QDumper &d)
|
|
{
|
|
const QByteArray &ba = *reinterpret_cast<const QByteArray *>(d.data);
|
|
|
|
if (!ba.isEmpty()) {
|
|
qCheckAccess(ba.constData());
|
|
qCheckAccess(ba.constData() + ba.size());
|
|
}
|
|
|
|
if (ba.size() <= 100)
|
|
P(d, "value", ba);
|
|
else
|
|
P(d, "value", ba.left(100) << " <size: " << ba.size() << ", cut...>");
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QByteArray");
|
|
P(d, "numchild", ba.size());
|
|
P(d, "childtype", "char");
|
|
P(d, "childnumchild", "0");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
char buf[20];
|
|
for (int i = 0; i != ba.size(); ++i) {
|
|
unsigned char c = ba.at(i);
|
|
unsigned char u = isprint(c) && c != '"' ? c : '?';
|
|
sprintf(buf, "%02x (%u '%c')", c, c, u);
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "]");
|
|
P(d, "value", buf);
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQDateTime(QDumper &d)
|
|
{
|
|
#ifdef QT_NO_DATESTRING
|
|
qDumpUnknown(d);
|
|
#else
|
|
const QDateTime &date = *reinterpret_cast<const QDateTime *>(d.data);
|
|
if (date.isNull()) {
|
|
P(d, "value", "(null)");
|
|
} else {
|
|
P(d, "value", date.toString());
|
|
P(d, "valueencoded", "1");
|
|
}
|
|
P(d, "type", NS"QDateTime");
|
|
P(d, "numchild", "3");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
BL(d, "isNull", date.isNull());
|
|
I(d, "toTime_t", (long)date.toTime_t());
|
|
S(d, "toString", date.toString());
|
|
S(d, "toString_(ISO)", date.toString(Qt::ISODate));
|
|
S(d, "toString_(SystemLocale)", date.toString(Qt::SystemLocaleDate));
|
|
S(d, "toString_(Locale)", date.toString(Qt::LocaleDate));
|
|
S(d, "toString", date.toString());
|
|
|
|
#if 0
|
|
d.beginHash();
|
|
P(d, "name", "toUTC");
|
|
P(d, "exp", "(("NSX"QDateTime"NSY"*)" << d.data << ")"
|
|
"->toTimeSpec('"NS"Qt::UTC')");
|
|
P(d, "type", NS"QDateTime");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
#endif
|
|
|
|
#if 0
|
|
d.beginHash();
|
|
P(d, "name", "toLocalTime");
|
|
P(d, "exp", "(("NSX"QDateTime"NSY"*)" << d.data << ")"
|
|
"->toTimeSpec('"NS"Qt::LocalTime')");
|
|
P(d, "type", NS"QDateTime");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
#endif
|
|
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
#endif // ifdef QT_NO_DATESTRING
|
|
}
|
|
|
|
static void qDumpQDir(QDumper &d)
|
|
{
|
|
const QDir &dir = *reinterpret_cast<const QDir *>(d.data);
|
|
P(d, "value", dir.path());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QDir");
|
|
P(d, "numchild", "3");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
S(d, "absolutePath", dir.absolutePath());
|
|
S(d, "canonicalPath", dir.canonicalPath());
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQFile(QDumper &d)
|
|
{
|
|
const QFile &file = *reinterpret_cast<const QFile *>(d.data);
|
|
P(d, "value", file.fileName());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QFile");
|
|
P(d, "numchild", "2");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
S(d, "fileName", file.fileName());
|
|
BL(d, "exists", file.exists());
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQFileInfo(QDumper &d)
|
|
{
|
|
const QFileInfo &info = *reinterpret_cast<const QFileInfo *>(d.data);
|
|
P(d, "value", info.filePath());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QFileInfo");
|
|
P(d, "numchild", "3");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
S(d, "absolutePath", info.absolutePath());
|
|
S(d, "absoluteFilePath", info.absoluteFilePath());
|
|
S(d, "canonicalPath", info.canonicalPath());
|
|
S(d, "canonicalFilePath", info.canonicalFilePath());
|
|
S(d, "completeBaseName", info.completeBaseName());
|
|
S(d, "completeSuffix", info.completeSuffix());
|
|
S(d, "baseName", info.baseName());
|
|
#ifdef Q_OS_MACX
|
|
BL(d, "isBundle", info.isBundle());
|
|
S(d, "bundleName", info.bundleName());
|
|
#endif
|
|
S(d, "completeSuffix", info.completeSuffix());
|
|
S(d, "fileName", info.fileName());
|
|
S(d, "filePath", info.filePath());
|
|
S(d, "group", info.group());
|
|
S(d, "owner", info.owner());
|
|
S(d, "path", info.path());
|
|
|
|
I(d, "groupid", (long)info.groupId());
|
|
I(d, "ownerid", (long)info.ownerId());
|
|
//QFile::Permissions permissions () const
|
|
I(d, "permissions", info.permissions());
|
|
|
|
//QDir absoluteDir () const
|
|
//QDir dir () const
|
|
|
|
BL(d, "caching", info.caching());
|
|
BL(d, "exists", info.exists());
|
|
BL(d, "isAbsolute", info.isAbsolute());
|
|
BL(d, "isDir", info.isDir());
|
|
BL(d, "isExecutable", info.isExecutable());
|
|
BL(d, "isFile", info.isFile());
|
|
BL(d, "isHidden", info.isHidden());
|
|
BL(d, "isReadable", info.isReadable());
|
|
BL(d, "isRelative", info.isRelative());
|
|
BL(d, "isRoot", info.isRoot());
|
|
BL(d, "isSymLink", info.isSymLink());
|
|
BL(d, "isWritable", info.isWritable());
|
|
|
|
d.beginHash();
|
|
P(d, "name", "created");
|
|
P(d, "value", info.created().toString());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "exp", "(("NSX"QFileInfo"NSY"*)" << d.data << ")->created()");
|
|
P(d, "type", NS"QDateTime");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
|
|
d.beginHash();
|
|
P(d, "name", "lastModified");
|
|
P(d, "value", info.lastModified().toString());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "exp", "(("NSX"QFileInfo"NSY"*)" << d.data << ")->lastModified()");
|
|
P(d, "type", NS"QDateTime");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
|
|
d.beginHash();
|
|
P(d, "name", "lastRead");
|
|
P(d, "value", info.lastRead().toString());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "exp", "(("NSX"QFileInfo"NSY"*)" << d.data << ")->lastRead()");
|
|
P(d, "type", NS"QDateTime");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
bool isOptimizedIntKey(const char *keyType)
|
|
{
|
|
return isEqual(keyType, "int")
|
|
#if defined(Q_BYTE_ORDER) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
|
|| isEqual(keyType, "short")
|
|
|| isEqual(keyType, "ushort")
|
|
#endif
|
|
|| isEqual(keyType, "uint");
|
|
}
|
|
|
|
int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned valueSize)
|
|
{
|
|
// int-key optimization, small value
|
|
struct NodeOS { void *next; uint k; uint v; } nodeOS;
|
|
// int-key optimiatzion, large value
|
|
struct NodeOL { void *next; uint k; void *v; } nodeOL;
|
|
// no optimization, small value
|
|
struct NodeNS { void *next; uint h; uint k; uint v; } nodeNS;
|
|
// no optimization, large value
|
|
struct NodeNL { void *next; uint h; uint k; void *v; } nodeNL;
|
|
// complex key
|
|
struct NodeL { void *next; uint h; void *k; void *v; } nodeL;
|
|
|
|
if (forKey) {
|
|
// offsetof(...,...) not yet in Standard C++
|
|
const ulong nodeOSk ( (char *)&nodeOS.k - (char *)&nodeOS );
|
|
const ulong nodeOLk ( (char *)&nodeOL.k - (char *)&nodeOL );
|
|
const ulong nodeNSk ( (char *)&nodeNS.k - (char *)&nodeNS );
|
|
const ulong nodeNLk ( (char *)&nodeNL.k - (char *)&nodeNL );
|
|
const ulong nodeLk ( (char *)&nodeL.k - (char *)&nodeL );
|
|
if (optimizedIntKey)
|
|
return valueSize > sizeof(int) ? nodeOLk : nodeOSk;
|
|
if (keySize > sizeof(int))
|
|
return nodeLk;
|
|
return valueSize > sizeof(int) ? nodeNLk : nodeNSk;
|
|
} else {
|
|
const ulong nodeOSv ( (char *)&nodeOS.v - (char *)&nodeOS );
|
|
const ulong nodeOLv ( (char *)&nodeOL.v - (char *)&nodeOL );
|
|
const ulong nodeNSv ( (char *)&nodeNS.v - (char *)&nodeNS );
|
|
const ulong nodeNLv ( (char *)&nodeNL.v - (char *)&nodeNL );
|
|
const ulong nodeLv ( (char *)&nodeL.v - (char *)&nodeL );
|
|
if (optimizedIntKey)
|
|
return valueSize > sizeof(int) ? nodeOLv : nodeOSv;
|
|
if (keySize > sizeof(int))
|
|
return nodeLv;
|
|
return valueSize > sizeof(int) ? nodeNLv : nodeNSv;
|
|
}
|
|
}
|
|
|
|
|
|
static void qDumpQHash(QDumper &d)
|
|
{
|
|
QHashData *h = *reinterpret_cast<QHashData *const*>(d.data);
|
|
const char *keyType = d.templateParameters[0];
|
|
const char *valueType = d.templateParameters[1];
|
|
|
|
qCheckPointer(h->fakeNext);
|
|
qCheckPointer(h->buckets);
|
|
|
|
unsigned keySize = d.extraInt[0];
|
|
unsigned valueSize = d.extraInt[1];
|
|
|
|
int n = h->size;
|
|
|
|
if (n < 0)
|
|
qCheck(false);
|
|
if (n > 0) {
|
|
qCheckPointer(h->fakeNext);
|
|
qCheckPointer(*h->buckets);
|
|
}
|
|
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "numchild", n);
|
|
if (d.dumpChildren) {
|
|
if (n > 1000)
|
|
n = 1000;
|
|
bool simpleKey = isShortKey(keyType);
|
|
bool simpleValue = isShortKey(valueType);
|
|
bool opt = isOptimizedIntKey(keyType);
|
|
int keyOffset = hashOffset(opt, true, keySize, valueSize);
|
|
int valueOffset = hashOffset(opt, false, keySize, valueSize);
|
|
|
|
P(d, "extra", "simplekey: " << simpleKey << " simpleValue: " << simpleValue
|
|
<< " keySize: " << keyOffset << " valueOffset: " << valueOffset
|
|
<< " opt: " << opt);
|
|
|
|
QHashData::Node *node = h->firstNode();
|
|
QHashData::Node *end = reinterpret_cast<QHashData::Node *>(h);
|
|
int i = 0;
|
|
|
|
d << ",children=[";
|
|
while (node != end) {
|
|
d.beginHash();
|
|
if (simpleKey) {
|
|
qDumpInnerValueHelper(d, keyType, addOffset(node, keyOffset), "name");
|
|
P(d, "nameisindex", "1");
|
|
if (simpleValue)
|
|
qDumpInnerValueHelper(d, valueType, addOffset(node, valueOffset));
|
|
P(d, "type", valueType);
|
|
P(d, "addr", addOffset(node, valueOffset));
|
|
} else {
|
|
P(d, "name", "[" << i << "]");
|
|
//P(d, "exp", "*(char*)" << node);
|
|
P(d, "exp", "*('"NS"QHashNode<" << keyType << "," << valueType << " >'*)" << node);
|
|
P(d, "type", "'"NS"QHashNode<" << keyType << "," << valueType << " >'");
|
|
}
|
|
d.endHash();
|
|
++i;
|
|
node = QHashData::nextNode(node);
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQHashNode(QDumper &d)
|
|
{
|
|
const QHashData *h = reinterpret_cast<const QHashData *>(d.data);
|
|
const char *keyType = d.templateParameters[0];
|
|
const char *valueType = d.templateParameters[1];
|
|
|
|
P(d, "value", "");
|
|
P(d, "numchild", 2);
|
|
if (d.dumpChildren) {
|
|
unsigned keySize = d.extraInt[0];
|
|
unsigned valueSize = d.extraInt[1];
|
|
bool opt = isOptimizedIntKey(keyType);
|
|
int keyOffset = hashOffset(opt, true, keySize, valueSize);
|
|
int valueOffset = hashOffset(opt, false, keySize, valueSize);
|
|
|
|
// there is a hash specialization in cast the key are integers or shorts
|
|
d << ",children=[";
|
|
d.beginHash();
|
|
P(d, "name", "key");
|
|
P(d, "type", keyType);
|
|
P(d, "addr", addOffset(h, keyOffset));
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "value");
|
|
P(d, "type", valueType);
|
|
P(d, "addr", addOffset(h, valueOffset));
|
|
d.endHash();
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQImage(QDumper &d)
|
|
{
|
|
#ifdef QT_GUI_LIB
|
|
const QImage &im = *reinterpret_cast<const QImage *>(d.data);
|
|
P(d, "value", "(" << im.width() << "x" << im.height() << ")");
|
|
P(d, "type", NS"QImage");
|
|
P(d, "numchild", "0");
|
|
d.disarm();
|
|
#else
|
|
Q_UNUSED(d);
|
|
#endif
|
|
}
|
|
|
|
static void qDumpQList(QDumper &d)
|
|
{
|
|
// This uses the knowledge that QList<T> has only a single member
|
|
// of type union { QListData p; QListData::Data *d; };
|
|
const QListData &ldata = *reinterpret_cast<const QListData*>(d.data);
|
|
const QListData::Data *pdata =
|
|
*reinterpret_cast<const QListData::Data* const*>(d.data);
|
|
int nn = ldata.size();
|
|
if (nn < 0)
|
|
qCheck(false);
|
|
if (nn > 0) {
|
|
qCheckAccess(ldata.d->array);
|
|
//qCheckAccess(ldata.d->array[0]);
|
|
//qCheckAccess(ldata.d->array[nn - 1]);
|
|
#if QT_VERSION >= 0x040400
|
|
if (ldata.d->ref._q_value <= 0)
|
|
qCheck(false);
|
|
#endif
|
|
}
|
|
|
|
int n = nn;
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "valuedisabled", "true");
|
|
P(d, "numchild", n);
|
|
P(d, "childtype", d.innertype);
|
|
if (d.dumpChildren) {
|
|
unsigned innerSize = d.extraInt[0];
|
|
bool innerTypeIsPointer = isPointerType(d.innertype);
|
|
QByteArray strippedInnerType = stripPointerType(d.innertype);
|
|
|
|
// The exact condition here is:
|
|
// QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic
|
|
// but this data is available neither in the compiled binary nor
|
|
// in the frontend.
|
|
// So as first approximation only do the 'isLarge' check:
|
|
bool isInternal = innerSize <= int(sizeof(void*))
|
|
&& isMovableType(d.innertype);
|
|
|
|
P(d, "internal", (int)isInternal);
|
|
|
|
P(d, "childtype", d.innertype);
|
|
if (n > 1000)
|
|
n = 1000;
|
|
d << ",children=[";
|
|
for (int i = 0; i != n; ++i) {
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "]");
|
|
if (innerTypeIsPointer) {
|
|
void *p = ldata.d->array + i + pdata->begin;
|
|
if (p) {
|
|
//P(d, "value","@" << p);
|
|
qDumpInnerValue(d, strippedInnerType.data(), deref(p));
|
|
} else {
|
|
P(d, "value", "<null>");
|
|
P(d, "numchild", "0");
|
|
}
|
|
} else {
|
|
void *p = ldata.d->array + i + pdata->begin;
|
|
if (isInternal) {
|
|
//qDumpInnerValue(d, d.innertype, p);
|
|
P(d, "addr", p);
|
|
qDumpInnerValueHelper(d, d.innertype, p);
|
|
} else {
|
|
//qDumpInnerValue(d, d.innertype, deref(p));
|
|
P(d, "addr", deref(p));
|
|
qDumpInnerValueHelper(d, d.innertype, deref(p));
|
|
}
|
|
}
|
|
d.endHash();
|
|
}
|
|
if (n < nn) {
|
|
d.beginHash();
|
|
P(d, "value", "<incomplete>");
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQLocale(QDumper &d)
|
|
{
|
|
const QLocale &locale = *reinterpret_cast<const QLocale *>(d.data);
|
|
P(d, "value", locale.name());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QLocale");
|
|
P(d, "numchild", "8");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
|
|
d.beginHash();
|
|
P(d, "name", "country");
|
|
P(d, "exp", "(("NSX"QLocale"NSY"*)" << d.data << ")->country()");
|
|
d.endHash();
|
|
|
|
d.beginHash();
|
|
P(d, "name", "language");
|
|
P(d, "exp", "(("NSX"QLocale"NSY"*)" << d.data << ")->language()");
|
|
d.endHash();
|
|
|
|
d.beginHash();
|
|
P(d, "name", "measurementSystem");
|
|
P(d, "exp", "(("NSX"QLocale"NSY"*)" << d.data << ")->measurementSystem()");
|
|
d.endHash();
|
|
|
|
d.beginHash();
|
|
P(d, "name", "numberOptions");
|
|
P(d, "exp", "(("NSX"QLocale"NSY"*)" << d.data << ")->numberOptions()");
|
|
d.endHash();
|
|
|
|
S(d, "timeFormat_(short)", locale.timeFormat(QLocale::ShortFormat));
|
|
S(d, "timeFormat_(long)", locale.timeFormat(QLocale::LongFormat));
|
|
|
|
QC(d, "decimalPoint", locale.decimalPoint());
|
|
QC(d, "exponential", locale.exponential());
|
|
QC(d, "percent", locale.percent());
|
|
QC(d, "zeroDigit", locale.zeroDigit());
|
|
QC(d, "groupSeparator", locale.groupSeparator());
|
|
QC(d, "negativeSign", locale.negativeSign());
|
|
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQMap(QDumper &d)
|
|
{
|
|
QMapData *h = *reinterpret_cast<QMapData *const*>(d.data);
|
|
const char *keyType = d.templateParameters[0];
|
|
const char *valueType = d.templateParameters[1];
|
|
|
|
int n = h->size;
|
|
|
|
if (n < 0)
|
|
qCheck(false);
|
|
if (n > 0) {
|
|
qCheckAccess(h->backward);
|
|
qCheckAccess(h->forward[0]);
|
|
qCheckPointer(h->backward->backward);
|
|
qCheckPointer(h->forward[0]->backward);
|
|
}
|
|
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "numchild", n);
|
|
if (d.dumpChildren) {
|
|
if (n > 1000)
|
|
n = 1000;
|
|
|
|
//unsigned keySize = d.extraInt[0];
|
|
//unsigned valueSize = d.extraInt[1];
|
|
unsigned mapnodesize = d.extraInt[2];
|
|
unsigned valueOff = d.extraInt[3];
|
|
|
|
bool simpleKey = isShortKey(keyType);
|
|
bool simpleValue = isShortKey(valueType);
|
|
// both negative:
|
|
int keyOffset = 2 * sizeof(void*) - int(mapnodesize);
|
|
int valueOffset = 2 * sizeof(void*) - int(mapnodesize) + valueOff;
|
|
|
|
P(d, "extra", "simplekey: " << simpleKey << " simpleValue: " << simpleValue
|
|
<< " keyOffset: " << keyOffset << " valueOffset: " << valueOffset
|
|
<< " mapnodesize: " << mapnodesize);
|
|
d << ",children=[";
|
|
|
|
QMapData::Node *node = reinterpret_cast<QMapData::Node *>(h->forward[0]);
|
|
QMapData::Node *end = reinterpret_cast<QMapData::Node *>(h);
|
|
int i = 0;
|
|
|
|
while (node != end) {
|
|
d.beginHash();
|
|
if (simpleKey) {
|
|
P(d, "type", valueType);
|
|
qDumpInnerValueHelper(d, keyType, addOffset(node, keyOffset), "name");
|
|
|
|
P(d, "nameisindex", "1");
|
|
if (simpleValue)
|
|
qDumpInnerValueHelper(d, valueType, addOffset(node, valueOffset));
|
|
|
|
P(d, "type", valueType);
|
|
P(d, "addr", addOffset(node, valueOffset));
|
|
} else {
|
|
P(d, "name", "[" << i << "]");
|
|
P(d, "type", NS"QMapNode<" << keyType << "," << valueType << " >");
|
|
// actually, any type (even 'char') will do...
|
|
P(d, "exp", "*('"NS"QMapNode<" << keyType << "," << valueType << " >'*)" << node);
|
|
//P(d, "exp", "*('"NS"QMapData'*)" << (void*)node);
|
|
//P(d, "exp", "*(char*)" << (void*)node);
|
|
|
|
// P(d, "addr", node); does not work as gdb fails to parse
|
|
// e.g. &((*('"NS"QMapNode<QString,Foo>'*)0x616658))
|
|
}
|
|
d.endHash();
|
|
|
|
++i;
|
|
node = node->forward[0];
|
|
}
|
|
d << "]";
|
|
}
|
|
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQModelIndex(QDumper &d)
|
|
{
|
|
const QModelIndex *mi = reinterpret_cast<const QModelIndex *>(d.data);
|
|
|
|
P(d, "type", NS"QModelIndex");
|
|
if (mi->isValid()) {
|
|
P(d, "value", "(" << mi->row() << ", " << mi->column() << ")");
|
|
P(d, "numchild", 5);
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
I(d, "row", mi->row());
|
|
I(d, "column", mi->column());
|
|
|
|
d.beginHash();
|
|
P(d, "name", "parent");
|
|
const QModelIndex parent = mi->parent();
|
|
if (parent.isValid())
|
|
P(d, "value", "(" << mi->row() << ", " << mi->column() << ")");
|
|
else
|
|
P(d, "value", "<invalid>");
|
|
P(d, "exp", "(("NSX"QModelIndex"NSY"*)" << d.data << ")->parent()");
|
|
P(d, "type", NS"QModelIndex");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
|
|
S(d, "internalId", QString::number(mi->internalId(), 10));
|
|
|
|
d.beginHash();
|
|
P(d, "name", "model");
|
|
P(d, "value", static_cast<const void *>(mi->model()));
|
|
P(d, "type", NS"QAbstractItemModel*");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
|
|
d << "]";
|
|
}
|
|
} else {
|
|
P(d, "value", "<invalid>");
|
|
P(d, "numchild", 0);
|
|
}
|
|
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQMapNode(QDumper &d)
|
|
{
|
|
const QMapData *h = reinterpret_cast<const QMapData *>(d.data);
|
|
const char *keyType = d.templateParameters[0];
|
|
const char *valueType = d.templateParameters[1];
|
|
|
|
qCheckAccess(h->backward);
|
|
qCheckAccess(h->forward[0]);
|
|
|
|
P(d, "value", "");
|
|
P(d, "numchild", 2);
|
|
if (d.dumpChildren) {
|
|
//unsigned keySize = d.extraInt[0];
|
|
//unsigned valueSize = d.extraInt[1];
|
|
unsigned mapnodesize = d.extraInt[2];
|
|
unsigned valueOff = d.extraInt[3];
|
|
|
|
unsigned keyOffset = 2 * sizeof(void*) - mapnodesize;
|
|
unsigned valueOffset = 2 * sizeof(void*) - mapnodesize + valueOff;
|
|
|
|
d << ",children=[";
|
|
d.beginHash();
|
|
P(d, "name", "key");
|
|
qDumpInnerValue(d, keyType, addOffset(h, keyOffset));
|
|
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "value");
|
|
qDumpInnerValue(d, valueType, addOffset(h, valueOffset));
|
|
d.endHash();
|
|
d << "]";
|
|
}
|
|
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQObject(QDumper &d)
|
|
{
|
|
const QObject *ob = reinterpret_cast<const QObject *>(d.data);
|
|
const QMetaObject *mo = ob->metaObject();
|
|
unsigned childrenOffset = d.extraInt[0];
|
|
P(d, "value", ob->objectName());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QObject");
|
|
P(d, "displayedtype", mo->className());
|
|
P(d, "numchild", 4);
|
|
if (d.dumpChildren) {
|
|
const QObjectList &children = ob->children();
|
|
int slotCount = 0;
|
|
int signalCount = 0;
|
|
for (int i = mo->methodCount(); --i >= 0; ) {
|
|
QMetaMethod::MethodType mt = mo->method(i).methodType();
|
|
signalCount += (mt == QMetaMethod::Signal);
|
|
slotCount += (mt == QMetaMethod::Slot);
|
|
}
|
|
d << ",children=[";
|
|
d.beginHash();
|
|
P(d, "name", "properties");
|
|
// FIXME: Note that when simply using '(QObject*)'
|
|
// in the cast below, Gdb/MI _sometimes_ misparses
|
|
// expressions further down in the tree.
|
|
P(d, "exp", "*(class '"NS"QObject'*)" << d.data);
|
|
P(d, "type", NS"QObjectPropertyList");
|
|
P(d, "value", "<" << mo->propertyCount() << " items>");
|
|
P(d, "numchild", mo->propertyCount());
|
|
d.endHash();
|
|
#if 0
|
|
d.beginHash();
|
|
P(d, "name", "methods");
|
|
P(d, "exp", "*(class '"NS"QObject'*)" << d.data);
|
|
P(d, "type", NS"QObjectMethodList");
|
|
P(d, "value", "<" << mo->methodCount() << " items>");
|
|
P(d, "numchild", mo->methodCount());
|
|
d.endHash();
|
|
#endif
|
|
#if 0
|
|
d.beginHash();
|
|
P(d, "name", "senders");
|
|
P(d, "exp", "(*(class '"NS"QObjectPrivate'*)" << dfunc(ob) << ")->senders");
|
|
P(d, "type", NS"QList<"NS"QObjectPrivateSender>");
|
|
d.endHash();
|
|
#endif
|
|
#if PRIVATE_OBJECT_ALLOWED
|
|
d.beginHash();
|
|
P(d, "name", "signals");
|
|
P(d, "exp", "*(class '"NS"QObject'*)" << d.data);
|
|
P(d, "type", NS"QObjectSignalList");
|
|
P(d, "value", "<" << signalCount << " items>");
|
|
P(d, "numchild", signalCount);
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "slots");
|
|
P(d, "exp", "*(class '"NS"QObject'*)" << d.data);
|
|
P(d, "type", NS"QObjectSlotList");
|
|
P(d, "value", "<" << slotCount << " items>");
|
|
P(d, "numchild", slotCount);
|
|
d.endHash();
|
|
#endif
|
|
d.beginHash();
|
|
P(d, "name", "children");
|
|
// works always, but causes additional traffic on the list
|
|
//P(d, "exp", "((class '"NS"QObject'*)" << d.data << ")->children()");
|
|
//
|
|
//P(d, "addr", addOffset(dfunc(ob), childrenOffset));
|
|
//P(d, "type", NS"QList<QObject *>");
|
|
//P(d, "value", "<" << children.size() << " items>");
|
|
qDumpInnerValue(d, NS"QList<"NS"QObject *>",
|
|
addOffset(dfunc(ob), childrenOffset));
|
|
P(d, "numchild", children.size());
|
|
d.endHash();
|
|
#if 0
|
|
// Unneeded (and not working): Connections are listes as childen
|
|
// of the signal or slot they are connected to.
|
|
// d.beginHash();
|
|
// P(d, "name", "connections");
|
|
// P(d, "exp", "*(*(class "NS"QObjectPrivate*)" << dfunc(ob) << ")->connectionLists");
|
|
// P(d, "type", NS"QVector<"NS"QList<"NS"QObjectPrivate::Connection> >");
|
|
// d.endHash();
|
|
#endif
|
|
#if 0
|
|
d.beginHash();
|
|
P(d, "name", "objectprivate");
|
|
P(d, "type", NS"QObjectPrivate");
|
|
P(d, "addr", dfunc(ob));
|
|
P(d, "value", "");
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
#endif
|
|
d.beginHash();
|
|
P(d, "name", "parent");
|
|
qDumpInnerValueHelper(d, NS"QObject *", ob->parent());
|
|
d.endHash();
|
|
#if 1
|
|
d.beginHash();
|
|
P(d, "name", "className");
|
|
P(d, "value",ob->metaObject()->className());
|
|
P(d, "type", "");
|
|
P(d, "numchild", "0");
|
|
d.endHash();
|
|
#endif
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQObjectPropertyList(QDumper &d)
|
|
{
|
|
const QObject *ob = (const QObject *)d.data;
|
|
const QMetaObject *mo = ob->metaObject();
|
|
P(d, "addr", "<synthetic>");
|
|
P(d, "type", NS"QObjectPropertyList");
|
|
P(d, "numchild", mo->propertyCount());
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
for (int i = mo->propertyCount(); --i >= 0; ) {
|
|
const QMetaProperty & prop = mo->property(i);
|
|
d.beginHash();
|
|
P(d, "name", prop.name());
|
|
P(d, "exp", "((" << mo->className() << "*)" << ob
|
|
<< ")->" << prop.name() << "()");
|
|
if (isEqual(prop.typeName(), "QString")) {
|
|
P(d, "value", prop.read(ob).toString());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QString");
|
|
P(d, "numchild", "0");
|
|
} else if (isEqual(prop.typeName(), "bool")) {
|
|
P(d, "value", (prop.read(ob).toBool() ? "true" : "false"));
|
|
P(d, "numchild", "0");
|
|
} else if (isEqual(prop.typeName(), "int")) {
|
|
P(d, "value", prop.read(ob).toInt());
|
|
P(d, "numchild", "0");
|
|
}
|
|
P(d, "type", prop.typeName());
|
|
P(d, "numchild", "1");
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQObjectMethodList(QDumper &d)
|
|
{
|
|
const QObject *ob = (const QObject *)d.data;
|
|
const QMetaObject *mo = ob->metaObject();
|
|
P(d, "addr", "<synthetic>");
|
|
P(d, "type", NS"QObjectMethodList");
|
|
P(d, "numchild", mo->methodCount());
|
|
P(d, "childtype", "QMetaMethod::Method");
|
|
P(d, "childnumchild", "0");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
for (int i = 0; i != mo->methodCount(); ++i) {
|
|
const QMetaMethod & method = mo->method(i);
|
|
int mt = method.methodType();
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "] " << mo->indexOfMethod(method.signature())
|
|
<< " " << method.signature());
|
|
P(d, "value", (mt == QMetaMethod::Signal ? "<Signal>" : "<Slot>") << " (" << mt << ")");
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
#if PRIVATE_OBJECT_ALLOWED
|
|
const char * qConnectionTypes[] ={
|
|
"auto",
|
|
"direct",
|
|
"queued",
|
|
"autocompat",
|
|
"blockingqueued"
|
|
};
|
|
|
|
#if QT_VERSION >= 0x040400
|
|
static const QObjectPrivate::ConnectionList &qConnectionList(const QObject *ob, int signalNumber)
|
|
{
|
|
static const QObjectPrivate::ConnectionList emptyList;
|
|
const QObjectPrivate *p = reinterpret_cast<const QObjectPrivate *>(dfunc(ob));
|
|
if (!p->connectionLists)
|
|
return emptyList;
|
|
typedef QVector<QObjectPrivate::ConnectionList> ConnLists;
|
|
const ConnLists *lists = reinterpret_cast<const ConnLists *>(p->connectionLists);
|
|
// there's an optimization making the lists only large enough to hold the
|
|
// last non-empty item
|
|
if (signalNumber >= lists->size())
|
|
return emptyList;
|
|
return lists->at(signalNumber);
|
|
}
|
|
#endif
|
|
|
|
static void qDumpQObjectSignal(QDumper &d)
|
|
{
|
|
unsigned signalNumber = d.extraInt[0];
|
|
|
|
P(d, "addr", "<synthetic>");
|
|
P(d, "numchild", "1");
|
|
P(d, "type", NS"QObjectSignal");
|
|
|
|
#if QT_VERSION >= 0x040400
|
|
if (d.dumpChildren) {
|
|
const QObject *ob = reinterpret_cast<const QObject *>(d.data);
|
|
d << ",children=[";
|
|
const QObjectPrivate::ConnectionList &connList = qConnectionList(ob, signalNumber);
|
|
for (int i = 0; i != connList.size(); ++i) {
|
|
const QObjectPrivate::Connection &conn = connList.at(i);
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "] receiver");
|
|
qDumpInnerValueHelper(d, NS"QObject *", conn.receiver);
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "] slot");
|
|
P(d, "type", "");
|
|
P(d, "value", conn.receiver->metaObject()->method(conn.method).signature());
|
|
P(d, "numchild", "0");
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "] type");
|
|
P(d, "type", "");
|
|
P(d, "value", "<" << qConnectionTypes[conn.method] << " connection>");
|
|
P(d, "numchild", "0");
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
P(d, "numchild", connList.size());
|
|
}
|
|
#endif
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQObjectSignalList(QDumper &d)
|
|
{
|
|
const QObject *ob = reinterpret_cast<const QObject *>(d.data);
|
|
const QMetaObject *mo = ob->metaObject();
|
|
int count = 0;
|
|
for (int i = mo->methodCount(); --i >= 0; )
|
|
count += (mo->method(i).methodType() == QMetaMethod::Signal);
|
|
P(d, "addr", d.data);
|
|
P(d, "numchild", count);
|
|
#if QT_VERSION >= 0x040400
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
for (int i = 0; i != mo->methodCount(); ++i) {
|
|
const QMetaMethod & method = mo->method(i);
|
|
if (method.methodType() == QMetaMethod::Signal) {
|
|
int k = mo->indexOfSignal(method.signature());
|
|
const QObjectPrivate::ConnectionList &connList = qConnectionList(ob, k);
|
|
d.beginHash();
|
|
P(d, "name", "[" << k << "]");
|
|
P(d, "value", method.signature());
|
|
P(d, "numchild", connList.size());
|
|
//P(d, "numchild", "1");
|
|
P(d, "exp", "*(class '"NS"QObject'*)" << d.data);
|
|
P(d, "type", NS"QObjectSignal");
|
|
d.endHash();
|
|
}
|
|
}
|
|
d << "]";
|
|
}
|
|
#endif
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQObjectSlot(QDumper &d)
|
|
{
|
|
int slotNumber = d.extraInt[0];
|
|
|
|
P(d, "addr", d.data);
|
|
P(d, "numchild", "1");
|
|
P(d, "type", NS"QObjectSlot");
|
|
|
|
#if QT_VERSION >= 0x040400
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
int numchild = 0;
|
|
const QObject *ob = reinterpret_cast<const QObject *>(d.data);
|
|
const QObjectPrivate *p = reinterpret_cast<const QObjectPrivate *>(dfunc(ob));
|
|
for (int s = 0; s != p->senders.size(); ++s) {
|
|
const QObjectPrivate::Sender &sender = p->senders.at(s);
|
|
const QObjectPrivate::ConnectionList &connList
|
|
= qConnectionList(sender.sender, sender.signal);
|
|
for (int i = 0; i != connList.size(); ++i) {
|
|
const QObjectPrivate::Connection &conn = connList.at(i);
|
|
if (conn.receiver == ob && conn.method == slotNumber) {
|
|
++numchild;
|
|
const QMetaMethod & method =
|
|
sender.sender->metaObject()->method(sender.signal);
|
|
d.beginHash();
|
|
P(d, "name", "[" << s << "] sender");
|
|
qDumpInnerValueHelper(d, NS"QObject *", sender.sender);
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "[" << s << "] signal");
|
|
P(d, "type", "");
|
|
P(d, "value", method.signature());
|
|
P(d, "numchild", "0");
|
|
d.endHash();
|
|
d.beginHash();
|
|
P(d, "name", "[" << s << "] type");
|
|
P(d, "type", "");
|
|
P(d, "value", "<" << qConnectionTypes[conn.method] << " connection>");
|
|
P(d, "numchild", "0");
|
|
d.endHash();
|
|
}
|
|
}
|
|
}
|
|
d << "]";
|
|
P(d, "numchild", numchild);
|
|
}
|
|
#endif
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQObjectSlotList(QDumper &d)
|
|
{
|
|
const QObject *ob = reinterpret_cast<const QObject *>(d.data);
|
|
#if QT_VERSION >= 0x040400
|
|
const QObjectPrivate *p = reinterpret_cast<const QObjectPrivate *>(dfunc(ob));
|
|
#endif
|
|
const QMetaObject *mo = ob->metaObject();
|
|
|
|
int count = 0;
|
|
for (int i = mo->methodCount(); --i >= 0; )
|
|
count += (mo->method(i).methodType() == QMetaMethod::Slot);
|
|
|
|
P(d, "addr", d.data);
|
|
P(d, "numchild", count);
|
|
#if QT_VERSION >= 0x040400
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
for (int i = 0; i != mo->methodCount(); ++i) {
|
|
const QMetaMethod & method = mo->method(i);
|
|
if (method.methodType() == QMetaMethod::Slot) {
|
|
d.beginHash();
|
|
int k = mo->indexOfSlot(method.signature());
|
|
P(d, "name", "[" << k << "]");
|
|
P(d, "value", method.signature());
|
|
|
|
// count senders. expensive...
|
|
int numchild = 0;
|
|
for (int s = 0; s != p->senders.size(); ++s) {
|
|
const QObjectPrivate::Sender & sender = p->senders.at(s);
|
|
const QObjectPrivate::ConnectionList &connList
|
|
= qConnectionList(sender.sender, sender.signal);
|
|
for (int c = 0; c != connList.size(); ++c) {
|
|
const QObjectPrivate::Connection &conn = connList.at(c);
|
|
if (conn.receiver == ob && conn.method == k)
|
|
++numchild;
|
|
}
|
|
}
|
|
P(d, "numchild", numchild);
|
|
P(d, "exp", "*(class '"NS"QObject'*)" << d.data);
|
|
P(d, "type", NS"QObjectSlot");
|
|
d.endHash();
|
|
}
|
|
}
|
|
d << "]";
|
|
}
|
|
#endif
|
|
d.disarm();
|
|
}
|
|
#endif // PRIVATE_OBJECT_ALLOWED
|
|
|
|
|
|
static void qDumpQPixmap(QDumper &d)
|
|
{
|
|
#ifdef QT_GUI_LIB
|
|
const QPixmap &im = *reinterpret_cast<const QPixmap *>(d.data);
|
|
P(d, "value", "(" << im.width() << "x" << im.height() << ")");
|
|
P(d, "type", NS"QPixmap");
|
|
P(d, "numchild", "0");
|
|
d.disarm();
|
|
#else
|
|
Q_UNUSED(d);
|
|
#endif
|
|
}
|
|
|
|
static void qDumpQSet(QDumper &d)
|
|
{
|
|
// This uses the knowledge that QHash<T> has only a single member
|
|
// of union { QHashData *d; QHashNode<Key, T> *e; };
|
|
QHashData *hd = *(QHashData**)d.data;
|
|
QHashData::Node *node = hd->firstNode();
|
|
|
|
int n = hd->size;
|
|
if (n < 0)
|
|
qCheck(false);
|
|
if (n > 0) {
|
|
qCheckAccess(node);
|
|
qCheckPointer(node->next);
|
|
}
|
|
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "valuedisabled", "true");
|
|
P(d, "numchild", 2 * n);
|
|
if (d.dumpChildren) {
|
|
if (n > 100)
|
|
n = 100;
|
|
d << ",children=[";
|
|
int i = 0;
|
|
for (int bucket = 0; bucket != hd->numBuckets; ++bucket) {
|
|
for (node = hd->buckets[bucket]; node->next; node = node->next) {
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "]");
|
|
P(d, "type", d.innertype);
|
|
P(d, "exp", "(('QHashNode<" << d.innertype
|
|
<< ",QHashDummyValue>'*)"
|
|
<< static_cast<const void*>(node) << ")->key"
|
|
);
|
|
d.endHash();
|
|
++i;
|
|
}
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQString(QDumper &d)
|
|
{
|
|
const QString &str = *reinterpret_cast<const QString *>(d.data);
|
|
|
|
if (!str.isEmpty()) {
|
|
qCheckAccess(str.unicode());
|
|
qCheckAccess(str.unicode() + str.size());
|
|
}
|
|
|
|
P(d, "value", str);
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QString");
|
|
//P(d, "editvalue", str); // handled generically below
|
|
P(d, "numchild", "0");
|
|
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQStringList(QDumper &d)
|
|
{
|
|
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
|
|
int n = list.size();
|
|
if (n < 0)
|
|
qCheck(false);
|
|
if (n > 0) {
|
|
qCheckAccess(&list.front());
|
|
qCheckAccess(&list.back());
|
|
}
|
|
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "valuedisabled", "true");
|
|
P(d, "numchild", n);
|
|
P(d, "childtype", NS"QString");
|
|
P(d, "childnumchild", "0");
|
|
if (d.dumpChildren) {
|
|
if (n > 1000)
|
|
n = 1000;
|
|
d << ",children=[";
|
|
for (int i = 0; i != n; ++i) {
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "]");
|
|
P(d, "value", list[i]);
|
|
P(d, "valueencoded", "1");
|
|
d.endHash();
|
|
}
|
|
if (n < list.size()) {
|
|
d.beginHash();
|
|
P(d, "name", "Warning:");
|
|
P(d, "value", "<incomplete>");
|
|
P(d, "type", "");
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQTextCodec(QDumper &d)
|
|
{
|
|
const QTextCodec &codec = *reinterpret_cast<const QTextCodec *>(d.data);
|
|
P(d, "value", codec.name());
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", NS"QTextCodec");
|
|
P(d, "numchild", "2");
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
S(d, "name", codec.name());
|
|
I(d, "mibEnum", codec.mibEnum());
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQVariantHelper(const void *data, QString *value,
|
|
QString *exp, int *numchild)
|
|
{
|
|
const QVariant &v = *reinterpret_cast<const QVariant *>(data);
|
|
switch (v.type()) {
|
|
case QVariant::Invalid:
|
|
*value = QLatin1String("<invalid>");
|
|
*numchild = 0;
|
|
break;
|
|
case QVariant::String:
|
|
*value = QLatin1Char('"') + v.toString() + QLatin1Char('"');
|
|
*numchild = 0;
|
|
break;
|
|
case QVariant::StringList:
|
|
*exp = QString(QLatin1String("((QVariant*)%1)->d.data.c"))
|
|
.arg((quintptr)data);
|
|
*numchild = v.toStringList().size();
|
|
break;
|
|
case QVariant::Int:
|
|
*value = QString::number(v.toInt());
|
|
*numchild= 0;
|
|
break;
|
|
case QVariant::Double:
|
|
*value = QString::number(v.toDouble());
|
|
*numchild = 0;
|
|
break;
|
|
default: {
|
|
char buf[1000];
|
|
const char *format = (v.typeName()[0] == 'Q')
|
|
? "'"NS"%s "NS"qVariantValue<"NS"%s >'(*('"NS"QVariant'*)%p)"
|
|
: "'%s "NS"qVariantValue<%s >'(*('"NS"QVariant'*)%p)";
|
|
qsnprintf(buf, sizeof(buf) - 1, format, v.typeName(), v.typeName(), data);
|
|
*exp = QLatin1String(buf);
|
|
*numchild = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void qDumpQVariant(QDumper &d)
|
|
{
|
|
const QVariant &v = *reinterpret_cast<const QVariant *>(d.data);
|
|
QString value;
|
|
QString exp;
|
|
int numchild = 0;
|
|
qDumpQVariantHelper(d.data, &value, &exp, &numchild);
|
|
bool isInvalid = (v.typeName() == 0);
|
|
if (isInvalid) {
|
|
P(d, "value", "(invalid)");
|
|
} else if (value.isEmpty()) {
|
|
P(d, "value", "(" << v.typeName() << ") " << qPrintable(value));
|
|
} else {
|
|
QByteArray ba;
|
|
ba += '(';
|
|
ba += v.typeName();
|
|
ba += ") ";
|
|
ba += qPrintable(value);
|
|
P(d, "value", ba);
|
|
P(d, "valueencoded", "1");
|
|
}
|
|
P(d, "type", NS"QVariant");
|
|
P(d, "numchild", (isInvalid ? "0" : "1"));
|
|
if (d.dumpChildren) {
|
|
d << ",children=[";
|
|
d.beginHash();
|
|
P(d, "name", "value");
|
|
if (!exp.isEmpty())
|
|
P(d, "exp", qPrintable(exp));
|
|
if (!value.isEmpty()) {
|
|
P(d, "value", value);
|
|
P(d, "valueencoded", "1");
|
|
}
|
|
P(d, "type", v.typeName());
|
|
P(d, "numchild", numchild);
|
|
d.endHash();
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpQVector(QDumper &d)
|
|
{
|
|
QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data);
|
|
|
|
// Try to provoke segfaults early to prevent the frontend
|
|
// from asking for unavailable child details
|
|
int nn = v->size;
|
|
if (nn < 0)
|
|
qCheck(false);
|
|
if (nn > 0) {
|
|
//qCheckAccess(&vec.front());
|
|
//qCheckAccess(&vec.back());
|
|
}
|
|
|
|
unsigned innersize = d.extraInt[0];
|
|
unsigned typeddatasize = d.extraInt[1];
|
|
|
|
int n = nn;
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "valuedisabled", "true");
|
|
P(d, "numchild", n);
|
|
if (d.dumpChildren) {
|
|
bool innerTypeIsPointer = isPointerType(d.innertype);
|
|
QByteArray strippedInnerType = stripPointerType(d.innertype);
|
|
|
|
if (n > 1000)
|
|
n = 1000;
|
|
d << ",children=[";
|
|
for (int i = 0; i != n; ++i) {
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "]");
|
|
const void *p = addOffset(v, i * innersize + typeddatasize);
|
|
if (innerTypeIsPointer) {
|
|
if (deref(p)) {
|
|
//P(d, "value","@" << p);
|
|
qDumpInnerValue(d, strippedInnerType.data(), deref(p));
|
|
} else {
|
|
P(d, "type", d.innertype);
|
|
P(d, "value", "<null>");
|
|
P(d, "numchild", "0");
|
|
}
|
|
} else {
|
|
qDumpInnerValue(d, d.innertype, p);
|
|
}
|
|
d.endHash();
|
|
}
|
|
if (n < nn) {
|
|
d.beginHash();
|
|
P(d, "name", "[...]");
|
|
P(d, "value", "<incomplete>");
|
|
P(d, "type", d.innertype);
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpStdString(QDumper &d)
|
|
{
|
|
const std::string &str = *reinterpret_cast<const std::string *>(d.data);
|
|
|
|
if (!str.empty()) {
|
|
qCheckAccess(str.c_str());
|
|
qCheckAccess(str.c_str() + str.size() - 1);
|
|
}
|
|
|
|
d << ",value=\"";
|
|
d.putBase64Encoded(str.c_str(), str.size());
|
|
d << "\"";
|
|
P(d, "valueencoded", "1");
|
|
P(d, "type", "std::string");
|
|
P(d, "numchild", "0");
|
|
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpStdWString(QDumper &d)
|
|
{
|
|
const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
|
|
|
|
if (!str.empty()) {
|
|
qCheckAccess(str.c_str());
|
|
qCheckAccess(str.c_str() + str.size() - 1);
|
|
}
|
|
|
|
d << "value=\"";
|
|
d.putBase64Encoded((const char *)str.c_str(), str.size() * sizeof(wchar_t));
|
|
d << "\"";
|
|
P(d, "valueencoded", (sizeof(wchar_t) == 2 ? "2" : "3"));
|
|
P(d, "type", "std::wstring");
|
|
P(d, "numchild", "0");
|
|
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpStdVector(QDumper &d)
|
|
{
|
|
// Correct type would be something like:
|
|
// std::_Vector_base<int,std::allocator<int, std::allocator<int> >>::_Vector_impl
|
|
struct VectorImpl {
|
|
char *start;
|
|
char *finish;
|
|
char *end_of_storage;
|
|
};
|
|
const VectorImpl *v = static_cast<const VectorImpl *>(d.data);
|
|
|
|
// Try to provoke segfaults early to prevent the frontend
|
|
// from asking for unavailable child details
|
|
int nn = (v->finish - v->start) / d.extraInt[0];
|
|
if (nn < 0)
|
|
qCheck(false);
|
|
if (nn > 0) {
|
|
qCheckAccess(v->start);
|
|
qCheckAccess(v->finish);
|
|
qCheckAccess(v->end_of_storage);
|
|
}
|
|
|
|
int n = nn;
|
|
P(d, "value", "<" << n << " items>");
|
|
P(d, "valuedisabled", "true");
|
|
P(d, "numchild", n);
|
|
if (d.dumpChildren) {
|
|
unsigned innersize = d.extraInt[0];
|
|
bool innerTypeIsPointer = isPointerType(d.innertype);
|
|
QByteArray strippedInnerType = stripPointerType(d.innertype);
|
|
|
|
if (n > 1000)
|
|
n = 1000;
|
|
d << ",children=[";
|
|
for (int i = 0; i != n; ++i) {
|
|
d.beginHash();
|
|
P(d, "name", "[" << i << "]");
|
|
const void *p = addOffset(v->start, i * innersize);
|
|
if (innerTypeIsPointer) {
|
|
if (deref(p)) {
|
|
//P(d, "value","@" << p);
|
|
qDumpInnerValue(d, strippedInnerType.data(), deref(p));
|
|
} else {
|
|
P(d, "type", d.innertype);
|
|
P(d, "value", "<null>");
|
|
P(d, "numchild", "0");
|
|
}
|
|
} else {
|
|
qDumpInnerValue(d, d.innertype, p);
|
|
}
|
|
d.endHash();
|
|
}
|
|
if (n < nn) {
|
|
d.beginHash();
|
|
P(d, "name", "[...]");
|
|
P(d, "value", "<incomplete>");
|
|
P(d, "type", d.innertype);
|
|
d.endHash();
|
|
}
|
|
d << "]";
|
|
}
|
|
d.disarm();
|
|
}
|
|
|
|
static void qDumpStdVectorBool(QDumper &d)
|
|
{
|
|
// FIXME
|
|
return qDumpStdVector(d);
|
|
}
|
|
|
|
static void handleProtocolVersion2and3(QDumper & d)
|
|
{
|
|
if (!d.outertype[0]) {
|
|
qDumpUnknown(d);
|
|
return;
|
|
}
|
|
|
|
d.setupTemplateParameters();
|
|
P(d, "iname", d.iname);
|
|
P(d, "addr", d.data);
|
|
|
|
#ifdef QT_NO_QDATASTREAM
|
|
if (d.protocolVersion == 3) {
|
|
QVariant::Type type = QVariant::nameToType(d.outertype);
|
|
if (type != QVariant::Invalid) {
|
|
QVariant v(type, d.data);
|
|
QByteArray ba;
|
|
QDataStream ds(&ba, QIODevice::WriteOnly);
|
|
ds << v;
|
|
P(d, "editvalue", ba);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
const char *type = stripNamespace(d.outertype);
|
|
// type[0] is usally 'Q', so don't use it
|
|
switch (type[1]) {
|
|
case 'B':
|
|
if (isEqual(type, "QByteArray"))
|
|
qDumpQByteArray(d);
|
|
break;
|
|
case 'D':
|
|
if (isEqual(type, "QDateTime"))
|
|
qDumpQDateTime(d);
|
|
else if (isEqual(type, "QDir"))
|
|
qDumpQDir(d);
|
|
break;
|
|
case 'F':
|
|
if (isEqual(type, "QFile"))
|
|
qDumpQFile(d);
|
|
else if (isEqual(type, "QFileInfo"))
|
|
qDumpQFileInfo(d);
|
|
break;
|
|
case 'H':
|
|
if (isEqual(type, "QHash"))
|
|
qDumpQHash(d);
|
|
else if (isEqual(type, "QHashNode"))
|
|
qDumpQHashNode(d);
|
|
break;
|
|
case 'I':
|
|
if (isEqual(type, "QImage"))
|
|
qDumpQImage(d);
|
|
break;
|
|
case 'L':
|
|
if (isEqual(type, "QList"))
|
|
qDumpQList(d);
|
|
else if (isEqual(type, "QLocale"))
|
|
qDumpQLocale(d);
|
|
break;
|
|
case 'M':
|
|
if (isEqual(type, "QMap"))
|
|
qDumpQMap(d);
|
|
else if (isEqual(type, "QMapNode"))
|
|
qDumpQMapNode(d);
|
|
else if (isEqual(type, "QModelIndex"))
|
|
qDumpQModelIndex(d);
|
|
break;
|
|
case 'O':
|
|
if (isEqual(type, "QObject"))
|
|
qDumpQObject(d);
|
|
else if (isEqual(type, "QObjectPropertyList"))
|
|
qDumpQObjectPropertyList(d);
|
|
else if (isEqual(type, "QObjectMethodList"))
|
|
qDumpQObjectMethodList(d);
|
|
#if PRIVATE_OBJECT_ALLOWED
|
|
else if (isEqual(type, "QObjectSignal"))
|
|
qDumpQObjectSignal(d);
|
|
else if (isEqual(type, "QObjectSignalList"))
|
|
qDumpQObjectSignalList(d);
|
|
else if (isEqual(type, "QObjectSlot"))
|
|
qDumpQObjectSlot(d);
|
|
else if (isEqual(type, "QObjectSlotList"))
|
|
qDumpQObjectSlotList(d);
|
|
#endif
|
|
break;
|
|
case 'P':
|
|
if (isEqual(type, "QPixmap"))
|
|
qDumpQPixmap(d);
|
|
break;
|
|
case 'S':
|
|
if (isEqual(type, "QSet"))
|
|
qDumpQSet(d);
|
|
else if (isEqual(type, "QString"))
|
|
qDumpQString(d);
|
|
else if (isEqual(type, "QStringList"))
|
|
qDumpQStringList(d);
|
|
break;
|
|
case 'T':
|
|
if (isEqual(type, "QTextCodec"))
|
|
qDumpQTextCodec(d);
|
|
break;
|
|
case 'V':
|
|
if (isEqual(type, "QVariant"))
|
|
qDumpQVariant(d);
|
|
else if (isEqual(type, "QVector"))
|
|
qDumpQVector(d);
|
|
break;
|
|
case 's':
|
|
if (isEqual(type, "wstring"))
|
|
qDumpStdWString(d);
|
|
break;
|
|
case 't':
|
|
if (isEqual(type, "std::vector"))
|
|
qDumpStdVector(d);
|
|
else if (isEqual(type, "std::vector::bool"))
|
|
qDumpStdVectorBool(d);
|
|
else if (isEqual(type, "string"))
|
|
qDumpStdString(d);
|
|
else if (isEqual(type, "std::string"))
|
|
qDumpStdString(d);
|
|
else if (isEqual(type, "std::wstring"))
|
|
qDumpStdWString(d);
|
|
break;
|
|
}
|
|
|
|
if (!d.success)
|
|
qDumpUnknown(d);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
extern "C" Q_DECL_EXPORT
|
|
void qDumpObjectData440(
|
|
int protocolVersion,
|
|
int token,
|
|
void *data,
|
|
bool dumpChildren,
|
|
int extraInt0,
|
|
int extraInt1,
|
|
int extraInt2,
|
|
int extraInt3)
|
|
{
|
|
if (protocolVersion == -2) {
|
|
// close socket
|
|
QDumper d;
|
|
d.protocolVersion = protocolVersion;
|
|
d.token = token;
|
|
d.flush();
|
|
d.disarm();
|
|
}
|
|
|
|
else if (protocolVersion == -1) {
|
|
// finalize Startup
|
|
QDumper d;
|
|
d.protocolVersion = protocolVersion;
|
|
d.token = token;
|
|
d.disarm();
|
|
}
|
|
|
|
else if (protocolVersion == 0) {
|
|
QDumper d;
|
|
d.protocolVersion = protocolVersion;
|
|
d.token = token;
|
|
// used to test whether error output gets through
|
|
//fprintf(stderr, "using stderr, qDebug follows: %d\n", token);
|
|
//qDebug() << "using qDebug, stderr already used: " << token;
|
|
d.disarm();
|
|
}
|
|
|
|
else if (protocolVersion == 1) {
|
|
QDumper d;
|
|
d.protocolVersion = protocolVersion;
|
|
d.token = token;
|
|
|
|
//qDebug() << "SOCKET: after connect: state: " << qDumperSocket.state();
|
|
// simpledumpers is a list of all available dumpers that are
|
|
// _not_ templates. templates currently require special
|
|
// hardcoded handling in the debugger plugin.
|
|
// don't mention them here in this list
|
|
d << "simpledumpers=["
|
|
"\""NS"QByteArray\","
|
|
"\""NS"QDir\","
|
|
"\""NS"QImage\","
|
|
"\""NS"QFile\","
|
|
"\""NS"QFileInfo\","
|
|
"\""NS"QLocale\","
|
|
"\""NS"QModelIndex\","
|
|
//"\""NS"QHash\"," // handled on GH side
|
|
//"\""NS"QHashNode\","
|
|
//"\""NS"QMap\"," // handled on GH side
|
|
//"\""NS"QMapNode\","
|
|
"\""NS"QObject\","
|
|
"\""NS"QObjectMethodList\"," // hack to get nested properties display
|
|
"\""NS"QObjectPropertyList\","
|
|
#if PRIVATE_OBJECT_ALLOWED
|
|
"\""NS"QObjectSignal\","
|
|
"\""NS"QObjectSignalList\","
|
|
"\""NS"QObjectSlot\","
|
|
"\""NS"QObjectSlotList\","
|
|
#endif // PRIVATE_OBJECT_ALLOWED
|
|
"\""NS"QString\","
|
|
"\""NS"QStringList\","
|
|
"\""NS"QTextCodec\","
|
|
"\""NS"QVariant\","
|
|
"\""NS"QWidget\","
|
|
"\""NS"QDateTime\","
|
|
"\"string\","
|
|
"\"wstring\","
|
|
"\"std::string\","
|
|
"\"std::wstring\","
|
|
// << "\""NS"QRegion\","
|
|
"]";
|
|
d << ",namespace=\""NS"\"";
|
|
d.disarm();
|
|
}
|
|
|
|
else if (protocolVersion == 2 || protocolVersion == 3) {
|
|
QDumper d;
|
|
|
|
d.protocolVersion = protocolVersion;
|
|
d.token = token;
|
|
d.data = data;
|
|
d.dumpChildren = dumpChildren;
|
|
d.extraInt[0] = extraInt0;
|
|
d.extraInt[1] = extraInt1;
|
|
d.extraInt[2] = extraInt2;
|
|
d.extraInt[3] = extraInt3;
|
|
|
|
const char *inbuffer = qDumpInBuffer;
|
|
d.outertype = inbuffer; while (*inbuffer) ++inbuffer; ++inbuffer;
|
|
d.iname = inbuffer; while (*inbuffer) ++inbuffer; ++inbuffer;
|
|
d.exp = inbuffer; while (*inbuffer) ++inbuffer; ++inbuffer;
|
|
d.innertype = inbuffer; while (*inbuffer) ++inbuffer; ++inbuffer;
|
|
d.iname = inbuffer; while (*inbuffer) ++inbuffer; ++inbuffer;
|
|
|
|
handleProtocolVersion2and3(d);
|
|
}
|
|
|
|
else {
|
|
qDebug() << "Unsupported protocol version" << protocolVersion;
|
|
}
|
|
}
|
|
|
|
|
|
// ]})
|