Revision control
++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QPROPERTYPRIVATE_H
++ #define QPROPERTYPRIVATE_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 <QtCore/qglobal.h>
++ #include <QtCore/qtaggedpointer.h>
++ #include <QtCore/qmetatype.h>
++ #include <QtCore/qcontainerfwd.h>
++ #include <QtCore/qttypetraits.h>
++
++ #include <functional>
++
++ QT_BEGIN_NAMESPACE
++
++ class QBindingStorage;
++
++ template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
++ class QObjectCompatProperty;
++
++ class QPropertyBindingPrivatePtr;
++ using PendingBindingObserverList = QVarLengthArray<QPropertyBindingPrivatePtr>;
++
++ namespace QtPrivate {
++ // QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
++ // the constructor and copy constructor
++ struct RefCounted {
++
++ int refCount() const { return ref; }
++ void addRef() { ++ref; }
++ bool deref() { return --ref != 0; }
++
++ private:
++ int ref = 0;
++ };
++ }
++
++ class QQmlPropertyBinding;
++ class QPropertyBindingPrivate;
++ class QPropertyBindingPrivatePtr
++ {
++ public:
++ using T = QtPrivate::RefCounted;
++ T &operator*() const { return *d; }
++ T *operator->() noexcept { return d; }
++ T *operator->() const noexcept { return d; }
++ explicit operator T *() { return d; }
++ explicit operator const T *() const noexcept { return d; }
++ T *data() const noexcept { return d; }
++ T *get() const noexcept { return d; }
++ const T *constData() const noexcept { return d; }
++ T *take() noexcept { T *x = d; d = nullptr; return x; }
++
++ QPropertyBindingPrivatePtr() noexcept : d(nullptr) { }
++ ~QPropertyBindingPrivatePtr()
++ {
++ if (d && !d->deref())
++ destroyAndFreeMemory();
++ }
++ Q_CORE_EXPORT void destroyAndFreeMemory();
++
++ explicit QPropertyBindingPrivatePtr(T *data) noexcept : d(data) { if (d) d->addRef(); }
++ QPropertyBindingPrivatePtr(const QPropertyBindingPrivatePtr &o) noexcept
++ : d(o.d) { if (d) d->addRef(); }
++
++ void reset(T *ptr = nullptr) noexcept;
++
++ QPropertyBindingPrivatePtr &operator=(const QPropertyBindingPrivatePtr &o) noexcept
++ {
++ reset(o.d);
++ return *this;
++ }
++ QPropertyBindingPrivatePtr &operator=(T *o) noexcept
++ {
++ reset(o);
++ return *this;
++ }
++ QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
++ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPropertyBindingPrivatePtr)
++
++ operator bool () const noexcept { return d != nullptr; }
++ bool operator!() const noexcept { return d == nullptr; }
++
++ void swap(QPropertyBindingPrivatePtr &other) noexcept
++ { qt_ptr_swap(d, other.d); }
++
++ private:
++ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
++ const QPropertyBindingPrivatePtr &rhs) noexcept
++ { return lhs.d == rhs.d; }
++ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr)
++ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
++ const T *rhs) noexcept
++ { return lhs.d == rhs; }
++ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, T*)
++ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
++ std::nullptr_t) noexcept
++ { return !lhs; }
++ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, std::nullptr_t)
++
++ QtPrivate::RefCounted *d;
++ };
++
++ class QUntypedPropertyBinding;
++ class QPropertyBindingPrivate;
++ struct QPropertyBindingDataPointer;
++ class QPropertyObserver;
++ struct QPropertyObserverPointer;
++
++ class QUntypedPropertyData
++ {
++ };
++
++ namespace QtPrivate {
++ template <typename T>
++ using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>;
++ }
++
++ template <typename T>
++ class QPropertyData;
++
++ // Used for grouped property evaluations
++ namespace QtPrivate {
++ class QPropertyBindingData;
++ }
++ struct QPropertyDelayedNotifications;
++ struct QPropertyProxyBindingData
++ {
++ // acts as QPropertyBindingData::d_ptr
++ quintptr d_ptr;
++ /*
++ The two members below store the original binding data and property
++ data pointer of the property which gets proxied.
++ They are set in QPropertyDelayedNotifications::addProperty
++ */
++ const QtPrivate::QPropertyBindingData *originalBindingData;
++ QUntypedPropertyData *propertyData;
++ };
++
++ namespace QtPrivate {
++ struct BindingEvaluationState;
++
++ /* used in BindingFunctionVTable::createFor; on all other compilers, void would work, but on
++ MSVC this causes C2182 when compiling in C++20 mode. As we only need to provide some default
++ value which gets ignored, we introduce this dummy type.
++ */
++ struct MSVCWorkAround {};
++
++ struct BindingFunctionVTable
++ {
++ using CallFn = bool(*)(QMetaType, QUntypedPropertyData *, void *);
++ using DtorFn = void(*)(void *);
++ using MoveCtrFn = void(*)(void *, void *);
++ const CallFn call;
++ const DtorFn destroy;
++ const MoveCtrFn moveConstruct;
++ const qsizetype size;
++
++ template<typename Callable, typename PropertyType=MSVCWorkAround>
++ static constexpr BindingFunctionVTable createFor()
++ {
++ static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!");
++ return {
++ /*call=*/[](QMetaType metaType, QUntypedPropertyData *dataPtr, void *f){
++ if constexpr (!std::is_invocable_v<Callable>) {
++ // we got an untyped callable
++ static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> );
++ auto untypedEvaluationFunction = static_cast<Callable *>(f);
++ return std::invoke(*untypedEvaluationFunction, metaType, dataPtr);
++ } else if constexpr (!std::is_same_v<PropertyType, MSVCWorkAround>) {
++ Q_UNUSED(metaType);
++ QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr);
++ // That is allowed by POSIX even if Callable is a function pointer
++ auto evaluationFunction = static_cast<Callable *>(f);
++ PropertyType newValue = std::invoke(*evaluationFunction);
++ if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) {
++ if (newValue == propertyPtr->valueBypassingBindings())
++ return false;
++ }
++ propertyPtr->setValueBypassingBindings(std::move(newValue));
++ return true;
++ } else {
++ // Our code will never instantiate this
++ Q_UNREACHABLE_RETURN(false);
++ }
++ },
++ /*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); },
++ /*moveConstruct*/[](void *addr, void *other){
++ new (addr) Callable(std::move(*static_cast<Callable *>(other)));
++ },
++ /*size*/sizeof(Callable)
++ };
++ }
++ };
++
++ template<typename Callable, typename PropertyType=MSVCWorkAround>
++ inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>();
++
++
++ // writes binding result into dataPtr
++ struct QPropertyBindingFunction {
++ const QtPrivate::BindingFunctionVTable *vtable;
++ void *functor;
++ };
++
++ using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
++ using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction);
++
++ /*!
++ \internal
++ A property normally consists of the actual property value and metadata for the binding system.
++ QPropertyBindingData is the latter part. It stores a pointer to either
++ - a (potentially empty) linked list of notifiers, in case there is no binding set,
++ - an actual QUntypedPropertyBinding when the property has a binding,
++ - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update.
++
++ \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup
++ */
++ class Q_CORE_EXPORT QPropertyBindingData
++ {
++ // Mutable because the address of the observer of the currently evaluating binding is stored here, for
++ // notification later when the value changes.
++ mutable quintptr d_ptr = 0;
++ friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
++ friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
++ friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications);
++
++ template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
++ friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty);
++
++ Q_DISABLE_COPY(QPropertyBindingData)
++ public:
++ QPropertyBindingData() = default;
++ QPropertyBindingData(QPropertyBindingData &&other);
++ QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete;
++ ~QPropertyBindingData();
++
++ // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
++ static inline constexpr quintptr BindingBit = 0x1;
++ // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers?
++ static inline constexpr quintptr DelayedNotificationBit = 0x2;
++
++ bool hasBinding() const { return d_ptr & BindingBit; }
++ bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; }
++
++ QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
++ QUntypedPropertyData *propertyDataPtr,
++ QPropertyObserverCallback staticObserverCallback = nullptr,
++ QPropertyBindingWrapper bindingWrapper = nullptr);
++
++ QPropertyBindingPrivate *binding() const
++ {
++ quintptr dd = d();
++ if (dd & BindingBit)
++ return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit);
++ return nullptr;
++
++ }
++
++ void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused
++
++ void removeBinding()
++ {
++ if (hasBinding())
++ removeBinding_helper();
++ }
++
++ void registerWithCurrentlyEvaluatingBinding(QtPrivate::BindingEvaluationState *currentBinding) const
++ {
++ if (!currentBinding)
++ return;
++ registerWithCurrentlyEvaluatingBinding_helper(currentBinding);
++ }
++ void registerWithCurrentlyEvaluatingBinding() const;
++ void notifyObservers(QUntypedPropertyData *propertyDataPtr) const;
++ void notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const;
++ private:
++ /*!
++ \internal
++ Returns a reference to d_ptr, except when d_ptr points to a proxy.
++ In that case, a reference to proxy->d_ptr is returned instead.
++
++ To properly support proxying, direct access to d_ptr only occurs when
++ - a function actually deals with proxying (e.g.
++ QPropertyDelayedNotifications::addProperty),
++ - only the tag value is accessed (e.g. hasBinding) or
++ - inside a constructor.
++ */
++ quintptr &d_ref() const
++ {
++ quintptr &d = d_ptr;
++ if (isNotificationDelayed())
++ return proxyData()->d_ptr;
++ return d;
++ }
++ quintptr d() const { return d_ref(); }
++ QPropertyProxyBindingData *proxyData() const
++ {
++ Q_ASSERT(isNotificationDelayed());
++ return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit));
++ }
++ void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
++ void removeBinding_helper();
++
++ enum NotificationResult { Delayed, Evaluated };
++ NotificationResult notifyObserver_helper(
++ QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
++ QPropertyObserverPointer observer,
++ PendingBindingObserverList &bindingObservers) const;
++ };
++
++ template <typename T, typename Tag>
++ class QTagPreservingPointerToPointer
++ {
++ public:
++ constexpr QTagPreservingPointerToPointer() = default;
++
++ QTagPreservingPointerToPointer(T **ptr)
++ : d(reinterpret_cast<quintptr*>(ptr))
++ {}
++
++ QTagPreservingPointerToPointer<T, Tag> &operator=(T **ptr)
++ {
++ d = reinterpret_cast<quintptr *>(ptr);
++ return *this;
++ }
++
++ QTagPreservingPointerToPointer<T, Tag> &operator=(QTaggedPointer<T, Tag> *ptr)
++ {
++ d = reinterpret_cast<quintptr *>(ptr);
++ return *this;
++ }
++
++ void clear()
++ {
++ d = nullptr;
++ }
++
++ void setPointer(T *ptr)
++ {
++ *d = reinterpret_cast<quintptr>(ptr) | (*d & QTaggedPointer<T, Tag>::tagMask());
++ }
++
++ T *get() const
++ {
++ return reinterpret_cast<T*>(*d & QTaggedPointer<T, Tag>::pointerMask());
++ }
++
++ explicit operator bool() const
++ {
++ return d != nullptr;
++ }
++
++ private:
++ quintptr *d = nullptr;
++ };
++
++ namespace detail {
++ template <typename F>
++ struct ExtractClassFromFunctionPointer;
++
++ template<typename T, typename C>
++ struct ExtractClassFromFunctionPointer<T C::*> { using Class = C; };
++
++ constexpr size_t getOffset(size_t o)
++ {
++ return o;
++ }
++ constexpr size_t getOffset(size_t (*offsetFn)())
++ {
++ return offsetFn();
++ }
++ }
++
++ } // namespace QtPrivate
++
++ QT_END_NAMESPACE
++
++ #endif // QPROPERTYPRIVATE_H