diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2010-08-31 00:29:26 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2010-08-31 00:29:26 (GMT) |
commit | b3da295dc3a4e72d185f9eb767dfe1f518fb0b8a (patch) | |
tree | 804e18e85f8cfa91e6a16c08e60158117c657259 | |
parent | f70b38f9e7befd675998d39005a82072ddd2862a (diff) | |
parent | 3509fce85cc317168aa302b01a59d4be380b10a8 (diff) | |
download | Qt-b3da295dc3a4e72d185f9eb767dfe1f518fb0b8a.zip Qt-b3da295dc3a4e72d185f9eb767dfe1f518fb0b8a.tar.gz Qt-b3da295dc3a4e72d185f9eb767dfe1f518fb0b8a.tar.bz2 |
Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into master-integration
* 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2:
Fix compilation on mac.
Doc update: Mention the Qt::AA_X11InitThreads application attribute.
Fix incorrect include order
Add missing QT_BEGIN_NAMESPACE
Fix crash introduced with d34287af6fc1c7558e8ed15dbb29c0e6b58b7b00
find .rodata in qt plugins to optimize loading of plugins with no qt section
fix missing include
Optimize plugin loading on ELF platforms
use QFile:map instead of ::mmap
QAbstractItemView: optimize handling of editors for view with large numbers of editors.
23 files changed, 589 insertions, 100 deletions
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index a8758cf..ede6dcd 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -2612,6 +2612,12 @@ QT_LICENSED_MODULE(DBus) # define QT_NO_PROCESS #endif +#if defined (__ELF__) +# if defined (Q_OS_LINUX) || defined (Q_OS_SOLARIS) || defined (Q_OS_FREEBSD) || defined (Q_OS_OPENBSD) || defined (Q_OS_IRIX) +# define Q_OF_ELF +# endif +#endif + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/plugin/plugin.pri b/src/corelib/plugin/plugin.pri index c05ff48..2e7db5d 100644 --- a/src/corelib/plugin/plugin.pri +++ b/src/corelib/plugin/plugin.pri @@ -7,13 +7,15 @@ HEADERS += \ plugin/qlibrary_p.h \ plugin/qplugin.h \ plugin/quuid.h \ - plugin/qfactoryloader_p.h + plugin/qfactoryloader_p.h \ + plugin/qelfparser_p.h SOURCES += \ plugin/qpluginloader.cpp \ plugin/qfactoryloader.cpp \ plugin/quuid.cpp \ - plugin/qlibrary.cpp + plugin/qlibrary.cpp \ + plugin/qelfparser_p.cpp win32 { SOURCES += plugin/qlibrary_win.cpp diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp new file mode 100644 index 0000000..97008fd --- /dev/null +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibrary_p.h" +#include "qelfparser_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +// #define QELFPARSER_DEBUG 1 + +const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh) +{ + sh->name = read<qelfword_t>(data); + data += sizeof(qelfword_t); // sh_name + sh->type = read<qelfword_t>(data); + data += sizeof(qelfword_t) // sh_type + + sizeof(qelfaddr_t) // sh_flags + + sizeof(qelfaddr_t); // sh_addr + sh->offset = read<qelfoff_t>(data); + data += sizeof(qelfoff_t); // sh_offset + sh->size = read<qelfword_t>(data); + data += sizeof(qelfword_t); // sh_size + return data; +} + +int QElfParser::parse(const char *dataStart, ulong fdlen, const QString &library, QLibraryPrivate *lib, long *pos, ulong *sectionlen) +{ +#if defined(QELFPARSER_DEBUG) + qDebug() << "QElfParser::parse " << library; +#endif + + if (fdlen < 64){ + if (lib) + lib->errorString = QLibrary::tr("'%1' is not an ELF object (%2)").arg(library).arg(QLatin1String("file too small")); + return NotElf; + } + const char *data = dataStart; + if (qstrncmp(data, "\177ELF", 4) != 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is not an ELF object").arg(library); + return NotElf; + } + // 32 or 64 bit + if (data[4] != 1 && data[4] != 2) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("odd cpu architecture")); + return Corrupt; + } + m_bits = (data[4] << 5); + + /* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs + to match the _plugin_ architecture. + */ + if ((sizeof(void*) == 4 && m_bits != 32) || (sizeof(void*) == 8 && m_bits != 64)) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("wrong cpu architecture")); + return Corrupt; + } + // endian + if (data[5] == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("odd endianess")); + return Corrupt; + } + m_endian = (data[5] == 1 ? ElfLittleEndian : ElfBigEndian); + + data += 16 // e_ident + + sizeof(qelfhalf_t) // e_type + + sizeof(qelfhalf_t) // e_machine + + sizeof(qelfword_t) // e_version + + sizeof(qelfaddr_t) // e_entry + + sizeof(qelfoff_t); // e_phoff + + qelfoff_t e_shoff = read<qelfoff_t> (data); + data += sizeof(qelfoff_t) // e_shoff + + sizeof(qelfword_t); // e_flags + + qelfhalf_t e_shsize = read<qelfhalf_t> (data); + + if (e_shsize > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("unexpected e_shsize")); + return Corrupt; + } + + data += sizeof(qelfhalf_t) // e_ehsize + + sizeof(qelfhalf_t) // e_phentsize + + sizeof(qelfhalf_t); // e_phnum + + qelfhalf_t e_shentsize = read<qelfhalf_t> (data); + + if (e_shentsize % 8){ + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("unexpected e_shentsize")); + return Corrupt; + } + data += sizeof(qelfhalf_t); // e_shentsize + qelfhalf_t e_shnum = read<qelfhalf_t> (data); + data += sizeof(qelfhalf_t); // e_shnum + qelfhalf_t e_shtrndx = read<qelfhalf_t> (data); + data += sizeof(qelfhalf_t); // e_shtrndx + + if ((e_shnum * e_shentsize) > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("announced %2 sections, each %3 bytes, exceed file size")) + .arg(e_shnum).arg(e_shentsize); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes"; +#endif + + ElfSectionHeader strtab; + qulonglong soff = e_shoff + e_shentsize * (e_shtrndx); + + if ((soff + e_shentsize) > fdlen || soff % 8 || soff == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("strtab section seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + + parseSectionHeader(dataStart + soff, &strtab); + m_stringTableFileOffset = strtab.offset; + + if ((m_stringTableFileOffset + e_shentsize) >= fdlen || m_stringTableFileOffset == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("string table seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data()); +#endif + + const char *s = dataStart + e_shoff; + for (int i = 0; i < e_shnum; ++i) { + ElfSectionHeader sh; + parseSectionHeader(s, &sh); + if (sh.name == 0) { + s += e_shentsize; + continue; + } + const char *shnam = dataStart + m_stringTableFileOffset + sh.name; + + if (m_stringTableFileOffset + sh.name > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("section name %2 of %3 behind end of file")) + .arg(i).arg(e_shnum); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug() << "++++" << i << shnam; +#endif + + if (qstrcmp(shnam, ".qtplugin") == 0 || qstrcmp(shnam, ".rodata") == 0) { + if (!(sh.type & 0x1)) { + if (shnam[1] == 'r') { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("empty .rodata. not a library.")); + return Corrupt; + } +#if defined(QELFPARSER_DEBUG) + qDebug()<<"section is not program data. skipped."; +#endif + s += e_shentsize; + continue; + } + + if (sh.offset == 0 || (sh.offset + sh.size) > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("missing section data. This is not a library.")); + return Corrupt; + } + *pos = sh.offset; + *sectionlen = sh.size - 1; + if (shnam[1] == 'q') + return Ok; + } + s += e_shentsize; + } + return NoQtSection; +} + +QT_END_NAMESPACE + diff --git a/src/corelib/plugin/qelfparser_p.h b/src/corelib/plugin/qelfparser_p.h new file mode 100644 index 0000000..380d5a1 --- /dev/null +++ b/src/corelib/plugin/qelfparser_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QELFPARSER_P_H +#define QELFPARSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qendian.h> +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +class QString; +class QLibraryPrivate; + +typedef quint16 qelfhalf_t; +typedef quint32 qelfword_t; +typedef quintptr qelfoff_t; +typedef quintptr qelfaddr_t; + +class QElfParser +{ +public: + enum {Ok = 0, NotElf = 1, NoQtSection = 2, Corrupt = 3}; + enum {ElfLittleEndian = 0, ElfBigEndian = 1}; + + struct ElfSectionHeader + { + qelfword_t name; + qelfword_t type; + qelfoff_t offset; + qelfword_t size; + }; + + int m_endian; + int m_bits; + int m_stringTableFileOffset; + + template <typename T> + T read(const char *s) + { + if (m_endian == ElfBigEndian) + return qFromBigEndian<T>(reinterpret_cast<const uchar *>(s)); + else + return qFromLittleEndian<T>(reinterpret_cast<const uchar *>(s)); + } + + const char *parseSectionHeader(const char* s, ElfSectionHeader *sh); + int parse(const char *m_s, ulong fdlen, const QString &library, QLibraryPrivate *lib, long *pos, ulong *sectionlen); +}; + +QT_END_NAMESPACE + +#endif // QELFPARSER_P_H + diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 1ca9d70..8f82cc4 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -38,7 +38,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - #include "qplatformdefs.h" #include "qlibrary.h" @@ -61,6 +60,7 @@ #include <qdebug.h> #include <qvector.h> #include <qdir.h> +#include "qelfparser_p.h" QT_BEGIN_NAMESPACE @@ -365,11 +365,31 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB fdlen = data.size(); } - // verify that the pattern is present in the plugin + /* + ELF binaries on GNU, have .qplugin sections. + */ + long pos = 0; const char pattern[] = "pattern=QT_PLUGIN_VERIFICATION_DATA"; const ulong plen = qstrlen(pattern); - long pos = qt_find_pattern(filedata, fdlen, pattern, plen); - +#if defined (Q_OF_ELF) && defined(Q_CC_GNU) + int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen); + if (r == QElfParser::NoQtSection) { + if (pos > 0) { + // find inside .rodata + long rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); + if (rel < 0) { + pos = -1; + } else { + pos += rel; + } + } else { + pos = qt_find_pattern(filedata, fdlen, pattern, plen); + } + } else if (r != QElfParser::Ok) + return false; +#else + pos = qt_find_pattern(filedata, fdlen, pattern, plen); +#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) bool ret = false; if (pos >= 0) ret = qt_parse_pattern(filedata + pos, version, debug, key); diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 7f541f1..bd49b15 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -101,13 +101,17 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio // NOTE: if you change pattern, you MUST change the pattern in // qlibrary.cpp as well. changing the pattern will break all // backwards compatibility as well (no old plugins will be loaded). +// QT5: should probably remove the entire pattern thing and do the section +// trick for all platforms. for now, keep it and fallback to scan for it. # ifdef QPLUGIN_DEBUG_STR # undef QPLUGIN_DEBUG_STR # endif # ifdef QT_NO_DEBUG # define QPLUGIN_DEBUG_STR "false" +# define QPLUGIN_SECTION_DEBUG_STR "" # else # define QPLUGIN_DEBUG_STR "true" +# define QPLUGIN_SECTION_DEBUG_STR ".debug" # endif # define Q_PLUGIN_VERIFICATION_DATA \ static const char qt_plugin_verification_data[] = \ @@ -116,6 +120,13 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio "debug="QPLUGIN_DEBUG_STR"\n" \ "buildkey="QT_BUILD_KEY; +# if defined (Q_OF_ELF) && defined (Q_CC_GNU) +# define Q_PLUGIN_VERIFICATION_SECTION \ + __attribute__ ((section (".qtplugin"))) __attribute__((used)) +# else +# define Q_PLUGIN_VERIFICATION_SECTION +# endif + # if defined (Q_OS_WIN32) && defined(Q_CC_BOR) # define Q_STANDARD_CALL __stdcall # else @@ -123,7 +134,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio # endif # define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \ - Q_PLUGIN_VERIFICATION_DATA \ + Q_PLUGIN_VERIFICATION_SECTION Q_PLUGIN_VERIFICATION_DATA \ Q_EXTERN_C Q_DECL_EXPORT \ const char * Q_STANDARD_CALL qt_plugin_query_verification_data() \ { return qt_plugin_verification_data; } \ diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 12add05..0842ee8 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -1037,10 +1037,12 @@ void QAbstractItemView::reset() { Q_D(QAbstractItemView); d->delayedReset.stop(); //make sure we stop the timer - QList<QEditorInfo>::const_iterator it = d->editors.constBegin(); - for (; it != d->editors.constEnd(); ++it) - d->releaseEditor(it->editor); - d->editors.clear(); + foreach (const QEditorInfo &info, d->indexEditorHash) { + if (info.widget) + d->releaseEditor(info.widget.data()); + } + d->editorIndexHash.clear(); + d->indexEditorHash.clear(); d->persistent.clear(); d->currentIndexSet = false; setState(NoState); @@ -2334,7 +2336,7 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) case Qt::Key_Return: // Propagate the enter if you couldn't edit the item and there are no // current editors (if there are editors, the event was most likely propagated from it). - if (!edit(currentIndex(), EditKeyPressed, event) && d->editors.isEmpty()) + if (!edit(currentIndex(), EditKeyPressed, event) && d->editorIndexHash.isEmpty()) event->ignore(); break; #else @@ -2519,7 +2521,7 @@ bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEve if (!d->isIndexValid(index)) return false; - if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).editor.data())) { + if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).widget.data())) { if (w->focusPolicy() == Qt::NoFocus) return false; w->setFocus(); @@ -2579,15 +2581,15 @@ void QAbstractItemView::updateEditorData() void QAbstractItemView::updateEditorGeometries() { Q_D(QAbstractItemView); - if(d->editors.isEmpty()) + if(d->editorIndexHash.isEmpty()) return; QStyleOptionViewItemV4 option = d->viewOptionsV4(); - QList<QEditorInfo>::iterator it = d->editors.begin(); + QEditorIndexHash::iterator it = d->editorIndexHash.begin(); QWidgetList editorsToRelease; QWidgetList editorsToHide; - while (it != d->editors.end()) { - QModelIndex index = it->index; - QWidget *editor = it->editor; + while (it != d->editorIndexHash.end()) { + QModelIndex index = it.value(); + QWidget *editor = it.key(); if (index.isValid() && editor) { option.rect = visualRect(index); if (option.rect.isValid()) { @@ -2600,13 +2602,14 @@ void QAbstractItemView::updateEditorGeometries() } ++it; } else { - it = d->editors.erase(it); + d->indexEditorHash.remove(it.value()); + it = d->editorIndexHash.erase(it); editorsToRelease << editor; } } //we hide and release the editor outside of the loop because it might change the focus and try - //to change the d->editors list. + //to change the editors hashes. for (int i = 0; i < editorsToHide.count(); ++i) { editorsToHide.at(i)->hide(); } @@ -2957,7 +2960,7 @@ int QAbstractItemView::sizeHintForRow(int row) const QModelIndex index; for (int c = 0; c < colCount; ++c) { index = d->model->index(row, c, d->root); - if (QWidget *editor = d->editorForIndex(index).editor) + if (QWidget *editor = d->editorForIndex(index).widget.data()) height = qMax(height, editor->height()); int hint = d->delegateForIndex(index)->sizeHint(option, index).height(); height = qMax(height, hint); @@ -2988,7 +2991,7 @@ int QAbstractItemView::sizeHintForColumn(int column) const QModelIndex index; for (int r = 0; r < rows; ++r) { index = d->model->index(r, column, d->root); - if (QWidget *editor = d->editorForIndex(index).editor) + if (QWidget *editor = d->editorForIndex(index).widget.data()) width = qMax(width, editor->sizeHint().width()); int hint = d->delegateForIndex(index)->sizeHint(option, index).width(); width = qMax(width, hint); @@ -3024,8 +3027,7 @@ void QAbstractItemView::openPersistentEditor(const QModelIndex &index) void QAbstractItemView::closePersistentEditor(const QModelIndex &index) { Q_D(QAbstractItemView); - QWidget *editor = d->editorForIndex(index).editor; - if (editor) { + if (QWidget *editor = d->editorForIndex(index).widget.data()) { if (index == selectionModel()->currentIndex()) closeEditor(editor, QAbstractItemDelegate::RevertModelCache); d->persistent.remove(editor); @@ -3089,9 +3091,11 @@ void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const { Q_D(const QAbstractItemView); - if (!d->isIndexValid(index)) - return 0; - return d->editorForIndex(index).editor; + if (d->isIndexValid(index)) + if (QWidget *editor = d->editorForIndex(index).widget.data()) + return editor; + + return 0; } /*! @@ -3153,12 +3157,12 @@ void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelInde // Single item changed Q_D(QAbstractItemView); if (topLeft == bottomRight && topLeft.isValid()) { - const QEditorInfo editorInfo = d->editorForIndex(topLeft); + const QEditorInfo &editorInfo = d->editorForIndex(topLeft); //we don't update the edit data if it is static - if (!editorInfo.isStatic && editorInfo.editor) { + if (!editorInfo.isStatic && editorInfo.widget) { QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft); if (delegate) { - delegate->setEditorData(editorInfo.editor, topLeft); + delegate->setEditorData(editorInfo.widget.data(), topLeft); } } if (isVisible() && !d->delayedPendingLayout) { @@ -3232,12 +3236,17 @@ void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int star } // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes - for (int i = d->editors.size() - 1; i >= 0; --i) { - const QModelIndex index = d->editors.at(i).index; - QWidget *editor = d->editors.at(i).editor; + QEditorIndexHash::iterator i = d->editorIndexHash.begin(); + while (i != d->editorIndexHash.end()) { + const QModelIndex index = i.value(); if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) { - d->editors.removeAt(i); - d->releaseEditor(editor); + QWidget *editor = i.key(); + QEditorInfo info = d->indexEditorHash.take(index); + i = d->editorIndexHash.erase(i); + if (info.widget) + d->releaseEditor(editor); + } else { + ++i; } } } @@ -3294,17 +3303,20 @@ void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &par } // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes - QList<QEditorInfo>::iterator it = editors.begin(); - while (it != editors.end()) { - QModelIndex index = it->index; + QEditorIndexHash::iterator it = editorIndexHash.begin(); + while (it != editorIndexHash.end()) { + QModelIndex index = it.value(); if (index.column() <= start && index.column() >= end && model->parent(index) == parent) { - QWidget *editor = it->editor; - it = editors.erase(it); - releaseEditor(editor); + QWidget *editor = it.key(); + QEditorInfo info = indexEditorHash.take(it.value()); + it = editorIndexHash.erase(it); + if (info.widget) + releaseEditor(editor); } else { ++it; } } + } /*! @@ -3386,7 +3398,7 @@ void QAbstractItemView::currentChanged(const QModelIndex ¤t, const QModelI if (previous.isValid()) { QModelIndex buddy = d->model->buddy(previous); - QWidget *editor = d->editorForIndex(buddy).editor; + QWidget *editor = d->editorForIndex(buddy).widget.data(); if (editor && !d->persistent.contains(editor)) { commitData(editor); if (current.row() != previous.row()) @@ -3912,7 +3924,7 @@ QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, const QStyleOptionViewItem &options) { Q_Q(QAbstractItemView); - QWidget *w = editorForIndex(index).editor; + QWidget *w = editorForIndex(index).widget.data(); if (!w) { QAbstractItemDelegate *delegate = delegateForIndex(index); if (!delegate) @@ -3943,6 +3955,7 @@ QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, #endif } } + return w; } @@ -3951,11 +3964,11 @@ void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QMo // we are counting on having relatively few editors const bool checkIndexes = tl.isValid() && br.isValid(); const QModelIndex parent = tl.parent(); - QList<QEditorInfo>::const_iterator it = editors.constBegin(); - for (; it != editors.constEnd(); ++it) { - QWidget *editor = it->editor; - const QModelIndex index = it->index; - if (it->isStatic || editor == 0 || !index.isValid() || + QIndexEditorHash::const_iterator it = indexEditorHash.constBegin(); + for (; it != indexEditorHash.constEnd(); ++it) { + QWidget *editor = it.value().widget.data(); + const QModelIndex index = it.key(); + if (it.value().isStatic || !editor || !index.isValid() || (checkIndexes && (index.row() < tl.row() || index.row() > br.row() || index.column() < tl.column() || index.column() > br.column() @@ -4028,41 +4041,40 @@ void QAbstractItemViewPrivate::checkPersistentEditorFocus() } -QEditorInfo QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const +const QEditorInfo & QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const { - QList<QEditorInfo>::const_iterator it = editors.constBegin(); - for (; it != editors.constEnd(); ++it) { - if (it->index == index) - return *it; - } + static QEditorInfo nullInfo; - return QEditorInfo(); + QIndexEditorHash::const_iterator it = indexEditorHash.find(index); + if (it == indexEditorHash.end()) + return nullInfo; + + return it.value(); } QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const { - QList<QEditorInfo>::const_iterator it = editors.constBegin(); - for (; it != editors.constEnd(); ++it) { - if (it->editor == editor) - return it->index; - } - return QModelIndex(); + QEditorIndexHash::const_iterator it = editorIndexHash.find(editor); + if (it == editorIndexHash.end()) + return QModelIndex(); + + return it.value(); } void QAbstractItemViewPrivate::removeEditor(QWidget *editor) { - QList<QEditorInfo>::iterator it = editors.begin(); - for (; it != editors.end(); ) { - if (it->editor == editor) - it = editors.erase(it); - else - ++it; + QEditorIndexHash::iterator it = editorIndexHash.find(editor); + if (it != editorIndexHash.end()) + { + indexEditorHash.remove(it.value()); + editorIndexHash.erase(it); } } void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic) { - editors.append(QEditorInfo(index, editor, isStatic)); + editorIndexHash.insert(editor, index); + indexEditorHash.insert(index, QEditorInfo(editor, isStatic)); } bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 969553a..03b413a 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -70,22 +70,18 @@ QT_BEGIN_NAMESPACE -struct QEditorInfo -{ - QEditorInfo() : isStatic(false) - { - } - - QEditorInfo(const QPersistentModelIndex &i, QWidget *e, bool b) : index(i), editor(e), isStatic(b) - { - } - - QPersistentModelIndex index; - QPointer<QWidget> editor; - bool isStatic; //true when called from setIndexWidget +struct QEditorInfo { + QEditorInfo(QWidget *e, bool s): widget(QWeakPointer<QWidget>(e)), isStatic(s) {} + QEditorInfo(): isStatic(false) {} + QWeakPointer<QWidget> widget; + bool isStatic; }; +// Fast associativity between Persistent editors and indices. +typedef QHash<QWidget *, QPersistentModelIndex> QEditorIndexHash; +typedef QHash<QPersistentModelIndex, QEditorInfo> QIndexEditorHash; + typedef QPair<QRect, QModelIndex> QItemViewPaintPair; typedef QList<QItemViewPaintPair> QItemViewPaintPairs; @@ -248,9 +244,9 @@ public: : q->horizontalOffset(), q->verticalOffset()); } - QEditorInfo editorForIndex(const QModelIndex &index) const; + const QEditorInfo &editorForIndex(const QModelIndex &index) const; inline bool hasEditor(const QModelIndex &index) const { - return editorForIndex(index).editor != 0; + return indexEditorHash.find(index) != indexEditorHash.constEnd(); } QModelIndex indexForEditor(QWidget *editor) const; @@ -353,7 +349,8 @@ public: QAbstractItemView::SelectionMode selectionMode; QAbstractItemView::SelectionBehavior selectionBehavior; - QList<QEditorInfo> editors; + QEditorIndexHash editorIndexHash; + QIndexEditorHash indexEditorHash; QSet<QWidget*> persistent; QWidget *currentlyCommittingEditor; diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index e9e2e38..d8fef55 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -2159,7 +2159,7 @@ int QTableView::sizeHintForRow(int row) const option.rect.setWidth(columnWidth(index.column())); } - QWidget *editor = d->editorForIndex(index).editor; + QWidget *editor = d->editorForIndex(index).widget.data(); if (editor && d->persistent.contains(editor)) { hint = qMax(hint, editor->sizeHint().height()); int min = editor->minimumSize().height(); @@ -2212,7 +2212,7 @@ int QTableView::sizeHintForColumn(int column) const continue; index = d->model->index(logicalRow, column, d->root); - QWidget *editor = d->editorForIndex(index).editor; + QWidget *editor = d->editorForIndex(index).widget.data(); if (editor && d->persistent.contains(editor)) { hint = qMax(hint, editor->sizeHint().width()); int min = editor->minimumSize().width(); diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index ccc8e00..e393902 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -1493,7 +1493,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, // when the row contains an index widget which has focus, // we want to paint the entire row as active bool indexWidgetHasFocus = false; - if ((current.row() == index.row()) && !d->editors.isEmpty()) { + if ((current.row() == index.row()) && !d->editorIndexHash.isEmpty()) { const int r = index.row(); QWidget *fw = QApplication::focusWidget(); for (int c = 0; c < header->count(); ++c) { @@ -2388,7 +2388,7 @@ void QTreeView::scrollContentsBy(int dx, int dy) int viewCount = d->viewport->height() / itemHeight; int maxDeltaY = qMin(d->viewItems.count(), viewCount); // no need to do a lot of work if we are going to redraw the whole thing anyway - if (qAbs(dy) > qAbs(maxDeltaY) && d->editors.isEmpty()) { + if (qAbs(dy) > qAbs(maxDeltaY) && d->editorIndexHash.isEmpty()) { verticalScrollBar()->update(); d->viewport->update(); return; @@ -2718,7 +2718,7 @@ int QTreeView::sizeHintForColumn(int column) const continue; // we have no good size hint QModelIndex index = viewItems.at(i).index; index = index.sibling(index.row(), column); - QWidget *editor = d->editorForIndex(index).editor; + QWidget *editor = d->editorForIndex(index).widget.data(); if (editor && d->persistent.contains(editor)) { w = qMax(w, editor->sizeHint().width()); int min = editor->minimumSize().width(); @@ -2782,7 +2782,7 @@ int QTreeView::indexRowSizeHint(const QModelIndex &index) const continue; QModelIndex idx = d->model->index(index.row(), logicalColumn, parent); if (idx.isValid()) { - QWidget *editor = d->editorForIndex(idx).editor; + QWidget *editor = d->editorForIndex(idx).widget.data(); if (editor && d->persistent.contains(editor)) { height = qMax(height, editor->sizeHint().height()); int min = editor->minimumSize().height(); @@ -3031,9 +3031,9 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons //and now let's render the editors the editors QStyleOptionViewItemV4 option = viewOptionsV4(); - for (QList<QEditorInfo>::const_iterator it = editors.constBegin(); it != editors.constEnd(); ++it) { - QWidget *editor = it->editor; - QModelIndex index = it->index; + for (QEditorIndexHash::const_iterator it = editorIndexHash.constBegin(); it != editorIndexHash.constEnd(); ++it) { + QWidget *editor = it.key(); + const QModelIndex &index = it.value(); option.rect = q->visualRect(index); if (option.rect.isValid()) { diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index f6ea6be..5a50716 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -3610,12 +3610,16 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context) \section1 Threading - It is possible to render into a QGLWidget from another thread, but it - requires that all access to the GL context is safe guarded. The Qt GUI - thread will try to use the context in resizeEvent and paintEvent, so in - order for threaded rendering using a GL widget to work, these functions - need to be intercepted in the GUI thread and handled accordingly in the - application. + It is possible to render into a QGLWidget from another thread, but + it requires that all access to the GL context is safe guarded. The + Qt GUI thread will try to use the context in resizeEvent and + paintEvent, so in order for threaded rendering using a GL widget + to work, these functions need to be intercepted in the GUI thread + and handled accordingly in the application. Under X11, set the + Qt::AA_X11InitThreads application attribute, before you create the + QApplication object, to make the X11 library and GLX calls thread + safe. + \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other countries.} diff --git a/tests/auto/qpluginloader/elftest/.gitattributes b/tests/auto/qpluginloader/elftest/.gitattributes new file mode 100644 index 0000000..891192c --- /dev/null +++ b/tests/auto/qpluginloader/elftest/.gitattributes @@ -0,0 +1,10 @@ +corrupt1.elf64.so set -crlf -diff +corrupt2.elf64.so set -crlf -diff +corrupt3.elf64.so set -crlf -diff +debugobj.so set -crlf -diff +garbage1.so set -crlf -diff +garbage2.so set -crlf -diff +garbage3.so set -crlf -diff +garbage4.so set -crlf -diff +garbage5.so set -crlf -diff + diff --git a/tests/auto/qpluginloader/elftest/corrupt1.elf64.so b/tests/auto/qpluginloader/elftest/corrupt1.elf64.so Binary files differnew file mode 100755 index 0000000..12ce736 --- /dev/null +++ b/tests/auto/qpluginloader/elftest/corrupt1.elf64.so diff --git a/tests/auto/qpluginloader/elftest/corrupt2.elf64.so b/tests/auto/qpluginloader/elftest/corrupt2.elf64.so Binary files differnew file mode 100755 index 0000000..11fdc2c --- /dev/null +++ b/tests/auto/qpluginloader/elftest/corrupt2.elf64.so diff --git a/tests/auto/qpluginloader/elftest/corrupt3.elf64.so b/tests/auto/qpluginloader/elftest/corrupt3.elf64.so Binary files differnew file mode 100755 index 0000000..94a2bc3 --- /dev/null +++ b/tests/auto/qpluginloader/elftest/corrupt3.elf64.so diff --git a/tests/auto/qpluginloader/elftest/debugobj.so b/tests/auto/qpluginloader/elftest/debugobj.so Binary files differnew file mode 100644 index 0000000..f0ee056 --- /dev/null +++ b/tests/auto/qpluginloader/elftest/debugobj.so diff --git a/tests/auto/qpluginloader/elftest/garbage1.so b/tests/auto/qpluginloader/elftest/garbage1.so new file mode 100644 index 0000000..0c74530 --- /dev/null +++ b/tests/auto/qpluginloader/elftest/garbage1.so @@ -0,0 +1,4 @@ +pcdL+&&e= +oÒʎIٝmg]!Z +L')t +N(eP)Y8G 6-y"Zk4?^n5$Y=#y
\ No newline at end of file diff --git a/tests/auto/qpluginloader/elftest/garbage2.so b/tests/auto/qpluginloader/elftest/garbage2.so new file mode 100644 index 0000000..c06338e --- /dev/null +++ b/tests/auto/qpluginloader/elftest/garbage2.so @@ -0,0 +1 @@ +v.YtKW3
\ No newline at end of file diff --git a/tests/auto/qpluginloader/elftest/garbage3.so b/tests/auto/qpluginloader/elftest/garbage3.so new file mode 100644 index 0000000..a24c523 --- /dev/null +++ b/tests/auto/qpluginloader/elftest/garbage3.so @@ -0,0 +1 @@ +ȂT-ڥ 쾜i8_xIx=4@[BKS$
\ No newline at end of file diff --git a/tests/auto/qpluginloader/elftest/garbage4.so b/tests/auto/qpluginloader/elftest/garbage4.so new file mode 100644 index 0000000..4f45cf5 --- /dev/null +++ b/tests/auto/qpluginloader/elftest/garbage4.so @@ -0,0 +1 @@ +!\~Uu:9T+91QEǚxng5zh^t'mm*ˈdXH;vw+G
9L0!
\ No newline at end of file diff --git a/tests/auto/qpluginloader/elftest/garbage5.so b/tests/auto/qpluginloader/elftest/garbage5.so new file mode 100644 index 0000000..f8c0a1d --- /dev/null +++ b/tests/auto/qpluginloader/elftest/garbage5.so @@ -0,0 +1,2 @@ +Q +-9
\ No newline at end of file diff --git a/tests/auto/qpluginloader/tst/tst.pro b/tests/auto/qpluginloader/tst/tst.pro index 2d757e7..e270120 100644 --- a/tests/auto/qpluginloader/tst/tst.pro +++ b/tests/auto/qpluginloader/tst/tst.pro @@ -27,3 +27,5 @@ symbian: { DEPLOYMENT += libDep pluginDep } + +DEFINES += SRCDIR=\\\"$$PWD/../\\\" diff --git a/tests/auto/qpluginloader/tst_qpluginloader.cpp b/tests/auto/qpluginloader/tst_qpluginloader.cpp index 1e382b8..9374dd0 100644 --- a/tests/auto/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/qpluginloader/tst_qpluginloader.cpp @@ -100,6 +100,14 @@ # define PREFIX "lib" #endif +#ifdef QT_NO_DEBUG +# define QLIBRARY_AS_DEBUG false +#else +# define QLIBRARY_AS_DEBUG true +#endif + +// #define SHOW_ERRORS 1 + static QString sys_qualifiedLibraryName(const QString &fileName) { QString currDir = QDir::currentPath(); @@ -119,10 +127,14 @@ public: virtual ~tst_QPluginLoader(); private slots: + void clearPluginCache(); void errorString(); void loadHints(); void deleteinstanceOnUnload(); void checkingStubsFromDifferentDrives(); + void loadDebugObj(); + void loadCorruptElf(); + void loadGarbage(); }; tst_QPluginLoader::tst_QPluginLoader() @@ -134,7 +146,25 @@ tst_QPluginLoader::~tst_QPluginLoader() { } -//#define SHOW_ERRORS 1 +void tst_QPluginLoader::clearPluginCache() +{ + QString regkey = QString::fromLatin1("Qt Plugin Cache %1.%2.%3") + .arg((QT_VERSION & 0xff0000) >> 16) + .arg((QT_VERSION & 0xff00) >> 8) + .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false")); +#ifdef Q_WS_MAC +#if defined(__x86_64__) + regkey += QLatin1String("-x86_64"); +#elif defined(__i386__) + regkey += QLatin1String("-i386"); +#elif defined(__ppc64__) + regkey += QLatin1String("-ppc64"); +#elif defined(__ppc__) + regkey += QLatin1String("-ppc"); +#endif +#endif // Q_WS_MAC + QSettings(QSettings::UserScope, QLatin1String("Trolltech")).remove(regkey); +} void tst_QPluginLoader::errorString() { @@ -350,5 +380,54 @@ void tst_QPluginLoader::checkingStubsFromDifferentDrives() #endif//Q_OS_SYMBIAN } +void tst_QPluginLoader::loadDebugObj() +{ +#if defined (__ELF__) + QVERIFY(QFile::exists(SRCDIR "elftest/debugobj.so")); + QPluginLoader lib1(SRCDIR "elftest/debugobj.so"); + QCOMPARE(lib1.load(), false); +#endif +} + +void tst_QPluginLoader::loadCorruptElf() +{ +#if defined (__ELF__) +if (sizeof(void*) == 8) { + QVERIFY(QFile::exists(SRCDIR "elftest/corrupt1.elf64.so")); + + QPluginLoader lib1(SRCDIR "elftest/corrupt1.elf64.so"); + QCOMPARE(lib1.load(), false); + QVERIFY(lib1.errorString().contains("not an ELF object")); + + QPluginLoader lib2(SRCDIR "elftest/corrupt2.elf64.so"); + QCOMPARE(lib2.load(), false); + QVERIFY(lib2.errorString().contains("invalid")); + + QPluginLoader lib3(SRCDIR "elftest/corrupt3.elf64.so"); + QCOMPARE(lib3.load(), false); + QVERIFY(lib3.errorString().contains("invalid")); +} else if (sizeof(void*) == 4) { + QPluginLoader libW(SRCDIR "elftest/corrupt3.elf64.so"); + QCOMPARE(libW.load(), false); + QVERIFY(libW.errorString().contains("architecture")); +} else { + QFAIL("Please port QElfParser to this platform or blacklist this test."); +} +#endif +} + +void tst_QPluginLoader::loadGarbage() +{ +#if defined (Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + for (int i=0; i<5; i++) { + QPluginLoader lib(QString(SRCDIR "elftest/garbage%1.so").arg(i)); + QCOMPARE(lib.load(), false); +#ifdef SHOW_ERRORS + qDebug() << lib.errorString(); +#endif + } +#endif +} + QTEST_APPLESS_MAIN(tst_QPluginLoader) #include "tst_qpluginloader.moc" |