From 4a756726ee874ff2ce496e1b113707c65c39a76b Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 18 May 2009 22:35:42 +0200 Subject: Use a per object lock for signal/slots That way we prevent the current possible race condition that may appears while touching signals slot while object are moved to different threads. For exemple, when we do sender->threadData->mutex->lock(); the sender can possibly be moved to another thread between the moment we get the pointer to the threadData and the moment we aquire the lock. We could check if we locked the right mutex and retry otherwise, but that would break if the threadData (and hence the mutex) get destroyed in between. The per object mutex is implemented with a thread pool. I'm not using the global QThreadPool because we might ends up locking two of their mutex, and that would be dangerous if something else holds a lock on the same mutex (possible deadlock) Putting the mutex pool in a Q_GLOBAL_STATIC doesn't work as it might result of the ppol being deleted before some other object in others Q_GLOBAL_STATIC structures There is no need to lock this mutex in moveToThread as this is safe. When emiting a signal, we do not need to lock the thread data, as the user must ensure that the object is not moved to a thread while emiting a AutoConnection signal. Reviewed-by: Brad --- src/corelib/kernel/qobject.cpp | 66 ++++++++++++++++++++---------- tests/auto/qobjectrace/tst_qobjectrace.cpp | 24 +++++++++-- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 9f803fa..cfd8493 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -58,6 +58,7 @@ #include #include +#include #include @@ -91,12 +92,35 @@ static int *queuedConnectionTypes(const QList &typeNames) return types; } +QBasicAtomicPointer signalSlotMutexes = Q_BASIC_ATOMIC_INITIALIZER(0); +QBasicAtomicInt objectCount = Q_BASIC_ATOMIC_INITIALIZER(0); + +/** \internal + * mutex to be locked when accessing the connectionlists or the senders list + */ +static QMutex *signalSlotLock(const QObject *o) +{ + if (!signalSlotMutexes) { + QMutexPool *mp = new QMutexPool; + if (!signalSlotMutexes.testAndSetOrdered(0, mp)) { + delete mp; + } + } + return signalSlotMutexes->get(o); +} + extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) { + objectCount.ref(); } extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) { + if(!objectCount.deref()) { + QMutexPool *old = signalSlotMutexes; + signalSlotMutexes.testAndSetAcquire(old, 0); + delete old; + } } QObjectPrivate::QObjectPrivate(int version) @@ -223,7 +247,7 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const int signal_index = q->metaObject()->indexOfSignal(signal); if (signal_index < 0) return false; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q)); if (connectionLists) { if (signal_index < connectionLists->count()) { const ConnectionList &connectionList = connectionLists->at(signal_index); @@ -245,7 +269,7 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const int signal_index = q->metaObject()->indexOfSignal(signal); if (signal_index < 0) return returnValue; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q)); if (connectionLists) { if (signal_index < connectionLists->count()) { const ConnectionList &connectionList = connectionLists->at(signal_index); @@ -263,7 +287,7 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const QObjectList QObjectPrivate::senderList() const { QObjectList returnValue; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q_func())); for (int i = 0; i < senders.count(); ++i) returnValue << senders.at(i)->sender; return returnValue; @@ -712,7 +736,7 @@ QObject::~QObject() emit destroyed(this); { - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); // set ref to zero to indicate that this object has been deleted if (d->currentSender != 0) @@ -731,7 +755,7 @@ QObject::~QObject() continue; } - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); c = connectionList[i]; if (c->receiver) @@ -755,7 +779,7 @@ QObject::~QObject() for (int i = 0; i < d->senders.count(); ) { QObjectPrivate::Connection *s = d->senders[i]; - QMutex *m = &s->sender->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(s->sender); bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); if (m < locker.mutex()) { if (i >= d->senders.count() || s != d->senders[i]) { @@ -765,11 +789,9 @@ QObject::~QObject() } } s->receiver = 0; - if (s->sender) { - QObjectConnectionListVector *senderLists = s->sender->d_func()->connectionLists; - if (senderLists) - senderLists->dirty = true; - } + QObjectConnectionListVector *senderLists = s->sender->d_func()->connectionLists; + if (senderLists) + senderLists->dirty = true; if (needToUnlock) m->unlock(); @@ -2254,7 +2276,7 @@ QObject *QObject::sender() const { Q_D(const QObject); - QMutexLocker(&d->threadData->mutex); + QMutexLocker(signalSlotLock(this)); if (!d->currentSender) return 0; @@ -2310,7 +2332,7 @@ int QObject::receivers(const char *signal) const } Q_D(const QObject); - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); if (d->connectionLists) { if (signal_index < d->connectionLists->count()) { const QObjectPrivate::ConnectionList &connectionList = @@ -2757,8 +2779,8 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, c->connectionType = type; c->argumentTypes = types; - QOrderedMutexLocker locker(&s->d_func()->threadData->mutex, - &r->d_func()->threadData->mutex); + QOrderedMutexLocker locker(signalSlotLock(sender), + signalSlotLock(receiver)); s->d_func()->addConnection(signal_index, c); r->d_func()->senders.append(c); @@ -2783,8 +2805,8 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, QObject *s = const_cast(sender); QObject *r = const_cast(receiver); - QMutex *senderMutex = &s->d_func()->threadData->mutex; - QMutex *receiverMutex = r ? &r->d_func()->threadData->mutex : 0; + QMutex *senderMutex = signalSlotLock(sender); + QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0; QOrderedMutexLocker locker(senderMutex, receiverMutex); QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; @@ -2804,7 +2826,7 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, if (c->receiver && (r == 0 || (c->receiver == r && (method_index < 0 || c->method == method_index)))) { - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); bool needToUnlock = false; if (!receiverMutex && senderMutex != m) { // need to relock this receiver and sender in the correct order @@ -2831,7 +2853,7 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, if (c->receiver && (r == 0 || (c->receiver == r && (method_index < 0 || c->method == method_index)))) { - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); bool needToUnlock = false; if (!receiverMutex && senderMutex != m) { // need to relock this receiver and sender in the correct order @@ -2972,7 +2994,7 @@ static void blocking_activate(QObject *sender, int signal, QObjectPrivate::Conne #else QSemaphore semaphore; queued_activate(sender, signal, c, argv, &semaphore); - QMutex *mutex = &QThreadData::get2(sender->thread())->mutex; + QMutex *mutex = signalSlotLock(sender); mutex->unlock(); semaphore.acquire(); mutex->lock(); @@ -2992,7 +3014,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal argv ? argv : empty_argv); } - QMutexLocker locker(&sender->d_func()->threadData->mutex); + QMutexLocker locker(signalSlotLock(sender)); QThreadData *currentThreadData = QThreadData::current(); QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; @@ -3344,7 +3366,7 @@ void QObject::dumpObjectInfo() objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data()); Q_D(QObject); - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); // first, look for connections where this object is the sender qDebug(" SIGNALS OUT"); diff --git a/tests/auto/qobjectrace/tst_qobjectrace.cpp b/tests/auto/qobjectrace/tst_qobjectrace.cpp index fcfd528..aa80534 100644 --- a/tests/auto/qobjectrace/tst_qobjectrace.cpp +++ b/tests/auto/qobjectrace/tst_qobjectrace.cpp @@ -43,6 +43,7 @@ #include #include + enum { OneMinute = 60 * 1000, TwoMinutes = OneMinute * 2 }; class tst_QObjectRace: public QObject @@ -70,12 +71,18 @@ public: public slots: void theSlot() { - enum { step = 1000 }; + enum { step = 35 }; if ((++count % step) == 0) { QThread *nextThread = threads.at((count / step) % threads.size()); moveToThread(nextThread); } } + + void destroSlot() { + emit theSignal(); + } +signals: + void theSignal(); }; class RaceThread : public QThread @@ -120,6 +127,10 @@ private slots: if (stopWatch.elapsed() >= OneMinute / 2) #endif quit(); + + QObject o; + connect(&o, SIGNAL(destroyed()) , object, SLOT(destroSlot())); + connect(object, SIGNAL(destroyed()) , &o, SLOT(deleteLater())); } }; @@ -138,10 +149,17 @@ void tst_QObjectRace::moveToThreadRace() for (int i = 0; i < ThreadCount; ++i) threads[i]->start(); - QVERIFY(threads[0]->wait(TwoMinutes)); + + while(!threads[0]->isFinished()) { + QPointer foo (object); + QObject o; + connect(&o, SIGNAL(destroyed()) , object, SLOT(destroSlot())); + connect(object, SIGNAL(destroyed()) , &o, SLOT(deleteLater())); + QTest::qWait(10); + } // the other threads should finish pretty quickly now for (int i = 1; i < ThreadCount; ++i) - QVERIFY(threads[i]->wait(30000)); + QVERIFY(threads[i]->wait(300)); for (int i = 0; i < ThreadCount; ++i) delete threads[i]; -- cgit v0.12 From afa4bcc90f79f3a68bed3f3429f2515fc8c7d23d Mon Sep 17 00:00:00 2001 From: Daniel Molkentin Date: Wed, 20 May 2009 15:53:07 +0200 Subject: Changing the addressbook tutorial so it doesn't leak. Reviewed-By: Kavindra Palaraja --- doc/src/tutorials/addressbook.qdoc | 9 ++++++--- examples/tutorials/addressbook-fr/part1/main.cpp | 4 ++-- examples/tutorials/addressbook-fr/part2/main.cpp | 4 ++-- examples/tutorials/addressbook-fr/part3/main.cpp | 4 ++-- examples/tutorials/addressbook-fr/part4/main.cpp | 4 ++-- examples/tutorials/addressbook-fr/part5/main.cpp | 4 ++-- examples/tutorials/addressbook-fr/part6/main.cpp | 4 ++-- examples/tutorials/addressbook-fr/part7/main.cpp | 4 ++-- examples/tutorials/addressbook/part1/main.cpp | 4 ++-- examples/tutorials/addressbook/part2/main.cpp | 4 ++-- examples/tutorials/addressbook/part3/main.cpp | 4 ++-- examples/tutorials/addressbook/part4/main.cpp | 4 ++-- examples/tutorials/addressbook/part5/main.cpp | 4 ++-- examples/tutorials/addressbook/part6/main.cpp | 4 ++-- examples/tutorials/addressbook/part7/main.cpp | 4 ++-- 15 files changed, 34 insertions(+), 31 deletions(-) diff --git a/doc/src/tutorials/addressbook.qdoc b/doc/src/tutorials/addressbook.qdoc index 3b0d2bc..38200b0 100644 --- a/doc/src/tutorials/addressbook.qdoc +++ b/doc/src/tutorials/addressbook.qdoc @@ -242,12 +242,15 @@ \snippet tutorials/addressbook/part1/main.cpp main function - We construct a new \c AddressBook widget on the heap using the \c new - keyword and invoke its \l{QWidget::show()}{show()} function to display it. + We construct a new \c AddressBook widget on the stack and invoke + its \l{QWidget::show()}{show()} function to display it. However, the widget will not be shown until the application's event loop is started. We start the event loop by calling the application's \l{QApplication::}{exec()} function; the result returned by this function - is used as the return value from the \c main() function. + is used as the return value from the \c main() function. At this point, + it becomes apparent why we instanciated \c AddressBook on the stack: It + will now go out of scope. Therefore, \c AddressBook and all its child widgets + will be deleted, thus preventing memory leaks. */ /*! diff --git a/examples/tutorials/addressbook-fr/part1/main.cpp b/examples/tutorials/addressbook-fr/part1/main.cpp index 22bfd3e..9c96882 100644 --- a/examples/tutorials/addressbook-fr/part1/main.cpp +++ b/examples/tutorials/addressbook-fr/part1/main.cpp @@ -47,8 +47,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook-fr/part2/main.cpp b/examples/tutorials/addressbook-fr/part2/main.cpp index 22bfd3e..9c96882 100644 --- a/examples/tutorials/addressbook-fr/part2/main.cpp +++ b/examples/tutorials/addressbook-fr/part2/main.cpp @@ -47,8 +47,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook-fr/part3/main.cpp b/examples/tutorials/addressbook-fr/part3/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook-fr/part3/main.cpp +++ b/examples/tutorials/addressbook-fr/part3/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook-fr/part4/main.cpp b/examples/tutorials/addressbook-fr/part4/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook-fr/part4/main.cpp +++ b/examples/tutorials/addressbook-fr/part4/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook-fr/part5/main.cpp b/examples/tutorials/addressbook-fr/part5/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook-fr/part5/main.cpp +++ b/examples/tutorials/addressbook-fr/part5/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook-fr/part6/main.cpp b/examples/tutorials/addressbook-fr/part6/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook-fr/part6/main.cpp +++ b/examples/tutorials/addressbook-fr/part6/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook-fr/part7/main.cpp b/examples/tutorials/addressbook-fr/part7/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook-fr/part7/main.cpp +++ b/examples/tutorials/addressbook-fr/part7/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part1/main.cpp b/examples/tutorials/addressbook/part1/main.cpp index 22bfd3e..9c96882 100644 --- a/examples/tutorials/addressbook/part1/main.cpp +++ b/examples/tutorials/addressbook/part1/main.cpp @@ -47,8 +47,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part2/main.cpp b/examples/tutorials/addressbook/part2/main.cpp index 22bfd3e..9c96882 100644 --- a/examples/tutorials/addressbook/part2/main.cpp +++ b/examples/tutorials/addressbook/part2/main.cpp @@ -47,8 +47,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part3/main.cpp b/examples/tutorials/addressbook/part3/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook/part3/main.cpp +++ b/examples/tutorials/addressbook/part3/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part4/main.cpp b/examples/tutorials/addressbook/part4/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook/part4/main.cpp +++ b/examples/tutorials/addressbook/part4/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part5/main.cpp b/examples/tutorials/addressbook/part5/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook/part5/main.cpp +++ b/examples/tutorials/addressbook/part5/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part6/main.cpp b/examples/tutorials/addressbook/part6/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook/part6/main.cpp +++ b/examples/tutorials/addressbook/part6/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } diff --git a/examples/tutorials/addressbook/part7/main.cpp b/examples/tutorials/addressbook/part7/main.cpp index b25d656..46f6f03 100644 --- a/examples/tutorials/addressbook/part7/main.cpp +++ b/examples/tutorials/addressbook/part7/main.cpp @@ -46,8 +46,8 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - AddressBook *addressBook = new AddressBook; - addressBook->show(); + AddressBook addressBook; + addressBook.show(); return app.exec(); } -- cgit v0.12 From d2e3f2a79a2f8cae66a94bbd2d16f92171455a8d Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Wed, 20 May 2009 12:46:18 +0200 Subject: allow to debug QVariant of float type Reviewed-by: ogoffart --- src/corelib/kernel/qvariant.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index b504604..2ff9818 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1021,7 +1021,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, #if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) static void streamDebug(QDebug dbg, const QVariant &v) { - switch (v.type()) { + switch (v.userType()) { case QVariant::Int: dbg.nospace() << v.toInt(); break; @@ -1034,6 +1034,9 @@ static void streamDebug(QDebug dbg, const QVariant &v) case QVariant::ULongLong: dbg.nospace() << v.toULongLong(); break; + case QMetaType::Float: + dbg.nospace() << qVariantValue(v); + break; case QVariant::Double: dbg.nospace() << v.toDouble(); break; -- cgit v0.12 From 3830edd3c62ce1e66fe6b7edaad6834230d7e8c3 Mon Sep 17 00:00:00 2001 From: Thierry Bastian Date: Wed, 20 May 2009 16:21:44 +0200 Subject: Remove QDumper. It was used in a previous version of creator but is now useless. --- src/corelib/tools/qdumper.cpp | 1157 ----------------------------------------- src/corelib/tools/tools.pri | 1 - 2 files changed, 1158 deletions(-) delete mode 100644 src/corelib/tools/qdumper.cpp diff --git a/src/corelib/tools/qdumper.cpp b/src/corelib/tools/qdumper.cpp deleted file mode 100644 index c3b8524..0000000 --- a/src/corelib/tools/qdumper.cpp +++ /dev/null @@ -1,1157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 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 the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(Q_OS_WINCE) && !defined(QT_NO_DUMPER) - -#include -#include - -#ifdef Q_OS_WIN -# include -#endif - -QT_BEGIN_NAMESPACE - -namespace { - -// 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 void qCheckAccess(const void *d) -{ - // provoke segfault when address is not readable - qProvokeSegFaultHelper = *(char*)d; -} - -static void qCheckPointer(const void *d) -{ - if (!d) - return; - qProvokeSegFaultHelper = *(char*)d; -} - -static void qProvokeSegFault() -{ - // provoke segfault unconditionally - qCheckAccess(0); -} - -static char qDumpInBuffer[100]; -static char qDumpBuffer[1000]; -#ifdef Q_OS_WIN -static char qDumpBuffer2[sizeof(qDumpBuffer) + 100]; -#endif - -static char toHex(int n) -{ - return n < 10 ? '0' + n : 'a' - 10 + n; -} - - -struct QDumper -{ - explicit QDumper(); - ~QDumper(); - void flush(); - QDumper &operator<<(long c); - QDumper &operator<<(int i); - QDumper &operator<<(unsigned long c); - QDumper &operator<<(unsigned int i); - QDumper &operator<<(const void *p); - void put(char c); - void addCommaIfNeeded(); - void putEncoded(unsigned c); - QDumper &operator<<(const char *str); - QDumper &operator<<(const QString &str); - void disarm(); - - void beginHash(); // start of data hash output - void endHash(); // start of data hash output - - // 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? - size_t pos; -}; - - -QDumper::QDumper() -{ - success = false; - pos = 0; -} - -QDumper::~QDumper() -{ - flush(); - put(0); // our end marker -#ifdef Q_OS_WIN - sprintf(qDumpBuffer2, "@@CDD/%d/done\n", token); - OutputDebugStringA(qDumpBuffer2); -#else - fprintf(stderr, "%d/done\n", token); -#endif - qDumpInBuffer[0] = 0; -} - -void QDumper::flush() -{ - qDumpBuffer[pos++] = 0; -#ifdef Q_OS_WIN - sprintf(qDumpBuffer2, "@@CDD#%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); - OutputDebugStringA(qDumpBuffer2); -#else - fprintf(stderr, "%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); -#endif - pos = 0; -} - -void QDumper::setupTemplateParameters() -{ - char *s = const_cast(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 c) -{ - static char buf[100]; - sprintf(buf, "%lu", c); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(unsigned int i) -{ - static char buf[100]; - sprintf(buf, "%u", i); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(long c) -{ - static char buf[100]; - sprintf(buf, "%ld", c); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(int i) -{ - static char buf[100]; - sprintf(buf, "%d", i); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(const void *p) -{ - static char buf[100]; - 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'); - } - return (*this) << buf; -} - -void QDumper::put(char c) -{ - if (pos >= sizeof(qDumpBuffer) - 100) - flush(); - qDumpBuffer[pos++] = c; -} - -void QDumper::addCommaIfNeeded() -{ - if (pos == 0) - return; - if (qDumpBuffer[pos - 1] == '}' || qDumpBuffer[pos - 1] == '"') - put(','); -} - -void QDumper::putEncoded(unsigned c) -{ - if (c >= 32 && c <= 126 && c != '"' && c != '\\') { - put(c); - } else { - put('\\'); - put('u'); - put(toHex((c >> 12) & 0xf)); - put(toHex((c >> 8) & 0xf)); - put(toHex((c >> 4) & 0xf)); - put(toHex( c & 0xf)); - } -} - -QDumper &QDumper::operator<<(const char *str) -{ - while (*str) - put(*(str++)); - return *this; -} - -QDumper &QDumper::operator<<(const QString &str) -{ - int n = str.size(); - if (n < 0) { - qProvokeSegFault(); - } else { - //(*this) << "[" << n << "]"; - if (n > 1000000) - n = 1000000; - //put(' '); - put('\\'); - put('"'); - for (int i = 0; i != n; ++i) - putEncoded(str[i].unicode()); - put('\\'); - put('"'); - if (n < str.size()) - (*this) << ""; - } - 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", "QString"); \ - P(dumper, "numchild", "0"); \ - 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(); - -#undef TT -#define TT(type, value) \ - "" << type << " : " << value << "" - -static void qDumpUnknown(QDumper &d) -{ - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "type", d.outertype); - P(d, "numchild", "0"); - d.disarm(); -} - -static void qDumpQPropertyList(QDumper &d) -{ - const QObject *ob = (const QObject *)d.data; - const QMetaObject *mo = ob->metaObject(); - P(d, "iname", d.iname); - P(d, "addr", ""); - P(d, "type", "QObject"); - 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()); - if (QLatin1String(prop.typeName()) == QLatin1String("QString")) { - P(d, "value", prop.read(ob).toString()); - P(d, "numchild", "0"); - } else if (QLatin1String(prop.typeName()) == QLatin1String("bool")) { - P(d, "value", (prop.read(ob).toBool() ? "true" : "false")); - P(d, "numchild", "0"); - } else if (QLatin1String(prop.typeName()) == QLatin1String("int")) { - P(d, "value", prop.read(ob).toInt()); - P(d, "numchild", "0"); - } else { - P(d, "exp", "((" << mo->className() << "*)" << ob - << ")->" << prop.name() << "()"); - } - P(d, "type", prop.typeName()); - P(d, "numchild", "1"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQObject(QDumper &d) -{ - const QObject *ob = reinterpret_cast(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", (void*)d.data); - P(d, "type", "QObject"); - P(d, "numchild", 4); - if (d.dumpChildren) { - const QMetaObject *mo = ob->metaObject(); - const QObjectList &children = ob->children(); - d << ",children=["; - S(d, "objectName", ob->objectName()); - 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 QObject*)" << d.data); - P(d, "type", "QPropertyList"); - P(d, "value", "<" << mo->propertyCount() << " items>"); - P(d, "numchild", mo->propertyCount()); - d.endHash(); - d.beginHash(); - P(d, "name", "children"); - P(d, "exp", "((class QObject*)" << d.data << ")->children()"); - P(d, "type", "QList"); - P(d, "value", "<" << children.size() << " items>"); - P(d, "numchild", children.size()); - d.endHash(); - d.beginHash(); - P(d, "name", "parent"); - P(d, "exp", "((class QObject*)" << d.data << ")->parent()"); - P(d, "type", "QObject *"); - P(d, "numchild", (ob->parent() ? "1" : "0")); - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQDir(QDumper &d) -{ - const QDir &dir = *reinterpret_cast(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", dir.path()); - P(d, "type", "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 qDumpQFileInfo(QDumper &d) -{ - const QFileInfo &info = *reinterpret_cast(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", info.filePath()); - P(d, "type", "QDir"); - 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()); - -#ifndef QT_NO_DATESTRING - d.beginHash(); - P(d, "name", "created"); - P(d, "value", info.created().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->created()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "lastModified"); - P(d, "value", info.lastModified().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->lastModified()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "lastRead"); - P(d, "value", info.lastRead().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->lastRead()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); -#endif - - d << "]"; - } - d.disarm(); -} - -static void qDumpQDateTime(QDumper &d) -{ -#ifdef QT_NO_DATESTRING - qDumpUnknown(d); -#else - const QDateTime &date = *reinterpret_cast(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", date.toString()); - P(d, "type", "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()); - - d.beginHash(); - P(d, "name", "toUTC"); - P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::UTC)"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "toLocalTime"); - P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::LocalTime)"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d << "]"; - } - d.disarm(); -#endif // ifdef QT_NO_DATESTRING -} - -static void qDumpQString(QDumper &d) -{ - const QString &str = *reinterpret_cast(d.data); - - // Try to provoke segfaults early to prevent the frontend - // from asking for unavailable child details - if (!str.isEmpty()) { - volatile ushort dummy = 0; - dummy += str.at(0).unicode(); - dummy += str.at(str.size() - 1).unicode(); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", str); - P(d, "type", "QString"); - P(d, "numchild", "0"); - d.disarm(); -} - -static void qDumpQStringList(QDumper &d) -{ - const QStringList &list = *reinterpret_cast(d.data); - int n = list.size(); - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(&list.front()); - qCheckAccess(&list.back()); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - S(d, "[" << i << "]", list[i]); - } - if (n < list.size()) { - d.beginHash(); - P(d, "value", ""); - P(d, "type", " "); - P(d, "numchild", "0"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQVariantHelper(const void *data, QString *value, - QString *exp, int *numchild) -{ - const QVariant &v = *reinterpret_cast(data); - switch (v.type()) { - case QVariant::Invalid: - *value = QLatin1String(""); - *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((qulonglong)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: - // FIXME - //*exp = QString("qVariantValue<" << v.typeName() << ">" - // << "(*(QVariant*)" << data << ")"); - break; - } -} - -static void qDumpQVariant(QDumper &d) -{ - const QVariant &v = *reinterpret_cast(d.data); - QString value; - QString exp; - int numchild = 0; - qDumpQVariantHelper(d.data, &value, &exp, &numchild); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "(" << v.typeName() << ") " << qPrintable(value)); - P(d, "type", "QVariant"); - P(d, "numchild", 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", qPrintable(value)); - P(d, "type", v.typeName()); - P(d, "numchild", numchild); - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQList(QDumper &d) -{ - // This uses the knowledge that QList has only a single member - // of type union { QListData p; QListData::Data *d; }; - const QListData &ldata = *reinterpret_cast(d.data); - const QListData::Data *pdata = *reinterpret_cast(d.data); - int nn = ldata.size(); - if (nn < 0) - qProvokeSegFault(); - if (nn > 0) { - qCheckAccess(ldata.d->array); - //qCheckAccess(ldata.d->array[0]); - //qCheckAccess(ldata.d->array[nn - 1]); - } - - int n = nn; - P(d, "iname", d.iname); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - // The exact condition here is: - // QTypeInfo::isLarge || QTypeInfo::isStatic - // but this data is not available in the compiled binary. - // So as first approximation only do the 'isLarge' check: - void *p = &(ldata.d->array[i + pdata->begin]); - unsigned long voidpsize = sizeof(void*); - P(d, "exp", "(sizeof(" << d.innertype << ")>" << voidpsize << - "?(**(" << d.innertype << "**)(" << p << "))" - ":(*(" << d.innertype << "*)(" << p << ")))"); - P(d, "type", d.innertype); - d.endHash(); - } - if (n < nn) { - d << ",{"; - P(d, "value", ""); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQVector(QDumper &d) -{ - // Use 'int' as representative value. No way (and no need) - // to deduce proper type here. - const QVector &vec = *reinterpret_cast *>(d.data); - const int nn = vec.size(); - - // Try to provoke segfaults early to prevent the frontend - // from asking for unavailable child details - if (nn < 0) - qProvokeSegFault(); - if (nn > 0) { - qCheckAccess(&vec.front()); - qCheckAccess(&vec.back()); - } - - //int innersize = 0; - //scanf(qDumpInBuffer, "%d", &innersize); - - int n = nn; - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - if (i) - d << ","; - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "exp", "(" << d.exp << ".d->array[" << i << "])"); - P(d, "type", d.innertype); - d.endHash(); - } - if (n < nn) { - d << ",{"; - P(d, "value", ""); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQHashNode(QDumper &d) -{ - struct NodeOS { void *next; uint k; uint v; } nodeOS; // int-key optimization, small value - struct NodeOL { void *next; uint k; void *v; } nodeOL; // int-key optimiatzion, large value - struct NodeNS { void *next; uint h; uint k; uint v; } nodeNS; // no optimization, small value - struct NodeNL { void *next; uint h; uint k; void *v; } nodeNL; // no optimization, large value - struct NodeL { void *next; uint h; void *k; void *v; } nodeL; // complex key - - // offsetof(...,...) not yet in Standard C++ - const ulong nodeOSk ( (char *)&nodeOS.k - (char *)&nodeOS ); - const ulong nodeOSv ( (char *)&nodeOS.v - (char *)&nodeOS ); - const ulong nodeOLk ( (char *)&nodeOL.k - (char *)&nodeOL ); - const ulong nodeOLv ( (char *)&nodeOL.v - (char *)&nodeOL ); - const ulong nodeNSk ( (char *)&nodeNS.k - (char *)&nodeNS ); - const ulong nodeNSv ( (char *)&nodeNS.v - (char *)&nodeNS ); - const ulong nodeNLk ( (char *)&nodeNL.k - (char *)&nodeNL ); - const ulong nodeNLv ( (char *)&nodeNL.v - (char *)&nodeNL ); - const ulong nodeLk ( (char *)&nodeL.k - (char *)&nodeL ); - const ulong nodeLv ( (char *)&nodeL.v - (char *)&nodeL ); - - const QHashData *h = reinterpret_cast(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "numchild", 2); - if (d.dumpChildren) { - // there is a hash specialization in cast the key are integers or shorts - bool isOptimizedIntKey = qstrcmp(keyType, "int") == 0 -#if defined(Q_BYTE_ORDER) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN - || qstrcmp(keyType, "short") == 0 - || qstrcmp(keyType, "ushort") == 0 -#endif - || qstrcmp(keyType, "uint") == 0; - - d << ",children=["; - d.beginHash(); - P(d, "name", "key"); - P(d, "type", keyType); - unsigned long intsize = sizeof(int); - if (isOptimizedIntKey) { - P(d, "exp", "*(" << keyType << "*)" - "(((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeOLk << ":" << nodeOSk << - ")+(char*)" << h << ")"); - } else { - P(d, "exp", "*(" << keyType << "*)" - "(((sizeof(" << keyType << ")>" << intsize << ")?" - << nodeLk << ":" - "((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeNLk << ":" << nodeNSk << "))+(char*)" << h << ")"); - } - d.endHash(); - d.beginHash(); - P(d, "name", "value"); - P(d, "type", valueType); - if (isOptimizedIntKey) { - P(d, "exp", "*(" << valueType << "*)" - "(((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeOLv << ":" << nodeOSv << ")+(char*)" << h << ")"); - } else { - P(d, "exp", "*(" << valueType << "*)" - "(((sizeof(" << keyType << ")>" << intsize << ")?" << nodeLv << ":" - "((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeNLv << ":" << nodeNSv << "))+(char*)" << h << ")"); - } - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQHash(QDumper &d) -{ - QHashData *h = *reinterpret_cast(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - qCheckPointer(h->fakeNext); - qCheckPointer(h->buckets); - - int n = h->size; - - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckPointer(h->fakeNext); - qCheckPointer(*h->buckets); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - - QHashData::Node *node = h->firstNode(); - QHashData::Node *end = reinterpret_cast(h); - int i = 0; - - while (node != end) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", "QHashNode<" << keyType << "," << valueType << " >"); - P(d, "exp", "*(QHashNode<" << keyType << "," << valueType << " >*)" << node); - d.endHash(); - - ++i; - node = QHashData::nextNode(node); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQMapNode(QDumper &d) -{ - const QMapData *h = reinterpret_cast(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - qCheckAccess(h->backward); - qCheckAccess(h->forward[0]); - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "numchild", 2); - if (d.dumpChildren) { - unsigned long voidpsize = sizeof(void*); - d << ",children=["; - d.beginHash(); - P(d, "name", "key"); - P(d, "type", keyType); - P(d, "exp", "*(" << keyType << "*)" - << "(" - << 2 * voidpsize - << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" - << "+(char*)" << h - << ")"); - d.endHash(); - d.beginHash(); - P(d, "name", "value"); - P(d, "type", valueType); - P(d, "exp", "*(" << valueType << "*)" - << "(" - << "(size_t)&(('QMap<" << keyType << "," << valueType << ">::Node'*)0)->value" - << "+" << 2 * voidpsize - << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" - << "+(char*)" << h - << ")"); - d.endHash(); - d << "]"; - } - - d.disarm(); -} - -static void qDumpQMap(QDumper &d) -{ - QMapData *h = *reinterpret_cast(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - int n = h->size; - - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(h->backward); - qCheckAccess(h->forward[0]); - qCheckPointer(h->backward->backward); - qCheckPointer(h->forward[0]->backward); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - - QMapData::Node *node = reinterpret_cast(h->forward[0]); - QMapData::Node *end = reinterpret_cast(h); - int i = 0; - - while (node != end) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", "QMap<" << keyType << "," << valueType << ">::Node"); - P(d, "exp", "*('QMap<" << keyType << "," << valueType << ">::Node'*)" << node); - d.endHash(); - - ++i; - node = node->forward[0]; - } - d << "]"; - } - - d.disarm(); -} - -static void qDumpQSet(QDumper &d) -{ - // This uses the knowledge that QHash has only a single member - // of union { QHashData *d; QHashNode *e; }; - QHashData *hd = *(QHashData**)d.data; - QHashData::Node *node = hd->firstNode(); - - int n = hd->size; - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(node); - qCheckPointer(node->next); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - 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(node) << ")->key" - ); - d.endHash(); - ++i; - } - } - d << "]"; - } - d.disarm(); -} - -static void handleProtocolVersion2(QDumper & d) -{ - if (!d.outertype[0]) { - qDumpUnknown(d); - return; - } - - d.setupTemplateParameters(); - // d.outertype[0] is usally 'Q', so don't use it - switch (d.outertype[1]) { - case 'D': - if (qstrcmp(d.outertype, "QDateTime") == 0) - qDumpQDateTime(d); - else if (qstrcmp(d.outertype, "QDir") == 0) - qDumpQDir(d); - break; - case 'F': - if (qstrcmp(d.outertype, "QFileInfo") == 0) - qDumpQFileInfo(d); - break; - case 'H': - if (qstrcmp(d.outertype, "QHash") == 0) - qDumpQHash(d); - else if (qstrcmp(d.outertype, "QHashNode") == 0) - qDumpQHashNode(d); - break; - case 'L': - if (qstrcmp(d.outertype, "QList") == 0) - qDumpQList(d); - break; - case 'M': - if (qstrcmp(d.outertype, "QMap") == 0) - qDumpQMap(d); - else if (qstrcmp(d.outertype, "QMap::Node") == 0) - qDumpQMapNode(d); - break; - case 'O': - if (qstrcmp(d.outertype, "QObject") == 0) - qDumpQObject(d); - break; - case 'P': - if (qstrcmp(d.outertype, "QPropertyList") == 0) - qDumpQPropertyList(d); - break; - case 'S': - if (qstrcmp(d.outertype, "QSet") == 0) - qDumpQSet(d); - else if (qstrcmp(d.outertype, "QString") == 0) - qDumpQString(d); - else if (qstrcmp(d.outertype, "QStringList") == 0) - qDumpQStringList(d); - break; - case 'V': - if (qstrcmp(d.outertype, "QVariant") == 0) - qDumpQVariant(d); - else if (qstrcmp(d.outertype, "QVector") == 0) - qDumpQVector(d); - break; - } - - if (!d.success) - qDumpUnknown(d); -} - -} // anonymous namespace - - -extern "C" Q_CORE_EXPORT void qDumpObjectData( - int protocolVersion, - int token, - const char *outertype, - const char *iname, - const char *exp, - const char *innertype, - const void *data, - bool dumpChildren) -{ - if (protocolVersion == 1) { - // used to test whether error output gets through - //fprintf(stderr, "using stderr, qDebug follows: %d\n", token); - //qDebug() << "using qDebug, stderr already used: " << token; - } - - else if (protocolVersion == 2) { - QDumper d; - d.protocolVersion = protocolVersion; - d.token = token; - d.outertype = outertype ? outertype : ""; - d.iname = iname ? iname : ""; - d.exp = exp ? exp : ""; - d.innertype = innertype ? innertype : ""; - d.data = data ? data : ""; - d.dumpChildren = dumpChildren; - handleProtocolVersion2(d); - } - - else { - qDebug() << "Unsupported protocol version" << protocolVersion; - } -} - -QT_END_NAMESPACE - -#endif // !Q_OS_WINCE && !QT_NO_QDUMPER diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index aaf3f21..a6fdac7 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -46,7 +46,6 @@ SOURCES += \ tools/qbytearraymatcher.cpp \ tools/qcryptographichash.cpp \ tools/qdatetime.cpp \ - tools/qdumper.cpp \ tools/qhash.cpp \ tools/qline.cpp \ tools/qlinkedlist.cpp \ -- cgit v0.12 From f5772762c9da55157e11249da00715def2b693f5 Mon Sep 17 00:00:00 2001 From: Benjamin Poulain Date: Wed, 20 May 2009 17:26:34 +0200 Subject: Sync the French addressbook tutorial with the English version Reviewed-by: Thierry --- doc/src/tutorials/addressbook-fr.qdoc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/src/tutorials/addressbook-fr.qdoc b/doc/src/tutorials/addressbook-fr.qdoc index 2847f1b..512a404 100644 --- a/doc/src/tutorials/addressbook-fr.qdoc +++ b/doc/src/tutorials/addressbook-fr.qdoc @@ -239,13 +239,16 @@ \snippet tutorials/addressbook/part1/main.cpp main function - On construit un nouveau widget \c AddressBook sur le tas en utilisant le mot-clé - \c new et en invoquant sa méthode \l{QWidget::show()}{show()} pour l'afficher. + On construit un nouveau widget \c AddressBook sur la pile et on invoque + sa méthode \l{QWidget::show()}{show()} pour l'afficher. Cependant, le widget ne sera pas visible tant que la boucle d'évènements n'aura pas été lancée. On démarre la boucle d'évènements en appelant la méthode \l{QApplication::}{exec()} de l'application; le résultat renvoyé par cette méthode est lui même utilisé comme valeur de retour pour la méthode \c main(). + On comprend maintenant pourquoi \c AddressBook a été créé sur la pile: à la fin + du programme, l'objet sort du scope de la fonction \c main() et tous ses widgets enfants + sont supprimés, assurant ainsi qu'il n'y aura pas de fuites de mémoire. */ /*! -- cgit v0.12 From 1ea9fd89e39ce519fd406174a0b5fb4efee67ab7 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Wed, 20 May 2009 17:25:25 +0200 Subject: tst_QHttpNetworkConnection: Compile fix Reviewed-by: Peter Hartmann --- tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp index b22397f..eb30147 100644 --- a/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp +++ b/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp @@ -42,6 +42,7 @@ #include #include "private/qhttpnetworkconnection_p.h" +#include "private/qnoncontiguousbytedevice_p.h" #include #include "../network-settings.h" @@ -303,8 +304,8 @@ void tst_QHttpNetworkConnection::put() QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Put); QByteArray array = data.toLatin1(); - QBuffer buffer(&array); - request.setData(&buffer); + QNonContiguousByteDevice *bd = QNonContiguousByteDeviceFactory::create(&array); + request.setUploadByteDevice(bd); finishedCalled = false; finishedWithErrorCalled = false; @@ -393,8 +394,8 @@ void tst_QHttpNetworkConnection::post() QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Post); QByteArray array = data.toLatin1(); - QBuffer buffer(&array); - request.setData(&buffer); + QNonContiguousByteDevice *bd = QNonContiguousByteDeviceFactory::create(&array); + request.setUploadByteDevice(bd); QHttpNetworkReply *reply = connection.sendRequest(request); -- cgit v0.12