QStateMachine: replace a QHash key involving a QPointer
Using QPointers (or any type that makes a QPointer part of its identity) as a key in any associative container is wrong. They get externally set to nullptr, violating the associative container's class invariants, which could lead to data corruption, even though bucket-based hash implementations are less susceptible than binary trees. To fix, write a new class that acts much like the old QPair<QPointer<>,QByteArray>, but uses the QPointer only as a guard, not as part of its identity. To preseve identity, also saves the naked pointer originally passed and uses that for op== and qHash(). Change-Id: I4fa5a6bf86bad8fe7f5abe53d7c7f3ad3754d8d6 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
65eb573926
commit
3b9629e8bd
@ -947,14 +947,15 @@ QList<QPropertyAssignment> QStateMachinePrivate::restorablesToPropertyList(const
|
||||
QList<QPropertyAssignment> result;
|
||||
QHash<RestorableId, QVariant>::const_iterator it;
|
||||
for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) {
|
||||
if (!it.key().first) {
|
||||
const RestorableId &id = it.key();
|
||||
if (!id.object()) {
|
||||
// Property object was deleted
|
||||
continue;
|
||||
}
|
||||
#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
|
||||
qDebug() << q_func() << ": restoring" << it.key().first << it.key().second << "to" << it.value();
|
||||
qDebug() << q_func() << ": restoring" << id.object() << id.proertyName() << "to" << it.value();
|
||||
#endif
|
||||
result.append(QPropertyAssignment(it.key().first, it.key().second, it.value(), /*explicitlySet=*/false));
|
||||
result.append(QPropertyAssignment(id.object(), id.propertyName(), it.value(), /*explicitlySet=*/false));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -189,7 +189,24 @@ public:
|
||||
void cancelAllDelayedEvents();
|
||||
|
||||
#ifndef QT_NO_PROPERTIES
|
||||
typedef QPair<QPointer<QObject>, QByteArray> RestorableId;
|
||||
class RestorableId {
|
||||
QPointer<QObject> guard;
|
||||
QObject *obj;
|
||||
QByteArray prop;
|
||||
// two overloads because friends can't have default arguments
|
||||
friend uint qHash(const RestorableId &key, uint seed) Q_DECL_NOEXCEPT_EXPR(noexcept(std::declval<QByteArray>()))
|
||||
{ return qHash(qMakePair(key.obj, key.prop), seed); }
|
||||
friend uint qHash(const RestorableId &key) Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(key, 0U)))
|
||||
{ return qHash(key, 0U); }
|
||||
friend bool operator==(const RestorableId &lhs, const RestorableId &rhs) Q_DECL_NOTHROW
|
||||
{ return lhs.obj == rhs.obj && lhs.prop == rhs.prop; }
|
||||
friend bool operator!=(const RestorableId &lhs, const RestorableId &rhs) Q_DECL_NOTHROW
|
||||
{ return !operator==(lhs, rhs); }
|
||||
public:
|
||||
explicit RestorableId(QObject *o, QByteArray p) Q_DECL_NOTHROW : guard(o), obj(o), prop(qMove(p)) {}
|
||||
QObject *object() const Q_DECL_NOTHROW { return guard; }
|
||||
QByteArray propertyName() const Q_DECL_NOTHROW { return prop; }
|
||||
};
|
||||
QHash<QAbstractState*, QHash<RestorableId, QVariant> > registeredRestorablesForState;
|
||||
bool hasRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName) const;
|
||||
QVariant savedValueForRestorable(const QList<QAbstractState*> &exitedStates_sorted,
|
||||
|
Loading…
x
Reference in New Issue
Block a user