Re-add QPropertyAlias functionality

As QPropertyAlias was public by accident in 6.0, we have to ensure that
it still works in 6.2.
This re-adds some tests for it, and reimplements the unlinking
functionality. To avoid performance regressions in hot-paths,
a new unlink_fast function is added, which behaves like the old unlink:
It ignores the special handling for QPropertyAlias, so that we can skip
the tag check. It is only used in QPropertyObserverNodeProtector and
clearDependencyObservers, where we already know the type of the
observer.

Fixes: QTBUG-95846
Pick-to: 6.2
Change-Id: Ifb405b8327c4d61c673b1a912ed6e169d27c2d8f
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
bb10
Fabian Kosmale 2021-08-18 14:10:12 +02:00 committed by Ulf Hermann
parent 17357856b5
commit e4d62651c2
4 changed files with 57 additions and 12 deletions

View File

@ -558,9 +558,10 @@ QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
d.setChangeHandler(changeHandler);
}
QPropertyObserver::QPropertyObserver(QUntypedPropertyData *)
QPropertyObserver::QPropertyObserver(QUntypedPropertyData *data)
{
// ### Qt 7: Remove, currently left for binary compatibility
aliasData = data;
next.setTag(ObserverIsAlias);
}
/*! \internal
@ -609,16 +610,38 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
return *this;
}
#define UNLINK_COMMON \
if (ptr->next) \
ptr->next->prev = ptr->prev; \
if (ptr->prev) \
ptr->prev.setPointer(ptr->next.data()); \
ptr->next = nullptr; \
ptr->prev.clear();
/*!
\internal
Unlinks
*/
void QPropertyObserverPointer::unlink()
{
if (ptr->next)
ptr->next->prev = ptr->prev;
if (ptr->prev)
ptr->prev.setPointer(ptr->next.data());
ptr->next = nullptr;
ptr->prev.clear();
UNLINK_COMMON
if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias)
ptr->aliasData = nullptr;
}
/*!
\internal
Like unlink, but does not handle ObserverIsAlias.
Must only be called in places where we know that we are not dealing
with such an observer.
*/
void QPropertyObserverPointer::unlink_fast()
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias);
UNLINK_COMMON
}
#undef UNLINK_COMMON
void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
@ -666,7 +689,7 @@ struct [[nodiscard]] QPropertyObserverNodeProtector {
~QPropertyObserverNodeProtector() {
QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
d.unlink();
d.unlink_fast();
}
};
@ -721,6 +744,8 @@ void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
case QPropertyObserver::ObserverIsPlaceholder:
// recursion is already properly handled somewhere else
break;
case QPropertyObserver::ObserverIsAlias:
break;
default: Q_UNREACHABLE();
}
observer = next;

View File

@ -223,7 +223,8 @@ public:
enum ObserverTag {
ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed
ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
ObserverIsPlaceholder // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
ObserverIsAlias
};
protected:
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
@ -244,6 +245,7 @@ private:
union {
QPropertyBindingPrivate *binding = nullptr;
ChangeHandler changeHandler;
QUntypedPropertyData *aliasData;
};
};
@ -266,7 +268,7 @@ protected:
QUntypedPropertyData *aliasedProperty() const
{
return nullptr;
return aliasData;
}
private:

View File

@ -103,6 +103,7 @@ struct QPropertyObserverPointer
QPropertyObserver *ptr = nullptr;
void unlink();
void unlink_fast();
void setBindingToNotify(QPropertyBindingPrivate *binding);
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
@ -289,7 +290,7 @@ public:
void clearDependencyObservers() {
for (size_t i = 0; i < qMin(dependencyObserverCount, inlineDependencyObservers.size()); ++i) {
QPropertyObserverPointer p{&inlineDependencyObservers[i]};
p.unlink();
p.unlink_fast();
}
if (heapObservers)
heapObservers->clear();

View File

@ -115,6 +115,8 @@ private slots:
void bindableInterfaceOfCompatPropertyUsesSetter();
void selfBindingShouldNotCrash();
void qpropertyAlias();
};
void tst_QProperty::functorBinding()
@ -1856,6 +1858,21 @@ void tst_QProperty::selfBindingShouldNotCrash()
QVERIFY(i.binding().error().hasError());
}
void tst_QProperty::qpropertyAlias()
{
std::unique_ptr<QProperty<int>> i {new QProperty<int>};
QPropertyAlias<int> alias(i.get());
QVERIFY(alias.isValid());
alias.setValue(42);
QCOMPARE(i->value(), 42);
QProperty<int> j;
i->setBinding([&]() -> int { return j; });
j.setValue(42);
QCOMPARE(alias.value(), 42);
i.reset();
QVERIFY(!alias.isValid());
}
QTEST_MAIN(tst_QProperty);
#undef QT_SOURCE_LOCATION_NAMESPACE